diff options
Diffstat (limited to 'includes')
121 files changed, 574 insertions, 604 deletions
diff --git a/includes/.htaccess b/includes/.htaccess index b66e80882967..2e5c00314d2f 100644 --- a/includes/.htaccess +++ b/includes/.htaccess @@ -1 +1,2 @@ Require all denied +Satisfy All diff --git a/includes/DomainEvent/DomainEvent.php b/includes/DomainEvent/DomainEvent.php index ec8e5399b002..cb70bd604c12 100644 --- a/includes/DomainEvent/DomainEvent.php +++ b/includes/DomainEvent/DomainEvent.php @@ -26,7 +26,6 @@ use Wikimedia\Timestamp\ConvertibleTimestamp; * @note Subclasses must call declareEventType() in their constructor! * * @since 1.44 - * @unstable until 1.45, should become stable to extend */ abstract class DomainEvent { diff --git a/includes/DomainEvent/DomainEventDispatcher.php b/includes/DomainEvent/DomainEventDispatcher.php index 9bae3d17f807..7a8a77b8fd12 100644 --- a/includes/DomainEvent/DomainEventDispatcher.php +++ b/includes/DomainEvent/DomainEventDispatcher.php @@ -7,7 +7,6 @@ use Wikimedia\Rdbms\IConnectionProvider; * Service for sending domain events to registered listeners. * * @since 1.44 - * @unstable until 1.45 */ interface DomainEventDispatcher { diff --git a/includes/DomainEvent/EventIngressBase.php b/includes/DomainEvent/DomainEventIngress.php index fb4d65ee0002..f069914e8d99 100644 --- a/includes/DomainEvent/EventIngressBase.php +++ b/includes/DomainEvent/DomainEventIngress.php @@ -13,13 +13,13 @@ use LogicException; * which event should trigger which logic in the component and for mapping from * the model used by the emitter of the event to the component's own model. * - * EventIngressBase implements InitializableDomainEventSubscriber so it can be + * DomainEventIngress implements InitializableDomainEventSubscriber so it can be * registered with and initialized by a DomainEventSource. Registration is * typically done in the form of an object spec for lazy instantiation. For * extensions' ingress objects that object spec can be provided in the * DomainEventSubscribers section of extension.json. * - * After instantiating a subscriber (typically a subclass of EventIngressBase), + * After instantiating a subscriber (typically a subclass of DomainEventIngress), * the event source will call initSubscriber() to initialize the subscriber and * then registerListeners() to allow the subscriber to register listeners for * the events it is interested in. @@ -37,9 +37,8 @@ use LogicException; * in extension.json. * * @since 1.44 - * @unstable until 1.45, should become stable to extend */ -abstract class EventIngressBase implements InitializableDomainEventSubscriber { +abstract class DomainEventIngress implements InitializableDomainEventSubscriber { /** * @var string[] @@ -153,7 +152,7 @@ abstract class EventIngressBase implements InitializableDomainEventSubscriber { if ( !$this->eventTypes ) { throw new LogicException( - 'Subclassed of EventIngressBase must either override ' . + 'Subclassed of DomainEventIngress must either override ' . 'registerListeners or provide a list of event types via ' . 'initSubscriber() or initEvents().' ); @@ -167,4 +166,6 @@ abstract class EventIngressBase implements InitializableDomainEventSubscriber { } /** @deprecated temporary alias, remove before 1.44 release (T389033) */ -class_alias( EventIngressBase::class, 'MediaWiki\DomainEvent\EventSubscriberBase' ); +class_alias( DomainEventIngress::class, 'MediaWiki\DomainEvent\EventSubscriberBase' ); +/** @deprecated temporary alias, remove before 1.44 release (T389033) */ +class_alias( DomainEventIngress::class, 'MediaWiki\DomainEvent\EventIngressBase' ); diff --git a/includes/DomainEvent/DomainEventSource.php b/includes/DomainEvent/DomainEventSource.php index d440705f528f..2b8b2e5a2a8f 100644 --- a/includes/DomainEvent/DomainEventSource.php +++ b/includes/DomainEvent/DomainEventSource.php @@ -5,7 +5,6 @@ namespace MediaWiki\DomainEvent; * Service object for registering listeners for domain events. * * @since 1.44 - * @unstable until 1.45 */ interface DomainEventSource { diff --git a/includes/DomainEvent/DomainEventSubscriber.php b/includes/DomainEvent/DomainEventSubscriber.php index 93778de5a000..6009bcee9b04 100644 --- a/includes/DomainEvent/DomainEventSubscriber.php +++ b/includes/DomainEvent/DomainEventSubscriber.php @@ -7,9 +7,8 @@ namespace MediaWiki\DomainEvent; * related event listeners. * * @since 1.44 - * @stable to type * @note Extensions should not implement this interface directly but should - * extend EventIngressBase. + * extend DomainEventIngress. */ interface DomainEventSubscriber { diff --git a/includes/DomainEvent/InitializableDomainEventSubscriber.php b/includes/DomainEvent/InitializableDomainEventSubscriber.php index 64fdff11d0ba..17f09f662fd9 100644 --- a/includes/DomainEvent/InitializableDomainEventSubscriber.php +++ b/includes/DomainEvent/InitializableDomainEventSubscriber.php @@ -8,7 +8,6 @@ namespace MediaWiki\DomainEvent; * * This is useful when constructing an DomainEventSubscriber from an object spec. * - * @since 1.44 * @internal for use by DomainEventSubscriber */ interface InitializableDomainEventSubscriber extends DomainEventSubscriber { diff --git a/includes/Output/OutputPage.php b/includes/Output/OutputPage.php index 0a33bb608ccd..80fd513cc61b 100644 --- a/includes/Output/OutputPage.php +++ b/includes/Output/OutputPage.php @@ -2102,6 +2102,7 @@ class OutputPage extends ContextSource { * @deprecated since 1.44, use ::getMetadata()->setRevisionTimestamp(...) */ public function setRevisionTimestamp( $timestamp ) { + wfDeprecated( __METHOD__, '1.44' ); $previousValue = $this->metadata->getRevisionTimestamp(); $this->metadata->setRevisionTimestamp( $timestamp ); return $previousValue; diff --git a/includes/ResourceLoader/ResourceLoaderEventIngress.php b/includes/ResourceLoader/ResourceLoaderEventIngress.php index aa1711bcff29..2ae9800afddd 100644 --- a/includes/ResourceLoader/ResourceLoaderEventIngress.php +++ b/includes/ResourceLoader/ResourceLoaderEventIngress.php @@ -2,7 +2,7 @@ namespace MediaWiki\ResourceLoader; -use MediaWiki\DomainEvent\EventIngressBase; +use MediaWiki\DomainEvent\DomainEventIngress; use MediaWiki\Page\Event\PageDeletedEvent; use MediaWiki\Page\Event\PageDeletedListener; use MediaWiki\Page\Event\PageRevisionUpdatedEvent; @@ -18,7 +18,7 @@ use Wikimedia\Rdbms\LBFactory; * @internal */ class ResourceLoaderEventIngress - extends EventIngressBase + extends DomainEventIngress implements PageRevisionUpdatedListener, PageDeletedListener { diff --git a/includes/Rest/Handler/CompareHandler.php b/includes/Rest/Handler/CompareHandler.php index 2829f9f6a4cd..ce8267f8e454 100644 --- a/includes/Rest/Handler/CompareHandler.php +++ b/includes/Rest/Handler/CompareHandler.php @@ -111,11 +111,11 @@ class CompareHandler extends Handler { return $rev->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ); } - private function getRole() { + private function getRole(): string { return SlotRecord::MAIN; } - private function getRevisionText( $paramName ) { + private function getRevisionText( string $paramName ): string { if ( !isset( $this->textCache[$paramName] ) ) { $revision = $this->getRevision( $paramName ); try { diff --git a/includes/Rest/Handler/DiscoveryHandler.php b/includes/Rest/Handler/DiscoveryHandler.php index 04fc989269ce..0a80c0b61b37 100644 --- a/includes/Rest/Handler/DiscoveryHandler.php +++ b/includes/Rest/Handler/DiscoveryHandler.php @@ -69,7 +69,7 @@ class DiscoveryHandler extends Handler { ]; } - private function getInfoSpec() { + private function getInfoSpec(): array { return [ 'title' => $this->options->get( MainConfigNames::Sitename ), 'mediawiki' => MW_VERSION, diff --git a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php index bd0c794e142c..9a5f844d92d1 100644 --- a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php +++ b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php @@ -294,18 +294,6 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper { } /** - * Controls how the parser cache is used. - * - * @param bool $read Whether we should look for cached output before parsing - * @param bool $write Whether we should cache output after parsing - */ - public function setUseParserCache( bool $read, bool $write ) { - $this->parserOutputAccessOptions = - ( $read ? 0 : ParserOutputAccess::OPT_FORCE_PARSE ) | - ( $write ? 0 : ParserOutputAccess::OPT_NO_UPDATE_CACHE ); - } - - /** * Determine whether stashing should be applied. * * @param bool $stash diff --git a/includes/Rest/Handler/Helper/RestStatusTrait.php b/includes/Rest/Handler/Helper/RestStatusTrait.php index ec0257920430..1d19309b8cd3 100644 --- a/includes/Rest/Handler/Helper/RestStatusTrait.php +++ b/includes/Rest/Handler/Helper/RestStatusTrait.php @@ -37,7 +37,7 @@ trait RestStatusTrait { throw new LocalizedHttpException( $msg, $code, $data ); } - private function getStatusErrorKeys( StatusValue $status ) { + private function getStatusErrorKeys( StatusValue $status ): array { $keys = []; foreach ( $status->getMessages() as $msg ) { diff --git a/includes/Rest/Handler/LanguageLinksHandler.php b/includes/Rest/Handler/LanguageLinksHandler.php index fd63c7a5c948..400f9cd1eb40 100644 --- a/includes/Rest/Handler/LanguageLinksHandler.php +++ b/includes/Rest/Handler/LanguageLinksHandler.php @@ -122,7 +122,7 @@ class LanguageLinksHandler extends SimpleHandler { ->createJson( $this->fetchLinks( $page->getId() ) ); } - private function fetchLinks( $pageId ) { + private function fetchLinks( int $pageId ): array { $result = []; $res = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder() ->select( [ 'll_title', 'll_lang' ] ) diff --git a/includes/Rest/Handler/ModuleSpecHandler.php b/includes/Rest/Handler/ModuleSpecHandler.php index 9025815c34b4..07ba328ea267 100644 --- a/includes/Rest/Handler/ModuleSpecHandler.php +++ b/includes/Rest/Handler/ModuleSpecHandler.php @@ -144,7 +144,7 @@ class ModuleSpecHandler extends SimpleHandler { return $operationSpec; } - private function getComponentsSpec( Module $module ) { + private function getComponentsSpec( Module $module ): array { $components = []; // XXX: also collect reusable components from handler specs (but how to avoid name collisions?). diff --git a/includes/Rest/Handler/PageHistoryCountHandler.php b/includes/Rest/Handler/PageHistoryCountHandler.php index 4c8c3b6e8dae..e699ff859806 100644 --- a/includes/Rest/Handler/PageHistoryCountHandler.php +++ b/includes/Rest/Handler/PageHistoryCountHandler.php @@ -108,7 +108,7 @@ class PageHistoryCountHandler extends SimpleHandler { ); } - private function normalizeType( $type ) { + private function normalizeType( string $type ): string { return self::DEPRECATED_COUNT_TYPES[$type] ?? $type; } diff --git a/includes/Rest/Handler/ParsoidHandler.php b/includes/Rest/Handler/ParsoidHandler.php index 092cf92f0894..5729dd1b4bc8 100644 --- a/includes/Rest/Handler/ParsoidHandler.php +++ b/includes/Rest/Handler/ParsoidHandler.php @@ -648,7 +648,7 @@ abstract class ParsoidHandler extends Handler { private function wtLint( PageConfig $pageConfig, array $attribs, ?array $linterOverrides = [] - ) { + ): array { $envOptions = $attribs['envOptions'] + [ 'linterOverrides' => $linterOverrides, 'offsetType' => $attribs['offsetType'], diff --git a/includes/Rest/Router.php b/includes/Rest/Router.php index ff2497d6d10b..6960a07f8db5 100644 --- a/includes/Rest/Router.php +++ b/includes/Rest/Router.php @@ -338,7 +338,7 @@ class Router { return $this->moduleMap; } - private function getModuleInfo( $module ): ?array { + private function getModuleInfo( string $module ): ?array { $map = $this->getModuleMap(); return $map[$module] ?? null; } diff --git a/includes/Rest/i18n/ar.json b/includes/Rest/i18n/ar.json index db780db3ae9f..7737778374e2 100644 --- a/includes/Rest/i18n/ar.json +++ b/includes/Rest/i18n/ar.json @@ -141,5 +141,6 @@ "rest-schema-desc-mock-desc": "وصف وهمي.", "rest-schema-desc-revision-metadata": "مراجعة بيانات التعريف", "rest-schema-desc-media-file": "معلومات حول الملف", + "rest-schema-desc-page-history": "تاريخ مراجعة الصفحة", "rest-schema-desc-search-results": "نتائج البحث" } diff --git a/includes/Rest/i18n/gl.json b/includes/Rest/i18n/gl.json index 54c07b691cca..ee2bdc9ab2eb 100644 --- a/includes/Rest/i18n/gl.json +++ b/includes/Rest/i18n/gl.json @@ -87,6 +87,14 @@ "rest-unsupported-language-conversion": "Conversión de lingua non compatible: $1 a $2", "rest-unknown-content-model": "Modelo de contido descoñecido: $1", "rest-page-bundle-validation-error": "PageBundle non coincide con contentVersion: $1", + "rest-module": "Módulo", + "rest-module-default": "Módulo predeterminado", + "rest-module-extra-routes-title": "API REST de MediaWiki", + "rest-module-extra-routes-desc": "Extremos REST non asociados a ningún módulo", + "rest-module-specs.v0-title": "Especificacións", + "rest-module-specs.v0-desc": "Módulo de autodocumentación que proporciona descubrimento, especificacións e esquemas para todos os módulos dispoñibles.", + "rest-module-content.v1-title": "Contido da páxina", + "rest-module-content.v1-desc": "Ofrece acceso ao contido e metadatos das páxinas e revisións", "rest-param-desc-revision-id": "Identificador da revisión", "rest-param-desc-source": "Contido da páxina no formato especificado pola propiedade content_model" } diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index f82bb3598935..68ec604820dd 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -2793,9 +2793,6 @@ return [ ), LoggerFactory::getProvider(), - // UserBlockConstraint - $services->getPermissionManager(), - // EditFilterMergedContentHookConstraint $services->getHookContainer(), @@ -2805,7 +2802,7 @@ return [ // SpamRegexConstraint $services->getSpamChecker(), - // UserRateLimitConstraint + // LinkPurgeRateLimitConstraint $services->getRateLimiter() ); }, diff --git a/includes/Storage/DerivedPageDataUpdater.php b/includes/Storage/DerivedPageDataUpdater.php index e8e596230585..83d68b309664 100644 --- a/includes/Storage/DerivedPageDataUpdater.php +++ b/includes/Storage/DerivedPageDataUpdater.php @@ -26,7 +26,6 @@ use MediaWiki\ChangeTags\ChangeTags; use MediaWiki\ChangeTags\ChangeTagsStore; use MediaWiki\Config\ServiceOptions; use MediaWiki\Content\Content; -use MediaWiki\Content\ContentHandler; use MediaWiki\Content\IContentHandlerFactory; use MediaWiki\Content\Transform\ContentTransformer; use MediaWiki\Deferred\DeferrableUpdate; @@ -36,11 +35,9 @@ use MediaWiki\Deferred\RefreshSecondaryDataUpdate; use MediaWiki\Deferred\SiteStatsUpdate; use MediaWiki\DomainEvent\DomainEventDispatcher; use MediaWiki\Edit\PreparedEdit; -use MediaWiki\Exception\MWUnknownContentModelException; use MediaWiki\HookContainer\HookContainer; use MediaWiki\HookContainer\HookRunner; use MediaWiki\JobQueue\JobQueueGroup; -use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob; use MediaWiki\JobQueue\Jobs\ParsoidCachePrewarmJob; use MediaWiki\Language\Language; use MediaWiki\MainConfigNames; @@ -160,11 +157,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { private $articleCountMethod; /** - * @var bool see $wgRCWatchCategoryMembership - */ - private $rcWatchCategoryMembership = false; - - /** * Stores (most of) the $options parameter of prepareUpdate(). * @see prepareUpdate() * @@ -535,14 +527,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { } /** - * @param bool $rcWatchCategoryMembership - * @see $wgRCWatchCategoryMembership - */ - public function setRcWatchCategoryMembership( $rcWatchCategoryMembership ) { - $this->rcWatchCategoryMembership = $rcWatchCategoryMembership; - } - - /** * @return Title */ private function getTitle() { @@ -738,16 +722,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { return $this->getRawSlot( $role )->getContent(); } - /** - * @param string $role slot role name - * @return ContentHandler - * @throws MWUnknownContentModelException - */ - private function getContentHandler( $role ): ContentHandler { - return $this->contentHandlerFactory - ->getContentHandler( $this->getRawSlot( $role )->getModel() ); - } - private function usePrimary(): bool { // TODO: can we just set a flag to true in prepareContent()? return $this->wikiPage->wasLoadedFrom( IDBAccessObject::READ_LATEST ); @@ -1591,24 +1565,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { 'defer' => DeferredUpdates::POSTSEND ] ); - // TODO: MCR: check if *any* changed slot supports categories! - if ( $this->rcWatchCategoryMembership - && $this->getContentHandler( SlotRecord::MAIN )->supportsCategories() === true - && $event->isNominalContentChange() - && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE ) - ) { - // Note: jobs are pushed after deferred updates, so the job should be able to see - // the recent change entry (also done via deferred updates) and carry over any - // bot/deletion/IP flags, ect. - $this->jobQueueGroup->lazyPush( - CategoryMembershipChangeJob::newSpec( - $this->getTitle(), - $this->revision->getTimestamp(), - $event->hasCause( PageUpdater::CAUSE_IMPORT ) - ) - ); - } - $id = $this->getPageId(); $title = $this->getTitle(); $wikiPage = $this->getWikiPage(); diff --git a/includes/Storage/PageUpdaterFactory.php b/includes/Storage/PageUpdaterFactory.php index 7bb77215d916..108bd2e3e8be 100644 --- a/includes/Storage/PageUpdaterFactory.php +++ b/includes/Storage/PageUpdaterFactory.php @@ -304,9 +304,6 @@ class PageUpdaterFactory { $derivedDataUpdater->setLogger( $this->logger ); $derivedDataUpdater->setArticleCountMethod( $this->options->get( MainConfigNames::ArticleCountMethod ) ); - $derivedDataUpdater->setRcWatchCategoryMembership( - $this->options->get( MainConfigNames::RCWatchCategoryMembership ) - ); return $derivedDataUpdater; } diff --git a/includes/api/ApiComparePages.php b/includes/api/ApiComparePages.php index e2ec95843ba5..334765d06b1d 100644 --- a/includes/api/ApiComparePages.php +++ b/includes/api/ApiComparePages.php @@ -38,6 +38,7 @@ use MediaWiki\Revision\SlotRoleRegistry; use MediaWiki\Title\Title; use MediaWiki\User\TempUser\TempUserCreator; use MediaWiki\User\UserFactory; +use MediaWiki\User\UserIdentity; use Wikimedia\ParamValidator\ParamValidator; use Wikimedia\RequestTimeout\TimeoutException; @@ -671,7 +672,7 @@ class ApiComparePages extends ApiBase { } } - private function getUserForPreview() { + private function getUserForPreview(): UserIdentity { $user = $this->getUser(); if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) { return $this->userFactory->newUnsavedTempUser( diff --git a/includes/api/ApiHelpParamValueMessage.php b/includes/api/ApiHelpParamValueMessage.php index 6949af8887a1..695cc7063d97 100644 --- a/includes/api/ApiHelpParamValueMessage.php +++ b/includes/api/ApiHelpParamValueMessage.php @@ -128,7 +128,7 @@ class ApiHelpParamValueMessage extends Message { return $this->message; } - private function subMessage( $key ) { + private function subMessage( string $key ): string { $msg = new Message( $key ); $msg->isInterface = $this->isInterface; $msg->language = $this->language; diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php index a1ccea47fedf..e3ab7a3e35f7 100644 --- a/includes/api/ApiOptions.php +++ b/includes/api/ApiOptions.php @@ -85,7 +85,7 @@ class ApiOptions extends ApiOptionsBase { ); } - private function getGlobalParam() { + private function getGlobalParam(): string { return $this->extractRequestParams()['global']; } diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index 5d5bd20848aa..f147178055cf 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -59,6 +59,7 @@ use MediaWiki\Title\TitleFormatter; use MediaWiki\Title\TitleValue; use MediaWiki\User\TempUser\TempUserCreator; use MediaWiki\User\UserFactory; +use MediaWiki\User\UserIdentity; use MediaWiki\Utils\UrlUtils; use MediaWiki\WikiMap\WikiMap; use Wikimedia\ParamValidator\ParamValidator; @@ -150,7 +151,7 @@ class ApiParse extends ApiBase { PageReference $page, ?RevisionRecord $revision, ParserOptions $popts - ) { + ): ParserOutput { $worker = new PoolCounterWorkViaCallback( 'ApiParser', $this->getPoolKey(), [ 'doWork' => function () use ( $content, $page, $revision, $popts ) { @@ -166,7 +167,7 @@ class ApiParse extends ApiBase { return $worker->execute(); } - private function getUserForPreview() { + private function getUserForPreview(): UserIdentity { $user = $this->getUser(); if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) { return $this->userFactory->newUnsavedTempUser( @@ -178,7 +179,7 @@ class ApiParse extends ApiBase { private function getPageParserOutput( WikiPage $page, - $revId, + ?int $revId, ParserOptions $popts, bool $suppressCache ) { @@ -974,7 +975,7 @@ class ApiParse extends ApiBase { return $result; } - private function formatCategoryLinks( $links ) { + private function formatCategoryLinks( array $links ): array { $result = []; if ( !$links ) { @@ -1038,7 +1039,7 @@ class ApiParse extends ApiBase { return $result; } - private function formatIWLinks( $iw ) { + private function formatIWLinks( array $iw ): array { $result = []; foreach ( $iw as $linkTarget ) { $entry = []; @@ -1055,7 +1056,7 @@ class ApiParse extends ApiBase { return $result; } - private function formatHeadItems( $headItems ) { + private function formatHeadItems( array $headItems ): array { $result = []; foreach ( $headItems as $tag => $content ) { $entry = []; @@ -1067,7 +1068,7 @@ class ApiParse extends ApiBase { return $result; } - private function formatLimitReportData( $limitReportData ) { + private function formatLimitReportData( array $limitReportData ): array { $result = []; foreach ( $limitReportData as $name => $value ) { @@ -1084,7 +1085,7 @@ class ApiParse extends ApiBase { return $result; } - private function setIndexedTagNames( &$array, $mapping ) { + private function setIndexedTagNames( array &$array, array $mapping ) { foreach ( $mapping as $key => $name ) { if ( isset( $array[$key] ) ) { ApiResult::setIndexedTagName( $array[$key], $name ); diff --git a/includes/api/ApiQueryBacklinksprop.php b/includes/api/ApiQueryBacklinksprop.php index 819a60896428..342972254908 100644 --- a/includes/api/ApiQueryBacklinksprop.php +++ b/includes/api/ApiQueryBacklinksprop.php @@ -359,7 +359,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase { } } - private function setContinue( $row, $sortby ) { + private function setContinue( \stdClass $row, array $sortby ) { $cont = []; foreach ( $sortby as $field => $v ) { $cont[] = $row->$field; diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php index ee28e4012cce..685ab9524884 100644 --- a/includes/api/ApiQueryExtLinksUsage.php +++ b/includes/api/ApiQueryExtLinksUsage.php @@ -190,7 +190,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { } } - private function setContinue( $orderBy, $row ) { + private function setContinue( array $orderBy, \stdClass $row ) { $fields = []; foreach ( $orderBy as $field ) { $fields[] = strtr( $row->$field, [ '%' => '%25', '|' => '%7C' ] ); diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php index 9c47efba6342..67cb161a2582 100644 --- a/includes/api/ApiQueryExternalLinks.php +++ b/includes/api/ApiQueryExternalLinks.php @@ -127,7 +127,7 @@ class ApiQueryExternalLinks extends ApiQueryBase { } } - private function setContinue( $orderBy, $row ) { + private function setContinue( array $orderBy, \stdClass $row ) { $fields = []; foreach ( $orderBy as $field ) { $fields[] = strtr( $row->$field, [ '%' => '%25', '|' => '%7C' ] ); diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index 9382d43652c7..b5bb5bcd9bcc 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -845,7 +845,7 @@ class ApiQueryInfo extends ApiQueryBase { } } - private function getAllVariants( $text, $ns = NS_MAIN ) { + private function getAllVariants( string $text, int $ns = NS_MAIN ): array { $result = []; foreach ( $this->languageConverter->getVariants() as $variant ) { $convertTitle = $this->languageConverter->autoConvert( $text, $variant ); diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index 8d9bdfb92d9c..3d0f42aa303c 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -328,7 +328,7 @@ class ApiQueryLogEvents extends ApiQueryBase { $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'item' ); } - private function extractRowInfo( $row ) { + private function extractRowInfo( \stdClass $row ): array { $logEntry = DatabaseLogEntry::newFromRow( $row ); $vals = [ ApiResult::META_TYPE => 'assoc', diff --git a/includes/api/ApiQueryRevisionsBase.php b/includes/api/ApiQueryRevisionsBase.php index 2e246b0519b1..7f525d836aeb 100644 --- a/includes/api/ApiQueryRevisionsBase.php +++ b/includes/api/ApiQueryRevisionsBase.php @@ -43,6 +43,7 @@ use MediaWiki\Revision\SlotRoleRegistry; use MediaWiki\Title\Title; use MediaWiki\User\TempUser\TempUserCreator; use MediaWiki\User\UserFactory; +use MediaWiki\User\UserIdentity; use MediaWiki\User\UserNameUtils; use stdClass; use Wikimedia\ParamValidator\ParamValidator; @@ -788,7 +789,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { return $vals; } - private function getUserForPreview() { + private function getUserForPreview(): UserIdentity { $user = $this->getUser(); if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) { return $this->userFactory->newUnsavedTempUser( diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index a5007c560704..771d30bce773 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -950,7 +950,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { return $this->getResult()->addValue( 'query', $property, $config ); } - private function getAutoPromoteConds() { + private function getAutoPromoteConds(): array { $allowedConditions = []; foreach ( get_defined_constants() as $constantName => $constantValue ) { if ( strpos( $constantName, 'APCOND_' ) !== false ) { @@ -960,7 +960,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { return $allowedConditions; } - private function processAutoPromote( $input, $allowedConditions ) { + private function processAutoPromote( array $input, array $allowedConditions ): array { $data = []; foreach ( $input as $groupName => $conditions ) { $row = $this->recAutopromote( $conditions, $allowedConditions ); @@ -972,7 +972,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { return $data; } - private function appendAutoPromote( $property ) { + private function appendAutoPromote( string $property ): bool { return $this->getResult()->addValue( 'query', $property, @@ -983,7 +983,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { ); } - private function appendAutoPromoteOnce( $property ) { + private function appendAutoPromoteOnce( string $property ): bool { $allowedConditions = $this->getAutoPromoteConds(); $data = []; foreach ( $this->getConfig()->get( MainConfigNames::AutopromoteOnce ) as $key => $value ) { diff --git a/includes/api/ApiQueryUserContribs.php b/includes/api/ApiQueryUserContribs.php index 3ac4bc5b234f..9dced1c324f1 100644 --- a/includes/api/ApiQueryUserContribs.php +++ b/includes/api/ApiQueryUserContribs.php @@ -641,7 +641,7 @@ class ApiQueryUserContribs extends ApiQueryBase { return $vals; } - private function continueStr( $row ) { + private function continueStr( \stdClass $row ): string { if ( $this->multiUserMode ) { switch ( $this->orderBy ) { case 'name': diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index 5f6db828bb46..68461145baa0 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -268,7 +268,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } } - private function getFieldsToInclude() { + private function getFieldsToInclude(): array { $includeFields = []; if ( $this->fld_flags ) { $includeFields[] = WatchedItemQueryService::INCLUDE_FLAGS; @@ -298,7 +298,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { return $includeFields; } - private function showParamsConflicting( array $show ) { + private function showParamsConflicting( array $show ): bool { return ( isset( $show[WatchedItemQueryService::FILTER_MINOR] ) && isset( $show[WatchedItemQueryService::FILTER_NOT_MINOR] ) ) || ( isset( $show[WatchedItemQueryService::FILTER_BOT] ) @@ -315,7 +315,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { && isset( $show[WatchedItemQueryService::FILTER_NOT_UNREAD] ) ); } - private function extractOutputData( WatchedItem $watchedItem, array $recentChangeInfo ) { + private function extractOutputData( WatchedItem $watchedItem, array $recentChangeInfo ): array { /* Determine the title of the page that has been changed. */ $target = $watchedItem->getTarget(); if ( $target instanceof LinkTarget ) { diff --git a/includes/api/ApiRevisionDelete.php b/includes/api/ApiRevisionDelete.php index 77bdb99dd18f..8a6df9da0636 100644 --- a/includes/api/ApiRevisionDelete.php +++ b/includes/api/ApiRevisionDelete.php @@ -134,7 +134,7 @@ class ApiRevisionDelete extends ApiBase { $result->addValue( null, $this->getModuleName(), $data ); } - private function extractStatusInfo( Status $status ) { + private function extractStatusInfo( Status $status ): array { $ret = [ 'status' => $status->isOK() ? 'Success' : 'Fail', ]; diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php index f7bd10cff434..4147d3176dbe 100644 --- a/includes/api/ApiStashEdit.php +++ b/includes/api/ApiStashEdit.php @@ -28,6 +28,7 @@ use MediaWiki\Revision\SlotRecord; use MediaWiki\Storage\PageEditStash; use MediaWiki\User\TempUser\TempUserCreator; use MediaWiki\User\UserFactory; +use MediaWiki\User\UserIdentity; use Wikimedia\ParamValidator\ParamValidator; use Wikimedia\Stats\StatsFactory; @@ -208,7 +209,7 @@ class ApiStashEdit extends ApiBase { $this->getResult()->addValue( null, $this->getModuleName(), $ret ); } - private function getUserForPreview() { + private function getUserForPreview(): UserIdentity { $user = $this->getUser(); if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) { return $this->userFactory->newUnsavedTempUser( diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index acb071a6a846..7d9dea1fffec 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -132,8 +132,8 @@ class ApiWatch extends ApiBase { } private function watchTitle( PageIdentity $page, User $user, array $params, - $compatibilityMode = false - ) { + bool $compatibilityMode = false + ): array { $res = [ 'title' => $this->titleFormatter->getPrefixedText( $page ), 'ns' => $page->getNamespace() ]; if ( !$this->watchlistManager->isWatchable( $page ) ) { diff --git a/includes/api/i18n/ja.json b/includes/api/i18n/ja.json index 7a7b7cca92f3..35e9fadef31c 100644 --- a/includes/api/i18n/ja.json +++ b/includes/api/i18n/ja.json @@ -27,12 +27,14 @@ "Yamagata Yusuke", "Yusuke1109", "Yuukin0248", + "もなー(偽物)", "ネイ" ] }, "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|ドキュメンテーション]]\n* [[mw:Special:MyLanguage/API:Etiquette|エチケットと使用ガイドライン]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api.lists.wikimedia.org/ メーリングリスト]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/ API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグと要望]\n</div>\n<strong>状態:</strong> MediaWiki APIは、サポートおよび更新が恒常的に続けられている、安定した多機能インターフェースです。時折大きな仕様変更が適用されることもありますが、[https://lists.wikimedia.org/hyperkitty/list/mediawiki-api-announce@lists.wikimedia.org/ the mediawiki-api-announce メーリングリスト]を購読すると適時にアップデート通知を受け取ることができます。\n\n<strong>リクエストエラー:</strong> 非適合形式でAPIにリクエストに送られた場合、\"MediaWiki-API-Error\"のキーを含むHTTPヘッダーが返され、レスポンスのヘッダー値とエラーコード値が同値になります。詳細は、[[mw:Special:MyLanguage/API:Errors_and_warnings|API:エラーと警告]]をご覧ください。\n\n<p class=\"mw-apisandbox-link\"><strong>テスト:</strong> [[Special:ApiSandbox|APIサンドボックス]]を使用すると、容易にAPIリクエストのテストが可能です。</p>", "apihelp-main-param-action": "実行する操作。", "apihelp-main-param-format": "出力フォーマット。", + "apihelp-main-param-maxlag": "最大ラグは、MediaWiki がデータベース複製クラスターにインストールされている場合に使用できます。サイトの複製ラグをさらに引き起こすアクションを防ぐために、このパラメータを使用すると、複製ラグが指定値より小さくなるまでクライアントを待機させることができます。ラグが大きすぎる場合は、エラー コード<samp>maxlag</samp>が返され、 <samp>Waiting for $host: $lag seconds lagged</samp>のようなメッセージが表示されます。<br />詳細については、[[mw:Special:MyLanguage/Manual:Maxlag_parameter|マニュアル: Maxlag パラメータ]] を参照してください。", "apihelp-main-param-smaxage": "<code>s-maxage</code>のHTTPキャッシュコントロールヘッダーをこの秒数に指定します。エラーはキャッシュされません。", "apihelp-main-param-maxage": "<code>max-age</code>のHTTPキャッシュコントロールヘッダーをこの秒数に指定します。エラーはキャッシュされません。", "apihelp-main-param-assert": "<kbd>user</kbd> に設定した場合は利用者がログインしていること(臨時利用者としてログインしている場合も含む)を、 <kbd>anon</kbd> に設定した場合はログインして<em>いない</em>ことを、<kbd>bot</kbd> の場合はボット利用者権限があることを検証します。", @@ -52,6 +54,7 @@ "apihelp-main-param-errorlang": "警告およびエラーで使用する言語。 <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> は言語コードの一覧を返します。 <kbd>content</kbd> を指定するとこのウィキのコンテンツ言語、<kbd>uselang</kbd> を指定すると <var>uselang</var> パラメーターと同じ値を使用します。", "apihelp-main-param-errorsuselocal": "指定された場合、エラーテキストは{{ns:MediaWiki}}名前空間からローカルにカスタマイズされたメッセージを使用します。", "apihelp-block-summary": "利用者をブロックします。", + "apihelp-block-param-id": "変更するブロックID。", "apihelp-block-param-user": "ブロックする利用者。", "apihelp-block-param-userid": "代わりに <kbd>$1user=#<var>ID</var></kbd> を指定してください。", "apihelp-block-param-expiry": "有効期限。相対的 (例: <kbd>5 months</kbd> または <kbd>2 weeks</kbd>) または絶対的 (e.g. <kbd>2014-09-18T12:34:56Z</kbd>) どちらでも構いません。<kbd>infinite</kbd>, <kbd>indefinite</kbd>, もしくは <kbd>never</kbd> と設定した場合, 無期限ブロックとなります。", @@ -63,18 +66,26 @@ "apihelp-block-param-hidename": "ブロック記録から利用者名を秘匿します。(<code>hideuser</code> 権限が必要です)", "apihelp-block-param-allowusertalk": "自身のトークページの編集を許可する (<var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var> に依存)。", "apihelp-block-param-reblock": "その利用者がすでにブロックされている場合、ブロックを上書きします。", + "apihelp-block-param-newblock": "利用者がすでにブロックされている場合でも、別のブロックを追加します。", "apihelp-block-param-watchuser": "その利用者またはIPアドレスの利用者ページとトークページをウォッチします。", "apihelp-block-param-watchlistexpiry": "ウォッチリストの有効期限のタイムスタンプ。現在の有効期限を変更せずそのままにするには、このパラメーターを完全に省略します。", "apihelp-block-param-tags": "ブロック記録の項目に適用する変更タグ。", "apihelp-block-param-partial": "サイト全体ではなく特定のページまたは名前空間での編集をブロックします。", "apihelp-block-param-pagerestrictions": "利用者が編集できないようにするページのタイトルのリスト。<var>partial</var> に true が設定されている場合のみ適用します。", "apihelp-block-param-namespacerestrictions": "利用者が編集できないようにする名前空間のID。<var>partial</var> に true が設定されている場合のみ適用します。", + "apihelp-block-param-actionrestrictions": "利用者に実行させない操作のリスト。<var>partial</var> が true に設定されている場合のみ適用されます。", "apihelp-block-example-ip-simple": "IPアドレス <kbd>192.0.2.5</kbd> を何らかの理由で3日ブロックする。", "apihelp-block-example-user-complex": "利用者 <kbd>Vandal</kbd> を何らかの理由で無期限ブロックし、新たなアカウント作成とメールの送信を禁止する。", "apihelp-changeauthenticationdata-summary": "現在の利用者の認証データを変更します。", "apihelp-changeauthenticationdata-example-password": "現在の利用者のパスワードを <kbd>ExamplePassword</kbd> に変更する。", "apihelp-changecontentmodel-summary": "ページのコンテンツモデルを変更します。", + "apihelp-changecontentmodel-param-title": "コンテンツモデルを変更するページ名。<var>$1pageid</var>と併用することはできません。", + "apihelp-changecontentmodel-param-pageid": "コンテンツモデルを変更するページID。<var>$1title</var> とは併用できません。", + "apihelp-changecontentmodel-param-summary": "編集の概要と記録エントリの理由", + "apihelp-changecontentmodel-param-tags": "記録エントリに適用するタグを変更して編集します。", "apihelp-changecontentmodel-param-model": "新しいコンテンツのコンテンツモデル。", + "apihelp-changecontentmodel-param-bot": "コンテンツモデルの変更をボット フラグでマークします。", + "apihelp-changecontentmodel-example": "メインページが<code>text</code>コンテンツモデルを持つよう変更する", "apihelp-checktoken-summary": "<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd> のトークンの妥当性を確認します。", "apihelp-checktoken-param-type": "調べるトークンの種類。", "apihelp-checktoken-param-token": "調べるトークン。", diff --git a/includes/api/i18n/pt-br.json b/includes/api/i18n/pt-br.json index 9dba97e178e7..e9cb4c0d51a6 100644 --- a/includes/api/i18n/pt-br.json +++ b/includes/api/i18n/pt-br.json @@ -32,45 +32,52 @@ "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:Etiquette|Etiqueta & diretrizes de uso]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api.lists.wikimedia.org/ Lista de discussão]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/ Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> A API do MediaWiki é uma interface madura e estável que é ativamente suportada e aprimorada. Por mais que tentemos evitar, ocasionalmente talvez precisemos fazer mudanças estruturais; inscreva-se na [https://lists.wikimedia.org/hyperkitty/list/mediawiki-api-announce@lists.wikimedia.org/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].</p>", "apihelp-main-param-action": "Qual ação executar.", "apihelp-main-param-format": "O formato da saída.", - "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] para mais informações.", - "apihelp-main-param-smaxage": "Define o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.", - "apihelp-main-param-maxage": "Define o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.", - "apihelp-main-param-assert": "Verifique se o usuário está logado se configurado para <kbd>user</kbd>, <em>not</em> logado se definido como <kbd>anon</kbd> ou tem o direito do usuário do bot se <kbd>bot</kbd>.", - "apihelp-main-param-assertuser": "Verificar que o usuário atual é o utilizador nomeado.", + "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.", + "apihelp-main-param-smaxage": "Definir o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.", + "apihelp-main-param-maxage": "Definir o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.", + "apihelp-main-param-assert": "Verificar se o usuário está logado (incluindo possivelmente como usuário temporário) quando definido como <kbd>user</kbd>, se <em>não</em> está logado quando definido como <kbd>anon</kbd>, ou se tem o direito de usuário de robô quando definido como <kbd>bot</kbd>.", + "apihelp-main-param-assertuser": "Verificar se o usuário atual é o usuário nomeado.", "apihelp-main-param-requestid": "Qualquer valor dado aqui será incluído na resposta. Pode ser usado para distinguir requisições.", - "apihelp-main-param-servedby": "Incluir nos resultados o nome do servidor que serviu o pedido.", - "apihelp-main-param-curtimestamp": "Inclui o timestamp atual no resultado.", - "apihelp-main-param-responselanginfo": "Inclua os idiomas usados para <var>uselang</var> e <var>errorlang</var> no resultado.", - "apihelp-main-param-origin": "Ao acessar a API usando uma solicitação AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda solicitação ''pre-flight'', sendo portanto parte do URI da solicitação (ao invés do corpo do POST).\n\nPara solicitações autenticadas, isto deve corresponder a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida (''whitelisted''), os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara solicitações não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos para usuários tornar-se-ão restritos.", - "apihelp-main-param-uselang": "Linguagem a ser usada para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.", - "apihelp-main-param-errorformat": "Formato a ser usado aviso e saída de texto de erro.\n; Texto simples: Texto wiki com tags HTML removidas e entidades substituídas.\n; Wikitext: Unparsed wikitext. \n; html: HTML.\n; Bruto: chave e parâmetros da mensagem.\n; Nenhum: sem saída de texto, apenas os códigos de erro.\n; Bc: Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> são ignorados.", + "apihelp-main-param-servedby": "Incluir nos resultados o hostname que serviu a requisição.", + "apihelp-main-param-curtimestamp": "Incluir no resultado o timestamp atual.", + "apihelp-main-param-responselanginfo": "Incluir no resultado os idiomas usados para <var>uselang</var> e <var>errorlang</var>.", + "apihelp-main-param-origin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nPara requisições autenticadas, isto deve corresponder exatamente a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida, os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara requisições não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos de usuário tornar-se-ão restritos.", + "apihelp-main-param-crossorigin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS) e um provedor de sessão seguro contra ataques de falsificação de requisição entre websites (CSRF), como o provedor OAuth, use isto em vez de <code>origin=*</code> para tornar a requisição autenticada (ou seja, evitar que ela não esteja logada). Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nNote que a maioria dos provedores de sessão, incluindo sessões padrões baseadas em cookies, não são compatíveis com CORS autenticado e, portanto, não podem ser utilizadas com este parâmetro.", + "apihelp-main-param-uselang": "Idioma a ser usado para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.", + "apihelp-main-param-variant": "Variante do idioma. Funciona somente se o idioma-base for compatível com conversão para suas variantes.", + "apihelp-main-param-errorformat": "Formato a ser usado para textos de avisos e erros na saída", + "apihelp-main-paramvalue-errorformat-plaintext": "Wikitexto com marcações HTML removidas e entidades substituidas.", "apihelp-main-paramvalue-errorformat-wikitext": "Wikitexto não analisado.", "apihelp-main-paramvalue-errorformat-raw": "Chave e parâmetros da mensagem.", "apihelp-main-paramvalue-errorformat-none": "Sem saída de texto, apenas os códigos de erro.", "apihelp-main-paramvalue-errorformat-bc": "Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> serão ignorados.", - "apihelp-main-param-errorlang": "Linguagem a utilizar para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou especifique <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.", - "apihelp-main-param-errorsuselocal": "Se for dado, os textos de erro usarão mensagens customizadas localmente a partir do espaço nominal {{ns: MediaWiki}}.", + "apihelp-main-param-errorlang": "Idioma a ser utilizado para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.", + "apihelp-main-param-errorsuselocal": "Se fornecido, os textos de erro usarão mensagens personalizadas localmente a partir do espaço nominal {{ns:MediaWiki}}.", + "apihelp-acquiretempusername-summary": "Adquirir um nome de usuário temporário e armazená-lo na sessão atual caso a criação de contas temporárias esteja ativada e o usuário atual não estiver logado. Se um nome já foi armazenado, retorna esse nome.", + "apihelp-acquiretempusername-extended-description": "Se o usuário posteriormente realizar uma ação que resulte na criação de conta temporária, o nome de usuário armazenado será utilizado para a conta criada. Ele também poderá ser utilizado em previsões. Entretanto, a conta ainda não terá sido criada, e o nome não estará visível a outros usuários.", "apihelp-block-summary": "Bloquear um usuário.", - "apihelp-block-param-user": "Usuário para bloquear.", - "apihelp-block-param-userid": "Especifique <kbd>$1user=#<var>ID</var></kbd>.", - "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (por exemplo <kbd>5 meses</kbd> ou <kbd>2 semanas</kbd>) ou absoluto (por exemplo <kbd>2014-09-18T12:34:56Z</kbd>). Se definido para <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.", + "apihelp-block-param-id": "O ID de bloqueio a ser modificado.", + "apihelp-block-param-user": "Usuário a ser bloqueado.", + "apihelp-block-param-userid": "Ao invés, especifique <kbd>$1user=#<var>ID</var></kbd>.", + "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (p. ex., <kbd>5 months</kbd> ou <kbd>2 weeks</kbd>) ou absoluto (p. ex., <kbd>2014-09-18T12:34:56Z</kbd>). Se definido como <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.", "apihelp-block-param-reason": "Razão do bloqueio.", - "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja. desativa edições anônimas para este endereço IP).", + "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja, desativa edições anônimas para este endereço IP, incluindo edições de contas temporárias).", "apihelp-block-param-nocreate": "Prevenir a criação de conta.", - "apihelp-block-param-autoblock": "Bloquear automaticamente o endereço IP usado e quaisquer endereços IPs subsequentes que tentarem acessar a partir deles.", + "apihelp-block-param-autoblock": "Bloquear automaticamente o último endereço IP utilizado e quaisquer endereços IP subsequentes a partir dos quais houver tentativa de login.", "apihelp-block-param-noemail": "Impedir que o usuário envie e-mails através da wiki. (Requer o direito <code>blockemail</code>).", - "apihelp-block-param-hidename": "Oculta o nome do usuário do ''log'' de bloqueio. (Requer o direito <code>hideuser</code>).", + "apihelp-block-param-hidename": "Oculta o nome do usuário dos registros de bloqueio. (Requer o direito <code>hideuser</code>).", "apihelp-block-param-allowusertalk": "Permitir que o usuário edite sua própria página de discussão (depende de <var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var>).", - "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado, sobrescrever o bloqueio existente.", + "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado por um único bloqueio, sobrescrever o bloqueio existente. Se o usuário estiver com múltiplos bloqueios, isso falhará; utilize o parâmetro <var>id</var> para especificar qual bloqueio será sobrescrito.", + "apihelp-block-param-newblock": "Adicionar outro bloqueio mesmo se o usuário já estiver bloqueado.", "apihelp-block-param-watchuser": "Vigiar as páginas de usuário e de discussão, do usuário ou do endereço IP.", "apihelp-block-param-watchlistexpiry": "Carimbo de data/hora de expiração da lista de páginas vigiadas. Omita esse parâmetro inteiramente para manter inalterada a validade atual.", "apihelp-block-param-tags": "As tags de alteração a serem aplicadas à entrada no registro de bloqueio.", - "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o site.", - "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).", - "apihelp-block-param-namespacerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).", - "apihelp-block-param-actionrestrictions": "Lista de ações para bloquear a execução do usuário. Aplica-se apenas quando <var>parcial</var> é definido como verdadeiro.", - "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com a razão;", - "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com a razão e o impedir de criar nova conta e de enviar e-mails.", + "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o website.", + "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.", + "apihelp-block-param-namespacerestrictions": "Lista de IDs de espaços nominais que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.", + "apihelp-block-param-actionrestrictions": "Lista de ações que o bloqueio impedirá o usuário de executar. Só se aplica quando <var>partial</var> estiver definido como true.", + "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com um motivo.", + "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com um motivo, e impedi-lo de criar nova conta e de enviar e-mails.", "apihelp-changeauthenticationdata-summary": "Alterar os dados de autenticação para o usuário atual.", "apihelp-changeauthenticationdata-example-password": "Tenta alterar a senha do usuário atual para <kbd>ExamplePassword</kbd>.", "apihelp-changecontentmodel-summary": "Alterar o modelo de conteúdo de uma página", @@ -88,7 +95,7 @@ "apihelp-checktoken-example-simple": "Testa a validade de um token <kbd>csrf</kbd>.", "apihelp-clearhasmsg-summary": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.", "apihelp-clearhasmsg-example-1": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.", - "apihelp-clientlogin-summary": "Faça o login no wiki usando o fluxo interativo.", + "apihelp-clientlogin-summary": "Entrar na wiki usando o fluxo interativo.", "apihelp-clientlogin-example-login": "Comeca o processo de logar na wiki como usuário <kbd>Exemple</kbd> com a senha <kbd>ExamplePassword</kbd>.", "apihelp-clientlogin-example-login2": "Continuar efetuando login após uma resposta <samp>UI</samp> para autenticação de dois fatores, fornecendo um <var>OATHToken</var> de <kbd>987654</ kbd>.", "apihelp-compare-summary": "Obter a diferença entre duas páginas.", @@ -197,7 +204,7 @@ "apihelp-expandtemplates-param-prop": "Quais peças de informação obter.\n\nNote que se nenhum valor for selecionado, o resultado conterá o texto wiki, mas o resultado será em um formato obsoleto.", "apihelp-expandtemplates-paramvalue-prop-wikitext": "O texto wiki expandido.", "apihelp-expandtemplates-paramvalue-prop-categories": "Quaisquer categorias presentes na entrada que não estão representadas na saída wikitext.", - "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no texto wiki.", + "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no wikitexto.", "apihelp-expandtemplates-paramvalue-prop-volatile": "Se a saída é volátil e não deve ser reutilizada em outro lugar dentro da página.", "apihelp-expandtemplates-paramvalue-prop-ttl": "O tempo máximo após o qual os caches do resultado devem ser invalidados.", "apihelp-expandtemplates-paramvalue-prop-modules": "Quaisquer módulos ResourceLoader que as funções do analisador solicitaram foram adicionados à saída. Contudo, <kbd>jsconfigvars</kbd> ou <kbd>encodedjsconfigvars</kbd> devem ser solicitados em conjunto com <kbd>modules</kbd>.", @@ -370,13 +377,13 @@ "apihelp-parse-param-prop": "Qual pedaço de informação obter:", "apihelp-parse-paramvalue-prop-text": "Fornece o texto analisado do texto wiki.", "apihelp-parse-paramvalue-prop-langlinks": "Fornece os links de idiomas do texto wiki analisado.", - "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no texto wiki analisado.", + "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no wikitexto analisado.", "apihelp-parse-paramvalue-prop-categorieshtml": "Fornece a versão HTML das categorias.", "apihelp-parse-paramvalue-prop-links": "Fornece os links internos do texto wiki analisado.", - "apihelp-parse-paramvalue-prop-templates": "Fornece a predefinição no texto wiki analisado.", - "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no texto wiki analisado.", - "apihelp-parse-paramvalue-prop-externallinks": "Fornece os links externos no texto wiki analisado.", - "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no texto wiki analisado.", + "apihelp-parse-paramvalue-prop-templates": "Fornece as predefinições no wikitexto analisado.", + "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no wikitexto analisado.", + "apihelp-parse-paramvalue-prop-externallinks": "Fornece as ligações externas no wikitexto analisado.", + "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no wikitexto analisado.", "apihelp-parse-paramvalue-prop-revid": "Adiciona o ID da revisão da página analisada.", "apihelp-parse-paramvalue-prop-displaytitle": "Adiciona o título do texto wiki analisado.", "apihelp-parse-paramvalue-prop-subtitle": "Adiciona o subtítulo de página da página a que foi feita a análise sintática.", @@ -386,9 +393,9 @@ "apihelp-parse-paramvalue-prop-jsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página. Para aplicar, use <code>mw.config.set()</code>.", "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página como uma string JSON.", "apihelp-parse-paramvalue-prop-indicators": "Fornece o HTML de indicadores de ''status'' de página utilizados na página.", - "apihelp-parse-paramvalue-prop-iwlinks": "Fornece links interwiki no texto wiki analisado.", + "apihelp-parse-paramvalue-prop-iwlinks": "Fornece ligações interwiki no wikitexto analisado.", "apihelp-parse-paramvalue-prop-wikitext": "Fornece o texto wiki original que foi analisado.", - "apihelp-parse-paramvalue-prop-properties": "Fornece várias propriedades definidas no texto wiki analisado.", + "apihelp-parse-paramvalue-prop-properties": "Fornece propriedades variadas definidas no wikitexto analisado.", "apihelp-parse-paramvalue-prop-limitreportdata": "Fornece o relatório limite de uma forma estruturada. Não informa dado, quando<var>$1disablelimitreport</var> está definido.", "apihelp-parse-paramvalue-prop-limitreporthtml": "Retorna a versão HTML do relatório de limite. Não retorna dados quando <var>$1disablelimitreport</var> está definido.", "apihelp-parse-paramvalue-prop-parsetree": "A árvore de análise XML do conteúdo da revisão (requer modelo de conteúdo <code>$1</code>)", diff --git a/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php b/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php index c37313376a44..b6d0384fd472 100644 --- a/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php +++ b/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php @@ -21,10 +21,14 @@ namespace MediaWiki\Auth; +use BadMethodCallException; use MediaWiki\Deferred\DeferredUpdates; use MediaWiki\MainConfigNames; use MediaWiki\Password\InvalidPassword; +use MediaWiki\Status\Status; use MediaWiki\User\UserRigorOptions; +use StatusValue; +use stdClass; use Wikimedia\Rdbms\DBAccessObjectUtils; use Wikimedia\Rdbms\IConnectionProvider; use Wikimedia\Rdbms\IDBAccessObject; @@ -60,8 +64,8 @@ class LocalPasswordPrimaryAuthenticationProvider * Check if the password has expired and needs a reset * * @param string $username - * @param \stdClass $row A row from the user table - * @return \stdClass|null + * @param stdClass $row A row from the user table + * @return stdClass|null */ protected function getPasswordResetData( $username, $row ) { $now = (int)wfTimestamp(); @@ -74,12 +78,12 @@ class LocalPasswordPrimaryAuthenticationProvider if ( (int)$expiration + $grace < $now ) { $data = [ 'hard' => true, - 'msg' => \MediaWiki\Status\Status::newFatal( 'resetpass-expired' )->getMessage(), + 'msg' => Status::newFatal( 'resetpass-expired' )->getMessage(), ]; } else { $data = [ 'hard' => false, - 'msg' => \MediaWiki\Status\Status::newFatal( 'resetpass-expired-soft' )->getMessage(), + 'msg' => Status::newFatal( 'resetpass-expired-soft' )->getMessage(), ]; } @@ -112,7 +116,7 @@ class LocalPasswordPrimaryAuthenticationProvider $oldRow = clone $row; // Check for *really* old password hashes that don't even have a type - // The old hash format was just an md5 hex hash, with no type information + // The old hash format was just an MD5 hex hash, with no type information if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) { $row->user_password = ":B:{$row->user_id}:{$row->user_password}"; } @@ -161,7 +165,9 @@ class LocalPasswordPrimaryAuthenticationProvider public function testUserCanAuthenticate( $username ) { $username = $this->userNameUtils->getCanonical( - $username, UserRigorOptions::RIGOR_USABLE ); + $username, + UserRigorOptions::RIGOR_USABLE + ); if ( $username === false ) { return false; } @@ -176,7 +182,7 @@ class LocalPasswordPrimaryAuthenticationProvider } // Check for *really* old password hashes that don't even have a type - // The old hash format was just an md5 hex hash, with no type information + // The old hash format was just an MD5 hex hash, with no type information if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) { return true; } @@ -186,7 +192,9 @@ class LocalPasswordPrimaryAuthenticationProvider public function testUserExists( $username, $flags = IDBAccessObject::READ_NORMAL ) { $username = $this->userNameUtils->getCanonical( - $username, UserRigorOptions::RIGOR_USABLE ); + $username, + UserRigorOptions::RIGOR_USABLE + ); if ( $username === false ) { return false; } @@ -205,12 +213,12 @@ class LocalPasswordPrimaryAuthenticationProvider // We only want to blank the password if something else will accept the // new authentication data, so return 'ignore' here. if ( $this->loginOnly ) { - return \StatusValue::newGood( 'ignored' ); + return StatusValue::newGood( 'ignored' ); } if ( get_class( $req ) === PasswordAuthenticationRequest::class ) { if ( !$checkData ) { - return \StatusValue::newGood(); + return StatusValue::newGood(); } $username = $this->userNameUtils->getCanonical( $req->username, @@ -222,7 +230,7 @@ class LocalPasswordPrimaryAuthenticationProvider ->where( [ 'user_name' => $username ] ) ->caller( __METHOD__ )->fetchRow(); if ( $row ) { - $sv = \StatusValue::newGood(); + $sv = StatusValue::newGood(); if ( $req->password !== null ) { if ( $req->password !== $req->retype ) { $sv->fatal( 'badretype' ); @@ -235,12 +243,12 @@ class LocalPasswordPrimaryAuthenticationProvider } } - return \StatusValue::newGood( 'ignored' ); + return StatusValue::newGood( 'ignored' ); } public function providerChangeAuthenticationData( AuthenticationRequest $req ) { - $username = $req->username !== null ? - $this->userNameUtils->getCanonical( $req->username, UserRigorOptions::RIGOR_USABLE ) + $username = $req->username !== null + ? $this->userNameUtils->getCanonical( $req->username, UserRigorOptions::RIGOR_USABLE ) : false; if ( $username === false ) { return; @@ -279,7 +287,7 @@ class LocalPasswordPrimaryAuthenticationProvider public function testForAccountCreation( $user, $creator, array $reqs ) { $req = AuthenticationRequest::getRequestByClass( $reqs, PasswordAuthenticationRequest::class ); - $ret = \StatusValue::newGood(); + $ret = StatusValue::newGood(); if ( !$this->loginOnly && $req && $req->username !== null && $req->password !== null ) { if ( $req->password !== $req->retype ) { $ret->fatal( 'badretype' ); @@ -294,7 +302,7 @@ class LocalPasswordPrimaryAuthenticationProvider public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) { if ( $this->accountCreationType() === self::TYPE_NONE ) { - throw new \BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' ); + throw new BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' ); } $req = AuthenticationRequest::getRequestByClass( $reqs, PasswordAuthenticationRequest::class ); @@ -314,7 +322,7 @@ class LocalPasswordPrimaryAuthenticationProvider public function finishAccountCreation( $user, $creator, AuthenticationResponse $res ) { if ( $this->accountCreationType() === self::TYPE_NONE ) { - throw new \BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' ); + throw new BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' ); } // Now that the user is in the DB, set the password on it. diff --git a/includes/composer/ComposerVendorHtaccessCreator.php b/includes/composer/ComposerVendorHtaccessCreator.php index e2a079b021d5..ef835fa1d898 100644 --- a/includes/composer/ComposerVendorHtaccessCreator.php +++ b/includes/composer/ComposerVendorHtaccessCreator.php @@ -40,6 +40,8 @@ class ComposerVendorHtaccessCreator { return; } - file_put_contents( $fname, "Require all denied\n" ); + file_put_contents( $fname, + "Require all denied\n" . + "Satisfy All\n" ); } } diff --git a/includes/deferred/LinksUpdate/CategoryLinksTable.php b/includes/deferred/LinksUpdate/CategoryLinksTable.php index 151566c9f408..bee5b26518ef 100644 --- a/includes/deferred/LinksUpdate/CategoryLinksTable.php +++ b/includes/deferred/LinksUpdate/CategoryLinksTable.php @@ -14,6 +14,7 @@ use MediaWiki\MainConfigNames; use MediaWiki\Page\PageReferenceValue; use MediaWiki\Page\WikiPageFactory; use MediaWiki\Parser\ParserOutput; +use MediaWiki\Parser\ParserOutputLinkTypes; use MediaWiki\Parser\Sanitizer; use MediaWiki\Storage\NameTableStore; use MediaWiki\Title\NamespaceInfo; @@ -142,8 +143,10 @@ class CategoryLinksTable extends TitleLinksTable { $this->newLinks = []; $sourceTitle = Title::castFromPageIdentity( $this->getSourcePage() ); $sortKeyInputs = []; - foreach ( $parserOutput->getCategoryNames() as $name ) { - $sortKey = $parserOutput->getCategorySortKey( $name ); + foreach ( + $parserOutput->getLinkList( ParserOutputLinkTypes::CATEGORY ) + as [ 'link' => $targetTitle, 'sort' => $sortKey ] + ) { '@phan-var string $sortKey'; // sort key will never be null if ( $sortKey == '' ) { @@ -160,7 +163,8 @@ class CategoryLinksTable extends TitleLinksTable { // categories, causing T27254. $sortKeyPrefix = mb_strcut( $sortKey, 0, 255 ); - $targetTitle = Title::makeTitle( NS_CATEGORY, $name ); + $name = $targetTitle->getDBkey(); + $targetTitle = Title::castFromLinkTarget( $targetTitle ); $this->languageConverter->findVariantLink( $name, $targetTitle, true ); // Ignore the returned text, DB key should be used for links (T328477). $name = $targetTitle->getDBKey(); diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index faca5c529263..e5a3b1428525 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -1238,7 +1238,7 @@ class DifferenceEngine extends ContextSource { $out->setRevisionId( $this->mNewid ); $out->setRevisionIsCurrent( $this->mNewRevisionRecord->isCurrent() ); - $out->setRevisionTimestamp( $this->mNewRevisionRecord->getTimestamp() ); + $out->getMetadata()->setRevisionTimestamp( $this->mNewRevisionRecord->getTimestamp() ); $out->setArticleFlag( true ); if ( !$this->hookRunner->onArticleRevisionViewCustom( diff --git a/includes/editpage/Constraint/AuthorizationConstraint.php b/includes/editpage/Constraint/AuthorizationConstraint.php new file mode 100644 index 000000000000..73247160fa7b --- /dev/null +++ b/includes/editpage/Constraint/AuthorizationConstraint.php @@ -0,0 +1,88 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +namespace MediaWiki\EditPage\Constraint; + +use MediaWiki\Page\PageIdentity; +use MediaWiki\Permissions\Authority; +use MediaWiki\Permissions\PermissionStatus; +use StatusValue; + +/** + * Verify authorization to edit the page (user rights, rate limits, blocks). + * + * @since 1.44 + * @internal + */ +class AuthorizationConstraint implements IEditConstraint { + + private PermissionStatus $status; + + private Authority $performer; + private PageIdentity $target; + private bool $new; + + public function __construct( + Authority $performer, + PageIdentity $target, + bool $new + ) { + $this->performer = $performer; + $this->target = $target; + $this->new = $new; + } + + public function checkConstraint(): string { + $this->status = PermissionStatus::newEmpty(); + + if ( $this->new && !$this->performer->authorizeWrite( 'create', $this->target, $this->status ) ) { + return self::CONSTRAINT_FAILED; + } + + if ( !$this->performer->authorizeWrite( 'edit', $this->target, $this->status ) ) { + return self::CONSTRAINT_FAILED; + } + + return self::CONSTRAINT_PASSED; + } + + public function getLegacyStatus(): StatusValue { + $statusValue = StatusValue::newGood(); + + if ( !$this->status->isGood() ) { + // Report the most specific errors first + if ( $this->status->isBlocked() ) { + $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER ); + } elseif ( $this->status->isRateLimitExceeded() ) { + $statusValue->setResult( false, self::AS_RATE_LIMITED ); + } elseif ( $this->status->getPermission() === 'create' ) { + $statusValue->setResult( false, self::AS_NO_CREATE_PERMISSION ); + } elseif ( !$this->performer->isRegistered() ) { + $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON ); + } else { + $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_LOGGED ); + } + } + + // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399 + return $statusValue; + } + +} diff --git a/includes/editpage/Constraint/ContentModelChangeConstraint.php b/includes/editpage/Constraint/ContentModelChangeConstraint.php index 8ed2e56debde..2581ebab3c14 100644 --- a/includes/editpage/Constraint/ContentModelChangeConstraint.php +++ b/includes/editpage/Constraint/ContentModelChangeConstraint.php @@ -21,6 +21,7 @@ namespace MediaWiki\EditPage\Constraint; use MediaWiki\Permissions\Authority; +use MediaWiki\Permissions\PermissionStatus; use MediaWiki\Title\Title; use StatusValue; @@ -28,6 +29,7 @@ use StatusValue; * Verify user permissions if changing content model: * Must have editcontentmodel rights * Must be able to edit under the new content model + * Must not have exceeded the rate limit * * @since 1.36 * @internal @@ -35,10 +37,11 @@ use StatusValue; */ class ContentModelChangeConstraint implements IEditConstraint { + private PermissionStatus $status; + private Authority $performer; private Title $title; private string $newContentModel; - private string $result; /** * @param Authority $performer @@ -56,45 +59,43 @@ class ContentModelChangeConstraint implements IEditConstraint { } public function checkConstraint(): string { + $this->status = PermissionStatus::newEmpty(); + if ( $this->newContentModel === $this->title->getContentModel() ) { // No change - $this->result = self::CONSTRAINT_PASSED; return self::CONSTRAINT_PASSED; } - if ( !$this->performer->isAllowed( 'editcontentmodel' ) ) { - $this->result = self::CONSTRAINT_FAILED; + if ( !$this->performer->authorizeWrite( 'editcontentmodel', $this->title, $this->status ) ) { return self::CONSTRAINT_FAILED; } - // Make sure the user can edit the page under the new content model too + // Make sure the user can edit the page under the new content model too. + // We rely on caching in UserAuthority to avoid bumping the rate limit counter twice. $titleWithNewContentModel = clone $this->title; $titleWithNewContentModel->setContentModel( $this->newContentModel ); - - $canEditModel = $this->performer->authorizeWrite( - 'editcontentmodel', - $titleWithNewContentModel - ); - if ( - !$canEditModel - || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel ) + !$this->performer->authorizeWrite( 'editcontentmodel', $titleWithNewContentModel, $this->status ) + || !$this->performer->authorizeWrite( 'edit', $titleWithNewContentModel, $this->status ) ) { - $this->result = self::CONSTRAINT_FAILED; return self::CONSTRAINT_FAILED; } - $this->result = self::CONSTRAINT_PASSED; return self::CONSTRAINT_PASSED; } public function getLegacyStatus(): StatusValue { $statusValue = StatusValue::newGood(); - if ( $this->result === self::CONSTRAINT_FAILED ) { - $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL ); + if ( !$this->status->isGood() ) { + if ( $this->status->isRateLimitExceeded() ) { + $statusValue->setResult( false, self::AS_RATE_LIMITED ); + } else { + $statusValue->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL ); + } } + // TODO: Use error messages from the PermissionStatus ($this->status) here - T384399 return $statusValue; } diff --git a/includes/editpage/Constraint/EditConstraintFactory.php b/includes/editpage/Constraint/EditConstraintFactory.php index 750c977d2312..e9fede9f256c 100644 --- a/includes/editpage/Constraint/EditConstraintFactory.php +++ b/includes/editpage/Constraint/EditConstraintFactory.php @@ -26,10 +26,8 @@ use MediaWiki\Context\IContextSource; use MediaWiki\EditPage\SpamChecker; use MediaWiki\HookContainer\HookContainer; use MediaWiki\Language\Language; -use MediaWiki\Linker\LinkTarget; use MediaWiki\Logger\Spi; use MediaWiki\MainConfigNames; -use MediaWiki\Permissions\PermissionManager; use MediaWiki\Permissions\RateLimiter; use MediaWiki\Permissions\RateLimitSubject; use MediaWiki\Title\Title; @@ -54,7 +52,6 @@ class EditConstraintFactory { private ServiceOptions $options; private Spi $loggerFactory; - private PermissionManager $permissionManager; private HookContainer $hookContainer; private ReadOnlyMode $readOnlyMode; private SpamChecker $spamRegexChecker; @@ -74,7 +71,6 @@ class EditConstraintFactory { * * @param ServiceOptions $options * @param Spi $loggerFactory - * @param PermissionManager $permissionManager * @param HookContainer $hookContainer * @param ReadOnlyMode $readOnlyMode * @param SpamChecker $spamRegexChecker @@ -83,7 +79,6 @@ class EditConstraintFactory { public function __construct( ServiceOptions $options, Spi $loggerFactory, - PermissionManager $permissionManager, HookContainer $hookContainer, ReadOnlyMode $readOnlyMode, SpamChecker $spamRegexChecker, @@ -95,9 +90,6 @@ class EditConstraintFactory { $this->options = $options; $this->loggerFactory = $loggerFactory; - // UserBlockConstraint - $this->permissionManager = $permissionManager; - // EditFilterMergedContentHookConstraint $this->hookContainer = $hookContainer; @@ -107,7 +99,7 @@ class EditConstraintFactory { // SpamRegexConstraint $this->spamRegexChecker = $spamRegexChecker; - // UserRateLimitConstraint + // LinkPurgeRateLimitConstraint $this->rateLimiter = $rateLimiter; } @@ -163,21 +155,15 @@ class EditConstraintFactory { /** * @param RateLimitSubject $subject - * @param string $oldModel - * @param string $newModel * - * @return UserRateLimitConstraint + * @return LinkPurgeRateLimitConstraint */ - public function newUserRateLimitConstraint( - RateLimitSubject $subject, - string $oldModel, - string $newModel - ): UserRateLimitConstraint { - return new UserRateLimitConstraint( + public function newLinkPurgeRateLimitConstraint( + RateLimitSubject $subject + ): LinkPurgeRateLimitConstraint { + return new LinkPurgeRateLimitConstraint( $this->rateLimiter, - $subject, - $oldModel, - $newModel + $subject ); } @@ -226,39 +212,4 @@ class EditConstraintFactory { ); } - /** - * @param LinkTarget $title - * @param User $user - * @return UserBlockConstraint - */ - public function newUserBlockConstraint( - LinkTarget $title, - User $user - ): UserBlockConstraint { - return new UserBlockConstraint( - $this->permissionManager, - $title, - $user - ); - } - - /** - * @param User $performer - * @param Title $title - * @param bool $new - * @return EditRightConstraint - */ - public function newEditRightConstraint( - User $performer, - Title $title, - bool $new - ): EditRightConstraint { - return new EditRightConstraint( - $performer, - $this->permissionManager, - $title, - $new - ); - } - } diff --git a/includes/editpage/Constraint/EditRightConstraint.php b/includes/editpage/Constraint/EditRightConstraint.php deleted file mode 100644 index 47be037e4dad..000000000000 --- a/includes/editpage/Constraint/EditRightConstraint.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -/** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -namespace MediaWiki\EditPage\Constraint; - -use MediaWiki\Permissions\PermissionManager; -use MediaWiki\Title\Title; -use MediaWiki\User\User; -use StatusValue; - -/** - * Verify user permissions: - * Must have edit rights - * - * @since 1.36 - * @internal - * @author DannyS712 - */ -class EditRightConstraint implements IEditConstraint { - - private User $performer; - private PermissionManager $permManager; - private Title $title; - private string $result; - private bool $new; - - /** - * @param User $performer - * @param PermissionManager $permManager - * @param Title $title - * @param bool $new - */ - public function __construct( - User $performer, - PermissionManager $permManager, - Title $title, - bool $new - ) { - $this->performer = $performer; - $this->permManager = $permManager; - $this->title = $title; - $this->new = $new; - } - - public function checkConstraint(): string { - if ( $this->new ) { - // Check isn't simple enough to just repeat when getting the status - if ( !$this->performer->authorizeWrite( 'create', $this->title ) ) { - $this->result = (string)self::AS_NO_CREATE_PERMISSION; - return self::CONSTRAINT_FAILED; - } - } - - // Check isn't simple enough to just repeat when getting the status - // Prior to 1.41 this checked if the user had edit rights in general - // instead of for the specific page in question. - if ( !$this->permManager->userCan( - 'edit', - $this->performer, - $this->title - ) ) { - $this->result = self::CONSTRAINT_FAILED; - return self::CONSTRAINT_FAILED; - } - - $this->result = self::CONSTRAINT_PASSED; - return self::CONSTRAINT_PASSED; - } - - public function getLegacyStatus(): StatusValue { - $statusValue = StatusValue::newGood(); - - if ( $this->result === self::CONSTRAINT_FAILED ) { - if ( !$this->performer->isRegistered() ) { - $statusValue->setResult( false, self::AS_READ_ONLY_PAGE_ANON ); - } else { - $statusValue->fatal( 'readonlytext' ); - $statusValue->value = self::AS_READ_ONLY_PAGE_LOGGED; - } - } elseif ( $this->result === (string)self::AS_NO_CREATE_PERMISSION ) { - $statusValue->fatal( 'nocreatetext' ); - $statusValue->value = self::AS_NO_CREATE_PERMISSION; - } - - return $statusValue; - } - -} diff --git a/includes/editpage/Constraint/UserRateLimitConstraint.php b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php index 8f35dd5879d0..d25cf9336d88 100644 --- a/includes/editpage/Constraint/UserRateLimitConstraint.php +++ b/includes/editpage/Constraint/LinkPurgeRateLimitConstraint.php @@ -25,31 +25,26 @@ use MediaWiki\Permissions\RateLimitSubject; use StatusValue; /** - * Verify user doesn't exceed rate limits + * Verify that the user doesn't exceed 'linkpurge' limits, which are weird and special. + * Other rate limits have been integrated into their respective permission checks. * - * @since 1.36 + * @since 1.44 * @internal * @author DannyS712 */ -class UserRateLimitConstraint implements IEditConstraint { +class LinkPurgeRateLimitConstraint implements IEditConstraint { private RateLimitSubject $subject; - private string $oldContentModel; - private string $newContentModel; private RateLimiter $limiter; private string $result; public function __construct( RateLimiter $limiter, - RateLimitSubject $subject, - string $oldContentModel, - string $newContentModel + RateLimitSubject $subject ) { $this->limiter = $limiter; $this->subject = $subject; - $this->oldContentModel = $oldContentModel; - $this->newContentModel = $newContentModel; } private function limit( string $action, int $inc = 1 ): bool { @@ -57,16 +52,10 @@ class UserRateLimitConstraint implements IEditConstraint { } public function checkConstraint(): string { - // Need to check for rate limits on `editcontentmodel` if it is changing - $contentModelChange = ( $this->newContentModel !== $this->oldContentModel ); - // TODO inject and use a ThrottleStore once available, see T261744 // Checking if the user is rate limited increments the counts, so we cannot perform // the check again when getting the status; thus, store the result - if ( $this->limit( 'edit' ) - || $this->limit( 'linkpurge', 0 ) // only counted after the fact - || ( $contentModelChange && $this->limit( 'editcontentmodel' ) ) - ) { + if ( $this->limit( 'linkpurge', /* only counted after the fact */ 0 ) ) { $this->result = self::CONSTRAINT_FAILED; } else { $this->result = self::CONSTRAINT_PASSED; diff --git a/includes/editpage/Constraint/UserBlockConstraint.php b/includes/editpage/Constraint/UserBlockConstraint.php deleted file mode 100644 index bdd33262d434..000000000000 --- a/includes/editpage/Constraint/UserBlockConstraint.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -namespace MediaWiki\EditPage\Constraint; - -use MediaWiki\Linker\LinkTarget; -use MediaWiki\Permissions\PermissionManager; -use MediaWiki\User\User; -use StatusValue; - -/** - * Verify user permissions: - * Must not be blocked from the page - * - * @since 1.36 - * @internal - * @author DannyS712 - */ -class UserBlockConstraint implements IEditConstraint { - - private PermissionManager $permissionManager; - private LinkTarget $title; - private User $user; - private string $result; - - /** - * @param PermissionManager $permissionManager - * @param LinkTarget $title - * @param User $user - */ - public function __construct( - PermissionManager $permissionManager, - LinkTarget $title, - User $user - ) { - $this->permissionManager = $permissionManager; - $this->title = $title; - $this->user = $user; - } - - public function checkConstraint(): string { - // Check isn't simple enough to just repeat when getting the status - if ( $this->permissionManager->isBlockedFrom( $this->user, $this->title ) ) { - $this->result = self::CONSTRAINT_FAILED; - return self::CONSTRAINT_FAILED; - } - - $this->result = self::CONSTRAINT_PASSED; - return self::CONSTRAINT_PASSED; - } - - public function getLegacyStatus(): StatusValue { - $statusValue = StatusValue::newGood(); - - if ( $this->result === self::CONSTRAINT_FAILED ) { - $statusValue->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER ); - } - - return $statusValue; - } - -} diff --git a/includes/editpage/EditPage.php b/includes/editpage/EditPage.php index 93a3ad5a28e8..f08196894d88 100644 --- a/includes/editpage/EditPage.php +++ b/includes/editpage/EditPage.php @@ -37,6 +37,7 @@ use MediaWiki\Context\IContextSource; use MediaWiki\Debug\DeprecationHelper; use MediaWiki\Deferred\DeferredUpdates; use MediaWiki\EditPage\Constraint\AccidentalRecreationConstraint; +use MediaWiki\EditPage\Constraint\AuthorizationConstraint; use MediaWiki\EditPage\Constraint\BrokenRedirectConstraint; use MediaWiki\EditPage\Constraint\ChangeTagsConstraint; use MediaWiki\EditPage\Constraint\ContentModelChangeConstraint; @@ -54,7 +55,6 @@ use MediaWiki\EditPage\Constraint\PageSizeConstraint; use MediaWiki\EditPage\Constraint\SelfRedirectConstraint; use MediaWiki\EditPage\Constraint\SpamRegexConstraint; use MediaWiki\EditPage\Constraint\UnicodeConstraint; -use MediaWiki\EditPage\Constraint\UserBlockConstraint; use MediaWiki\Exception\ErrorPageError; use MediaWiki\Exception\MWContentSerializationException; use MediaWiki\Exception\MWException; @@ -2189,24 +2189,31 @@ class EditPage implements IEditObject { ) ); $constraintRunner->addConstraint( - $constraintFactory->newUserBlockConstraint( $this->mTitle, $requestUser ) + $constraintFactory->newReadOnlyConstraint() ); + + // Load the page data from the primary DB. If anything changes in the meantime, + // we detect it by using page_latest like a token in a 1 try compare-and-swap. + $this->page->loadPageData( IDBAccessObject::READ_LATEST ); + $new = !$this->page->exists(); + $constraintRunner->addConstraint( - new ContentModelChangeConstraint( + new AuthorizationConstraint( $authority, $this->mTitle, - $this->contentModel + $new ) ); - $constraintRunner->addConstraint( - $constraintFactory->newReadOnlyConstraint() + new ContentModelChangeConstraint( + $authority, + $this->mTitle, + $this->contentModel + ) ); $constraintRunner->addConstraint( - $constraintFactory->newUserRateLimitConstraint( - $requestUser->toRateLimitSubject(), - $this->mTitle->getContentModel(), - $this->contentModel + $constraintFactory->newLinkPurgeRateLimitConstraint( + $requestUser->toRateLimitSubject() ) ); $constraintRunner->addConstraint( @@ -2230,16 +2237,6 @@ class EditPage implements IEditObject { ) ); - // Load the page data from the primary DB. If anything changes in the meantime, - // we detect it by using page_latest like a token in a 1 try compare-and-swap. - $this->page->loadPageData( IDBAccessObject::READ_LATEST ); - $new = !$this->page->exists(); - - // We do this last, as some of the other constraints are more specific - $constraintRunner->addConstraint( - $constraintFactory->newEditRightConstraint( $this->getUserForPermissions(), $this->mTitle, $new ) - ); - // Check the constraints if ( !$constraintRunner->checkConstraints() ) { $failed = $constraintRunner->getFailedConstraint(); @@ -2625,9 +2622,12 @@ class EditPage implements IEditObject { * result from the backend. */ private function handleFailedConstraint( IEditConstraint $failed ): void { - if ( $failed instanceof UserBlockConstraint ) { + if ( $failed instanceof AuthorizationConstraint ) { // Auto-block user's IP if the account was "hard" blocked - if ( !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) { + if ( + !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() + && $failed->getLegacyStatus()->value === self::AS_BLOCKED_PAGE_FOR_USER + ) { $this->context->getUser()->spreadAnyEditBlock(); } } elseif ( $failed instanceof DefaultTextConstraint ) { diff --git a/includes/filerepo/ThumbnailEntryPoint.php b/includes/filerepo/ThumbnailEntryPoint.php index a1ae53359a1b..e8367ada0d2b 100644 --- a/includes/filerepo/ThumbnailEntryPoint.php +++ b/includes/filerepo/ThumbnailEntryPoint.php @@ -673,7 +673,7 @@ EOT; return false; } - private function vary( $header ) { + private function vary( string $header ) { $this->varyHeader[] = $header; } @@ -823,7 +823,7 @@ EOT; return false; } - private function maybeEnforceRateLimits( File $img, array $params ) { + private function maybeEnforceRateLimits( File $img, array $params ): bool { $authority = $this->getContext()->getAuthority(); $status = PermissionStatus::newEmpty(); diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php index 30844f5c2059..d088b87fc001 100644 --- a/includes/filerepo/file/File.php +++ b/includes/filerepo/file/File.php @@ -1147,7 +1147,7 @@ abstract class File implements MediaHandlerState { return $thumbName; } - private function adjustThumbWidthForSteps( $params ) { + private function adjustThumbWidthForSteps( array $params ): array { $thumbnailSteps = MediaWikiServices::getInstance() ->getMainConfig()->get( MainConfigNames::ThumbnailSteps ); $thumbnailStepsRatio = MediaWikiServices::getInstance() @@ -2465,7 +2465,7 @@ abstract class File implements MediaHandlerState { } /** - * @return string + * @return string HTML */ public function getLongDesc() { $handler = $this->getHandler(); @@ -2477,7 +2477,7 @@ abstract class File implements MediaHandlerState { } /** - * @return string + * @return string HTML */ public function getShortDesc() { $handler = $this->getHandler(); @@ -2489,7 +2489,7 @@ abstract class File implements MediaHandlerState { } /** - * @return string + * @return string plain text */ public function getDimensionsString() { $handler = $this->getHandler(); diff --git a/includes/filerepo/file/FileSelectQueryBuilder.php b/includes/filerepo/file/FileSelectQueryBuilder.php index a61357b00949..23cf5597a9a1 100644 --- a/includes/filerepo/file/FileSelectQueryBuilder.php +++ b/includes/filerepo/file/FileSelectQueryBuilder.php @@ -73,7 +73,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder { return new FileSelectQueryBuilder( $db, 'archivedfile', $options ); } - private function initFileOld( $options ) { + private function initFileOld( array $options ) { $this->table( 'image' ) ->join( 'actor', 'image_actor', 'actor_id=img_actor' ) ->join( @@ -112,7 +112,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder { } } - private function initFileNew( $options ) { + private function initFileNew( array $options ) { $subquery = $this->newSubquery(); $subquery->table( 'file' ) ->join( 'filerevision', null, 'file_latest = fr_id' ) @@ -164,7 +164,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder { ->from( $subquery ); } - private function initOldFileOld( $options ) { + private function initOldFileOld( array $options ) { $this->table( 'oldimage' ) ->join( 'actor', 'oldimage_actor', 'actor_id=oi_actor' ) ->join( @@ -204,7 +204,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder { } } - private function initOldFileNew( $options ) { + private function initOldFileNew( array $options ) { $subquery = $this->newSubquery(); $subquery->table( 'filerevision' ) ->join( 'file', null, 'fr_file = file_id' ) @@ -253,7 +253,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder { ->from( $subquery ); } - private function initArchivedFile( $options ) { + private function initArchivedFile( array $options ) { $this->table( 'filearchive' ) ->join( 'actor', 'filearchive_actor', 'actor_id=fa_actor' ) ->join( diff --git a/includes/filerepo/file/OldLocalFile.php b/includes/filerepo/file/OldLocalFile.php index 76b624f1d5ad..8e4e7811b217 100644 --- a/includes/filerepo/file/OldLocalFile.php +++ b/includes/filerepo/file/OldLocalFile.php @@ -261,7 +261,9 @@ class OldLocalFile extends LocalFile { } } - private function buildQueryBuilderForLoad( IReadableDatabase $dbr, $options = [ 'omit-nonlazy' ] ) { + private function buildQueryBuilderForLoad( + IReadableDatabase $dbr, array $options = [ 'omit-nonlazy' ] + ): FileSelectQueryBuilder { $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr, $options ); $queryBuilder->where( [ 'oi_name' => $this->getName() ] ) ->orderBy( 'oi_timestamp', SelectQueryBuilder::SORT_DESC ); diff --git a/includes/filerepo/file/UnregisteredLocalFile.php b/includes/filerepo/file/UnregisteredLocalFile.php index e09923859f0c..1f4ccd182794 100644 --- a/includes/filerepo/file/UnregisteredLocalFile.php +++ b/includes/filerepo/file/UnregisteredLocalFile.php @@ -199,7 +199,7 @@ class UnregisteredLocalFile extends File { return $info['metadata']; } - private function getSizeAndMetadata() { + private function getSizeAndMetadata(): array { if ( $this->sizeAndMetadata === null ) { if ( !$this->getHandler() ) { $this->sizeAndMetadata = [ 'width' => 0, 'height' => 0, 'metadata' => [] ]; diff --git a/includes/installer/Task/SqliteCreateDatabaseTask.php b/includes/installer/Task/SqliteCreateDatabaseTask.php index 204a572ed06a..4298db4547e2 100644 --- a/includes/installer/Task/SqliteCreateDatabaseTask.php +++ b/includes/installer/Task/SqliteCreateDatabaseTask.php @@ -130,7 +130,9 @@ EOT; } } # Put a .htaccess file in case the user didn't take our advice - file_put_contents( "$dir/.htaccess", "Require all denied\n" ); + file_put_contents( "$dir/.htaccess", + "Require all denied\n" . + "Satisfy All\n" ); return Status::newGood(); } diff --git a/includes/installer/i18n/nan-hant.json b/includes/installer/i18n/nan-hant.json index 7ca51f8702fc..59cbdd5709e9 100644 --- a/includes/installer/i18n/nan-hant.json +++ b/includes/installer/i18n/nan-hant.json @@ -15,7 +15,7 @@ "config-no-session": "你連線的資料已經無去矣,看你的php.ini,並且確定<code>session.save_path</code>是正確的目錄。", "config-your-language": "你的語言:", "config-your-language-help": "選一个安裝過程時欲用的話語", - "config-wiki-language": "Wiki話語", + "config-wiki-language": "wiki語言:", "config-wiki-language-help": "選一个Wiki大部份用的話", "config-back": "← 倒退", "config-continue": "繼續 →", @@ -42,5 +42,6 @@ "config-header-postgres": "PostgreSQL設定", "config-header-sqlite": "SQLite設定", "config-project-namespace": "專案名空間:", - "config-ns-generic": "專案" + "config-ns-generic": "專案", + "mainpagedocfooter": "請查看[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 用者說明書]的資料通使用wiki 軟體\n\n== 入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 配置的設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki時常問答]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki的公布列單]" } diff --git a/includes/installer/i18n/nan-latn-pehoeji.json b/includes/installer/i18n/nan-latn-pehoeji.json index e3437e4d4805..8430b8f9272a 100644 --- a/includes/installer/i18n/nan-latn-pehoeji.json +++ b/includes/installer/i18n/nan-latn-pehoeji.json @@ -22,13 +22,10 @@ "config-session-error": "連線開始了的錯誤:$1", "config-session-expired": "你連線資料已經過時矣,連線的使用期限是設做$1。你會使改共php.ini的<code>session.gc_maxlifetime</code>改較長,並且重新安裝動作。", "config-no-session": "你連線的資料已經無去矣,看你的php.ini,並且確定<code>session.save_path</code>是正確的目錄。", - "config-your-language": "你的話語:", - "config-your-language-help": "選一个安裝過程時欲用的話語", - "config-wiki-language": "Wiki話語", - "config-wiki-language-help": "選一个Wiki大部份用的話", + "config-wiki-language": "Wiki gí-giân:", "config-back": "← 倒退", "config-continue": "繼續 →", - "config-page-language": "話語", + "config-page-language": "Gí-giân", "config-page-welcome": "歡迎來MediaWiki!", "config-page-dbconnect": "連接去資料庫", "config-page-upgrade": "共這馬的安裝升級", @@ -53,6 +50,5 @@ "config-no-db": "揣無適合的資料庫驅動程式!你需要安裝 PHP 資料庫驅動程式。\n這馬支援下跤類型的資料庫: $1 。\n\n若你是家己編譯 PHP,你需要重新設定並且開資料庫客戶端,譬如:用 <code>./configure --with-mysqli</code> 指令參數。\n如你是用 Debian 或 Ubuntu 的套件安裝,你著需要閣另外安裝,例:<code>php-mysql</code> 套件。", "config-outdated-sqlite": "<strong>Kéng-kò:</strong> Lí í-keng an-chng SQLite $1, m̄-koh i--ê pán-pún pí thang-chng--ê pán-pún $2 khah kū. Só͘-í lí bô-hoat-tō͘ ēng SQLite.", "config-no-fts3": "<strong>Kéng-kò: </strong> SQLite tī pian-e̍k--ê sî-chūn bô pau-koat [//sqlite.org/fts3.html FTS3 module], āu-tâi chhiau-chhoē kong-lêng tiō ē bô-hoat-tō͘ iōng.", - "mainpagetext": "'''MediaWiki已經裝好矣。'''", - "mainpagedocfooter": "請查看[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 用者說明書]的資料通使用wiki 軟體\n\n== 入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 配置的設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki時常問答]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki的公布列單]" + "mainpagetext": "'''MediaWiki已經裝好矣。'''" } diff --git a/includes/installer/i18n/zh-hant.json b/includes/installer/i18n/zh-hant.json index c081e46efc09..0321e4873481 100644 --- a/includes/installer/i18n/zh-hant.json +++ b/includes/installer/i18n/zh-hant.json @@ -169,6 +169,8 @@ "config-mysql-engine": "儲存引擎:", "config-mysql-innodb": "InnoDB(推薦)", "config-mysql-engine-help": "由於對同時連線有較好的處理能力,<strong>InnoDB</strong> 通常是最佳的選項。\n\n<strong>MyISAM</strong> 只在單人使用或者唯讀作業的情況之下才可能有較快的處理能力。\n相較於 InnoDB,MyISAM 也較容易出現資料損毀的情況。", + "config-server": "URL 主機名稱:", + "config-server-help": "用於存取您的 wiki 與 URL 的協定和主機名稱部分。自動偵測到的預設值在一般情況下會是正確的。", "config-site-name": "wiki 的名稱:", "config-site-name-help": "您所填入的內容會出現在瀏覽器的標題列以及各種其他地方。", "config-site-name-blank": "請輸入網站名稱。", @@ -302,6 +304,7 @@ "config-install-updates": "略過執行不需要的更新", "config-install-updates-failed": "<strong>錯誤:</strong> 插入更新鍵值至資料表失敗,並出現以下錯誤:$1", "config-install-sysop": "正在建立管理員使用者帳號", + "config-install-subscribe": "訂閱 mediawiki-announce", "config-install-subscribe-fail": "無法訂閱 [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ mediawiki-announce]:$1", "config-install-subscribe-notpossible": "未安裝 cURL,因此無法使用 <code>allow_url_fopen</code> 設定項目。", "config-install-subscribe-alreadysubscribed": "您已訂閱 mediawiki-announce。[https://lists.wikimedia.org/postorius/accounts/per-subscription-preferences/ 檢視或變更您的訂閱設定]。", @@ -315,6 +318,9 @@ "config-install-done-path": "<strong>恭喜!</strong>\n您已經成功安裝MediaWiki。\n\n安裝程式已自動產生 <code>LocalSettings.php</code> 檔案,\n該檔案中包含了您所有的設定項目。\n\n您需要下載該檔案,並將其放置在 <code>$4</code> 中,下載應已自動開始。\n\n若瀏覽器沒有提示您下載,或者您取消了下載,您可以點選下方連結重新下載:\n\n$3\n\n<strong>注意:</strong>如果您現在不下載此檔案,稍後結束安裝程式之後將無法再下載設定檔。\n\n當您完成本步驟後,您可以<strong>[$2 進入您的 wiki]</strong>。", "config-install-success": "MediaWiki 已安裝成功。您現在可以在 <$1$2> 上檢視您的 wiki。若您有任何問題,請閱讀常見問題清單:<https://www.mediawiki.org/wiki/Manual:FAQ/zh>,或是利用在頁面上所連結的支援論壇之一。", "config-install-db-success": "資料庫設定成功", + "config-install-generic": "正在執行任務「$1」", + "config-install-external-domains": "正在建立外部資料庫", + "config-skip-shared-domain": "略過可能的共享網域「$1」", "config-download-localsettings": "下載 <code>LocalSettings.php</code>", "config-help": "說明", "config-help-tooltip": "點選以展開", diff --git a/includes/jobqueue/jobs/ThumbnailRenderJob.php b/includes/jobqueue/jobs/ThumbnailRenderJob.php index 5fbdb3823bd6..f12532e27a9e 100644 --- a/includes/jobqueue/jobs/ThumbnailRenderJob.php +++ b/includes/jobqueue/jobs/ThumbnailRenderJob.php @@ -153,7 +153,7 @@ class ThumbnailRenderJob extends Job { return false; } - private function maybeEnqueueNextPage( $transformParams ) { + private function maybeEnqueueNextPage( array $transformParams ) { if ( ( $this->params['enqueueNextPage'] ?? false ) && ( $transformParams['page'] ?? 0 ) < ( $this->params['pageLimit'] ?? 0 ) diff --git a/includes/language/LanguageEventIngress.php b/includes/language/LanguageEventIngress.php index 18e083a47f39..d5960e04968e 100644 --- a/includes/language/LanguageEventIngress.php +++ b/includes/language/LanguageEventIngress.php @@ -2,7 +2,7 @@ namespace MediaWiki\Languages; -use MediaWiki\DomainEvent\EventIngressBase; +use MediaWiki\DomainEvent\DomainEventIngress; use MediaWiki\Page\Event\PageDeletedEvent; use MediaWiki\Page\Event\PageDeletedListener; use MediaWiki\Page\Event\PageRevisionUpdatedEvent; @@ -17,7 +17,7 @@ use MessageCache; * @internal */ class LanguageEventIngress - extends EventIngressBase + extends DomainEventIngress implements PageDeletedListener, PageRevisionUpdatedListener { diff --git a/includes/libs/ParamValidator/TypeDef/BooleanDef.php b/includes/libs/ParamValidator/TypeDef/BooleanDef.php index e2ab0cb1460b..19ee271bde00 100644 --- a/includes/libs/ParamValidator/TypeDef/BooleanDef.php +++ b/includes/libs/ParamValidator/TypeDef/BooleanDef.php @@ -57,7 +57,7 @@ class BooleanDef extends TypeDef { ); } - private function quoteVal( $v ) { + private function quoteVal( string $v ): ScalarParam { return new ScalarParam( ParamType::TEXT, "\"$v\"" ); } diff --git a/includes/libs/WRStats/LimitBatch.php b/includes/libs/WRStats/LimitBatch.php index 3758a40fc6e3..b83adb4cffda 100644 --- a/includes/libs/WRStats/LimitBatch.php +++ b/includes/libs/WRStats/LimitBatch.php @@ -72,7 +72,7 @@ class LimitBatch { return $this; } - private function queueOp( $type, $entity, $amount ) { + private function queueOp( string $type, ?EntityKey $entity, ?int $amount ) { $amount ??= $this->defaultAmount; if ( isset( $this->operations[$type] ) ) { throw new WRStatsError( 'Cannot queue multiple actions of the same type, ' . diff --git a/includes/libs/filebackend/FSFileBackend.php b/includes/libs/filebackend/FSFileBackend.php index 1d76f2bf9f5e..92ae176ee7e4 100644 --- a/includes/libs/filebackend/FSFileBackend.php +++ b/includes/libs/filebackend/FSFileBackend.php @@ -987,7 +987,8 @@ class FSFileBackend extends FileBackendStore { * @return string */ protected function htaccessPrivate() { - return "Require all denied\n"; + return "Require all denied\n" . + "Satisfy All\n"; } /** diff --git a/includes/libs/filebackend/FileBackendMultiWrite.php b/includes/libs/filebackend/FileBackendMultiWrite.php index 7be37112be27..7c59e161e396 100644 --- a/includes/libs/filebackend/FileBackendMultiWrite.php +++ b/includes/libs/filebackend/FileBackendMultiWrite.php @@ -776,7 +776,7 @@ class FileBackendMultiWrite extends FileBackend { return $this->backends[$this->masterIndex]->getFileList( $realParams ); } - private function getFileListForWrite( $params ) { + private function getFileListForWrite( array $params ): array { $files = []; // Get the list of thumbnails from all backends to allow // deleting all of them. Otherwise, old thumbnails existing on diff --git a/includes/libs/http/MultiHttpClient.php b/includes/libs/http/MultiHttpClient.php index 4aefaf1551b5..ff0bd5f6866c 100644 --- a/includes/libs/http/MultiHttpClient.php +++ b/includes/libs/http/MultiHttpClient.php @@ -734,7 +734,7 @@ class MultiHttpClient implements LoggerAwareInterface { } } - private function useReverseProxy( array &$req, $proxy ) { + private function useReverseProxy( array &$req, string $proxy ) { $parsedProxy = parse_url( $proxy ); if ( $parsedProxy === false ) { throw new InvalidArgumentException( "Invalid reverseProxy configured: $proxy" ); diff --git a/includes/libs/mime/MSCompoundFileReader.php b/includes/libs/mime/MSCompoundFileReader.php index ff6afe144088..398c2bdd8e9d 100644 --- a/includes/libs/mime/MSCompoundFileReader.php +++ b/includes/libs/mime/MSCompoundFileReader.php @@ -170,11 +170,11 @@ class MSCompoundFileReader { $this->valid = true; } - private function sectorOffset( $sectorId ) { + private function sectorOffset( int $sectorId ): int { return $this->sectorLength * ( $sectorId + 1 ); } - private function decodeClsid( $binaryClsid ) { + private function decodeClsid( string $binaryClsid ): string { $parts = unpack( 'Va/vb/vc/C8d', $binaryClsid ); return sprintf( "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", $parts['a'], @@ -220,7 +220,7 @@ class MSCompoundFileReader { return $data; } - private function bin2dec( $str, $offset, $length ) { + private function bin2dec( string $str, int $offset, int $length ): int { $value = 0; for ( $i = $length - 1; $i >= 0; $i-- ) { $value *= 256; @@ -229,7 +229,7 @@ class MSCompoundFileReader { return $value; } - private function readOffset( $offset, $length ) { + private function readOffset( int $offset, int $length ): string { $this->fseek( $offset ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged $block = @fread( $this->file, $length ); @@ -243,7 +243,7 @@ class MSCompoundFileReader { return $block; } - private function readSector( $sectorId ) { + private function readSector( int $sectorId ): string { return $this->readOffset( $this->sectorOffset( $sectorId ), 1 << $this->header['sector_shift'] ); } @@ -256,7 +256,7 @@ class MSCompoundFileReader { throw new RuntimeException( $message, $code ); } - private function fseek( $offset ) { + private function fseek( int $offset ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged $result = @fseek( $this->file, $offset ); if ( $result !== 0 ) { @@ -287,14 +287,14 @@ class MSCompoundFileReader { } } - private function getNextSectorIdFromFat( $sectorId ) { + private function getNextSectorIdFromFat( int $sectorId ): int { $entriesPerSector = intdiv( $this->sectorLength, 4 ); $fatSectorId = intdiv( $sectorId, $entriesPerSector ); $fatSectorArray = $this->getFatSector( $fatSectorId ); return $fatSectorArray[$sectorId % $entriesPerSector]; } - private function getFatSector( $fatSectorId ) { + private function getFatSector( int $fatSectorId ): array { if ( !isset( $this->fat[$fatSectorId] ) ) { $fat = []; if ( !isset( $this->difat[$fatSectorId] ) ) { diff --git a/includes/libs/mime/XmlTypeCheck.php b/includes/libs/mime/XmlTypeCheck.php index b86fd406bef7..274bcbaafc4c 100644 --- a/includes/libs/mime/XmlTypeCheck.php +++ b/includes/libs/mime/XmlTypeCheck.php @@ -197,7 +197,7 @@ class XmlTypeCheck { } } - private function readNext( XMLReader $reader ) { + private function readNext( XMLReader $reader ): bool { set_error_handler( function ( $line, $file ) { $this->wellFormed = false; return true; @@ -207,7 +207,7 @@ class XmlTypeCheck { return $ret; } - private function validate( $reader ) { + private function validate( XMLReader $reader ) { // First, move through anything that isn't an element, and // handle any processing instructions with the callback do { diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php index 5edb35bbdd9f..5e73704c6088 100644 --- a/includes/libs/objectcache/BagOStuff.php +++ b/includes/libs/objectcache/BagOStuff.php @@ -557,17 +557,6 @@ abstract class BagOStuff implements } /** - * Clear the "last error" registry - * - * @since 1.23 - * @deprecated Since 1.38, hard deprecated in 1.43 - */ - public function clearLastError() { - wfDeprecated( __METHOD__, '1.38' ); - $this->lastError = self::ERR_NONE; - } - - /** * Set the "last error" registry due to a problem encountered during an attempted operation * * @param int $error BagOStuff:ERR_* constant diff --git a/includes/libs/objectcache/RESTBagOStuff.php b/includes/libs/objectcache/RESTBagOStuff.php index b2600b908c86..dcc7bd96a2f9 100644 --- a/includes/libs/objectcache/RESTBagOStuff.php +++ b/includes/libs/objectcache/RESTBagOStuff.php @@ -226,7 +226,7 @@ class RESTBagOStuff extends MediumSpecificBagOStuff { [ 'cacheKey' => $key ] ); } - $this->updateOpStats( self::METRIC_OP_SET, [ $key => [ strlen( $rbody ), 0 ] ] ); + $this->updateOpStats( self::METRIC_OP_SET, [ $key => [ strlen( $req['body'] ), 0 ] ] ); return $res; } diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php index 925a2709158d..d2661d68bd7a 100644 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -194,7 +194,7 @@ class WANObjectCache implements protected $coalesceScheme; /** @var TracerInterface */ - private $tracer = null; + private $tracer; /** @var array<int,array> List of (key, UNIX timestamp) tuples for get() cache misses */ private $missLog; @@ -461,6 +461,7 @@ class WANObjectCache implements // Also, if no $info parameter is provided, then it doesn't matter how it changes here. $legacyInfo = ( $info !== self::PASS_BY_REF ); + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key, $checkKeys ); $now = $this->getCurrentTime(); @@ -521,6 +522,7 @@ class WANObjectCache implements // Also, if no $info parameter is provided, then it doesn't matter how it changes here. $legacyInfo = ( $info !== self::PASS_BY_REF ); + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $keys, $checkKeys ); $curTTLs = []; @@ -803,6 +805,7 @@ class WANObjectCache implements * @return bool Success */ final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); $keygroup = $this->determineKeyGroupForStats( $key ); @@ -1055,6 +1058,7 @@ class WANObjectCache implements * @return bool True if the item was purged or not found, false on failure */ final public function delete( $key, $ttl = self::HOLDOFF_TTL ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); // Purge values must be stored under the value key so that WANObjectCache::set() @@ -1116,7 +1120,9 @@ class WANObjectCache implements * @return float UNIX timestamp */ final public function getCheckKeyTime( $key ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); + return $this->getMultiCheckKeyTime( [ $key ] )[$key]; } @@ -1182,6 +1188,7 @@ class WANObjectCache implements * @since 1.31 */ final public function getMultiCheckKeyTime( array $keys ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $keys ); $checkSisterKeysByKey = []; @@ -1246,6 +1253,7 @@ class WANObjectCache implements * @return bool True if the item was purged or not found, false on failure */ public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); $checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP ); @@ -1298,6 +1306,7 @@ class WANObjectCache implements * @return bool True if the item was purged or not found, false on failure */ public function resetCheckKey( $key ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); $checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP ); @@ -1618,7 +1627,9 @@ class WANObjectCache implements final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [], array $cbParams = [] ) { + /** @noinspection PhpUnusedLocalVariableInspection */ $span = $this->startOperationSpan( __FUNCTION__, $key ); + $version = $opts['version'] ?? null; $pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE; $pCache = ( $pcTTL >= 0 ) @@ -1828,6 +1839,7 @@ class WANObjectCache implements $preCallbackTime = $this->getCurrentTime(); ++$this->callbackDepth; // https://github.com/phan/phan/issues/4419 + /** @noinspection PhpUnusedLocalVariableInspection */ $value = null; try { $value = $callback( diff --git a/includes/libs/rdbms/ChronologyProtector.php b/includes/libs/rdbms/ChronologyProtector.php index 5b8783cbe047..95b9fd7d1440 100644 --- a/includes/libs/rdbms/ChronologyProtector.php +++ b/includes/libs/rdbms/ChronologyProtector.php @@ -591,7 +591,7 @@ class ChronologyProtector implements LoggerAwareInterface { $this->wallClockOverride =& $time; } - private function marshalPositions( array $positions ) { + private function marshalPositions( array $positions ): array { foreach ( $positions[ self::FLD_POSITIONS ] as $key => $pos ) { $positions[ self::FLD_POSITIONS ][ $key ] = $pos->toArray(); } diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 6904f654ead4..4c2e9495d265 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -877,7 +877,9 @@ abstract class Database implements Stringable, IDatabaseForOwner, IMaintainableD return $status; } - private function handleErroredQuery( QueryStatus $status, $sql, $fname, $queryRuntime, $priorSessInfo ) { + private function handleErroredQuery( + QueryStatus $status, Query $sql, string $fname, float $queryRuntime, CriticalSessionInfo $priorSessInfo + ): int { $errflags = self::ERR_NONE; $error = $status->message; $errno = $status->code; diff --git a/includes/libs/rdbms/database/DatabaseMySQL.php b/includes/libs/rdbms/database/DatabaseMySQL.php index f7afce6b6c6c..2dd9cc904254 100644 --- a/includes/libs/rdbms/database/DatabaseMySQL.php +++ b/includes/libs/rdbms/database/DatabaseMySQL.php @@ -808,7 +808,7 @@ class DatabaseMySQL extends Database { } } - private function mysqlRealEscapeString( $s ) { + private function mysqlRealEscapeString( $s ): string { $conn = $this->getBindingHandle(); return $conn->real_escape_string( (string)$s ); diff --git a/includes/libs/rdbms/database/QueryBuilderFromRawSql.php b/includes/libs/rdbms/database/QueryBuilderFromRawSql.php index 3be8e0e13f69..af268f9cc2e2 100644 --- a/includes/libs/rdbms/database/QueryBuilderFromRawSql.php +++ b/includes/libs/rdbms/database/QueryBuilderFromRawSql.php @@ -99,7 +99,7 @@ class QueryBuilderFromRawSql { ); } - private static function isWriteQuery( $rawSql ) { + private static function isWriteQuery( string $rawSql ): bool { // Treat SELECT queries without FOR UPDATE queries as non-writes. This matches // how MySQL enforces read_only (FOR SHARE and LOCK IN SHADE MODE are allowed). // Handle (SELECT ...) UNION (SELECT ...) queries in a similar fashion. diff --git a/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php b/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php index 0f06ee2f4679..ab4b813bfe8c 100644 --- a/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php +++ b/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php @@ -31,7 +31,7 @@ trait DoctrineAbstractSchemaTrait { private AbstractPlatform $platform; - private function addTableToSchema( Schema $schema, array $schemaSpec ) { + private function addTableToSchema( Schema $schema, array $schemaSpec ): Schema { $prefix = $this->platform->getName() === 'postgresql' ? '' : '/*_*/'; $table = $schema->createTable( $prefix . $schemaSpec['name'] ); diff --git a/includes/libs/rdbms/expression/LikeValue.php b/includes/libs/rdbms/expression/LikeValue.php index 58b732927887..95bd801defa5 100644 --- a/includes/libs/rdbms/expression/LikeValue.php +++ b/includes/libs/rdbms/expression/LikeValue.php @@ -59,7 +59,7 @@ class LikeValue { return $dbQuoter->addQuotes( $s ) . ' ESCAPE ' . $dbQuoter->addQuotes( $escapeChar ); } - private function escapeLikeInternal( $s, $escapeChar = '`' ) { + private function escapeLikeInternal( string $s, string $escapeChar = '`' ): string { return str_replace( [ $escapeChar, '%', '_' ], [ "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_" ], diff --git a/includes/libs/rdbms/lbfactory/LBFactorySimple.php b/includes/libs/rdbms/lbfactory/LBFactorySimple.php index 4d90b309acd0..80f2eaedd44a 100644 --- a/includes/libs/rdbms/lbfactory/LBFactorySimple.php +++ b/includes/libs/rdbms/lbfactory/LBFactorySimple.php @@ -124,7 +124,7 @@ class LBFactorySimple extends LBFactory { return $lbs; } - private function newLoadBalancer( string $clusterName, array $servers ) { + private function newLoadBalancer( string $clusterName, array $servers ): ILoadBalancerForOwner { $lb = new LoadBalancer( array_merge( $this->baseLoadBalancerParams(), [ diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php index 66e3a1ac30b8..db959970be33 100644 --- a/includes/libs/rdbms/loadbalancer/LoadBalancer.php +++ b/includes/libs/rdbms/loadbalancer/LoadBalancer.php @@ -233,7 +233,7 @@ class LoadBalancer implements ILoadBalancerForOwner { $this->defaultGroup = isset( $this->groupLoads[ $group ] ) ? $group : self::GROUP_GENERIC; } - private static function newTrackedConnectionsArray() { + private static function newTrackedConnectionsArray(): array { // Note that CATEGORY_GAUGE connections are untracked return [ self::CATEGORY_ROUND => [], diff --git a/includes/libs/rdbms/platform/SQLPlatform.php b/includes/libs/rdbms/platform/SQLPlatform.php index 6ac4f1c07865..a1bc9168485d 100644 --- a/includes/libs/rdbms/platform/SQLPlatform.php +++ b/includes/libs/rdbms/platform/SQLPlatform.php @@ -1596,7 +1596,7 @@ class SQLPlatform implements ISQLPlatform { ); } - private function scrubArray( $array, $listType = self::LIST_AND ) { + private function scrubArray( $array, int $listType = self::LIST_AND ): string { if ( is_array( $array ) ) { $scrubbedArray = []; foreach ( $array as $key => $value ) { diff --git a/includes/media/DjVuImage.php b/includes/media/DjVuImage.php index 1ff80602730e..d5a18e75bd6d 100644 --- a/includes/media/DjVuImage.php +++ b/includes/media/DjVuImage.php @@ -97,7 +97,7 @@ class DjVuImage { fclose( $file ); } - private function dumpForm( $file, $length, $indent ) { + private function dumpForm( $file, int $length, int $indent ) { $start = ftell( $file ); $secondary = fread( $file, 4 ); echo str_repeat( ' ', $indent * 4 ) . "($secondary)\n"; @@ -159,7 +159,7 @@ class DjVuImage { return $info; } - private function readChunk( $file ) { + private function readChunk( $file ): array { $header = fread( $file, 8 ); if ( strlen( $header ) < 8 ) { return [ false, 0 ]; @@ -169,7 +169,7 @@ class DjVuImage { return [ $arr['chunk'], $arr['length'] ]; } - private function skipChunk( $file, $chunkLength ) { + private function skipChunk( $file, int $chunkLength ) { fseek( $file, $chunkLength, SEEK_CUR ); if ( ( $chunkLength & 1 ) && !feof( $file ) ) { @@ -178,7 +178,7 @@ class DjVuImage { } } - private function getMultiPageInfo( $file, $formLength ) { + private function getMultiPageInfo( $file, int $formLength ) { // For now, we'll just look for the first page in the file // and report its information, hoping others are the same size. $start = ftell( $file ); @@ -366,7 +366,7 @@ EOR; return $json; } - private function pageTextCallback( string $match ) { + private function pageTextCallback( string $match ): string { # Get rid of invalid UTF-8 $val = UtfNormal\Validator::cleanUp( stripcslashes( $match ) ); return str_replace( '�', '', $val ); @@ -434,7 +434,7 @@ EOR; return $result; } - private function parseFormDjvu( $line ) { + private function parseFormDjvu( string $line ) { $parentLevel = strspn( $line, ' ' ); $line = strtok( "\n" ); # Find INFO diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php index c5ec3c403c8b..cc7a092e3341 100644 --- a/includes/media/MediaHandler.php +++ b/includes/media/MediaHandler.php @@ -818,7 +818,7 @@ abstract class MediaHandler { * @stable to override * * @param File $file - * @return string + * @return string HTML */ public function getShortDesc( $file ) { return self::getGeneralShortDesc( $file ); @@ -830,7 +830,7 @@ abstract class MediaHandler { * @stable to override * * @param File $file - * @return string + * @return string HTML */ public function getLongDesc( $file ) { return self::getGeneralLongDesc( $file ); @@ -840,7 +840,7 @@ abstract class MediaHandler { * Used instead of getShortDesc if there is no handler registered for file. * * @param File $file - * @return string + * @return string HTML */ public static function getGeneralShortDesc( $file ) { global $wgLang; @@ -852,7 +852,7 @@ abstract class MediaHandler { * Used instead of getLongDesc if there is no handler registered for file. * * @param File $file - * @return string + * @return string HTML */ public static function getGeneralLongDesc( $file ) { return wfMessage( 'file-info' )->sizeParams( $file->getSize() ) @@ -882,7 +882,7 @@ abstract class MediaHandler { * @stable to override * * @param File $file - * @return string Dimensions + * @return string Dimensions (plain text) */ public function getDimensionsString( $file ) { return ''; diff --git a/includes/media/SVGReader.php b/includes/media/SVGReader.php index f988456e2f66..f68f446f1ccb 100644 --- a/includes/media/SVGReader.php +++ b/includes/media/SVGReader.php @@ -314,7 +314,7 @@ class SVGReader { } } - private function debug( $data ) { + private function debug( string $data ) { if ( $this->mDebug ) { wfDebug( "SVGReader: $data" ); } diff --git a/includes/page/Article.php b/includes/page/Article.php index c6ff94f24503..962990012952 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -784,7 +784,7 @@ class Article implements Page { $outputPage->setRevisionId( $this->getRevIdFetched() ); $outputPage->setRevisionIsCurrent( $rev->isCurrent() ); # Preload timestamp to avoid a DB hit - $outputPage->setRevisionTimestamp( $rev->getTimestamp() ); + $outputPage->getMetadata()->setRevisionTimestamp( $rev->getTimestamp() ); # Pages containing custom CSS or JavaScript get special treatment if ( $this->getTitle()->isSiteConfigPage() || $this->getTitle()->isUserConfigPage() ) { @@ -929,7 +929,7 @@ class Article implements Page { # Preload timestamp to avoid a DB hit $cachedTimestamp = $pOutput->getRevisionTimestamp(); if ( $cachedTimestamp !== null ) { - $outputPage->setRevisionTimestamp( $cachedTimestamp ); + $outputPage->getMetadata()->setRevisionTimestamp( $cachedTimestamp ); $this->mPage->setTimestamp( $cachedTimestamp ); } } @@ -972,7 +972,7 @@ class Article implements Page { $cachedId = $pOutput->getCacheRevisionId(); if ( $cachedId !== null ) { $outputPage->setRevisionId( $cachedId ); - $outputPage->setRevisionTimestamp( $pOutput->getTimestamp() ); + $outputPage->getMetadata()->setRevisionTimestamp( $pOutput->getTimestamp() ); } } diff --git a/includes/page/Event/PageDeletedListener.php b/includes/page/Event/PageDeletedListener.php index 93ad81570fe1..9b3eaa2af8b5 100644 --- a/includes/page/Event/PageDeletedListener.php +++ b/includes/page/Event/PageDeletedListener.php @@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event; * event type 'PageDeleted', see PageDeletedEvent::TYPE. * * @see PageDeletedEvent + * @unstable until 1.45, should become stable to implement */ interface PageDeletedListener { diff --git a/includes/page/Event/PageMovedListener.php b/includes/page/Event/PageMovedListener.php index 041812c26574..28dc060c9b2c 100644 --- a/includes/page/Event/PageMovedListener.php +++ b/includes/page/Event/PageMovedListener.php @@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event; * event type 'PageMoved', see PageMovedEvent::TYPE. * * @see PageMovedEvent + * @unstable until 1.45, should become stable to implement */ interface PageMovedListener { diff --git a/includes/page/Event/PageRevisionUpdatedEvent.php b/includes/page/Event/PageRevisionUpdatedEvent.php index c65487d01570..7d8cab6ec4ff 100644 --- a/includes/page/Event/PageRevisionUpdatedEvent.php +++ b/includes/page/Event/PageRevisionUpdatedEvent.php @@ -48,11 +48,11 @@ use Wikimedia\Assert\Assert; * * Extensions that want to subscribe to this event should list * "PageRevisionUpdated" as a subscribed event type. - * Subscribers based on EventIngressBase should implement the + * Subscribers based on DomainEventIngress should implement the * handlePageRevisionUpdatedEvent() listener method to be informed when * a page update has been committed to the database. * - * See the documentation of EventIngressBase and DomainEventSource for + * See the documentation of DomainEventIngress and DomainEventSource for * more options and details. * * @unstable until 1.45 diff --git a/includes/page/Event/PageRevisionUpdatedListener.php b/includes/page/Event/PageRevisionUpdatedListener.php index 598da5cb848d..d2ed812d712a 100644 --- a/includes/page/Event/PageRevisionUpdatedListener.php +++ b/includes/page/Event/PageRevisionUpdatedListener.php @@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event; * event type 'PageRevisionUpdated', see PageRevisionUpdatedEvent::TYPE. * * @see PageRevisionUpdatedEvent + * @unstable until 1.45, should become stable to implement */ interface PageRevisionUpdatedListener { diff --git a/includes/page/Event/PageStateListener.php b/includes/page/Event/PageStateListener.php index 3ff3c1427ef2..6629bae38ba0 100644 --- a/includes/page/Event/PageStateListener.php +++ b/includes/page/Event/PageStateListener.php @@ -9,6 +9,7 @@ namespace MediaWiki\Page\Event; * event type 'PageState', see PageStateEvent::TYPE. * * @see PageStateEvent + * @unstable until 1.45, should become stable to implement */ interface PageStateListener { diff --git a/includes/pager/ContributionsPager.php b/includes/pager/ContributionsPager.php index ebd1efa87869..fdebeb41f92e 100644 --- a/includes/pager/ContributionsPager.php +++ b/includes/pager/ContributionsPager.php @@ -920,12 +920,14 @@ abstract class ContributionsPager extends RangeChronologicalPager { $revUserId = $revUser ? $revUser->getId() : 0; $revUserText = $revUser ? $revUser->getName() : ''; if ( $this->target !== $revUserText ) { - $userlink = ' <span class="mw-changeslist-separator"></span> ' - . Html::rawElement( 'bdi', [ 'dir' => $dir ], - Linker::userLink( $revUserId, $revUserText ) ); - $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams( - Linker::userTalkLink( $revUserId, $revUserText ) )->escaped() . ' '; + $userPageLink = Linker::userLink( $revUserId, $revUserText ); + $userTalkLink = Linker::userTalkLink( $revUserId, $revUserText ); + + $userlink = ' <span class="mw-changeslist-separator"></span> ' . + Html::rawElement( 'bdi', [ 'dir' => $dir ], $userPageLink ) . + Linker::renderUserToolLinksArray( [ $userTalkLink ], false ); } + return $userlink; } diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php index e364bf0ae2b0..971442f2c486 100644 --- a/includes/parser/ParserOptions.php +++ b/includes/parser/ParserOptions.php @@ -1099,6 +1099,7 @@ class ParserOptions { * Get a ParserOptions object from a given user. * Language will be taken from $wgLang. * + * @since 1.13 * @param UserIdentity $user * @return ParserOptions */ @@ -1109,6 +1110,7 @@ class ParserOptions { /** * Get a ParserOptions object from a given user and language * + * @since 1.19 * @param UserIdentity $user * @param Language $lang * @return ParserOptions @@ -1120,6 +1122,7 @@ class ParserOptions { /** * Get a ParserOptions object from a IContextSource object * + * @since 1.19 * @param IContextSource $context * @return ParserOptions */ diff --git a/includes/parser/Parsoid/Config/SiteConfig.php b/includes/parser/Parsoid/Config/SiteConfig.php index 24a6eef635b4..8a61b61cde11 100644 --- a/includes/parser/Parsoid/Config/SiteConfig.php +++ b/includes/parser/Parsoid/Config/SiteConfig.php @@ -749,6 +749,13 @@ class SiteConfig extends ISiteConfig { } /** @inheritDoc */ + protected function shouldValidateExtConfig(): bool { + // Only perform json schema validation for extension module + // configurations when running tests. + return defined( 'MW_PHPUNIT_TEST' ) || defined( 'MW_PARSER_TEST' ); + } + + /** @inheritDoc */ public function getMaxTemplateDepth(): int { return (int)$this->config->get( MainConfigNames::MaxTemplateDepth ); } @@ -815,6 +822,14 @@ class SiteConfig extends ISiteConfig { return $this->config->get( MainConfigNames::ExternalLinkTarget ); } + /** + * Return the localization key we should use for asynchronous + * fallback content. + */ + public function getAsyncFallbackMessageKey(): string { + return 'parsoid-async-not-ready-fallback'; + } + // MW-specific helper /** diff --git a/includes/recentchanges/ChangeTrackingEventIngress.php b/includes/recentchanges/ChangeTrackingEventIngress.php index f4efe4136820..3da2b54f4b35 100644 --- a/includes/recentchanges/ChangeTrackingEventIngress.php +++ b/includes/recentchanges/ChangeTrackingEventIngress.php @@ -5,10 +5,12 @@ namespace MediaWiki\RecentChanges; use LogicException; use MediaWiki\ChangeTags\ChangeTagsStore; use MediaWiki\Config\Config; -use MediaWiki\DomainEvent\EventIngressBase; +use MediaWiki\Content\IContentHandlerFactory; +use MediaWiki\DomainEvent\DomainEventIngress; use MediaWiki\HookContainer\HookContainer; use MediaWiki\HookContainer\HookRunner; use MediaWiki\JobQueue\JobQueueGroup; +use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob; use MediaWiki\JobQueue\Jobs\RevertedTagUpdateJob; use MediaWiki\MainConfigNames; use MediaWiki\Page\Event\PageRevisionUpdatedEvent; @@ -30,7 +32,7 @@ use MediaWiki\User\UserNameUtils; * @internal */ class ChangeTrackingEventIngress - extends EventIngressBase + extends DomainEventIngress implements PageRevisionUpdatedListener { @@ -60,7 +62,8 @@ class ChangeTrackingEventIngress 'UserNameUtils', 'TalkPageNotificationManager', 'MainConfig', - 'JobQueueGroup' + 'JobQueueGroup', + 'ContentHandlerFactory', ], 'events' => [ // see registerListeners() PageRevisionUpdatedEvent::TYPE @@ -75,7 +78,9 @@ class ChangeTrackingEventIngress private UserNameUtils $userNameUtils; private TalkPageNotificationManager $talkPageNotificationManager; private JobQueueGroup $jobQueueGroup; + private IContentHandlerFactory $contentHandlerFactory; private bool $useRcPatrol; + private bool $rcWatchCategoryMembership; public function __construct( ChangeTagsStore $changeTagsStore, @@ -86,7 +91,8 @@ class ChangeTrackingEventIngress UserNameUtils $userNameUtils, TalkPageNotificationManager $talkPageNotificationManager, Config $mainConfig, - JobQueueGroup $jobQueueGroup + JobQueueGroup $jobQueueGroup, + IContentHandlerFactory $contentHandlerFactory ) { // NOTE: keep in sync with self::OBJECT_SPEC $this->changeTagsStore = $changeTagsStore; @@ -97,8 +103,12 @@ class ChangeTrackingEventIngress $this->userNameUtils = $userNameUtils; $this->talkPageNotificationManager = $talkPageNotificationManager; $this->jobQueueGroup = $jobQueueGroup; + $this->contentHandlerFactory = $contentHandlerFactory; $this->useRcPatrol = $mainConfig->get( MainConfigNames::UseRCPatrol ); + $this->rcWatchCategoryMembership = $mainConfig->get( + MainConfigNames::RCWatchCategoryMembership + ); } public static function newForTesting( @@ -110,7 +120,8 @@ class ChangeTrackingEventIngress UserNameUtils $userNameUtils, TalkPageNotificationManager $talkPageNotificationManager, Config $mainConfig, - JobQueueGroup $jobQueueGroup + JobQueueGroup $jobQueueGroup, + IContentHandlerFactory $contentHandlerFactory ) { $ingress = new self( $changeTagsStore, @@ -121,7 +132,8 @@ class ChangeTrackingEventIngress $userNameUtils, $talkPageNotificationManager, $mainConfig, - $jobQueueGroup + $jobQueueGroup, + $contentHandlerFactory ); $ingress->initSubscriber( self::OBJECT_SPEC ); return $ingress; @@ -163,12 +175,16 @@ class ChangeTrackingEventIngress ); } - if ( $event->isEffectiveContentChange() && !$event->isImplicit() ) { - $this->updateUserEditTrackerAfterPageUpdated( - $event->getPerformer() - ); + if ( $event->isEffectiveContentChange() ) { + $this->generateCategoryMembershipChanges( $event ); + + if ( !$event->isImplicit() ) { + $this->updateUserEditTrackerAfterPageUpdated( + $event->getPerformer() + ); - $this->updateNewTalkAfterPageUpdated( $event ); + $this->updateNewTalkAfterPageUpdated( $event ); + } } if ( $event->isRevert() && $event->isEffectiveContentChange() ) { @@ -176,6 +192,49 @@ class ChangeTrackingEventIngress } } + /** + * Create RC entries for category changes that resulted from this update + * if the relevant config is enabled. + * This should only be triggered for actual edits, not reconciliation events (T390636). + * + * @param PageRevisionUpdatedEvent $event + */ + private function generateCategoryMembershipChanges( PageRevisionUpdatedEvent $event ): void { + if ( $this->rcWatchCategoryMembership + && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE ) + && $this->anyChangedSlotSupportsCategories( $event ) + ) { + // Note: jobs are pushed after deferred updates, so the job should be able to see + // the recent change entry (also done via deferred updates) and carry over any + // bot/deletion/IP flags, ect. + $this->jobQueueGroup->lazyPush( + CategoryMembershipChangeJob::newSpec( + $event->getPage(), + $event->getLatestRevisionAfter()->getTimestamp(), + $event->hasCause( PageRevisionUpdatedEvent::CAUSE_IMPORT ) + ) + ); + } + } + + /** + * Determine whether any slots changed in this update supports categories. + * @param PageRevisionUpdatedEvent $event + * @return bool + */ + private function anyChangedSlotSupportsCategories( PageRevisionUpdatedEvent $event ): bool { + $slotsUpdate = $event->getSlotsUpdate(); + foreach ( $slotsUpdate->getModifiedRoles() as $role ) { + $model = $slotsUpdate->getModifiedSlot( $role )->getModel(); + + if ( $this->contentHandlerFactory->getContentHandler( $model )->supportsCategories() ) { + return true; + } + } + + return false; + } + private function updateChangeTagsAfterPageUpdated( array $tags, int $revId ) { $this->changeTagsStore->addTags( $tags, null, $revId ); } diff --git a/includes/search/SearchEventIngress.php b/includes/search/SearchEventIngress.php index 5ee81f4150eb..f6aff749f797 100644 --- a/includes/search/SearchEventIngress.php +++ b/includes/search/SearchEventIngress.php @@ -2,7 +2,7 @@ namespace MediaWiki\Search; -use MediaWiki\DomainEvent\EventIngressBase; +use MediaWiki\DomainEvent\DomainEventIngress; use MediaWiki\Page\Event\PageDeletedEvent; use MediaWiki\Page\Event\PageDeletedListener; use MediaWiki\Page\Event\PageRevisionUpdatedEvent; @@ -17,7 +17,7 @@ use MediaWiki\Revision\SlotRecord; * @internal */ class SearchEventIngress - extends EventIngressBase + extends DomainEventIngress implements PageDeletedListener, PageRevisionUpdatedListener { diff --git a/includes/specialpage/ContributionsSpecialPage.php b/includes/specialpage/ContributionsSpecialPage.php index 7a5a43811f0f..dec9fdfe807f 100644 --- a/includes/specialpage/ContributionsSpecialPage.php +++ b/includes/specialpage/ContributionsSpecialPage.php @@ -126,6 +126,17 @@ class ContributionsSpecialPage extends IncludableSpecialPage { * @inheritDoc */ public function execute( $par ) { + $request = $this->getRequest(); + $target = $request->getText( 'target' ); + + if ( $target !== '' ) { + // Update the value in the request so that code reading it + // directly form the request gets the trimmed value (T378279). + $request->setVal( 'target', trim( $target ) ); + } + + $target = trim( $par ?? $target ); + $this->setHeaders(); $this->outputHeader(); $this->checkPermissions(); @@ -144,11 +155,6 @@ class ContributionsSpecialPage extends IncludableSpecialPage { ] ); $this->addHelpLink( 'Help:User contributions' ); - $request = $this->getRequest(); - - $target = $par ?? $request->getVal( 'target', '' ); - '@phan-var string $target'; // getVal does not return null here - // Normalize underscores that may be present in the target parameter // if it was passed in as a path param, rather than a query param // where HTMLForm may have already performed preprocessing (T372444). diff --git a/includes/specials/SpecialBotPasswords.php b/includes/specials/SpecialBotPasswords.php index e660e09b0dd2..89c33301f0b1 100644 --- a/includes/specials/SpecialBotPasswords.php +++ b/includes/specials/SpecialBotPasswords.php @@ -347,7 +347,7 @@ class SpecialBotPasswords extends FormSpecialPage { return false; } - private function save( array $data ) { + private function save( array $data ): Status { $bp = BotPassword::newUnsaved( [ 'centralId' => $this->userId, 'appId' => $this->par, diff --git a/includes/specials/SpecialComparePages.php b/includes/specials/SpecialComparePages.php index dac7d2be5f94..241dea83c156 100644 --- a/includes/specials/SpecialComparePages.php +++ b/includes/specials/SpecialComparePages.php @@ -146,7 +146,7 @@ class SpecialComparePages extends SpecialPage { } } - private function revOrTitle( $revision, $title ) { + private function revOrTitle( ?int $revision, ?string $title ): ?int { if ( $revision ) { return $revision; } elseif ( $title ) { diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php index 88e548a9e967..0cbbfb371be8 100644 --- a/includes/specials/SpecialDoubleRedirects.php +++ b/includes/specials/SpecialDoubleRedirects.php @@ -71,7 +71,7 @@ class SpecialDoubleRedirects extends QueryPage { return $this->msg( 'doubleredirectstext' )->parseAsBlock(); } - private function reallyGetQueryInfo( $namespace = null, $title = null ) { + private function reallyGetQueryInfo( ?int $namespace = null, ?string $title = null ): array { $limitToTitle = !( $namespace === null && $title === null ); $retval = [ 'tables' => [ diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 526c4ade508a..cdfd5dd707a9 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -276,7 +276,7 @@ class SpecialImport extends SpecialPage { } } - private function getMappingFormPart( $sourceName ) { + private function getMappingFormPart( string $sourceName ): array { $defaultNamespace = $this->getConfig()->get( MainConfigNames::ImportTargetNamespace ); return [ 'mapping' => [ diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index 450c72d30f23..48e34d52cb10 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -54,7 +54,7 @@ class SpecialLinkSearch extends QueryPage { private UrlUtils $urlUtils; - private function setParams( $params ) { + private function setParams( array $params ) { $this->mQuery = $params['query']; $this->mNs = $params['namespace']; $this->mProt = $params['protocol']; diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php index c4b1cdd800ea..fd7298bea7e3 100644 --- a/includes/specials/SpecialLog.php +++ b/includes/specials/SpecialLog.php @@ -321,7 +321,7 @@ class SpecialLog extends SpecialPage { } } - private function getActionButtons( $formcontents ) { + private function getActionButtons( string $formcontents ): string { $canRevDelete = $this->getAuthority() ->isAllowedAll( 'deletedhistory', 'deletelogentry' ); $showTagEditUI = ChangeTags::showTagEditingUI( $this->getAuthority() ); diff --git a/includes/specials/SpecialMovePage.php b/includes/specials/SpecialMovePage.php index 94e44f0d61e9..2a4000ab2754 100644 --- a/includes/specials/SpecialMovePage.php +++ b/includes/specials/SpecialMovePage.php @@ -964,7 +964,7 @@ class SpecialMovePage extends UnlistedSpecialPage { $this->watchlistManager->setWatch( $this->watch, $this->getAuthority(), $nt ); } - private function showLogFragment( $title ) { + private function showLogFragment( Title $title ) { $moveLogPage = new LogPage( 'move' ); $out = $this->getOutput(); $out->addHTML( Xml::element( 'h2', null, $moveLogPage->getName()->text() ) ); @@ -1010,7 +1010,9 @@ class SpecialMovePage extends UnlistedSpecialPage { } } - private function showSubpagesList( $subpages, $pagecount, $msg, $truncatedMsg, $noSubpageMsg = false ) { + private function showSubpagesList( + TitleArrayFromResult $subpages, int $pagecount, string $msg, string $truncatedMsg, bool $noSubpageMsg = false + ) { $out = $this->getOutput(); # No subpages. diff --git a/includes/specials/SpecialNewPages.php b/includes/specials/SpecialNewPages.php index 0569d9752df7..eb2fdedb34cb 100644 --- a/includes/specials/SpecialNewPages.php +++ b/includes/specials/SpecialNewPages.php @@ -380,7 +380,7 @@ class SpecialNewPages extends IncludableSpecialPage { $out->addModuleStyles( 'mediawiki.special' ); } - private function getNewPagesPager() { + private function getNewPagesPager(): NewPagesPager { return new NewPagesPager( $this->getContext(), $this->getLinkRenderer(), diff --git a/includes/specials/SpecialPageLanguage.php b/includes/specials/SpecialPageLanguage.php index 0272f2c34ce5..57f3d58a284d 100644 --- a/includes/specials/SpecialPageLanguage.php +++ b/includes/specials/SpecialPageLanguage.php @@ -308,7 +308,7 @@ class SpecialPageLanguage extends FormSpecialPage { $this->getOutput()->redirect( $this->goToUrl ); } - private function showLogFragment( $title ) { + private function showLogFragment( string $title ): string { $moveLogPage = new LogPage( 'pagelang' ); $out1 = Xml::element( 'h2', null, $moveLogPage->getName()->text() ); $out2 = ''; diff --git a/includes/specials/SpecialRandomPage.php b/includes/specials/SpecialRandomPage.php index 29babcfd45aa..c4049c8380a5 100644 --- a/includes/specials/SpecialRandomPage.php +++ b/includes/specials/SpecialRandomPage.php @@ -61,7 +61,7 @@ class SpecialRandomPage extends SpecialPage { $this->namespaces = [ $ns ]; } - private function isValidNS( $ns ) { + private function isValidNS( $ns ): bool { return $ns !== false && $ns >= 0; } @@ -208,7 +208,7 @@ class SpecialRandomPage extends SpecialPage { ]; } - private function selectRandomPageFromDB( $randstr, $fname ) { + private function selectRandomPageFromDB( $randstr, string $fname ) { $dbr = $this->dbProvider->getReplicaDatabase(); $query = $this->getQueryInfo( $randstr ); diff --git a/includes/specials/SpecialRenameUser.php b/includes/specials/SpecialRenameUser.php index 040f19055861..a048d621f797 100644 --- a/includes/specials/SpecialRenameUser.php +++ b/includes/specials/SpecialRenameUser.php @@ -251,7 +251,7 @@ class SpecialRenameUser extends SpecialPage { } } - private function getWarnings( $oldName, $newName ) { + private function getWarnings( string $oldName, string $newName ): array { $warnings = []; $oldUser = $this->userFactory->newFromName( $oldName, $this->userFactory::RIGOR_NONE ); if ( $oldUser && !$oldUser->isTemp() && $oldUser->getBlock() ) { @@ -264,7 +264,9 @@ class SpecialRenameUser extends SpecialPage { return $warnings; } - private function showForm( $oldName, $newName, $warnings, $reason, $moveChecked, $suppressChecked ) { + private function showForm( + ?string $oldName, ?string $newName, array $warnings, string $reason, bool $moveChecked, bool $suppressChecked + ) { $performer = $this->getUser(); $formDescriptor = [ diff --git a/includes/specials/SpecialSpecialPages.php b/includes/specials/SpecialSpecialPages.php index f41939a4e625..68240ac1d3a6 100644 --- a/includes/specials/SpecialSpecialPages.php +++ b/includes/specials/SpecialSpecialPages.php @@ -97,7 +97,7 @@ class SpecialSpecialPages extends UnlistedSpecialPage { return $groups; } - private function outputPageList( $groups ) { + private function outputPageList( array $groups ) { $out = $this->getOutput(); // Legend diff --git a/includes/specials/SpecialStatistics.php b/includes/specials/SpecialStatistics.php index 9efc01638d93..5ce81a8f2bd5 100644 --- a/includes/specials/SpecialStatistics.php +++ b/includes/specials/SpecialStatistics.php @@ -168,7 +168,7 @@ class SpecialStatistics extends SpecialPage { return $pageStatsHtml; } - private function getEditStats() { + private function getEditStats(): string { return Html::rawElement( 'tr', [], Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( 'statistics-header-edits' )->parse() @@ -184,7 +184,7 @@ class SpecialStatistics extends SpecialPage { ); } - private function getUserStats() { + private function getUserStats(): string { return Html::rawElement( 'tr', [], Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( 'statistics-header-users' )->parse() @@ -210,7 +210,7 @@ class SpecialStatistics extends SpecialPage { ); } - private function getGroupStats() { + private function getGroupStats(): string { $linkRenderer = $this->getLinkRenderer(); $lang = $this->getLanguage(); $text = ''; diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php index 625c82b97b79..159f02d5f9cf 100644 --- a/includes/specials/SpecialTags.php +++ b/includes/specials/SpecialTags.php @@ -193,8 +193,8 @@ class SpecialTags extends SpecialPage { } private function doTagRow( - $tag, $hitcount, $showManageActions, $showDeleteActions, $showEditLinks - ) { + string $tag, int $hitcount, bool $showManageActions, bool $showDeleteActions, bool $showEditLinks + ): string { $newRow = ''; $newRow .= Xml::tags( 'td', null, Xml::element( 'code', null, $tag ) ); diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index 930c03e39944..e3486316e8ce 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -198,7 +198,7 @@ class SpecialUndelete extends SpecialPage { return true; } - private function loadRequest( $par ) { + private function loadRequest( ?string $par ) { $request = $this->getRequest(); $user = $this->getUser(); @@ -551,7 +551,7 @@ class SpecialUndelete extends SpecialPage { return true; } - private function showRevision( $timestamp ) { + private function showRevision( string $timestamp ) { if ( !preg_match( '/[0-9]{14}/', $timestamp ) ) { return; } @@ -1416,7 +1416,7 @@ class SpecialUndelete extends SpecialPage { return Xml::tags( 'li', $attribs, $revisionRow ) . "\n"; } - private function formatFileRow( $row ) { + private function formatFileRow( \stdClass $row ): string { $file = ArchivedFile::newFromRow( $row ); $ts = wfTimestamp( TS_MW, $row->fa_timestamp ); $user = $this->getUser(); diff --git a/includes/specials/SpecialUserRights.php b/includes/specials/SpecialUserRights.php index 79018cd27eb0..0fa6b38273be 100644 --- a/includes/specials/SpecialUserRights.php +++ b/includes/specials/SpecialUserRights.php @@ -291,7 +291,7 @@ class SpecialUserRights extends SpecialPage { } } - private function getSuccessURL() { + private function getSuccessURL(): string { return $this->getPageTitle( $this->mTarget )->getFullURL(); } diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php index 6541721995e0..e1c9388f1884 100644 --- a/includes/specials/SpecialVersion.php +++ b/includes/specials/SpecialVersion.php @@ -225,6 +225,7 @@ class SpecialVersion extends SpecialPage { $this->getLibraries( $credits ), $this->getParserTags(), $this->getParserFunctionHooks(), + $this->getParsoidModules(), $this->getHooks(), $this->IPInfo(), ]; @@ -954,6 +955,45 @@ class SpecialVersion extends SpecialPage { } /** + * Obtains a list of installed Parsoid Modules and the associated H2 header + * + * @return string HTML output + */ + protected function getParsoidModules() { + $siteConfig = MediaWikiServices::getInstance()->getParsoidSiteConfig(); + $modules = $siteConfig->getExtensionModules(); + + if ( !$modules ) { + return ''; + } + + $this->addTocSection( 'version-parsoid-modules', 'mw-version-parsoid-modules' ); + + $out = Html::rawElement( + 'h2', + [ 'id' => 'mw-version-parsoid-modules' ], + Html::rawElement( + 'span', + [ 'class' => 'plainlinks' ], + $this->getLinkRenderer()->makeExternalLink( + 'https://www.mediawiki.org/wiki/Special:MyLanguage/Parsoid', + $this->msg( 'version-parsoid-modules' ), + $this->getFullTitle() + ) + ) + ); + + $moduleNames = array_map( + static fn ( $m )=>Html::element( 'code', [], $m->getConfig()['name'] ), + $modules + ); + + $out .= $this->getLanguage()->listToText( $moduleNames ); + + return $out; + } + + /** * Creates and returns the HTML for a single extension category. * * @since 1.17 @@ -1227,7 +1267,7 @@ class SpecialVersion extends SpecialPage { return implode( "\n", $ret ); } - private function openExtType( ?string $text = null, ?string $name = null ) { + private function openExtType( ?string $text = null, ?string $name = null ): string { $out = ''; $opt = [ 'class' => 'wikitable plainlinks mw-installed-software' ]; diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index 654e545d191e..4b4eb716770b 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -849,7 +849,7 @@ class SpecialWatchlist extends ChangesListSpecialPage { $this->setBottomText( $opts ); } - private function cutoffselector( $options ) { + private function cutoffselector( FormOptions $options ): string { $selected = (float)$options['days']; $maxDays = $this->getConfig()->get( MainConfigNames::RCMaxAge ) / ( 3600 * 24 ); if ( $selected <= 0 ) { diff --git a/includes/specials/SpecialWhatLinksHere.php b/includes/specials/SpecialWhatLinksHere.php index 81c95775248b..e2203c993e18 100644 --- a/includes/specials/SpecialWhatLinksHere.php +++ b/includes/specials/SpecialWhatLinksHere.php @@ -493,7 +493,7 @@ class SpecialWhatLinksHere extends FormSpecialPage { return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) ); } - private function listItem( stdClass $row, PageIdentity $nt, LinkTarget $target, bool $notClose = false ) { + private function listItem( stdClass $row, PageIdentity $nt, LinkTarget $target, bool $notClose = false ): string { $legacyTitle = $this->titleFactory->newFromPageIdentity( $nt ); if ( $row->rd_from || $row->page_is_redirect ) { @@ -594,7 +594,7 @@ class SpecialWhatLinksHere extends FormSpecialPage { return $this->getLanguage()->pipeList( $links ); } - private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ) { + private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ): string { $navBuilder = new PagerNavigationBuilder( $this->getContext() ); $navBuilder diff --git a/includes/specials/forms/PreferencesFormOOUI.php b/includes/specials/forms/PreferencesFormOOUI.php index 6ddf2793d288..e9642a18f263 100644 --- a/includes/specials/forms/PreferencesFormOOUI.php +++ b/includes/specials/forms/PreferencesFormOOUI.php @@ -141,7 +141,7 @@ class PreferencesFormOOUI extends OOUIHTMLForm { return $layout; } - private function isMobileLayout() { + private function isMobileLayout(): bool { if ( $this->useMobileLayout === null ) { $skin = $this->getSkin(); $this->useMobileLayout = false; diff --git a/includes/specials/pagers/AllMessagesTablePager.php b/includes/specials/pagers/AllMessagesTablePager.php index c06fdf99443e..01d9626f2b93 100644 --- a/includes/specials/pagers/AllMessagesTablePager.php +++ b/includes/specials/pagers/AllMessagesTablePager.php @@ -124,7 +124,7 @@ class AllMessagesTablePager extends TablePager { } } - private function getAllMessages( $descending ) { + private function getAllMessages( bool $descending ): array { $messageNames = $this->localisationCache->getSubitemList( 'en', 'messages' ); // Normalise message names so they look like page titles and sort correctly - T86139 diff --git a/includes/specials/pagers/ImageListPager.php b/includes/specials/pagers/ImageListPager.php index 0830e316e89e..7e78f7087bfc 100644 --- a/includes/specials/pagers/ImageListPager.php +++ b/includes/specials/pagers/ImageListPager.php @@ -190,7 +190,7 @@ class ImageListPager extends TablePager { return $conds + $this->mQueryConds; } - private function buildQueryConds() { + private function buildQueryConds(): array { $conds = [ 'file_deleted' => 0, 'fr_deleted' => 0, diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php index 48a8f1240273..17f97ee56fba 100644 --- a/includes/upload/UploadBase.php +++ b/includes/upload/UploadBase.php @@ -918,7 +918,7 @@ abstract class UploadBase { return $warnings; } - private function checkLocalFileWasDeleted( LocalFile $localFile ) { + private function checkLocalFileWasDeleted( LocalFile $localFile ): bool { return $localFile->wasDeleted() && !$localFile->exists(); } diff --git a/includes/upload/UploadFromChunks.php b/includes/upload/UploadFromChunks.php index 85a486dbeaf7..d484a3516022 100644 --- a/includes/upload/UploadFromChunks.php +++ b/includes/upload/UploadFromChunks.php @@ -449,7 +449,7 @@ class UploadFromChunks extends UploadFromFile { return $storeStatus; } - private function getChunkFileKey( $index = null ) { + private function getChunkFileKey( ?int $index = null ): string { return $this->mFileKey . '.' . ( $index ?? $this->getChunkIndex() ); } |