diff options
Diffstat (limited to 'includes')
143 files changed, 1144 insertions, 350 deletions
diff --git a/includes/Category/Category.php b/includes/Category/Category.php index 494befbaa621..38d849d22772 100644 --- a/includes/Category/Category.php +++ b/includes/Category/Category.php @@ -572,7 +572,7 @@ class Category { return false; } - private function addWhereonCategoryName( SelectQueryBuilder $queryBuilder, $name ) { + private function addWhereonCategoryName( SelectQueryBuilder $queryBuilder, string $name ) { if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) { $queryBuilder->where( [ 'cl_to' => $name ] ); } else { diff --git a/includes/DomainEvent/EventIngressBase.php b/includes/DomainEvent/EventIngressBase.php index effbca05d4a5..fb4d65ee0002 100644 --- a/includes/DomainEvent/EventIngressBase.php +++ b/includes/DomainEvent/EventIngressBase.php @@ -110,7 +110,7 @@ abstract class EventIngressBase implements InitializableDomainEventSubscriber { DomainEventSource $eventSource, string $eventType, string $suffix - ) { + ): bool { $method = "handle{$eventType}Event{$suffix}"; if ( !method_exists( $this, $method ) ) { return false; diff --git a/includes/ExternalLinks/LinkFilter.php b/includes/ExternalLinks/LinkFilter.php index 43781cf8c43e..e47b5751a194 100644 --- a/includes/ExternalLinks/LinkFilter.php +++ b/includes/ExternalLinks/LinkFilter.php @@ -287,7 +287,7 @@ class LinkFilter { return $index; } - private static function reverseDomain( $domain ) { + private static function reverseDomain( string $domain ): string { if ( substr( $domain, 0, 3 ) === 'V6.' ) { $ipv6 = str_replace( '.', ':', trim( substr( $domain, 3 ), '.' ) ); if ( IPUtils::isValid( $ipv6 ) ) { diff --git a/includes/Html/ListToggle.php b/includes/Html/ListToggle.php index f06fc14d70e1..baafb9b77103 100644 --- a/includes/Html/ListToggle.php +++ b/includes/Html/ListToggle.php @@ -43,7 +43,7 @@ class ListToggle { $output->addModuleStyles( 'mediawiki.checkboxtoggle.styles' ); } - private function checkboxLink( $checkboxType ) { + private function checkboxLink( string $checkboxType ): string { return Html::element( // CSS classes: mw-checkbox-all, mw-checkbox-none, mw-checkbox-invert 'a', [ 'class' => 'mw-checkbox-' . $checkboxType, 'role' => 'button', 'tabindex' => 0 ], diff --git a/includes/Html/TemplateParser.php b/includes/Html/TemplateParser.php index 94b4bb7afe3e..64e7ab491be9 100644 --- a/includes/Html/TemplateParser.php +++ b/includes/Html/TemplateParser.php @@ -2,6 +2,7 @@ namespace MediaWiki\Html; +use Exception; use FileContentsHasher; use LightnCandy\LightnCandy; use MediaWiki\MainConfigNames; @@ -208,7 +209,7 @@ class TemplateParser { * * @param string $templateName The name of the template * @return array An associative array containing the PHP code and metadata about its compilation - * @throws \Exception Thrown by LightnCandy if it could not compile the Mustache code + * @throws Exception Thrown by LightnCandy if it could not compile the Mustache code * @throws RuntimeException If LightnCandy could not compile the Mustache code but did not throw * an exception. This exception is indicative of a bug in LightnCandy * @suppress PhanTypeMismatchArgument diff --git a/includes/MainConfigSchema.php b/includes/MainConfigSchema.php index 84b1c8b40f8c..b6a10ba99d50 100644 --- a/includes/MainConfigSchema.php +++ b/includes/MainConfigSchema.php @@ -6646,7 +6646,8 @@ class MainConfigSchema { * Setting to `false` disables this support. Setting to 'true' or the * string 'v1' to enable "version 1" support. Setting to the string 'v2' * enables "version 2" support, which uses strip markers for extension - * tag content. + * tag content. Setting to the string 'v3' makes the use of strip markers + * for extension tag content a property of the Parser object. * @unstable EXPERIMENTAL */ public const ParsoidFragmentSupport = [ diff --git a/includes/Notification/Handlers/RecentChangeNotificationHandler.php b/includes/Notification/Handlers/RecentChangeNotificationHandler.php index 1f874c8be215..885d01d96326 100644 --- a/includes/Notification/Handlers/RecentChangeNotificationHandler.php +++ b/includes/Notification/Handlers/RecentChangeNotificationHandler.php @@ -39,6 +39,11 @@ class RecentChangeNotificationHandler implements NotificationHandler { return; } $properties = $notification->getProperties(); + $sourceMap = [ + RecentChangeNotification::ADMIN_NOTIFICATION => RecentChangeMailComposer::ALL_CHANGES, + RecentChangeNotification::TALK_NOTIFICATION => RecentChangeMailComposer::USER_TALK, + ]; + $source = $sourceMap[ $properties['source'] ] ?? RecentChangeMailComposer::ALL_CHANGES; $composer = new RecentChangeMailComposer( $this->userFactory->newFromUserIdentity( $notification->getAgent() ), @@ -52,9 +57,7 @@ class RecentChangeNotificationHandler implements NotificationHandler { foreach ( $recipients as $recipient ) { $user = $this->userFactory->newFromUserIdentity( $recipient ); if ( $this->checkNotificationRequirements( $notification, $user ) ) { - // TODO - for now it handles only ALL changes, future patches will provide support - // for WATCHLIST and USER_TALK - $composer->compose( $recipient, RecentChangeMailComposer::ALL_CHANGES ); + $composer->compose( $recipient, $source ); } } // TODO - sendEmails is deprecated, remove it in 1.45. need to keep it in parity in case diff --git a/includes/Notification/Middleware/SuppressNotificationByTypeMiddleware.php b/includes/Notification/Middleware/SuppressNotificationByTypeMiddleware.php index 96b77540d0b4..3f56fb69be64 100644 --- a/includes/Notification/Middleware/SuppressNotificationByTypeMiddleware.php +++ b/includes/Notification/Middleware/SuppressNotificationByTypeMiddleware.php @@ -18,7 +18,6 @@ class SuppressNotificationByTypeMiddleware implements NotificationMiddlewareInte /** * Suppress sending specific notification - * @param string $notificationTypeToSuppress */ public function __construct( string $notificationTypeToSuppress ) { $this->notificationToSuppress = $notificationTypeToSuppress; @@ -26,8 +25,6 @@ class SuppressNotificationByTypeMiddleware implements NotificationMiddlewareInte /** * Decide whether we want to remove notification from the list - * @param NotificationEnvelope $envelope - * @return bool */ protected function shouldKeep( NotificationEnvelope $envelope ): bool { return $envelope->getNotification()->getType() !== $this->notificationToSuppress; diff --git a/includes/Notification/MiddlewareChain.php b/includes/Notification/MiddlewareChain.php index eba6408a0121..26f8081f799b 100644 --- a/includes/Notification/MiddlewareChain.php +++ b/includes/Notification/MiddlewareChain.php @@ -24,13 +24,9 @@ class MiddlewareChain { $this->middlewareSpecs = $specs; } - /** - * @param NotificationsBatch $batch - * @return NotificationsBatch - */ public function process( NotificationsBatch $batch ): NotificationsBatch { if ( $this->isProcessing ) { - // If you need to send an additional notification from middleware + // If you need to send an additional notification from middleware, // use NotificationBatch::add() instead of calling NotificationService from middleware throw new MiddlewareException( "Middleware cannot re-trigger the notification processing while it is already in progress. " @@ -52,12 +48,7 @@ class MiddlewareChain { return $batch; } - /** - * @param NotificationsBatch $batch - * @param int $index - * @return void - */ - private function callNext( NotificationsBatch $batch, $index ): void { + private function callNext( NotificationsBatch $batch, int $index ): void { if ( $index < count( $this->middleware ) ) { $this->middleware[$index]->handle( $batch, diff --git a/includes/Notification/Notification.php b/includes/Notification/Notification.php index 7f1740f39df3..b08cf50cbe75 100644 --- a/includes/Notification/Notification.php +++ b/includes/Notification/Notification.php @@ -15,10 +15,10 @@ class Notification { /** * @TODO Idea: handle future types in format `namespace.type`, like `mediawiki.message`, - * `growth.welcome`. This way we can easily "override" some notifications, for example - * we can have `echo.mention`, and the `echo.mention` could supersede `mediawiki.mention`. Also - * it will be more difficult for notifications to conflict and we will be able to easily filter - * apply logic depends on the namespace (for example hide if extension not present). + * `growth.welcome`. This way we can easily "override" some notifications, for example, + * we can have `echo.mention`, and the `echo.mention` could supersede `mediawiki.mention`. Also, + * it will be more difficult for notifications to conflict, and we will be able to easily filter + * apply logic depends on the namespace (for example, hide if extension not present). * * @var string Required, Read-only - The Notification type */ @@ -32,7 +32,7 @@ class Notification { private array $custom; /** - * Base for Notifications. Type is the only required property and any additional data can be + * Base for Notifications. Type is the only required property, and any additional data can be * passed via $custom array. * * @see WikiNotification in case you want a Notification with Agent and Title @@ -46,8 +46,7 @@ class Notification { } /** - * Get Notification type - * @return string + * Get the Notification type */ public function getType(): string { return $this->type; @@ -62,7 +61,6 @@ class Notification { * * @param string $key * @param scalar|array|JsonCodecable $value - * @return void */ public function setProperty( string $key, $value ): void { $this->custom[$key] = $value; @@ -70,7 +68,6 @@ class Notification { /** * Retrieve Notification properties - * @return array */ public function getProperties(): array { return $this->custom; diff --git a/includes/Notification/NotificationEnvelope.php b/includes/Notification/NotificationEnvelope.php index a11f88ae1e3f..718e73fdced2 100644 --- a/includes/Notification/NotificationEnvelope.php +++ b/includes/Notification/NotificationEnvelope.php @@ -26,9 +26,6 @@ class NotificationEnvelope { /** * Syntax sugar, allows easy check if two envelopes point to the same thing - * - * @param NotificationEnvelope $envelope - * @return bool */ public function equals( NotificationEnvelope $envelope ): bool { return $envelope === $this; @@ -39,8 +36,6 @@ class NotificationEnvelope { * * Utility method for a very common check where middleware filters Notifications from * specific agent. - * - * @return bool */ public function hasAgent(): bool { return $this->notification instanceof AgentAware; diff --git a/includes/Notification/NotificationMiddlewareInterface.php b/includes/Notification/NotificationMiddlewareInterface.php index 4957e2728223..9ff7c097547e 100644 --- a/includes/Notification/NotificationMiddlewareInterface.php +++ b/includes/Notification/NotificationMiddlewareInterface.php @@ -12,7 +12,6 @@ interface NotificationMiddlewareInterface { * * @param NotificationsBatch $batch * @param callable():void $next Call this method to continue the chain - * @return void */ public function handle( NotificationsBatch $batch, callable $next ): void; diff --git a/includes/Notification/NotificationService.php b/includes/Notification/NotificationService.php index 15f4b2dadc03..a90bcb978535 100644 --- a/includes/Notification/NotificationService.php +++ b/includes/Notification/NotificationService.php @@ -49,7 +49,7 @@ class NotificationService { $this->specs = $specs; } - private function getHandlers() { + private function getHandlers(): array { if ( $this->handlersByType === [] ) { foreach ( $this->specs as $spec ) { $obj = $this->objectFactory->createObject( $spec, [ 'assertClass' => NotificationHandler::class ] ); diff --git a/includes/Notification/NotificationsBatch.php b/includes/Notification/NotificationsBatch.php index f7963cd6edd8..06d68cd16cf3 100644 --- a/includes/Notification/NotificationsBatch.php +++ b/includes/Notification/NotificationsBatch.php @@ -30,7 +30,6 @@ class NotificationsBatch implements Countable, IteratorAggregate { /** * @param callable(NotificationEnvelope): bool $callback Filter, return true to preserve the $envelope - * @return void */ public function filter( $callback ): void { $this->envelopes = diff --git a/includes/Notification/TitleAware.php b/includes/Notification/TitleAware.php index 0727514d9c8e..5c77f1ac5455 100644 --- a/includes/Notification/TitleAware.php +++ b/includes/Notification/TitleAware.php @@ -14,7 +14,6 @@ interface TitleAware { /** * Get the PageIdentity of the related page - * @return PageIdentity */ public function getTitle(): PageIdentity; diff --git a/includes/Output/OutputPage.php b/includes/Output/OutputPage.php index 196c1f4572d9..0a33bb608ccd 100644 --- a/includes/Output/OutputPage.php +++ b/includes/Output/OutputPage.php @@ -3062,7 +3062,7 @@ class OutputPage extends ContextSource { return json_encode( $output, JSON_UNESCAPED_SLASHES ); } - private function getFeaturePolicyReportOnly() { + private function getFeaturePolicyReportOnly(): string { $config = $this->getConfig(); $features = $config->get( MainConfigNames::FeaturePolicyReportOnly ); @@ -3754,7 +3754,7 @@ class OutputPage extends ContextSource { return $this->debugMode; } - private function getRlClientContext() { + private function getRlClientContext(): RL\Context { if ( !$this->rlClientContext ) { $query = ResourceLoader::makeLoaderQuery( [], // modules; not relevant diff --git a/includes/Permissions/RateLimiter.php b/includes/Permissions/RateLimiter.php index 18aeda7e6de7..20b009478351 100644 --- a/includes/Permissions/RateLimiter.php +++ b/includes/Permissions/RateLimiter.php @@ -342,7 +342,7 @@ class RateLimiter { return !$allowed; } - private function canBypass( string $action ) { + private function canBypass( string $action ): bool { return $this->rateLimits[$action]['&can-bypass'] ?? true; } diff --git a/includes/RenameUser/RenameUser.php b/includes/RenameUser/RenameUser.php index 4b022b0ad4b3..8f27fe501b85 100644 --- a/includes/RenameUser/RenameUser.php +++ b/includes/RenameUser/RenameUser.php @@ -270,7 +270,9 @@ class RenameUser { return Status::newGood(); } - private function movePagesAndSubPages( User $performer, Title $oldTitle, Title $newTitle, bool $suppressRedirect ) { + private function movePagesAndSubPages( + User $performer, Title $oldTitle, Title $newTitle, bool $suppressRedirect + ): Status { $status = Status::newGood(); $movePage = $this->movePageFactory->newMovePage( diff --git a/includes/ResourceLoader/ClientHtml.php b/includes/ResourceLoader/ClientHtml.php index 69bf6154db6c..dad5b5be509b 100644 --- a/includes/ResourceLoader/ClientHtml.php +++ b/includes/ResourceLoader/ClientHtml.php @@ -388,15 +388,15 @@ RLPAGEMODULES = {$pageModulesJson}; return WrappedString::join( "\n", $chunks ); } - private function getContext( $group, $type ): Context { + private function getContext( ?string $group, string $type ): Context { return self::makeContext( $this->context, $group, $type ); } - private function getLoad( $modules, $only, array $extraQuery = [] ) { + private function getLoad( $modules, string $only, array $extraQuery = [] ) { return self::makeLoad( $this->context, (array)$modules, $only, $extraQuery ); } - private static function makeContext( Context $mainContext, $group, $type, + private static function makeContext( Context $mainContext, ?string $group, string $type, array $extraQuery = [] ): DerivativeContext { // Allow caller to setVersion() and setModules() diff --git a/includes/ResourceLoader/CodexModule.php b/includes/ResourceLoader/CodexModule.php index 11d276faa81c..528d46826473 100644 --- a/includes/ResourceLoader/CodexModule.php +++ b/includes/ResourceLoader/CodexModule.php @@ -173,7 +173,7 @@ class CodexModule extends FileModule { return array_intersect_key( $cachedIcons, array_flip( $iconNames ) ); } - private static function getIconFilePath( Config $config ) { + private static function getIconFilePath( Config $config ): string { $devDir = $config->get( MainConfigNames::CodexDevelopmentDir ); $iconsDir = $devDir !== null ? "$devDir/packages/codex-icons/dist" : @@ -316,11 +316,11 @@ class CodexModule extends FileModule { return $this->makeFilePath( '' )->getLocalPath(); } - private function isDevelopmentMode() { + private function isDevelopmentMode(): bool { return $this->getConfig()->get( MainConfigNames::CodexDevelopmentDir ) !== null; } - private function getDevelopmentWarning() { + private function getDevelopmentWarning(): string { return $this->isDevelopmentMode() ? Html::encodeJsCall( 'mw.log.warn', [ "You are using a local development version of Codex, which may not match the latest version. " . diff --git a/includes/ResourceLoader/DateFormatterConfig.php b/includes/ResourceLoader/DateFormatterConfig.php new file mode 100644 index 000000000000..4fa261532950 --- /dev/null +++ b/includes/ResourceLoader/DateFormatterConfig.php @@ -0,0 +1,84 @@ +<?php + +namespace MediaWiki\ResourceLoader; + +use MediaWiki\Config\Config; +use MediaWiki\Language\Language; +use MediaWiki\Language\LanguageCode; +use MediaWiki\MainConfigNames; +use MediaWiki\MediaWikiServices; + +/** + * @internal + */ +class DateFormatterConfig extends Module { + /** + * Callback for mediawiki.DateFormatter/config.json + * + * @internal + * @param Context $context + * @param Config $config + * @return array + */ + public static function getData( Context $context, Config $config ) { + $lang = MediaWikiServices::getInstance()->getLanguageFactory() + ->getLanguage( $context->getLanguage() ); + return self::getDataForLang( $lang, $config ); + } + + /** + * Get configuration data for DateFormatter given parameters + * + * @internal + * @param Language $lang + * @param Config $config + * @return array + */ + public static function getDataForLang( Language $lang, Config $config ) { + $locales = [ $lang->getHtmlCode() ]; + $fallbacks = $lang->getFallbackLanguages(); + foreach ( $fallbacks as $code ) { + $locales[] = LanguageCode::bcp47( $code ); + } + + // Discover which fields are required + $formats = $lang->getJsDateFormats(); + $haveField = []; + foreach ( $formats as $format ) { + $pattern = $format['pattern'] ?? ''; + foreach ( [ 'mwMonth', 'mwMonthGen', 'mwMonthAbbrev' ] as $field ) { + if ( str_contains( $pattern, "{$field}" ) ) { + $haveField[$field] = true; + } + } + } + + // Include only the required month data + if ( $haveField ) { + $months = [ [] ]; + for ( $i = 1; $i <= 12; $i++ ) { + $data = [ + isset( $haveField['mwMonth'] ) ? $lang->getMonthName( $i ) : '', + isset( $haveField['mwMonthGen'] ) ? $lang->getMonthNameGen( $i ) : '', + isset( $haveField['mwMonthAbbrev'] ) ? $lang->getMonthAbbreviation( $i ) : '' + ]; + // Trim the end of the array + while ( end( $data ) === '' ) { + unset( $data[ array_key_last( $data ) ] ); + } + $months[] = $data; + } + } else { + $months = []; + } + + return [ + 'locales' => $locales, + 'formats' => $formats, + 'defaultStyle' => $lang->getDefaultDateFormat(), + 'localZone' => $config->get( MainConfigNames::Localtimezone ), + 'localOffset' => (int)$config->get( MainConfigNames::LocalTZoffset ), + 'months' => $months + ]; + } +} diff --git a/includes/ResourceLoader/ResourceLoader.php b/includes/ResourceLoader/ResourceLoader.php index 652891b954fe..aaddcca3f35a 100644 --- a/includes/ResourceLoader/ResourceLoader.php +++ b/includes/ResourceLoader/ResourceLoader.php @@ -1573,7 +1573,7 @@ MESSAGE; . ');'; } - private static function isEmptyObject( stdClass $obj ) { + private static function isEmptyObject( stdClass $obj ): bool { foreach ( $obj as $value ) { return false; } diff --git a/includes/ResourceLoader/StartUpModule.php b/includes/ResourceLoader/StartUpModule.php index a0203890da8b..72e0cc42f2cb 100644 --- a/includes/ResourceLoader/StartUpModule.php +++ b/includes/ResourceLoader/StartUpModule.php @@ -286,7 +286,7 @@ class StartUpModule extends Module { return $out; } - private function getGroupId( $groupName ): ?int { + private function getGroupId( ?string $groupName ): ?int { if ( $groupName === null ) { return null; } diff --git a/includes/ResourceLoader/UserOptionsModule.php b/includes/ResourceLoader/UserOptionsModule.php index 383a9905e404..58e74fb9b36f 100644 --- a/includes/ResourceLoader/UserOptionsModule.php +++ b/includes/ResourceLoader/UserOptionsModule.php @@ -2,8 +2,10 @@ namespace MediaWiki\ResourceLoader; +use MediaWiki\MainConfigNames; use MediaWiki\MediaWikiServices; use MediaWiki\User\Options\UserOptionsLookup; +use MediaWiki\User\UserTimeCorrection; /** * This program is free software; you can redistribute it and/or modify @@ -68,6 +70,16 @@ class UserOptionsModule extends Module { unset( $options[ $excludedKey ] ); } + // Update timezone offset (T323193) + if ( isset( $options['timecorrection'] ) ) { + $corr = new UserTimeCorrection( + $options['timecorrection'], + null, + $this->getConfig()->get( MainConfigNames::LocalTZoffset ) + ); + $options['timecorrection'] = $corr->toString(); + } + // Optimisation: Only output this function call if the user has non-default settings. if ( $options ) { $script .= 'mw.user.options.set(' . $context->encodeJson( $options ) . ');' . "\n"; diff --git a/includes/ResourceLoader/WikiModule.php b/includes/ResourceLoader/WikiModule.php index 74869a3fc7e2..26b7ce64ad6c 100644 --- a/includes/ResourceLoader/WikiModule.php +++ b/includes/ResourceLoader/WikiModule.php @@ -491,11 +491,11 @@ class WikiModule extends Module { return count( $revisions ) === 0; } - private function setTitleInfo( $batchKey, array $titleInfo ) { + private function setTitleInfo( string $batchKey, array $titleInfo ) { $this->titleInfo[$batchKey] = $titleInfo; } - private static function makeTitleKey( LinkTarget $title ) { + private static function makeTitleKey( LinkTarget $title ): string { // Used for keys in titleInfo. return "{$title->getNamespace()}:{$title->getDBkey()}"; } 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/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/fr.json b/includes/Rest/i18n/fr.json index 8553bf5f7438..de5193a42a17 100644 --- a/includes/Rest/i18n/fr.json +++ b/includes/Rest/i18n/fr.json @@ -95,6 +95,14 @@ "rest-unsupported-language-conversion": "Conversion de formats non prise en charge : $1 à $2", "rest-unknown-content-model": "Modèle de contenu inconnu : $1", "rest-page-bundle-validation-error": "PageBundle ne correspond pas à contentVersion : $1", + "rest-module": "Module", + "rest-module-default": "Module par défaut", + "rest-module-extra-routes-title": "API REST de MediaWiki", + "rest-module-extra-routes-desc": "Points de terminaison REST non associés à un module", + "rest-module-specs.v0-title": "Spécifications", + "rest-module-specs.v0-desc": "Module d'auto-documentation fournissant la découverte, les spécifications et les schémas de tous les modules disponibles.", + "rest-module-content.v1-title": "Contenu de la page.", + "rest-module-content.v1-desc": "Donne accès au contenu et aux métadonnées des pages et des révisions", "rest-param-desc-mock-desc": "Description fictive.", "rest-param-desc-revision-id": "Identifiant de la révision", "rest-param-desc-html-input-title": "Titre de la transformation de l'entrée HTML", @@ -204,7 +212,7 @@ "rest-property-desc-revision-html": "Contenu de révision en HTML, suivant la spécification HTML", "rest-property-desc-revision-url": "URL vers la dernière révision de la page", "rest-property-desc-search-excerpt": "Extrait du contenu de la page correspondant à la requête de recherche", - "rest-property-desc-search-matched-title": "Le titre d'une page de redirection, le cas échéant, ou nul", + "rest-property-desc-search-matched-title": "Titre de la page redirigée, si le terme de recherche correspond à une page de redirection, sinon nul", "rest-property-desc-search-description": "Bref résumé du sujet de la page ou null si aucun résumé n'existe.", "rest-property-desc-search-thumbnail": "Informations sur l'image miniature de la page, ou null si aucune miniature n'existe.", "rest-schema-desc-mock-desc": "Description fictive.", diff --git a/includes/Rest/i18n/it.json b/includes/Rest/i18n/it.json index 84a00f085ae3..3c4f0723c200 100644 --- a/includes/Rest/i18n/it.json +++ b/includes/Rest/i18n/it.json @@ -50,5 +50,6 @@ "rest-param-desc-search-limit": "Numero massimo di risultati di ricerca da restituire, tra 1 e 100. Predefinito: 50", "rest-param-desc-transform-format": "Trasforma formato", "rest-param-desc-transform-revision": "Trasforma versione", - "rest-property-desc-revision-id": "ID versione" + "rest-property-desc-revision-id": "ID versione", + "rest-property-desc-user-name": "Nome utente o indirizzo IP di origine per utenti anonimi" } diff --git a/includes/Rest/i18n/ko.json b/includes/Rest/i18n/ko.json index 7db861230bed..d67b0c55de15 100644 --- a/includes/Rest/i18n/ko.json +++ b/includes/Rest/i18n/ko.json @@ -3,6 +3,7 @@ "authors": [ "Bluehill", "Suleiman the Magnificent Television", + "Tensama0415", "Ykhwong" ] }, @@ -56,6 +57,7 @@ "rest-badtoken": "제공된 CSRF 토큰이 잘못되었습니다.", "rest-badtoken-missing": "CSRF 안전 인증 방법을 사용하지 않는 한 <var>token</var> 변수는 필수입니다.", "rest-badtoken-nosession": "CSRF 오류 - 세션이 없습니다.", + "rest-mock-error": "가짜 오류.", "rest-specified-revision-unavailable": "지정된 판은 삭제되었거나 숨겨져 있습니다.", "rest-previous-revision-unavailable": "이전 판을 찾을 수 없습니다. 문서가 잠겼거나 삭제되었습니까?", "rest-requires-content-type-header": "Content-Type 헤더는 요청 페이로드와 함께 제공되어야 합니다.", @@ -64,6 +66,8 @@ "rest-unsupported-target-format": "요청한 대상 형식은 지원되지 않습니다.", "rest-parsoid-resource-exceeded": "리소스 한도를 초과했습니다", "rest-parsoid-error": "파소이드 오류입니다.", + "rest-parsoid-bad-render-id": "잘못된 파소이드 렌더 ID: $1", + "rest-invalid-transform": "유효하지 않은 변환: $1에서 $2", "rest-request-body-expected": "$1 요청 메소드는 요청 본문을 예상합니다", "rest-request-body-unaccepted": "$1 요청 메소드는 요청 본문을 허용하지 않습니다", "rest-unmapped-action-error": "매핑되지 않은 작업 모듈 오류: $1", @@ -77,7 +81,18 @@ "rest-unknown-parsoid-transformation": "알 수 없는 변환.", "rest-unsupported-language-conversion": "지원되지 않는 언어 변환: $1에서 $2", "rest-unknown-content-model": "알 수 없는 콘텐츠 모델: $1", + "rest-module": "모듈", + "rest-module-default": "기본 모듈", + "rest-module-extra-routes-title": "미디어위키 REST API", + "rest-module-specs.v0-title": "사양", + "rest-module-content.v1-title": "문서 내용", "rest-param-desc-revision-id": "판 식별자", + "rest-param-desc-html-input-title": "HTML 입력 제목 변환", + "rest-param-desc-html-input-oldid": "HTML 입력 oldid 변환", + "rest-param-desc-html-input-from": "HTML 입력 시작 변환", + "rest-param-desc-html-input-format": "HTML 입력 형식 변환", + "rest-param-desc-compare-from-section-level": "문단 수준", + "rest-param-desc-compare-to-section-level": "문단 수준", "rest-param-desc-language-links-title": "위키 문서 제목", "rest-param-desc-media-links-title": "위키 문서 제목", "rest-param-desc-media-file-title": "위키 문서 제목", @@ -87,12 +102,26 @@ "rest-param-desc-pagehistory-filter": "문서 역사 필터", "rest-param-desc-search-q": "검색 용어", "rest-param-desc-search-limit": "반환할 항목 수", + "rest-param-desc-transform-from": "시작 변환", + "rest-param-desc-transform-format": "형식 변환", + "rest-param-desc-transform-title": "제목 변환", + "rest-param-desc-transform-revision": "판 변환", + "rest-param-desc-update-title": "업데이트된 문서 제목", + "rest-property-desc-mock-desc": "가짜 설명.", "rest-property-desc-revision-id": "판 ID", "rest-property-desc-revision-timestamp": "판 타임스탬프", "rest-property-desc-page-id": "문서 식별자", "rest-property-desc-page-title": "문서 제목", + "rest-property-desc-page-language-code": "언어 코드", + "rest-property-desc-page-language-name": "번역된 언어 이름", "rest-property-desc-page-timestamp": "최신 판의 타임스탬프", "rest-property-desc-page-license": "위키 라이선스 정보", "rest-property-desc-page-license-url": "해당 라이선스의 URL", - "rest-schema-desc-revision-metadata": "판 메타데이터" + "rest-property-desc-page-contentmodel": "문서 내용 유형", + "rest-schema-desc-mock-desc": "가짜 설명.", + "rest-schema-desc-revision-metadata": "판 메타데이터", + "rest-schema-desc-new-page": "소스 (보통 위키텍스트)를 포함한 새 문서", + "rest-schema-desc-bare-page": "내용이 없는 문서", + "rest-schema-desc-search-results": "검색 결과", + "rest-schema-desc-revision-compare": "개정판 비교" } diff --git a/includes/Rest/i18n/zh-hans.json b/includes/Rest/i18n/zh-hans.json index 2df1bfa46b54..801328d90fb6 100644 --- a/includes/Rest/i18n/zh-hans.json +++ b/includes/Rest/i18n/zh-hans.json @@ -99,7 +99,23 @@ "rest-unsupported-language-conversion": "不支持的语言转换:$1到$2", "rest-unknown-content-model": "未知的内容模型:$1", "rest-page-bundle-validation-error": "PageBundle与contentVersion不匹配:$1", + "rest-module": "模块", + "rest-module-default": "默认模块", + "rest-module-extra-routes-title": "MediaWiki REST API", + "rest-module-content.v1-title": "页面内容", "rest-param-desc-revision-id": "修订版本ID", "rest-param-desc-html-input-contentmodel": "页面内容格式:wikitext(默认)、css、javascript、json或text。", - "rest-property-desc-revision-id": "修订版本ID" + "rest-param-desc-compare-diff": "修订版本之间的差异", + "rest-param-desc-language-links-title": "Wiki页面标题", + "rest-param-desc-media-links-title": "文件标题", + "rest-param-desc-media-file-title": "文件标题", + "rest-param-desc-media-mediatype": "文件类型", + "rest-param-desc-media-original": "原始文件详细信息", + "rest-param-desc-media-thumbnail": "缩略图信息", + "rest-param-desc-pagehistory-count-title": "Wiki页面标题", + "rest-param-desc-pagehistory-title": "Wiki页面标题", + "rest-param-desc-search-q": "搜索词", + "rest-property-desc-revision-id": "修订版本ID", + "rest-property-desc-page-title": "页面标题", + "rest-property-desc-page-language-code": "语言代码" } diff --git a/includes/Revision/RevisionRenderer.php b/includes/Revision/RevisionRenderer.php index 999768829f88..8d28ca3641bf 100644 --- a/includes/Revision/RevisionRenderer.php +++ b/includes/Revision/RevisionRenderer.php @@ -181,7 +181,7 @@ class RevisionRenderer { return $renderedRevision; } - private function getSpeculativeRevId( $dbIndex ) { + private function getSpeculativeRevId( int $dbIndex ): int { // Use a separate primary DB connection in order to see the latest data, by avoiding // stale data from REPEATABLE-READ snapshots. $flags = ILoadBalancer::CONN_TRX_AUTOCOMMIT; @@ -194,7 +194,7 @@ class RevisionRenderer { ->caller( __METHOD__ )->fetchField(); } - private function getSpeculativePageId( $dbIndex ) { + private function getSpeculativePageId( int $dbIndex ): int { // Use a separate primary DB connection in order to see the latest data, by avoiding // stale data from REPEATABLE-READ snapshots. $flags = ILoadBalancer::CONN_TRX_AUTOCOMMIT; diff --git a/includes/Revision/RevisionStore.php b/includes/Revision/RevisionStore.php index dbd884e78f1e..ac17a3b85b1c 100644 --- a/includes/Revision/RevisionStore.php +++ b/includes/Revision/RevisionStore.php @@ -624,9 +624,9 @@ class RevisionStore implements RevisionFactory, RevisionLookup, LoggerAwareInter UserIdentity $user, CommentStoreComment $comment, PageIdentity $page, - $pageId, - $parentId - ) { + int $pageId, + int $parentId + ): RevisionRecord { $slotRoles = $rev->getSlotRoles(); $revisionRow = $this->insertRevisionRowOn( @@ -1438,7 +1438,7 @@ class RevisionStore implements RevisionFactory, RevisionLookup, LoggerAwareInter return $this->constructSlotRecords( $revId, $res, $queryFlags, $page ); } - private function loadSlotRecordsFromDb( $revId, $queryFlags, PageIdentity $page ): array { + private function loadSlotRecordsFromDb( int $revId, int $queryFlags, PageIdentity $page ): array { $revQuery = $this->getSlotsQueryInfo( [ 'content' ] ); $db = $this->getDBConnectionRefForQueryFlags( $queryFlags ); diff --git a/includes/Settings/Source/ReflectionSchemaSource.php b/includes/Settings/Source/ReflectionSchemaSource.php index 7937847a4bad..6ec18bd550e5 100644 --- a/includes/Settings/Source/ReflectionSchemaSource.php +++ b/includes/Settings/Source/ReflectionSchemaSource.php @@ -163,14 +163,14 @@ class ReflectionSchemaSource implements Stringable, SettingsSource { return 'class ' . $this->class; } - private function normalizeComment( string $doc ) { + private function normalizeComment( string $doc ): string { $doc = preg_replace( '/^\s*\/\*+\s*|\s*\*+\/\s*$/', '', $doc ); $doc = preg_replace( '/^\s*\**$/m', " ", $doc ); $doc = preg_replace( '/^\s*\**[ \t]?/m', '', $doc ); return $doc; } - private function normalizeDynamicDefault( string $name, $spec ) { + private function normalizeDynamicDefault( string $name, $spec ): array { if ( $spec === true ) { $spec = [ 'callback' => [ $this->class, "getDefault{$name}" ] ]; } diff --git a/includes/SiteStats/SiteStatsInit.php b/includes/SiteStats/SiteStatsInit.php index c83330793b64..ffa0bfad12d1 100644 --- a/includes/SiteStats/SiteStatsInit.php +++ b/includes/SiteStats/SiteStatsInit.php @@ -69,7 +69,7 @@ class SiteStatsInit { return $this->edits; } - private function countTableRows( string $tableName ) { + private function countTableRows( string $tableName ): int { return (int)$this->dbr->newSelectQueryBuilder() ->select( 'COUNT(*)' ) ->from( $tableName ) @@ -178,7 +178,7 @@ class SiteStatsInit { } } - private function getShardedValue( $value, $noShards, $rowId ) { + private function getShardedValue( int $value, int $noShards, int $rowId ): int { $remainder = $value % $noShards; $quotient = (int)( ( $value - $remainder ) / $noShards ); // Add the reminder to the first row diff --git a/includes/Storage/DerivedPageDataUpdater.php b/includes/Storage/DerivedPageDataUpdater.php index f299e5180342..e8e596230585 100644 --- a/includes/Storage/DerivedPageDataUpdater.php +++ b/includes/Storage/DerivedPageDataUpdater.php @@ -748,7 +748,7 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { ->getContentHandler( $this->getRawSlot( $role )->getModel() ); } - private function usePrimary() { + private function usePrimary(): bool { // TODO: can we just set a flag to true in prepareContent()? return $this->wikiPage->wasLoadedFrom( IDBAccessObject::READ_LATEST ); } @@ -1032,7 +1032,7 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { return $this->renderedRevision; } - private function assertHasPageState( $method ) { + private function assertHasPageState( string $method ) { if ( !$this->pageState ) { throw new LogicException( 'Must call grabCurrentRevision() or prepareContent() ' @@ -1041,7 +1041,7 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { } } - private function assertPrepared( $method ) { + private function assertPrepared( string $method ) { if ( !$this->revision ) { throw new LogicException( 'Must call prepareContent() or prepareUpdate() before calling ' . $method @@ -1049,7 +1049,7 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate { } } - private function assertHasRevision( $method ) { + private function assertHasRevision( string $method ) { if ( !$this->revision->getId() ) { throw new LogicException( 'Must call prepareUpdate() before calling ' . $method diff --git a/includes/Storage/NameTableStoreFactory.php b/includes/Storage/NameTableStoreFactory.php index b2f936eed2e0..64c7ef0e6391 100644 --- a/includes/Storage/NameTableStoreFactory.php +++ b/includes/Storage/NameTableStoreFactory.php @@ -40,7 +40,7 @@ class NameTableStoreFactory { /** @var LoggerInterface */ private $logger; - private static function getTableInfo() { + private static function getTableInfo(): array { if ( self::$info ) { return self::$info; } diff --git a/includes/Storage/PageUpdater.php b/includes/Storage/PageUpdater.php index 0ca376c3ba4f..4d5347833bce 100644 --- a/includes/Storage/PageUpdater.php +++ b/includes/Storage/PageUpdater.php @@ -1674,7 +1674,7 @@ class PageUpdater implements PageUpdateCauses { RevisionRecord $newRevisionRecord, CommentStoreComment $summary, array $hints = [] - ) { + ): AtomicSectionUpdate { return new AtomicSectionUpdate( $dbw, __METHOD__, @@ -1716,14 +1716,14 @@ class PageUpdater implements PageUpdateCauses { return $this->slotRoleRegistry->getAllowedRoles( $this->getPage() ); } - private function ensureRoleAllowed( $role ) { + private function ensureRoleAllowed( string $role ) { $allowedRoles = $this->getAllowedSlotRoles(); if ( !in_array( $role, $allowedRoles ) ) { throw new PageUpdateException( "Slot role `$role` is not allowed." ); } } - private function ensureRoleNotRequired( $role ) { + private function ensureRoleNotRequired( string $role ) { $requiredRoles = $this->getRequiredSlotRoles(); if ( in_array( $role, $requiredRoles ) ) { throw new PageUpdateException( "Slot role `$role` is required." ); diff --git a/includes/Storage/SqlBlobStore.php b/includes/Storage/SqlBlobStore.php index 15d6730ac594..348c55f8d7cf 100644 --- a/includes/Storage/SqlBlobStore.php +++ b/includes/Storage/SqlBlobStore.php @@ -461,7 +461,7 @@ class SqlBlobStore implements BlobStore { return [ $result, $errors ]; } - private static function getDBOptions( $bitfield ) { + private static function getDBOptions( int $bitfield ): array { if ( DBAccessObjectUtils::hasFlags( $bitfield, IDBAccessObject::READ_LATEST_IMMUTABLE ) ) { $index = DB_REPLICA; // override READ_LATEST if set $fallbackIndex = DB_PRIMARY; diff --git a/includes/actions/HistoryAction.php b/includes/actions/HistoryAction.php index a998bb4909d7..7e0439d199b6 100644 --- a/includes/actions/HistoryAction.php +++ b/includes/actions/HistoryAction.php @@ -424,7 +424,7 @@ class HistoryAction extends FormlessAction { $feed->outFooter(); } - private function feedEmpty() { + private function feedEmpty(): FeedItem { return new FeedItem( $this->msg( 'nohistory' )->inContentLanguage()->text(), $this->msg( 'history-feed-empty' )->inContentLanguage()->parseAsBlock(), diff --git a/includes/actions/McrUndoAction.php b/includes/actions/McrUndoAction.php index d838ad7ac149..7b42ec71c16a 100644 --- a/includes/actions/McrUndoAction.php +++ b/includes/actions/McrUndoAction.php @@ -286,7 +286,7 @@ class McrUndoAction extends FormAction { return $newRev; } - private function generateDiffOrPreview() { + private function generateDiffOrPreview(): string { $newRev = $this->getNewRevision(); if ( $newRev->hasSameContent( $this->curRev ) ) { throw new ErrorPageError( 'mcrundofailed', 'undo-nochange' ); diff --git a/includes/actions/pagers/HistoryPager.php b/includes/actions/pagers/HistoryPager.php index 3a7268edaabd..5eee82db7d59 100644 --- a/includes/actions/pagers/HistoryPager.php +++ b/includes/actions/pagers/HistoryPager.php @@ -308,7 +308,7 @@ class HistoryPager extends ReverseChronologicalPager { return $s; } - private function getRevisionButton( $name, $msg, $class ) { + private function getRevisionButton( string $name, string $msg, string $class ): string { $this->preventClickjacking = true; $element = Html::element( 'button', diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 22417b4c2c9f..d7f3342b5b4c 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -138,14 +138,8 @@ class ApiBlock extends ApiBase { if ( $params['newblock'] ) { $status = $this->insertBlock( $target, $params ); } else { - // Get non-auto blocks - // TODO: factor out to DatabaseBlockStore - $blocks = []; - foreach ( $this->blockStore->newListFromTarget( $target ) as $block ) { - if ( $block->getType() !== AbstractBlock::TYPE_AUTO ) { - $blocks[] = $block; - } - } + $blocks = $this->blockStore->newListFromTarget( + $target, null, false, DatabaseBlockStore::AUTO_NONE ); if ( count( $blocks ) === 0 ) { $status = $this->insertBlock( $target, $params ); } elseif ( count( $blocks ) === 1 ) { diff --git a/includes/api/i18n/es.json b/includes/api/i18n/es.json index a04a0e18fc97..509077c12aff 100644 --- a/includes/api/i18n/es.json +++ b/includes/api/i18n/es.json @@ -14,6 +14,7 @@ "Copper12", "Csbotero", "DDPAT", + "Danielyepezgarces", "DannyS712", "Destinid10 2", "Dgstranz", @@ -739,13 +740,14 @@ "apihelp-query+categorymembers-param-endsortkey": "Utilizar $1endhexsortkey en su lugar.", "apihelp-query+categorymembers-example-simple": "Obtener las primeras 10 páginas en <kbd>Category:Physics</kbd>.", "apihelp-query+categorymembers-example-generator": "Obtener información sobre las primeras 10 páginas de la <kbd>Category:Physics</kbd>.", + "apihelp-query+codexicons-param-names": "Nombres de iconos", "apihelp-query+contributors-summary": "Obtener la lista de contribuidores conectados y el número de contribuidores anónimos de una página.", "apihelp-query+contributors-param-group": "Solo incluir usuarios de los grupos especificados. No incluye grupos implícitos o autopromocionados, como *, usuario o autoconfirmado.", "apihelp-query+contributors-param-excludegroup": "Excluir usuarios de los grupos especificados. No incluye grupos implícitos o autopromocionados, como *, usuario o autoconfirmado.", "apihelp-query+contributors-param-rights": "Solo incluir usuarios con los derechos especificados. No incluye derechos concedidos a grupos implícitos o autopromocionados, como *, usuario o autoconfirmado.", "apihelp-query+contributors-param-excluderights": "Excluir usuarios con los derechos especificados. No incluye derechos concedidos a grupos implícitos o autopromocionados, como *, usuario o autoconfirmado.", "apihelp-query+contributors-param-limit": "Cuántos contribuyentes se devolverán.", - "apihelp-query+contributors-example-simple": "Mostrar los contribuyentes de la página <kbd>Main Page</kbd>.", + "apihelp-query+contributors-example-simple": "Mostrar los colaboradores de la página [[{{MediaWiki:Mainpage}}]].", "apihelp-query+deletedrevisions-summary": "Obtener información de revisión eliminada.", "apihelp-query+deletedrevisions-extended-description": "Puede ser utilizada de varias maneras:\n# Obtenga las revisiones eliminadas de un conjunto de páginas, estableciendo títulos o ID de paginas. Ordenadas por título y marca horaria.\n# Obtener datos sobre un conjunto de revisiones eliminadas estableciendo sus ID con identificación de revisión. Ordenado por ID de revisión.", "apihelp-query+deletedrevisions-param-start": "Marca de tiempo por la que empezar la enumeración. Se ignora cuando se esté procesando una lista de ID de revisión.", @@ -753,7 +755,7 @@ "apihelp-query+deletedrevisions-param-tag": "Listar solo las revisiones con esta etiqueta.", "apihelp-query+deletedrevisions-param-user": "Listar solo las revisiones de este usuario.", "apihelp-query+deletedrevisions-param-excludeuser": "No listar las revisiones de este usuario.", - "apihelp-query+deletedrevisions-example-titles": "Muestra la lista de revisiones borradas de las páginas <kbd>Main Page</kbd> y <kbd>Talk:Main Page</kbd>, con su contenido.", + "apihelp-query+deletedrevisions-example-titles": "Muestra la lista de revisiones borradas de las páginas [[{{MediaWiki:Mainpage}}]] y su página de discusión con su contenido.", "apihelp-query+deletedrevisions-example-revids": "Mostrar la información de la revisión borrada <kbd>123456</kbd>.", "apihelp-query+deletedrevs-summary": "Muestra la lista de revisiones borradas.", "apihelp-query+deletedrevs-extended-description": "Opera en tres modos:\n# Lista de revisiones borradas de los títulos dados, ordenadas por marca de tiempo.\n# Lista de contribuciones borradas del usuario dado, ordenadas por marca de tiempo.\n# Lista de todas las revisiones borradas en el espacio de nombres dado, ordenadas por título y marca de tiempo (donde no se ha especificado ningún título ni se ha fijado $1user).", @@ -768,9 +770,21 @@ "apihelp-query+deletedrevs-param-user": "Listar solo las revisiones de este usuario.", "apihelp-query+deletedrevs-param-excludeuser": "No listar las revisiones de este usuario.", "apihelp-query+deletedrevs-param-namespace": "Listar solo las páginas en este espacio de nombres.", - "apihelp-query+deletedrevs-param-limit": "La cantidad máxima de revisiones que listar.", - "apihelp-query+deletedrevs-param-prop": "Propiedades que obtener:\n;revid: Añade el identificador de la revisión borrada.\n;parentid: Añade el identificador de la revisión anterior de la página.\n;user: Añade el usuario que hizo la revisión.\n;userid: Añade el identificador del usuario que hizo la revisión.\n;comment: Añade el comentario de la revisión.\n;parsedcomment: Añade el comentario de la revisión, pasado por el analizador sintáctico.\n;minor: Añade una etiqueta si la revisión es menor.\n;len: Añade la longitud (en bytes) de la revisión.\n;sha1: Añade el SHA-1 (base 16) de la revisión.\n;content: Añade el contenido de la revisión.\n;token:<span class=\"apihelp-deprecated\">Obsoleto.</span> Devuelve el token de edición.\n;tags: Etiquetas de la revisión.", - "apihelp-query+deletedrevs-example-mode1": "Muestra las últimas revisiones borradas de las páginas <kbd>Main Page</kbd> y <kbd>Talk:Main Page</kbd>, con contenido (modo 1).", + "apihelp-query+deletedrevs-param-limit": "La cantidad máxima de revisiones que listar. Si se utiliza <var>$2prop=content</var>, el límite es $1.", + "apihelp-query+deletedrevs-param-prop": "Propiedades que obtener:", + "apihelp-query+deletedrevs-paramvalue-prop-revid": "Añade el identificador de la revisión borrada.", + "apihelp-query+deletedrevs-paramvalue-prop-parentid": "Añade el identificador de la revisión anterior de la página.", + "apihelp-query+deletedrevs-paramvalue-prop-user": "Añade el usuario que hizo la revisión.", + "apihelp-query+deletedrevs-paramvalue-prop-userid": "Añade el identificador del usuario que hizo la revisión.", + "apihelp-query+deletedrevs-paramvalue-prop-comment": "Añade el comentario de la revisión.", + "apihelp-query+deletedrevs-paramvalue-prop-parsedcomment": "Añade el comentario de la revisión, pasado por el analizador sintáctico.", + "apihelp-query+deletedrevs-paramvalue-prop-minor": "Añade una etiqueta si la revisión es menor.", + "apihelp-query+deletedrevs-paramvalue-prop-len": "Añade la longitud (en bytes) de la revisión.", + "apihelp-query+deletedrevs-paramvalue-prop-sha1": "Añade el SHA-1 (base 16) de la revisión.", + "apihelp-query+deletedrevs-paramvalue-prop-content": "Añade el contenido de la revisión. Por motivos de rendimiento, si se utiliza esta opción, se aplica a <var>$2limit</var> $1.", + "apihelp-query+deletedrevs-paramvalue-prop-token": "Devuelve el token de edición.", + "apihelp-query+deletedrevs-paramvalue-prop-tags": "Etiquetas de la revisión.", + "apihelp-query+deletedrevs-example-mode1": "Muestra las últimas revisiones borradas de las páginas [[{{MediaWiki:Mainpage}}]] y <kbd>Talk:Main Page</kbd>, con contenido (modo 1).", "apihelp-query+deletedrevs-example-mode2": "Muestra las últimas 50 contribuciones de <kbd>Bob</kbd> (modo 2).", "apihelp-query+deletedrevs-example-mode3-main": "Muestra las primeras 50 revisiones borradas del espacio principal (modo 3).", "apihelp-query+deletedrevs-example-mode3-talk": "Listar las primeras 50 páginas en el espacio de nombres {{ns:talk}} (modo 3).", @@ -888,7 +902,7 @@ "apihelp-query+images-param-limit": "Cuántos archivos se devolverán.", "apihelp-query+images-param-images": "Mostrar solo estos archivos. Útil para comprobar si una determinada página tiene un determinado archivo.", "apihelp-query+images-param-dir": "La dirección en que ordenar la lista.", - "apihelp-query+images-example-simple": "Obtener una lista de los archivos usados en la [[Main Page|Portada]].", + "apihelp-query+images-example-simple": "Obtener una lista de los archivos usados en la [[{{MediaWiki:Mainpage}}]].", "apihelp-query+images-example-generator": "Obtener información sobre todos los archivos empleados en [[Main Page]].", "apihelp-query+imageusage-summary": "Encontrar todas las páginas que usen el título de imagen dado.", "apihelp-query+imageusage-param-title": "Título a buscar. No puede usarse en conjunto con $1pageid.", @@ -916,7 +930,7 @@ "apihelp-query+info-param-testactions": "Comprobar su el usuario actual puede realizar determinadas acciones en la página.", "apihelp-query+info-param-testactionsdetail": "Nivel de detalle para <var>$1testactions</var>. Usa los parámetros <var>errorformat</var> y <var>errorlang</var> del [[Special:ApiHelp/main|módulo principal]] para controlar el formato de los mensajes devueltos.", "apihelp-query+info-paramvalue-testactionsdetail-boolean": "Devolver un valor booleano para cada acción.", - "apihelp-query+info-example-simple": "Obtener información acerca de la página <kbd>Main Page</kbd>.", + "apihelp-query+info-example-simple": "Obtener información acerca de la página [[{{MediaWiki:Mainpage}}]].", "apihelp-query+info-example-protection": "Obtén información general y protección acerca de la página <kbd>Main Page</kbd>.", "apihelp-query+iwbacklinks-summary": "Encontrar todas las páginas que enlazan al enlace interwiki dado.", "apihelp-query+iwbacklinks-extended-description": "Puede utilizarse para encontrar todos los enlaces con un prefijo, o todos los enlaces a un título (con un determinado prefijo). Si no se introduce ninguno de los parámetros, se entiende como «todos los enlaces interwiki».", @@ -1113,7 +1127,7 @@ "apihelp-query+revisions-param-excludeuser": "Excluir las revisiones realizadas por el usuario.", "apihelp-query+revisions-param-tag": "Mostrar solo revisiones marcadas con esta etiqueta.", "apihelp-query+revisions-example-content": "Obtener datos con el contenido de la última revisión de los títulos <kbd>API</kbd> y <kbd>Main Page</kbd>.", - "apihelp-query+revisions-example-last5": "Mostrar las últimas 5 revisiones de la <kbd>Main Page</kbd>.", + "apihelp-query+revisions-example-last5": "Mostrar las últimas 5 revisiones de la [[{{MediaWiki:Mainpage}}]].", "apihelp-query+revisions-example-first5": "Obtener las primeras 5 revisiones de <kbd>Main Page</kbd>.", "apihelp-query+revisions-example-first5-after": "Obtener las primeras 5 revisiones de <kbd>Main Page</kbd> realizadas después de 2006-05-01.", "apihelp-query+revisions-example-first5-not-localhost": "Obtener las primeras 5 revisiones de <kbd>Main Page</kbd> que no fueron realizadas por el usuario anónimo <kbd>127.0.0.1</kbd>.", @@ -1585,11 +1599,13 @@ "apierror-emptynewsection": "Crear secciones vacías no es posible.", "apierror-emptypage": "Crear páginas vacías no está permitido.", "apierror-exceptioncaught": "[$1] Excepción capturada: $2", + "apierror-exceptioncaughttype": "[$1] Excepción capturada de tipo $2", "apierror-filedoesnotexist": "El archivo no existe.", "apierror-fileexists-sharedrepo-perm": "El archivo objetivo existe en un repositorio compartido. Usa el parámetro <var>ignorewarnings</var> para reemplazarlo.", "apierror-filenopath": "No se pudo obtener la ruta local del archivo.", "apierror-filetypecannotberotated": "El tipo de archivo no se puede girar.", "apierror-formatphp": "Esta respuesta no se puede representar con <kbd>format=php</kbd>. Véase https://phabricator.wikimedia.org/T68776.", + "apierror-http-contenttoolarge": "Contenido de la solicitud HTTP demasiado grande. Longitud máxima: $1.", "apierror-imageusage-badtitle": "El título de <kbd>$1</kbd> debe ser un archivo.", "apierror-import-unknownerror": "Error desconocido en la importación: $1.", "apierror-integeroutofrange-abovebotmax": "<var>$1</var> no puede ser mayor que $2 (fijado a $3) para bots o administradores de sistema.", @@ -1656,6 +1672,7 @@ "apierror-paramempty": "El parámetro <var>$1</var> no puede estar vacío.", "apierror-parsetree-notwikitext": "<kbd>prop=parsetree</kbd> solo es compatible con el contenido en wikitexto.", "apierror-parsetree-notwikitext-title": "<kbd>prop=parsetree</kbd> solo es compatible con el contenido en wikitexto. $1 usa el modelo de contenido $2.", + "apierror-pastexpiry": "El tiempo de expiración «$1» está en el pasado.", "apierror-permissiondenied": "No tienes permiso para $1.", "apierror-permissiondenied-generic": "Permiso denegado.", "apierror-permissiondenied-unblock": "No tienes permiso para desbloquear usuarios.", diff --git a/includes/api/i18n/fr.json b/includes/api/i18n/fr.json index 7fa9adfc549c..1e8a44efd5e0 100644 --- a/includes/api/i18n/fr.json +++ b/includes/api/i18n/fr.json @@ -1375,7 +1375,7 @@ "apihelp-query+userinfo-paramvalue-prop-options": "Liste toutes les préférences qu’a définies l’utilisateur actuel.", "apihelp-query+userinfo-paramvalue-prop-editcount": "Ajoute le compteur de modifications de l’utilisateur actuel.", "apihelp-query+userinfo-paramvalue-prop-ratelimits": "Liste toutes les limites de débit s’appliquant à l’utilisateur actuel.", - "apihelp-query+userinfo-paramvalue-prop-theoreticalratelimits": "Liste toutes les limites de taux qui s’appliqueraient à l’utilisateur actuel s’il n’était pas exempté de toutes les limites de débit d’après ses droits utilisateur ou son adresse IP", + "apihelp-query+userinfo-paramvalue-prop-theoreticalratelimits": "Liste toutes les limites de taux qui s’appliqueraient à l’utilisateur actuel s’il n’était pas exempté de toutes les limites de débit d’après ses droits utilisateur ou son adresse IP.", "apihelp-query+userinfo-paramvalue-prop-realname": "Ajoute le vrai nom de l’utilisateur actuel.", "apihelp-query+userinfo-paramvalue-prop-email": "Ajoute l’adresse de courriel de l’utilisateur et sa date d’authentification.", "apihelp-query+userinfo-paramvalue-prop-acceptlang": "Renvoie en écho l’entête <code>Accept-Language</code> envoyé par le client dans un format structuré.", diff --git a/includes/api/i18n/pa.json b/includes/api/i18n/pa.json index 151f6f0c2332..60768f0ecd9b 100644 --- a/includes/api/i18n/pa.json +++ b/includes/api/i18n/pa.json @@ -69,5 +69,6 @@ "apihelp-setpagelanguage-summary": "ਸਫ਼ੇ ਦੀ ਭਾਸ਼ਾ ਬਦਲੋ।", "apihelp-validatepassword-summary": "ਵਿਕੀ ਦੀਆਂ ਪਾਰਸ਼ਬਦ ਨੀਤੀਆਂ ਦੇ ਖ਼ਿਲਾਫ ਇੱਕ ਪਾਰਸ਼ਬਦ ਦੀ ਤਸਦੀਕ ਕਰੋ।", "apihelp-validatepassword-param-password": "ਤਸਦੀਕ ਕਰਨ ਲਈ ਪਾਰਸ਼ਬਦ", - "api-help-templatedparams-header": "ਫਰਮੇ ਦੇ ਮਾਪਦੰਡ" + "api-help-templatedparams-header": "ਫਰਮੇ ਦੇ ਮਾਪਦੰਡ", + "api-help-param-type-presenceboolean": "ਕਿਸਮ: ਬੂਲੀਅਨ (ਸੱਚ/ਝੂਠ ਕਿਸਮ) ([[Special:ApiHelp/main#main/datatype/boolean|ਵੇਰਵੇ]])" } diff --git a/includes/api/i18n/sl.json b/includes/api/i18n/sl.json index 9f3063075832..28baf0720814 100644 --- a/includes/api/i18n/sl.json +++ b/includes/api/i18n/sl.json @@ -128,10 +128,10 @@ "apihelp-delete-param-reason": "Razlog za izbris. Če ni nastavljen, bo uporabljen samodejno ustvarjen razlog.", "apihelp-delete-param-tags": "Sprememba oznak za uporabo v vnosu v dnevniku brisanja.", "apihelp-delete-param-deletetalk": "Izbris pogovorne strani, če obstaja.", - "apihelp-delete-param-watch": "Dodajanje strani trenutnemu uporabnikovemu spisku nadzorov.", - "apihelp-delete-param-watchlist": "Brezpogojno dodajte ali odstranite stran s spiska nadzorov trenutnega uporabnika, uporabite nastavitve (prezrto za uporabnike botov) ali ne spremenite opazovanja.", + "apihelp-delete-param-watch": "Dodajanje strani nadzornem seznamu trenutnega uporabnika.", + "apihelp-delete-param-watchlist": "Brezpogojno dodajte ali odstranite stran z nadzornega seznama trenutnega uporabnika, uporabite nastavitve (prezrto za uporabnike botov) ali ne spremenite opazovanja.", "apihelp-delete-param-watchlistexpiry": "Časovni žig preteka opazovanja. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", - "apihelp-delete-param-unwatch": "Odstranjevanje strani s trenutnega uporabnikovega spiska nadzorov.", + "apihelp-delete-param-unwatch": "Odstranjevanje strani z nadzornega seznama trenutnega uporabnika.", "apihelp-delete-param-oldimage": "Ime stare slike za brisanje, kot je navedeno v [[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]].", "apihelp-delete-example-simple": "Izbris strani [[{{MediaWiki:Mainpage}}]].", "apihelp-delete-example-reason": "Izbris [[{{MediaWiki:Mainpage}}]] z razlogom <kbd>priprava za prestavitev</kbd>.", @@ -153,9 +153,9 @@ "apihelp-edit-param-recreate": "Preglasitev vseh napak o tem, da je bila stran medtem izbrisana.", "apihelp-edit-param-createonly": "Brez urejanja strani, če že obstaja.", "apihelp-edit-param-nocreate": "Izpis napake, če stran ne obstaja.", - "apihelp-edit-param-watch": "Dodajanje strani trenutnemu spisku nadzorov uporabnika.", - "apihelp-edit-param-unwatch": "Odstranitev strani s trenutnega spiska nadzorov uporabnika.", - "apihelp-edit-param-watchlist": "Brezpogojno dodajanje ali odstranitev stran s spiska nadzorov trenutnega uporabnika, uporaba nastavitev (prezrto za uporabnike botov) ali brez spremembe opazovanja.", + "apihelp-edit-param-watch": "Dodajanje strani nadzornem seznamu trenutnega uporabnika.", + "apihelp-edit-param-unwatch": "Odstranitev strani z nadzornega seznama trenutnega uporabnika.", + "apihelp-edit-param-watchlist": "Brezpogojno dodajanje ali odstranitev stran z nadzornega seznama trenutnega uporabnika, uporaba nastavitev (prezrto za uporabnike botov) ali brez spremembe opazovanja.", "apihelp-edit-param-watchlistexpiry": "Časovni žig preteka opazovanja. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", "apihelp-edit-param-md5": "Zgoščena vrednost MD5 parametra $1text ali povezanih parametrov $1prependtext in $1appendtext. Če je nastavljeno, urejanje ne bo izvedeno, razen če je zgoščena vrednost pravilna.", "apihelp-edit-param-prependtext": "Dodajanje tega besedila na začetek strani ali razdelka. Preglasi $1text.", @@ -231,11 +231,11 @@ "apihelp-feedrecentchanges-param-showlinkedto": "Namesto tega prikaži spremembe na straneh, ki se povezujejo z izbrano stranjo.", "apihelp-feedrecentchanges-example-simple": "Prikaži zadnje spremembe.", "apihelp-feedrecentchanges-example-30days": "Prikaže zadnje spremembe za 30 dni.", - "apihelp-feedwatchlist-summary": "Vrne vir spiska nadzorov.", + "apihelp-feedwatchlist-summary": "Vrne vir nadzornega seznama.", "apihelp-feedwatchlist-param-feedformat": "Format vira.", "apihelp-feedwatchlist-param-hours": "Seznam strani, spremenjenih v toliko urah od zdaj.", "apihelp-feedwatchlist-param-linktosections": "Povezava neposredno na spremenjene razdelke, če je mogoče.", - "apihelp-feedwatchlist-example-default": "Prikaz vira spiska nadzorov.", + "apihelp-feedwatchlist-example-default": "Prikaz vira nadzornega seznama.", "apihelp-feedwatchlist-example-all6hrs": "Prikaži vse spremembe opazovanih strani v zadnjih 6 urah.", "apihelp-filerevert-summary": "Vrnitev datoteke na staro različico.", "apihelp-filerevert-param-filename": "Ime ciljne datoteke brez predpone File:.", @@ -291,29 +291,126 @@ "apihelp-managetags-paramvalue-operation-delete": "Odstranite oznako spremembe iz baze podatkov, vključno z odstranitvijo oznake iz vseh revizij, nedavnih vnosov sprememb in vnosov v dnevnik, v katerih se uporablja.", "apihelp-managetags-paramvalue-operation-activate": "Deaktiviranje oznake spremembe, kar uporabnikom prepreči, da bi jo uporabili ročno.", "apihelp-managetags-paramvalue-operation-deactivate": "Deaktiviranje oznake spremembe, kar uporabnikom prepreči, da bi jo uporabili ročno.", + "apihelp-managetags-param-tag": "Označite za ustvarjanje, brisanje, aktiviranje ali deaktiviranje. Za ustvaritev oznake oznaka ne sme obstajati. Za izbris oznake mora oznaka obstajati. Za aktivacijo oznake mora oznaka obstajati in jo mora uporabljati ena od razširitev. Za deaktivacijo oznake mora biti oznaka trenutno aktivna in ročno določena.", + "apihelp-managetags-param-reason": "Neobvezni razlog za ustvaritev, izbris, aktivacijo ali deaktivacijo oznake.", + "apihelp-managetags-param-ignorewarnings": "Ali naj bodo morebitna opozorila med operacijo prezrta.", + "apihelp-managetags-param-tags": "Sprememba oznak za uporabo pri vnosu v dnevniku upravljanja oznak.", + "apihelp-managetags-example-create": "Ustvaritev oznake z imenom <kbd>spam</kbd> z razlogom <kbd>For use in edit patrolling</kbd>", + "apihelp-managetags-example-delete": "Izbris oznake <kbd>vandlaism</kbd> z razlogom <kbd>Misspelt</kbd>", + "apihelp-managetags-example-activate": "Aktivacija oznake <kbd>spam</kbd> z razlogom <kbd>For use in edit patrolling</kbd>", + "apihelp-managetags-example-deactivate": "Deaktivacija oznake <kbd>spam</kbd> z razlogom <kbd>No longer required</kbd>", + "apihelp-mergehistory-summary": "Združevanje zgodovine strani.", + "apihelp-mergehistory-param-from": "Naslov strani, iz katere naj se združi zgodovina. Ni mogoče uporabljati skupaj z <var>$1fromid</var>.", + "apihelp-mergehistory-param-fromid": "ID strani, v katero naj se združi zgodovina. Ni mogoče uporabljati skupaj z <var>$1from</var>.", "apihelp-mergehistory-param-to": "Naslov strani, v katero naj se združi zgodovina. Ni ga mogoče uporabiti skupaj z <var>$1toid</var>.", "apihelp-mergehistory-param-toid": "ID strani, v katero naj se združi zgodovina. Ni ga mogoče uporabiti skupaj z <var>$1to</var>.", + "apihelp-mergehistory-param-timestamp": "Časovni žig, do katerega bodo redakcije prestavljene iz zgodovine izvorne strani v zgodovino ciljne strani. Če je izpuščeno, bo v ciljno stran združena celotna zgodovina strani izvorne strani.", "apihelp-mergehistory-param-reason": "Razlog za združitev zgodovine.", + "apihelp-mergehistory-example-merge": "Združitev celotne zgodovine <kbd>Oldpage</kbd> v <kbd>Newpage</kbd>.", + "apihelp-mergehistory-example-merge-timestamp": "Združitev redakcij strani <kbd>Oldpage</kbd> do datuma <kbd>2015-12-31T04:37:41Z</kbd> v <kbd>Newpage</kbd>.", "apihelp-move-summary": "Prestavi stran.", "apihelp-move-param-from": "Naslov strani za preimenovanje. Ni ga mogoče uporabiti skupaj z <var>$1fromid</var>.", + "apihelp-move-param-fromid": "ID strani za preimenovanje. Ni mogoče uporabljati skupaj z <var>$1from</var>.", "apihelp-move-param-to": "Novi naslov strani.", + "apihelp-move-param-reason": "Razlog za preimenovanje.", + "apihelp-move-param-movetalk": "Preimenovanje pogovorne strani, če obstaja.", "apihelp-move-param-movesubpages": "Preimenovanje strani, če je mogoče.", "apihelp-move-param-noredirect": "Ne ustvari preusmeritve.", + "apihelp-move-param-watch": "Dodajanje strani in preusmeritev na nadzorni seznam trenutnega uporabnika.", + "apihelp-move-param-unwatch": "Odstranitev strani in preusmeritev z nadzornega seznama trenutnega uporabnika.", + "apihelp-move-param-watchlist": "Brezpogojno dodanje ali odstranitev strani z nadzornega seznama trenutnega uzporabnika, uporaba nastavitev (prezrto za bote) ali brez spremembe nadzora.", + "apihelp-move-param-watchlistexpiry": "Časovni žig preteka nadzornega seznama. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", + "apihelp-move-param-ignorewarnings": "Prezri vsa opozorila.", + "apihelp-move-param-tags": "Sprememba oznak za uporabo pri vnosu v dnevniku prestavljanja in ničelni redakciji na ciljnih straneh.", + "apihelp-move-example-move": "Prestavitev <kbd>Badtitle</kbd> na <kbd>Goodtitle</kbd>, ne da bi zapustili preusmeritev.", + "apihelp-opensearch-summary": "Iskanje po vikiju s protokolom OpenSearch.", "apihelp-opensearch-param-search": "Iskalni niz.", + "apihelp-opensearch-param-limit": "Največje število zadetkov za vrnitev.", + "apihelp-opensearch-param-namespace": "Imenski prostori za iskanje. Prezrto, če se <var>$1search</var> začne z veljavno predpono imenskega prostora.", + "apihelp-opensearch-param-suggest": "Ni več v uporabi.", "apihelp-opensearch-param-redirects": "Kako ravnati s preusmeritvami:", "apihelp-opensearch-paramvalue-redirects-return": "Vrnjena naj bo preusmeritev sama.", "apihelp-opensearch-paramvalue-redirects-resolve": "Vrnite ciljno stran. Lahko vrne zadetke z omejitvijo manj kot $1limit.", "apihelp-opensearch-param-redirects-append": "Iz zgodovinskih razlogov je privzeta vrednost »return« za $1 format=json in »resolve« za druge formate.", "apihelp-opensearch-param-format": "Oblika izhoda.", + "apihelp-opensearch-param-warningsaserror": "Če se opozorila prikažejo s <kbd>format=json</kbd>, vrnitev napake API-ja namesto prezrtja.", "apihelp-opensearch-example-te": "Iskanje strani, ki se začnejo s <kbd>Te</kbd>.", + "apihelp-options-summary": "Sprememba nastavitev trenutnega uporabnika.", + "apihelp-options-extended-description": "Nastavite lahko samo možnosti, ki so registrirane v jedru ali v eni od nameščenih razširitev, ali možnosti s ključi s predpono <code>userjs-</code> (namenjene uporabniškim skriptom).", + "apihelp-options-param-reset": "Ponastavitev nastavitev na privzete vrednosti projekta.", + "apihelp-options-param-resetkinds": "Naštetje vrst možnosti za ponastavitev, ko je nastavljena možnost <var>$1reset</var>.", + "apihelp-options-param-change": "Seznam sprememb, oblikovan kot ime=vrednost (npr. skin=vector). Če ni podana nobena vrednost (niti enačaj), npr. imenastavitve|druganastavitev|..., bo možnost ponastavljena na privzeto vrednost. Če katera koli posredovana vrednost vsebuje navpičnico (<kbd>|</kbd>), za pravilno delovanje uporabite [[Special:ApiHelp/main#main/datatypes|alternativno ločilo za več vrednosti]].", + "apihelp-options-param-optionname": "Ime možnosti, ki mora biti nastavljena na vrednost, podano z <var>optionvalue</var>.", "apihelp-options-param-optionvalue": "Vrednost za možnost, ki jo določa <var>$1optionname</var>. Ko je nastavljeno <var>$1optionname,</var> vendar je <var>$1 optionvalue</var> izpuščeno, bo možnost ponastavljena na privzeto vrednost.", + "apihelp-options-param-global": "Kaj storiti, če je bila možnost nastavljena globalno z uporabo GlobalPreferences extension.\n\n* <kbd>ignore</kbd>: Ne zgodi se nič. Možnost ohrani svojo prejšnjo vrednost.\n* <kbd>override</kbd>: Dodanje lokalne preglasitve.\n* <kbd>update</kbd>: Globalna posodobitev možnosti.", + "apihelp-options-example-reset": "Ponastavi vse nastavitve.", + "apihelp-options-example-change": "Sprememba nastavitev <kbd>skin</kbd> in <kbd>hideminor</kbd>.", + "apihelp-options-example-complex": "Ponastavitev vseh nastavitev, nato nastavitev <kbd>skin</kbd> in <kbd>nickname</kbd>.", + "apihelp-paraminfo-summary": "Pridobitev informacij o modulih API-ja.", + "apihelp-paraminfo-param-modules": "Seznam imen modulov (vrednost parametra <var>action</var>, <var>format</var> ali <kbd>main</kbd>). Lahko določi podmodule z <kbd>+</kbd> ali vse podmodule z <kbd>+*</kbd> ali vse podmodule rekurzivno z <kbd>+**</kbd>.", "apihelp-paraminfo-param-helpformat": "Format nizov pomoči.", + "apihelp-paraminfo-param-querymodules": "Seznam imen modulov poizvedb (vrednost parametra <var>prop</var>, <var>meta</var> ali <var>list</var>). Uporabite <kbd>$1 modules=query+foo</kbd> namesto <kbd>$1 querymodules=foo</kbd>.", + "apihelp-paraminfo-param-mainmodule": "Pridobi tudi informacije o glavnem (najvišjem) modulu. Namesto tega uporabite <kbd>$1 modules=main</kbd>.", + "apihelp-paraminfo-param-pagesetmodule": "Pridobitev informacij o modulu pageset (z določitvijo titles= in sorodnih).", + "apihelp-paraminfo-param-formatmodules": "Seznam imen formatnih modulov (vrednost parametra <var>format</var>). Namesto tega uporabite <var>$1modules</var>.", + "apihelp-paraminfo-example-1": "Prikaz informacij za <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> in <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.", + "apihelp-paraminfo-example-2": "Prikaz informacije za vse podmodule <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.", + "apihelp-parse-summary": "Razčlemba vsebine in vrnitev izhoda razčlenjevalnika.", + "apihelp-parse-extended-description": "Oglejte si različne prop-module <kbd>[[Special:ApiHelp/query|action=query]],</kbd> če želite pridobiti informacije iz trenutne različice strani.\n\nObstaja več načinov za določitev besedila za razčlembo:\n# Določite stran ali redakcijo z uporabo <var>$1page</var>, <var>$1pageid</var> ali <var>$1oldid</var>.\n# Izrecno določite vsebino z uporabo <var>$1text</var>, <var>$1title</var>, <var>$1revid</var> in <var>$1contentmodel</var>.\n# Podajte samo povzetek za razčlembo. <var>$1prop</var> mora imeti prazno vrednost.", + "apihelp-parse-param-title": "Naslov strani, ki ji pripada besedilo. Če je izpuščen, je treba določiti <var>$1contentmodel</var> in bo [[API]] uporabljen kot naslov.", + "apihelp-parse-param-text": "Besedilo za razčlembo. Za nadzor vsebinskega modela uporabite <var>$1title</var> ali <var>$1contentmodel</var>.", + "apihelp-parse-param-revid": "ID redakcije za <code><nowiki>{{REVISIONID}}</nowiki></code> in podobne spremenljivke.", + "apihelp-parse-param-summary": "Povzetek za razčlembo.", + "apihelp-parse-param-page": "Razčlemba vsebine te strani. Ni ga mogoče uporabiti skupaj z <var>$1text</var> in <var>$1title</var>.", + "apihelp-parse-param-pageid": "Razčlemba vsebine te strani. Preglasi <var>$1page</var>.", + "apihelp-parse-param-redirects": "Če je <var>$1page</var> ali <var>$1pageid</var> nastavljeno na preusmeritev, se to razreši.", + "apihelp-parse-param-oldid": "Razčlemba vsebine te redakcije. Preglasi <var>$1page</var> in <var>$1pageid</var>.", + "apihelp-parse-param-prop": "Katere informacije pridobiti:", + "apihelp-parse-paramvalue-prop-text": "Poda razčlenjeno besedilo vikibesedila.", + "apihelp-parse-paramvalue-prop-langlinks": "Poda jezikovne povezave v razčlenjenem vikibesedilu.", "apihelp-parse-paramvalue-prop-categories": "Poda kategorije v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-categorieshtml": "Poda različico HTML kategorij.", + "apihelp-parse-paramvalue-prop-links": "Poda notranje povezave v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-templates": "Poda predloge v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-images": "Poda slike v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-externallinks": "Poda zunanje povezave v razčlenjenem vikibesedilu.", "apihelp-parse-paramvalue-prop-sections": "Poda razdelke v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-revid": "Doda ID redakcije razčlenjene strani.", + "apihelp-parse-paramvalue-prop-displaytitle": "Doda naslov razčlenjenega vikibesedila.", + "apihelp-parse-paramvalue-prop-subtitle": "Doda podnaslov strani za razčlenjeno stran.", + "apihelp-parse-paramvalue-prop-headitems": "Poda elemente za vstavitev v <code><head></code> strani.", + "apihelp-parse-paramvalue-prop-headhtml": "Poda razčlenjene doctype, uvodni <code><html></code>, element <code><head></code> in uvodni <code><body></code> strani.", + "apihelp-parse-paramvalue-prop-modules": "Poda module ResourceLoader, ki so uporabljeni na strani. Za naložitev uporabite <code>mw.loader.using()</code>. Skupaj s <kbd>modules</kbd> je treba zahtevati bodisi <kbd>jsconfigvars</kbd> bodisi <kbd>encodedjsconfigvars</kbd>.", + "apihelp-parse-paramvalue-prop-jsconfigvars": "Poda konfiguracijske spremenljivke JavaScript, specifične za stran. Za uporabo uporabite <code>mw.config.set()</code>.", + "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Poda konfiguracijske spremenljivke za JavaScript, specifične za stran, kot niz JSON.", + "apihelp-parse-paramvalue-prop-indicators": "Poda HTML indikatorjev stanja strani, ki so uporabljeni na strani.", + "apihelp-parse-paramvalue-prop-iwlinks": "Poda medviki povezave v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-wikitext": "Poda izvorno vikibesedilo, ki je bilo razčlenjeno.", + "apihelp-parse-paramvalue-prop-properties": "Poda različne lastnosti, definirane v razčlenjenem vikibesedilu.", + "apihelp-parse-paramvalue-prop-limitreportdata": "Na strukturiran način poda poročilo o omejitvah. Če je nastavljeno <var>$1disablelimitreport</var>, ne poda podatkov.", + "apihelp-parse-paramvalue-prop-limitreporthtml": "Poda različico HTMl poročila o omejitvah. Če je nastavljeno <var>$1disablelimitreport</var>, ne poda podatkov.", + "apihelp-parse-paramvalue-prop-parsetree": "Drevo razčlembe XML vsebine redakcije (zahteva vsebinski model <code>$1</code>)", + "apihelp-parse-paramvalue-prop-parsewarnings": "Poda opozorila, ki so se pojavila med razčlenjevanjem vsebine (kot vikibesedilo).", + "apihelp-parse-paramvalue-prop-parsewarningshtml": "Poda opozorila, ki so se pojavila med razčlenjevanjem vsebine (kot HTML).", + "apihelp-parse-param-wrapoutputclass": "Razred CSS za uporabo za prelom izhoda razčlenjevalnika.", + "apihelp-parse-param-usearticle": "Uporaba vstavka ArticleParserOptions za zagotovitev, da se uporabljene možnosti ujemajo s tistimi, ki se uporabljajo za oglede strani člankov", "apihelp-parse-param-parsoid": "Ustvari HTML, ki ustreza [[mw:Specs/HTML|specifikaciji MediaWiki DOM]] z uporabo [[mw:Parsoid|Parsoida]].", + "apihelp-parse-param-pst": "Izvedba predhodne shranjevalne transformacije vnosa, preden se ta razčleni. Velja samo, če se uporablja z besedilom.", + "apihelp-parse-param-onlypst": "Izvede pretvorbo pred shranjevanjem (PST) na vnosu, vendar ga ne razčleni. Vrne isto vikibesedilo, potem ko je bil uporabljen PST. Veljavno samo pri uporabi z <var>$1text</var>.", + "apihelp-parse-param-effectivelanglinks": "Vključuje jezikovne povezave, ki jih zagotavljajo razširitve (za uporabo z <kbd>$1prop=langlinks</kbd>).", + "apihelp-parse-param-section": "Razčleni samo vsebino razdelka s tem identifikatorjem.\n\nKo je <kbd>new</kbd>, razčleni <var>$1text</var> in <var>$1sectiontitle</var>, kot da bi se strani dodal nov razdelek.\n\n<kbd>new</kbd> je dovoljeno samo pri podajanju <var>text</var>.", + "apihelp-parse-param-sectiontitle": "Nov naslov razdelka, ko je <var>section</var> <kbd>new</kbd>.\n\nZa razliko od urejanja strani se to ne vrne na <var>summary</var>, če je izpuščeno ali prazno.", + "apihelp-parse-param-disablelimitreport": "Iz izhoda razčlenjevalnika izpusti poročilo o omejitvi (»poročilo o omejitvi NewPP«).", + "apihelp-parse-param-disablepp": "Namesto tega uporabite <var>$1disablelimitreport</var>.", + "apihelp-parse-param-disableeditsection": "Izpusti povezave za urejanje razdelka iz izhoda razčlenjevalnika.", + "apihelp-parse-param-disablestylededuplication": "V izhodu razčlenjevalnika ne odstrani podvojenih vključenih slogovnih listov.", + "apihelp-parse-param-showstrategykeys": "Ali naj bodo v jsconfigvars vključene informacije o interni strategiji združevanja.", + "apihelp-parse-param-generatexml": "Ustvari drevo za razčlembo XML (zahteva vsebinski model <code>$1</code>; zamenjano z <kbd>$2prop=parsetree</kbd>).", "apihelp-parse-param-preview": "Razčleni v načinu predogleda.", "apihelp-parse-param-sectionpreview": "Razčleni v načinu predogleda razdelka (omogoča tudi način predogleda).", "apihelp-parse-param-disabletoc": "Izpustitev kazala vsebine v izhodu.", + "apihelp-parse-param-useskin": "Uporabi izbrano preobleko na izhod razčlenjevalnika. Lahko vpliva na naslednje lastnosti: <kbd>text</kbd>, <kbd>langlinks</kbd>, <kbd>headitems</kbd>, <kbd>modules</kbd>, <kbd>jsconfigvars</kbd>, <kbd>indicators</kbd>.", + "apihelp-parse-param-contentformat": "Format serializacije vsebine, uporabljen za vhodno vsebino. Veljavno samo pri uporabi z $1text.", "apihelp-parse-example-text": "Razčlemba vikibesedila.", "apihelp-parse-example-texttitle": "Razčleni vikibesedilo ob navedbi naslova strani.", "apihelp-protect-param-title": "Naslov strani za (od)zaščito. Ni ga mogoče uporabiti hkrati z $1pageid.", @@ -514,7 +611,7 @@ "apihelp-query+usercontribs-example-user": "Prikaži prispevke uporabnika <kbd>Zgled</kbd>.", "apihelp-query+userinfo-paramvalue-prop-cancreateaccount": "Označuje, ali je uporabniku dovoljeno ustvarjati račune. Če želite preveriti, ali je mogoče ustvariti določen račun, uporabite [[Special:ApiHelp/query+users|<kbd>action=query&list=users&usprop=cancreate</kbd>]].", "apihelp-query+users-paramvalue-prop-cancreate": "Označuje, ali je mogoče ustvariti račun za veljavna, toda neregistrirana uporabniška imena. Če želite preveriti, ali lahko trenutni uporabnik ustvari račun, uporabite [[Special:ApiHelp/query+userinfo|<kbd>action=query&meta=userinfo&uiprop=cancreateaccount</kbd>]].", - "apihelp-query+watchlist-summary": "Pridobi zadnje spremembe strani na spisku nadzorov trenutnega uporabnika.", + "apihelp-query+watchlist-summary": "Pridobi zadnje spremembe strani na nadzornem seznamu trenutnega uporabnika.", "apihelp-query+watchlist-param-prop": "Katere dodatne lastnosti naj se pridobijo:", "apihelp-query+watchlist-paramvalue-prop-title": "Doda naslov strani.", "apihelp-query+watchlist-paramvalue-prop-tags": "Navede oznake za vnos.", @@ -541,7 +638,7 @@ "apihelp-tag-param-remove": "Oznake za odstranitev. Odstraniti je mogoče samo oznake, ki so opredeljene ročno ali popolnoma neopredeljene.", "apihelp-unblock-param-tags": "Sprememba oznak za uporabo v vnosu v dnevniku blokiranja.", "apihelp-unblock-param-watchuser": "Opazovanje uporabniške in pogovorne strani uporabnika ali IP-naslova.", - "apihelp-unblock-param-watchlistexpiry": "Časovni žig preteka spiska nadzorov. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", + "apihelp-unblock-param-watchlistexpiry": "Časovni žig preteka nadzornega seznama. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", "apihelp-undelete-summary": "Odizbris redakcij izbrisane strani.", "apihelp-undelete-param-title": "Naslov strani za odizbris.", "apihelp-undelete-param-tags": "Sprememba oznak za uporabo pri vnosu v dnevniku brisanja.", @@ -558,7 +655,7 @@ "apihelp-userrights-param-watchuser": "Opazuj uporabnikovo uporabniško in pogovorno stran", "apihelp-userrights-param-watchlistexpiry": "Časovni žig preteka opazovanja. Da ostane trenutni pretek nespremenjen, ta parameter v celoti izpustite.", "apihelp-userrights-example-expiry": "Dodajanje uporabnika <kbd>SometimeSysop</kbd> v skupino <kbd>sysop</kbd> za 1 mesec.", - "apihelp-watch-summary": "Dodatek ali odstranitev strani s spiska nadzora", + "apihelp-watch-summary": "Dodatek ali odstranitev strani z nadzornega seznama", "apihelp-watch-example-watch": "Opazovanje strani [[{{MediaWiki:Mainpage}}]].", "apihelp-watch-example-watch-expiry": "Opazovanje strani [[{{MediaWiki:Mainpage}}]], <kbd>Foo</kbd> in <kbd>Bar</kbd> en mesec.", "apihelp-watch-example-unwatch": "Odopazovanje strani [[{{MediaWiki:Mainpage}}]].", diff --git a/includes/api/i18n/zh-hans.json b/includes/api/i18n/zh-hans.json index 421777ca19b9..7d053f994b05 100644 --- a/includes/api/i18n/zh-hans.json +++ b/includes/api/i18n/zh-hans.json @@ -777,6 +777,8 @@ "apihelp-query+categorymembers-param-endsortkey": "请改用$1endhexsortkey。", "apihelp-query+categorymembers-example-simple": "获取<kbd>Category:Physics</kbd>中的前10个页面。", "apihelp-query+categorymembers-example-generator": "获取有关<kbd>Category:Physics</kbd>中的前10个页面的页面信息。", + "apihelp-query+codexicons-summary": "获取Codex图标", + "apihelp-query+codexicons-param-names": "图标名称", "apihelp-query+contributors-summary": "获取一个页面的已登录贡献者列表和匿名贡献者数量。", "apihelp-query+contributors-summary-tempusers-enabled": "获取页面的登录贡献者(包括临时用户)列表以及退出登录的贡献者的数量。", "apihelp-query+contributors-param-group": "只包括指定用户组中的用户。不包括隐性的或自动提升的用户组,例如*、用户或自动确认用户。", 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/block/BlockUser.php b/includes/block/BlockUser.php index 22cbc02c0701..aa9ac9269fd4 100644 --- a/includes/block/BlockUser.php +++ b/includes/block/BlockUser.php @@ -388,17 +388,11 @@ class BlockUser { */ private function getPriorBlocksForTarget() { if ( $this->priorBlocksForTarget === null ) { - $priorBlocks = $this->blockStore->newListFromTarget( $this->target, null, true ); - foreach ( $priorBlocks as $i => $block ) { + $this->priorBlocksForTarget = $this->blockStore->newListFromTarget( + $this->target, null, true, // If we're blocking an IP, ignore any matching autoblocks (T287798) - // TODO: put this in the query conditions - if ( $this->target->getType() !== Block::TYPE_AUTO - && $block->getType() === Block::TYPE_AUTO - ) { - unset( $priorBlocks[$i] ); - } - } - $this->priorBlocksForTarget = array_values( $priorBlocks ); + DatabaseBlockStore::AUTO_SPECIFIED + ); } return $this->priorBlocksForTarget; } diff --git a/includes/block/DatabaseBlockStore.php b/includes/block/DatabaseBlockStore.php index d8c8d71e12d9..c33157b58157 100644 --- a/includes/block/DatabaseBlockStore.php +++ b/includes/block/DatabaseBlockStore.php @@ -57,6 +57,13 @@ use function array_key_exists; * @author DannyS712 */ class DatabaseBlockStore { + /** Load all autoblocks */ + public const AUTO_ALL = 'all'; + /** Load only autoblocks specified by ID */ + public const AUTO_SPECIFIED = 'specified'; + /** Do not load autoblocks */ + public const AUTO_NONE = 'none'; + /** * @internal For use by ServiceWiring */ @@ -195,12 +202,14 @@ class DatabaseBlockStore { * @param BlockTarget|null $vagueTarget Also search for blocks affecting * this target. Doesn't make any sense to use TYPE_AUTO here. Leave blank to * skip IP lookups. + * @param string $auto One of the self::AUTO_* constants * @return DatabaseBlock[] Any relevant blocks */ private function newLoad( $specificTarget, $fromPrimary, - $vagueTarget = null + $vagueTarget = null, + $auto = self::AUTO_ALL ) { if ( $fromPrimary ) { $db = $this->getPrimaryDB(); @@ -268,10 +277,14 @@ class DatabaseBlockStore { return []; } + // Exclude autoblocks unless AUTO_ALL was requested. + $autoConds = $auto === self::AUTO_ALL ? [] : [ 'bt_auto' => 0 ]; + $blockQuery = $this->getQueryInfo(); $res = $db->newSelectQueryBuilder() ->queryInfo( $blockQuery ) ->where( $db->orExpr( $orConds ) ) + ->andWhere( $autoConds ) ->caller( __METHOD__ ) ->fetchResultSet(); @@ -450,6 +463,10 @@ class DatabaseBlockStore { * block which affects that target (so for an IP address, get ranges containing that IP; * and also get any relevant autoblocks). Leave empty or blank to skip IP-based lookups. * @param bool $fromPrimary Whether to use the DB_PRIMARY database + * @param string $auto Since 1.44. One of the self::AUTO_* constants: + * - AUTO_ALL: always load autoblocks + * - AUTO_SPECIFIED: load only autoblocks specified in the input by ID + * - AUTO_NONE: do not load autoblocks * @return DatabaseBlock|null (null if no relevant block could be found). The target and type * of the returned block will refer to the actual block which was found, which might * not be the same as the target you gave if you used $vagueTarget! @@ -457,9 +474,10 @@ class DatabaseBlockStore { public function newFromTarget( $specificTarget, $vagueTarget = null, - $fromPrimary = false + $fromPrimary = false, + $auto = self::AUTO_ALL ) { - $blocks = $this->newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary ); + $blocks = $this->newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary, $auto ); return $this->chooseMostSpecificBlock( $blocks ); } @@ -470,12 +488,17 @@ class DatabaseBlockStore { * @param BlockTarget|string|UserIdentity|int|null $specificTarget * @param BlockTarget|string|UserIdentity|int|null $vagueTarget * @param bool $fromPrimary + * @param string $auto Since 1.44. One of the self::AUTO_* constants: + * - AUTO_ALL: always load autoblocks + * - AUTO_SPECIFIED: load only autoblocks specified in the input by ID + * - AUTO_NONE: do not load autoblocks * @return DatabaseBlock[] Any relevant blocks */ public function newListFromTarget( $specificTarget, $vagueTarget = null, - $fromPrimary = false + $fromPrimary = false, + $auto = self::AUTO_ALL ) { if ( !( $specificTarget instanceof BlockTarget ) ) { $specificTarget = $this->blockTargetFactory->newFromLegacyUnion( $specificTarget ); @@ -484,13 +507,16 @@ class DatabaseBlockStore { $vagueTarget = $this->blockTargetFactory->newFromLegacyUnion( $vagueTarget ); } if ( $specificTarget instanceof AutoBlockTarget ) { + if ( $auto === self::AUTO_NONE ) { + return []; + } $block = $this->newFromID( $specificTarget->getId() ); return $block ? [ $block ] : []; } elseif ( $specificTarget === null && $vagueTarget === null ) { // We're not going to find anything useful here return []; } else { - return $this->newLoad( $specificTarget, $fromPrimary, $vagueTarget ); + return $this->newLoad( $specificTarget, $fromPrimary, $vagueTarget, $auto ); } } @@ -962,7 +988,8 @@ class DatabaseBlockStore { DatabaseBlock $block, IDatabase $dbw ) { - $targetConds = $this->getTargetConds( $block ); + // @phan-suppress-next-line PhanTypeMismatchArgumentNullable + $targetConds = $this->getTargetConds( $block->getTarget() ); return $dbw->newSelectQueryBuilder() ->select( [ 'bl_id', 'bl_target' ] ) ->from( 'block' ) @@ -975,17 +1002,18 @@ class DatabaseBlockStore { /** * Get conditions matching an existing block's block_target row * - * @param DatabaseBlock $block + * @param BlockTarget $target * @return array */ - private function getTargetConds( DatabaseBlock $block ) { - $target = $block->getTarget(); + private function getTargetConds( BlockTarget $target ) { if ( $target instanceof UserBlockTarget ) { return [ 'bt_user' => $target->getUserIdentity()->getId( $this->wikiId ) ]; - } else { + } elseif ( $target instanceof AnonIpBlockTarget || $target instanceof RangeBlockTarget ) { return [ 'bt_address' => $target->toString() ]; + } else { + throw new \InvalidArgumentException( 'Invalid target type' ); } } @@ -1207,7 +1235,8 @@ class DatabaseBlockStore { $newTarget = $this->blockTargetFactory->newFromLegacyUnion( $newTarget ); } - $oldTargetConds = $this->getTargetConds( $block ); + // @phan-suppress-next-line PhanTypeMismatchArgumentNullable + $oldTargetConds = $this->getTargetConds( $block->getTarget() ); $block->setTarget( $newTarget ); $dbw->startAtomic( __METHOD__ ); @@ -1528,7 +1557,7 @@ class DatabaseBlockStore { : false; } - private function getAutoblockReason( DatabaseBlock $parentBlock ) { + private function getAutoblockReason( DatabaseBlock $parentBlock ): string { return wfMessage( 'autoblocker', $parentBlock->getTargetName(), diff --git a/includes/block/UnblockUser.php b/includes/block/UnblockUser.php index fc56f9dc8e1e..f4f071656f85 100644 --- a/includes/block/UnblockUser.php +++ b/includes/block/UnblockUser.php @@ -228,7 +228,8 @@ class UnblockUser { return $this->blockToRemove; } - $activeBlocks = $this->blockStore->newListFromTarget( $this->target ); + $activeBlocks = $this->blockStore->newListFromTarget( + $this->target, null, false, DatabaseBlockStore::AUTO_SPECIFIED ); if ( !$activeBlocks ) { $status->fatal( 'ipb_cant_unblock', $this->target ); return null; diff --git a/includes/cache/GenderCache.php b/includes/cache/GenderCache.php index ec5a31d74dd1..c2328423b658 100644 --- a/includes/cache/GenderCache.php +++ b/includes/cache/GenderCache.php @@ -158,7 +158,7 @@ class GenderCache { } } - private static function normalizeUsername( $username ) { + private static function normalizeUsername( string $username ): string { // Strip off subpages $indexSlash = strpos( $username, '/' ); if ( $indexSlash !== false ) { diff --git a/includes/collation/CustomUppercaseCollation.php b/includes/collation/CustomUppercaseCollation.php index d6bacd0d43bb..22c04411cac5 100644 --- a/includes/collation/CustomUppercaseCollation.php +++ b/includes/collation/CustomUppercaseCollation.php @@ -92,7 +92,7 @@ class CustomUppercaseCollation extends NumericUppercaseCollation { parent::__construct( $languageFactory, $digitTransformLang ); } - private function convertToPua( $string ) { + private function convertToPua( string $string ): string { return str_replace( $this->alphabet, $this->puaSubset, $string ); } diff --git a/includes/collation/IcuCollation.php b/includes/collation/IcuCollation.php index 06e80812be87..b1418bd39249 100644 --- a/includes/collation/IcuCollation.php +++ b/includes/collation/IcuCollation.php @@ -326,7 +326,7 @@ class IcuCollation extends Collation { return $sortLetter; } - private function getPrimarySortKey( $string ) { + private function getPrimarySortKey( string $string ): string { return $this->primaryCollator->getSortKey( $string ); } diff --git a/includes/composer/PhpUnitSplitter/PhpUnitXmlManager.php b/includes/composer/PhpUnitSplitter/PhpUnitXmlManager.php index a85c86349a9d..bac35f6649f9 100644 --- a/includes/composer/PhpUnitSplitter/PhpUnitXmlManager.php +++ b/includes/composer/PhpUnitSplitter/PhpUnitXmlManager.php @@ -93,7 +93,7 @@ class PhpUnitXmlManager { return ( new PhpUnitTestFileScanner( $this->rootDir ) )->scanForFiles(); } - private static function extractNamespaceFromFile( $filename ): array { + private static function extractNamespaceFromFile( string $filename ): array { $contents = file_get_contents( $filename ); $matches = []; if ( preg_match( '/^namespace\s+([^\s;]+)/m', $contents, $matches ) ) { diff --git a/includes/deferred/LinksUpdate/CategoryLinksTable.php b/includes/deferred/LinksUpdate/CategoryLinksTable.php index 3a06391e824c..151566c9f408 100644 --- a/includes/deferred/LinksUpdate/CategoryLinksTable.php +++ b/includes/deferred/LinksUpdate/CategoryLinksTable.php @@ -243,7 +243,7 @@ class CategoryLinksTable extends TitleLinksTable { return $this->existingLinks; } - private function getSavedTimestamps() { + private function getSavedTimestamps(): array { if ( $this->savedTimestamps === null ) { $this->fetchExistingLinks(); } diff --git a/includes/deferred/LinksUpdate/PagePropsTable.php b/includes/deferred/LinksUpdate/PagePropsTable.php index bcd0a93c957f..ea42853e1b6b 100644 --- a/includes/deferred/LinksUpdate/PagePropsTable.php +++ b/includes/deferred/LinksUpdate/PagePropsTable.php @@ -104,7 +104,7 @@ class PagePropsTable extends LinksTable { && $this->encodeValue( $this->newProps[$name] ) === $this->encodeValue( $value ); } - private function encodeValue( $value ) { + private function encodeValue( $value ): string { if ( is_bool( $value ) ) { return (string)(int)$value; } elseif ( $value === null ) { diff --git a/includes/editpage/Constraint/UserRateLimitConstraint.php b/includes/editpage/Constraint/UserRateLimitConstraint.php index d21409a95a84..8f35dd5879d0 100644 --- a/includes/editpage/Constraint/UserRateLimitConstraint.php +++ b/includes/editpage/Constraint/UserRateLimitConstraint.php @@ -52,7 +52,7 @@ class UserRateLimitConstraint implements IEditConstraint { $this->newContentModel = $newContentModel; } - private function limit( string $action, int $inc = 1 ) { + private function limit( string $action, int $inc = 1 ): bool { return $this->limiter->limit( $this->subject, $action, $inc ); } diff --git a/includes/editpage/PreloadedContentBuilder.php b/includes/editpage/PreloadedContentBuilder.php index 61c9a81464bb..ab0a124eb66e 100644 --- a/includes/editpage/PreloadedContentBuilder.php +++ b/includes/editpage/PreloadedContentBuilder.php @@ -220,7 +220,7 @@ class PreloadedContentBuilder { Content $content, PageReference $title, array $preloadParams = [] - ) { + ): Content { return $this->contentTransformer->preloadTransform( $content, $title, diff --git a/includes/import/WikiImporter.php b/includes/import/WikiImporter.php index 8868c4cc8ffc..5dcf0e599550 100644 --- a/includes/import/WikiImporter.php +++ b/includes/import/WikiImporter.php @@ -979,7 +979,7 @@ class WikiImporter { } } - private function handleContent() { + private function handleContent(): array { $this->debug( "Enter content handler" ); $contentInfo = []; diff --git a/includes/installer/CliInstaller.php b/includes/installer/CliInstaller.php index 8a17085d7029..ec3c20b572c2 100644 --- a/includes/installer/CliInstaller.php +++ b/includes/installer/CliInstaller.php @@ -175,7 +175,7 @@ class CliInstaller extends Installer { $this->setVar( '_WithDevelopmentSettings', isset( $options['with-developmentsettings'] ) ); } - private function validateExtensions( $type, $directory, $nameLists ) { + private function validateExtensions( string $type, string $directory, $nameLists ): Status { $extensions = []; $status = new Status; foreach ( (array)$nameLists as $nameList ) { diff --git a/includes/installer/InstallerOverrides.php b/includes/installer/InstallerOverrides.php index b476479b7971..eadbe7d8d004 100644 --- a/includes/installer/InstallerOverrides.php +++ b/includes/installer/InstallerOverrides.php @@ -29,7 +29,7 @@ use MediaWiki\Request\WebRequest; * @since 1.20 */ class InstallerOverrides { - private static function getOverrides() { + private static function getOverrides(): array { static $overrides; if ( !$overrides ) { diff --git a/includes/installer/PostgresSettingsForm.php b/includes/installer/PostgresSettingsForm.php index f74136498b2d..f16ab4695f7a 100644 --- a/includes/installer/PostgresSettingsForm.php +++ b/includes/installer/PostgresSettingsForm.php @@ -94,7 +94,7 @@ class PostgresSettingsForm extends DatabaseSettingsForm { return $this->dbInstaller; } - private function getPostgresUtils() { + private function getPostgresUtils(): PostgresUtils { return new PostgresUtils( $this->dbInstaller ); } diff --git a/includes/installer/SqliteConnectForm.php b/includes/installer/SqliteConnectForm.php index 4080dd3808b6..060178a8b20e 100644 --- a/includes/installer/SqliteConnectForm.php +++ b/includes/installer/SqliteConnectForm.php @@ -55,7 +55,7 @@ class SqliteConnectForm extends DatabaseConnectForm { return realpath( $path ) ?: $path; } - private function getSqliteUtils() { + private function getSqliteUtils(): SqliteUtils { return new SqliteUtils; } diff --git a/includes/installer/SqliteDatabaseCreator.php b/includes/installer/SqliteDatabaseCreator.php index 298dad63c75f..53c2b0281d21 100644 --- a/includes/installer/SqliteDatabaseCreator.php +++ b/includes/installer/SqliteDatabaseCreator.php @@ -34,7 +34,7 @@ class SqliteDatabaseCreator extends DatabaseCreator { return $this->createLocally( $database ); } - private function makeStubDBFile( $db ) { + private function makeStubDBFile( string $db ): Status { $file = DatabaseSqlite::generateFileName( $this->dataDir, $db ); if ( file_exists( $file ) ) { diff --git a/includes/installer/Task/CreateExternalDomainsTask.php b/includes/installer/Task/CreateExternalDomainsTask.php index dba40ffdb268..db14fd4bfd2f 100644 --- a/includes/installer/Task/CreateExternalDomainsTask.php +++ b/includes/installer/Task/CreateExternalDomainsTask.php @@ -40,7 +40,7 @@ class CreateExternalDomainsTask extends Task { $this->esFactory = $services->getExternalStoreFactory(); } - private function createVirtualDomains() { + private function createVirtualDomains(): Status { $status = Status::newGood(); foreach ( $this->getVirtualDomains() as $virtualDomain ) { if ( !$this->shouldDoShared() @@ -62,7 +62,7 @@ class CreateExternalDomainsTask extends Task { return $status; } - private function createExternalStoreDomains() { + private function createExternalStoreDomains(): Status { $status = Status::newGood(); $localDomainId = $this->lbFactory->getLocalDomainID(); foreach ( $this->esFactory->getWriteBaseUrls() as $url ) { diff --git a/includes/installer/Task/CreateSysopTask.php b/includes/installer/Task/CreateSysopTask.php index cf626819487f..a8ff8d386caf 100644 --- a/includes/installer/Task/CreateSysopTask.php +++ b/includes/installer/Task/CreateSysopTask.php @@ -38,7 +38,7 @@ class CreateSysopTask extends Task { return $status; } - private function createUser( $name ) { + private function createUser( string $name ): Status { $user = $this->userFactory->newFromName( $name ); if ( !$user ) { diff --git a/includes/installer/Task/PostgresCreateUserTask.php b/includes/installer/Task/PostgresCreateUserTask.php index b2487ad68d6d..8a05bbb770ec 100644 --- a/includes/installer/Task/PostgresCreateUserTask.php +++ b/includes/installer/Task/PostgresCreateUserTask.php @@ -60,7 +60,7 @@ class PostgresCreateUserTask extends Task { return Status::newGood(); } - private function getPostgresUtils() { + private function getPostgresUtils(): PostgresUtils { return new PostgresUtils( $this->getContext() ); } } diff --git a/includes/installer/Task/RestoredServicesProvider.php b/includes/installer/Task/RestoredServicesProvider.php index 7cb64c69f8c2..50a82e9b6d7f 100644 --- a/includes/installer/Task/RestoredServicesProvider.php +++ b/includes/installer/Task/RestoredServicesProvider.php @@ -8,6 +8,7 @@ use MediaWiki\Status\Status; use MediaWiki\Utils\UrlUtils; use MWLBFactory; use Wikimedia\Rdbms\LBFactorySingle; +use Wikimedia\Services\ServiceContainer; /** * Provide a service container with storage enabled. @@ -57,7 +58,7 @@ class RestoredServicesProvider extends Task { return Status::newGood(); } - private function resetMediaWikiServices( $serviceOverrides ) { + private function resetMediaWikiServices( array $serviceOverrides ): ServiceContainer { // Reset all services and inject config overrides. MediaWikiServices::resetGlobalInstance( null, 'reload' ); diff --git a/includes/installer/Task/SqliteCreateDatabaseTask.php b/includes/installer/Task/SqliteCreateDatabaseTask.php index a83deb56a299..204a572ed06a 100644 --- a/includes/installer/Task/SqliteCreateDatabaseTask.php +++ b/includes/installer/Task/SqliteCreateDatabaseTask.php @@ -113,7 +113,7 @@ EOT; return $mainConnStatus; } - private function getSqliteUtils() { + private function getSqliteUtils(): SqliteUtils { return new SqliteUtils; } diff --git a/includes/installer/Task/TaskFactory.php b/includes/installer/Task/TaskFactory.php index 5d7faf18267c..337ce788f3fa 100644 --- a/includes/installer/Task/TaskFactory.php +++ b/includes/installer/Task/TaskFactory.php @@ -146,7 +146,7 @@ class TaskFactory { return $task; } - private function getCoreSchemaBasePath() { + private function getCoreSchemaBasePath(): string { return MW_INSTALL_PATH . '/sql'; } } diff --git a/includes/installer/i18n/lb.json b/includes/installer/i18n/lb.json index 27d61e0c3142..f419747d0e7f 100644 --- a/includes/installer/i18n/lb.json +++ b/includes/installer/i18n/lb.json @@ -154,9 +154,9 @@ "config-advanced-settings": "Erweidert Astellungen", "config-extensions": "Erweiderungen", "config-skins": "Ausgesinn", - "config-skins-help": "D'Ausgesinn déi hei driwwer stinn goufen am Repertoire <code>./skins</code> fonnt. Dir musst mindestens eent aktivéieren an de Standard eraussichen.", + "config-skins-help": "D'Benotzeruewerflächen, déi hei driwwer stinn, goufen an Ärem Repertoire <code>./skins</code> fonnt. Dir musst mindestens eng aktivéieren an de Standard eraussichen.", "config-skins-use-as-default": "Dëst Ausgesinn als Standard benotzen", - "config-skins-missing": "Et goufe keen Ausgesinn (Skin) fonnt; MediaWiki benotzt e Fallback-Ausgesinnbis Dir anerer installéiert.", + "config-skins-missing": "Et goufe keng Benotzeruewerfläche fonnt; MediaWiki benotzt eng Fallback-Benotzeruewerfläch, bis Dir anerer installéiert.", "config-skins-must-enable-some": "Dir musst mindestens een Ausgesinn fir z'aktivéieren eraussichen.", "config-skins-must-enable-default": "Dat als Standard erausgesichten Ausgesinn muss aktivéiert sinn.", "config-install-step-done": "fäerdeg", diff --git a/includes/installer/i18n/pa.json b/includes/installer/i18n/pa.json index 13cc1085bb83..2bf3b9fdfc29 100644 --- a/includes/installer/i18n/pa.json +++ b/includes/installer/i18n/pa.json @@ -22,7 +22,7 @@ "config-page-welcome": "ਮੀਡੀਆਵਿਕੀ 'ਤੇ ਜੀ ਆਇਆਂ ਨੂੰ!", "config-page-dbconnect": "ਤੱਥ-ਅਧਾਰ ਨਾਲ਼ ਜੁੜੋ", "config-page-upgrade": "ਮੌਜੂਦਾ ਜੜਾਈ ਦਾ ਮਿਆਰ ਚੁੱਕੋ", - "config-page-dbsettings": "ਤੱਥ-ਅਧਾਰ ਦੀਆਂ ਸੈਟਿੰਗਾਂ", + "config-page-dbsettings": "ਤੱਥ-ਅਧਾਰ ਤਰਜੀਹਾਂ", "config-page-name": "ਨਾਂ", "config-page-options": "ਚੋਣਾਂ", "config-page-install": "ਜੜੋ", @@ -40,7 +40,7 @@ "config-apcu": "[https://www.php.net/apcu APCu] ਥਾਪਿਆ ਗਿਆ ਹੈ", "config-db-type": "ਡਾਟਾਬੇਸ ਦੀ ਕਿਸਮ:", "config-db-wiki-settings": "ਇਸ ਵਿਕੀ ਦੀ ਪਛਾਣ ਕਰਾਉ", - "config-db-name": "ਤੱਥ-ਅਧਾਰ ਦਾ ਨਾਂ:", + "config-db-name": "ਤੱਥ-ਅਧਾਰ ਦਾ ਨਾਂ (ਕੋਈ ਜੋੜਨੀ ਨਹੀਂ):", "config-db-wiki-account": "ਆਮ ਕਾਰਵਾਈ ਲਈ ਵਰਤੋਂਕਾਰ ਖਾਤਾ", "config-db-web-create": "ਜੇ ਖਾਤਾ ਪਹਿਲਾਂ ਤੋਂ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਤਾਂ ਬਣਾਓ", "config-server": "URL ਮੇਜ਼ਬਾਨ ਦਾ ਨਾਂ:", diff --git a/includes/installer/i18n/sl.json b/includes/installer/i18n/sl.json index 226f7d2ee94b..54857de512a7 100644 --- a/includes/installer/i18n/sl.json +++ b/includes/installer/i18n/sl.json @@ -169,7 +169,7 @@ "config-admin-password-blank": "Vnesite geslo za administratorski račun.", "config-admin-password-mismatch": "Vneseni gesli se ne ujemata.", "config-admin-email": "E-poštni naslov:", - "config-admin-email-help": "Tu vnesite e-poštni naslov, da boste lahko prejemali e-pošto od drugih uporabnikov v vikiju, ponastavili svoje geslo in bili obveščeni o spremembah strani na vašem spisku nadzorov. To polje lahko pustite prazno.", + "config-admin-email-help": "Tu vnesite e-poštni naslov, da boste lahko prejemali e-pošto od drugih uporabnikov v vikiju, ponastavili svoje geslo in bili obveščeni o spremembah strani na vašem nadzornem seznamu. To polje lahko pustite prazno.", "config-admin-error-user": "Med ustvarjanjem administratorja »<nowiki>$1</nowiki>« je prišlo do notranje napake.", "config-admin-error-password": "Med nastavljanjem gesla za administratorja »<nowiki>$1</nowiki>« je prišlo do notranje napake: <pre>$2</pre>", "config-admin-error-password-invalid": "Neveljavno administratorsko geslo vikija: $1", @@ -204,7 +204,7 @@ "config-email-user-help": "Dovolite vsem uporabnikom, da si pošiljajo e-pošto, če so to omogočili v svojih nastavitvah.", "config-email-usertalk": "Omogoči obveščanje o uporabniški pogovorni strani", "config-email-usertalk-help": "Omogoči uporabnikom prejemanje obvestil o spremembah uporabniške pogovorne strani, če so to omogočili v svojih nastavitvah.", - "config-email-watchlist": "Omogoči obveščanje o spisku nadzora", + "config-email-watchlist": "Omogoči obveščanje o nadzornem seznamu", "config-email-watchlist-help": "Omogočite uporabnikom, da prejemajo obvestila o straneh, ki jih opazujejo, če so to omogočili v svojih nastavitvah.", "config-email-auth": "Omogoči avtentikacijo po e-pošti", "config-email-auth-help": "Če je ta možnost omogočena, morajo uporabniki potrditi svoj e-poštni naslov s povezavo, ki jim je poslana, vsakič, ko ga nastavijo ali spremenijo.\nSamo preverjeni e-poštni naslovi lahko prejemajo e-poštna sporočila drugih uporabnikov ali spreminjajo e-poštna obvestila.\nNastavitev te možnosti je za javne vikije <strong>priporočljiva</strong> zaradi možne zlorabe e-poštnih možnosti.", diff --git a/includes/installer/i18n/zh-hans.json b/includes/installer/i18n/zh-hans.json index f42fdd5e03a9..0cb57437d688 100644 --- a/includes/installer/i18n/zh-hans.json +++ b/includes/installer/i18n/zh-hans.json @@ -22,6 +22,7 @@ "Liuxinyu970226", "Makecat", "Mywood", + "NekoCharm", "PhiLiP", "Qiyue2001", "Shimamura Sakura", @@ -186,6 +187,8 @@ "config-mysql-engine": "存储引擎:", "config-mysql-innodb": "InnoDB(推荐)", "config-mysql-engine-help": "<strong>InnoDB</strong>通常是最佳选项,因为它对并发操作有着良好的支持。\n\n<strong>MyISAM</strong>在单用户或只读环境下可能会有更快的性能表现。但MyISAM数据库出错的概率一般要大于InnoDB数据库。", + "config-server": "URL主机名:", + "config-server-help": "用于访问您的wiki的URL的协议和主机名部分。自动检测的默认值通常是正确的。", "config-site-name": "wiki名称:", "config-site-name-help": "填入的内容会出现在浏览器的标题栏以及其他多处位置中。", "config-site-name-blank": "输入网站的名称。", diff --git a/includes/jobqueue/jobs/CategoryMembershipChangeJob.php b/includes/jobqueue/jobs/CategoryMembershipChangeJob.php index 2e25cd883f83..0fe85621cf64 100644 --- a/includes/jobqueue/jobs/CategoryMembershipChangeJob.php +++ b/includes/jobqueue/jobs/CategoryMembershipChangeJob.php @@ -240,7 +240,7 @@ class CategoryMembershipChangeJob extends Job { private function getExplicitCategoriesChanges( WikiPage $page, RevisionRecord $newRev, ?RevisionRecord $oldRev = null - ) { + ): array { // Inject the same timestamp for both revision parses to avoid seeing category changes // due to time-based parser functions. Inject the same page title for the parses too. // Note that REPEATABLE-READ makes template/file pages appear unchanged between parses. diff --git a/includes/jobqueue/jobs/RefreshLinksJob.php b/includes/jobqueue/jobs/RefreshLinksJob.php index b6647c55a290..ed54a1bbc034 100644 --- a/includes/jobqueue/jobs/RefreshLinksJob.php +++ b/includes/jobqueue/jobs/RefreshLinksJob.php @@ -574,7 +574,7 @@ class RefreshLinksJob extends Job { private function canUseParserOutputFromCache( ParserOutput $cachedOutput, RevisionRecord $currentRevision - ) { + ): bool { // As long as the cache rev ID matches the current rev ID and it reflects // the job's triggering change, then it is usable. return $cachedOutput->getCacheRevisionId() == $currentRevision->getId() diff --git a/includes/language/Language.php b/includes/language/Language.php index 1bc40326681b..be64c56e24de 100644 --- a/includes/language/Language.php +++ b/includes/language/Language.php @@ -1956,6 +1956,367 @@ class Language implements Bcp47Code { } /** + * Convert a format string as consumed by sprintfDate() to an options array + * as used by the client-side mediawiki.DateFormatter. + * + * @param string $format + * @return array An associative array with the following keys: + * - pattern: A string with parameters surrounded by braces, like {year}, + * where the parameter names are those returned by Intl.DateTimeFormat.formatToParts(), + * with a few custom extensions, specifically: + * - mwMonth: The month name message text + * - mwMonthGen: The genitive month name message text + * - mwMonthAbbrev: The abbreviated month name message text + * - locale: A fixed locale to override the default one + * - options: Associative options to pass to the Intl.DateTimeFormat constructor + */ + private function convertDateFormatToJs( $format ) { + // CLDR calendar IDs by format code + $calendars = [ + 'j' => 'hebrew', + 'i' => 'persian', // Iranian + 'm' => 'islamic', // Hijri + 'k' => 'buddhist', // Thai + 'o' => 'roc', // Minguo + 't' => 'japanese' // Tenno + ]; + + $s = ''; + $mwOpts = []; + $intlOpts = [ + 'numberingSystem' => $this->localisationCache + ->getItem( $this->mCode, 'numberingSystem' ) ?? 'latn' + ]; + $unsupported = []; + $formatLength = strlen( $format ); + + for ( $p = 0; $p < $formatLength; $p++ ) { + $code = $format[$p]; + if ( $code == 'x' && $p < $formatLength - 1 ) { + $code .= $format[++$p]; + } + + if ( ( $code === 'xi' + || $code === 'xj' + || $code === 'xk' + || $code === 'xm' + || $code === 'xo' + || $code === 'xt' ) + && $p < $formatLength - 1 + ) { + $code .= $format[++$p]; + } + + switch ( $code ) { + case 'xx': + $s .= 'x'; + break; + + case 'xn': + case 'xN': + // Raw number -- usually safe enough in ISO 8601 styles + // although we don't support switching + $mwOpts['locale'] = 'en'; + unset( $intlOpts['numberingSystem'] ); + break; + + case 'xr': + // Roman numerals + // Multiple numbering systems are usually used in the one format, + // which is unsupported. Also, browsers do not implement it. + $unsupported[] = $code; + break; + + case 'xh': + // Hebrew numerals. Browsers do not implement it as of 2025, + // and usually multiple numbering systems are desired in a + // single format. + $unsupported[] = $code; + $intlOpts['numberingSystem'] = 'hebr'; + break; + + case 'xg': + // Genitive month name + $intlOpts['month'] = 'long'; + $s .= '{mwMonthGen}'; + break; + + case 'xjx': + // Genitive month name in Hebrew calendar + $intlOpts['calendar'] = 'hebrew'; + $intlOpts['month'] = 'long'; + $s .= '{mwMonthGen}'; + break; + + case 'd': + $intlOpts['day'] = '2-digit'; + $s .= '{day}'; + break; + + case 'D': + $intlOpts['weekday'] = 'short'; + $s .= '{weekday}'; + break; + + case 'j': + $intlOpts['day'] = 'numeric'; + $s .= '{day}'; + break; + + case 'xij': + case 'xmj': + case 'xjj': + // Day number in special calendar + $intlOpts['day'] = 'numeric'; + $intlOpts['calendar'] = $calendars[$code[1]]; + $s .= '{day}'; + break; + + case 'l': + $intlOpts['weekday'] = 'long'; + $s .= '{weekday}'; + break; + + case 'F': + $intlOpts['month'] = 'long'; + $s .= '{mwMonth}'; + break; + + case 'xiF': + case 'xmF': + case 'xjF': + // Full month name in special calendar + $intlOpts['month'] = 'long'; + $intlOpts['calendar'] = $calendars[$code[1]]; + $s .= '{month}'; + break; + + case 'm': + $intlOpts['month'] = '2-digit'; + $s .= '{month}'; + break; + + case 'M': + $intlOpts['month'] = 'short'; + $s .= '{mwMonthAbbrev}'; + break; + + case 'n': + $intlOpts['month'] = 'numeric'; + $s .= '{month}'; + break; + + case 'xin': + case 'xmn': + case 'xjn': + // Numeric month in special calendar + $intlOpts['month'] = 'numeric'; + $intlOpts['calendar'] = $calendars[$code[1]]; + $s .= '{month}'; + break; + + case 'xjt': + // Number of days in the given Hebrew month -- not supported + $unsupported[] = $code; + break; + + case 'Y': + $intlOpts['year'] = 'numeric'; + $s .= '{year}'; + break; + + case 'xiY': + case 'xmY': + case 'xjY': + case 'xkY': + case 'xoY': + // Year number in special calendar + $intlOpts['year'] = 'numeric'; + $intlOpts['calendar'] = $calendars[$code[1]]; + $s .= '{year}'; + break; + + case 'xtY': + // Japanese year needs to be prefixed with the era name to + // be consistent with tsToJapaneseGengo() + $intlOpts['era'] = 'short'; + $intlOpts['year'] = 'numeric'; + $intlOpts['calendar'] = $calendars[$code[1]]; + $s .= '{era}{year}'; + break; + + case 'y': + $intlOpts['year'] = '2-digit'; + $s .= '{year}'; + break; + + case 'xiy': + // Iranian 2-digit year + $intlOpts['calendar'] = 'persian'; + $intlOpts['year'] = '2-digit'; + $s .= '{year}'; + break; + + case 'xit': + // Number of days in Iranian month -- not supported + $unsupported[] = $code; + break; + + case 'xiz': + // Day of the year -- not supported + $unsupported[] = $code; + break; + + case 'a': + case 'A': + // AM/PM + $intlOpts['hour12'] = true; + $s .= '{dayPeriod}'; + break; + + case 'g': + // Hour in 12-hour clock, without leading zero + $intlOpts['hour'] = 'numeric'; + $intlOpts['hour12'] = true; + $s .= '{hour}'; + break; + + case 'G': + // Hour in 24-hour clock, without leading zero + $intlOpts['hour'] = 'numeric'; + $s .= '{hour}'; + break; + + case 'h': + // Hour in 12-hour clock, with leading zero + $intlOpts['hour'] = '2-digit'; + $intlOpts['hour12'] = true; + $s .= '{hour}'; + break; + + case 'H': + // Hour in 24-hour clock, without leading zero + $intlOpts['hour'] = '2-digit'; + $intlOpts['hour12'] = false; + $s .= '{hour}'; + break; + + case 'i': + $intlOpts['minute'] = '2-digit'; + $s .= '{minute}'; + break; + + case 's': + $intlOpts['second'] = '2-digit'; + $s .= '{second}'; + break; + + case 'c': + // ISO 8601 + $mwOpts['locale'] = 'en'; + $intlOpts += [ + 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', + 'hour' => '2-digit', 'minute' => '2-digit', 'second' => '2-digit', + 'hour12' => false, 'timeZoneName' => 'longOffset' + ]; + $s .= '{year}-{month}-{day}T{hour}:{minute}:{second}{timeZoneName}'; + break; + + case 'r': + // RFC 2822 + $mwOpts['locale'] = 'en'; + $intlOpts += [ + 'year' => 'numeric', 'month' => 'short', 'day' => 'numeric', + 'hour' => '2-digit', 'minute' => '2-digit', 'second' => '2-digit', + 'hour12' => false, 'weekday' => 'short', 'timeZoneName' => 'longOffset', + ]; + $s .= '{weekday}, {day} {month} {year} {hour}:{minute}:{second} {timeZoneName}'; + break; + + case 'e': + // Time zone identifier -- not supported, fall through to offset + case 'O': + // Offset without colon not supported + $unsupported[] = $code; + // Fall through to with colon + case 'P': + $intlOpts['timeZoneName'] = 'longOffset'; + $s .= '{timeZoneName}'; + break; + + case 'T': + // Time zone abbreviation + $intlOpts['timeZoneName'] = 'short'; + $s .= '{timeZoneName}'; + break; + + case 'w': + case 'N': + // Numeric day of week -- not supported + $unsupported[] = $code; + $intlOpts['weekday'] = 'short'; + $s .= '{weekday}'; + break; + + case 'z': + // Day of year + case 'W': + // Week number + case 't': + // Number of days in month + case 'L': + // Leap year flag + case 'o': + // ISO week numbering year + case 'U': + // Seconds since UNIX epoch + case 'I': + // DST flag + case 'Z': + // Timezone offset in seconds + $unsupported[] = $code; + break; + + case '\\': + # Backslash escaping + if ( $p < $formatLength - 1 ) { + $s .= $format[++$p]; + } else { + $s .= '\\'; + } + break; + + case '"': + # Quoted literal + if ( $p < $formatLength - 1 ) { + $endQuote = strpos( $format, '"', $p + 1 ); + if ( $endQuote === false ) { + # No terminating quote, assume literal " + $s .= '"'; + } else { + $s .= substr( $format, $p + 1, $endQuote - $p - 1 ); + $p = $endQuote; + } + } else { + # Quote at the end of the string, assume literal " + $s .= '"'; + } + break; + + default: + $s .= $format[$p]; + } + } + $mwOpts['options'] = $intlOpts; + $mwOpts['pattern'] = $s; + if ( $unsupported ) { + $mwOpts['error'] = 'Unsupported format code(s): ' . + implode( ', ', $unsupported ); + } + return $mwOpts; + } + + /** * Convenience function to convert a PHP DateTime object to a 14-character MediaWiki timestamp, * falling back to the specified timestamp if the DateTime object holds a too large date (T32148, T277809). * This is a private utility method as it is only really useful for {@link userAdjust}. @@ -4581,6 +4942,54 @@ class Language implements Bcp47Code { 'bcp47Map' => LanguageCode::getNonstandardLanguageCodeMapping(), ]; } + + /** + * @internal For DateFormatterConfig + * @return array + */ + public function getJsDateFormats() { + $jsLcFormats = $this->localisationCache->getItem( $this->mCode, 'jsDateFormats' ); + $phpLcFormats = $this->localisationCache->getItem( $this->mCode, 'dateFormats' ); + + $styles = $this->getDatePreferences() ?: [ $this->getDefaultDateFormat() ]; + $results = []; + foreach ( $styles as $style ) { + if ( $style === 'default' ) { + // Default is not a real style for our purposes + continue; + } + foreach ( [ 'time', 'date', 'both', 'pretty' ] as $type ) { + $key = "$style $type"; + $resolvedType = $type; + $resolvedStyle = $style; + $resolvedKey = $key; + + // If the PHP localisation lacks the "pretty" type, fall back to "date" + if ( !isset( $phpLcFormats[$key] ) && $type === 'pretty' ) { + $resolvedType = 'date'; + $resolvedKey = "$resolvedStyle $resolvedType"; + } + + // If $jsDateFormats has an alias, follow it. + // This is used to gracefully remove formats that don't work in the browser. + $alias = $jsLcFormats[$resolvedKey]['alias'] ?? ''; + if ( preg_match( '/^(.*) ([^ ]*)$/', $alias, $m ) ) { + $resolvedType = $m[2]; + $resolvedStyle = $m[1]; + $resolvedKey = "$resolvedStyle $resolvedType"; + } + + $jsFormat = $this->convertDateFormatToJs( + $this->getDateFormatString( $resolvedType, $resolvedStyle ) ); + if ( isset( $jsLcFormats[$resolvedKey] ) ) { + $results[$key] = array_merge_recursive( $jsFormat, $jsLcFormats[$resolvedKey] ); + } else { + $results[$key] = $jsFormat; + } + } + } + return $results; + } } /** @deprecated class alias since 1.43 */ diff --git a/includes/language/LocalisationCache.php b/includes/language/LocalisationCache.php index 542897394155..8522e1131787 100644 --- a/includes/language/LocalisationCache.php +++ b/includes/language/LocalisationCache.php @@ -151,13 +151,13 @@ class LocalisationCache { 'fallback', 'namespaceNames', 'bookstoreList', 'magicWords', 'messages', 'rtl', 'digitTransformTable', 'separatorTransformTable', - 'minimumGroupingDigits', 'fallback8bitEncoding', + 'minimumGroupingDigits', 'numberingSystem', 'fallback8bitEncoding', 'linkPrefixExtension', 'linkTrail', 'linkPrefixCharset', - 'namespaceAliases', 'dateFormats', 'datePreferences', + 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'datePreferences', 'datePreferenceMigrationMap', 'defaultDateFormat', 'specialPageAliases', 'imageFiles', 'preloadedMessages', 'namespaceGenderAliases', 'digitGroupingPattern', 'pluralRules', - 'pluralRuleTypes', 'compiledPluralRules', 'formalityIndex', + 'pluralRuleTypes', 'compiledPluralRules', 'formalityIndex' ]; /** @@ -169,7 +169,8 @@ class LocalisationCache { */ private const CORE_ONLY_KEYS = [ 'fallback', 'rtl', 'digitTransformTable', 'separatorTransformTable', - 'minimumGroupingDigits', 'fallback8bitEncoding', 'linkPrefixExtension', + 'minimumGroupingDigits', 'numberingSystem', + 'fallback8bitEncoding', 'linkPrefixExtension', 'linkTrail', 'linkPrefixCharset', 'datePreferences', 'datePreferenceMigrationMap', 'defaultDateFormat', 'digitGroupingPattern', 'formalityIndex', @@ -185,7 +186,7 @@ class LocalisationCache { */ private const ALL_EXCEPT_CORE_ONLY_KEYS = [ 'namespaceNames', 'bookstoreList', 'magicWords', 'messages', - 'namespaceAliases', 'dateFormats', 'specialPageAliases', + 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'specialPageAliases', 'imageFiles', 'preloadedMessages', 'namespaceGenderAliases', 'pluralRules', 'pluralRuleTypes', 'compiledPluralRules', ]; @@ -198,7 +199,7 @@ class LocalisationCache { * by a fallback sequence. */ private const MERGEABLE_MAP_KEYS = [ 'messages', 'namespaceNames', - 'namespaceAliases', 'dateFormats', 'imageFiles', 'preloadedMessages' + 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'imageFiles', 'preloadedMessages' ]; /** diff --git a/includes/language/converters/CrhConverter.php b/includes/language/converters/CrhConverter.php index f3c10215ca17..7a8ae1edd95f 100644 --- a/includes/language/converters/CrhConverter.php +++ b/includes/language/converters/CrhConverter.php @@ -255,7 +255,7 @@ class CrhConverter extends LanguageConverterSpecific { } } - private function regsConverter( $text, $toVariant ) { + private function regsConverter( string $text, string $toVariant ): string { if ( $text == '' ) { return $text; } diff --git a/includes/language/converters/MniConverter.php b/includes/language/converters/MniConverter.php index 20a421ae9c1a..495d0b716c3a 100644 --- a/includes/language/converters/MniConverter.php +++ b/includes/language/converters/MniConverter.php @@ -140,12 +140,12 @@ class MniConverter extends LanguageConverterSpecific { self::NUMERALS + self::MTEI_TO_BENG_MAP_EXTRA; - private function isBeginning( $position, $text ) { + private function isBeginning( int $position, string $text ): bool { $at_first = $position === 0; return $at_first || preg_match( self::NON_WORD_CHARACTER_PATTERN, $text[$position - 1] ); } - private function isEndOfWord( $char ) { + private function isEndOfWord( string $char ): bool { if ( $char === self::PERIOD ) { return true; } @@ -153,7 +153,7 @@ class MniConverter extends LanguageConverterSpecific { return count( $matches ) > 0; } - private function mteiToBengali( $text ) { + private function mteiToBengali( string $text ): iterable { $chars = mb_str_split( $text ); $l = count( $chars ); $i = 0; diff --git a/includes/language/dependency/MainConfigDependency.php b/includes/language/dependency/MainConfigDependency.php index 6735ad22aec1..d6126256fae8 100644 --- a/includes/language/dependency/MainConfigDependency.php +++ b/includes/language/dependency/MainConfigDependency.php @@ -17,6 +17,8 @@ * * @file */ + +use MediaWiki\Config\Config; use MediaWiki\MediaWikiServices; /** @@ -35,7 +37,7 @@ class MainConfigDependency extends CacheDependency { $this->value = $this->getConfig()->get( $this->name ); } - private function getConfig() { + private function getConfig(): Config { return MediaWikiServices::getInstance()->getMainConfig(); } diff --git a/includes/languages/data/CrhExceptions.php b/includes/languages/data/CrhExceptions.php index 8ae58937a801..1538a7a4959e 100644 --- a/includes/languages/data/CrhExceptions.php +++ b/includes/languages/data/CrhExceptions.php @@ -29,7 +29,7 @@ class CrhExceptions { /** @var string[] */ private array $uc2lc = []; - private function initLcUc( $lcChars, $ucChars, $reinit = false ) { + private function initLcUc( string $lcChars, string $ucChars, bool $reinit = false ) { # bail if we've already done this, unless we are re-initializing if ( !$reinit && $this->lc2uc && $this->uc2lc ) { return; @@ -49,19 +49,21 @@ class CrhExceptions { $this->uc2lc = array_combine( array_values( $myUc ), array_values( $myLc ) ); } - private function myLc( $string ) { + private function myLc( string $string ): string { return strtr( $string, $this->uc2lc ); } - private function myUc( $string ) { + private function myUc( string $string ): string { return strtr( $string, $this->lc2uc ); } - private function myUcWord( $string ) { + private function myUcWord( string $string ): string { return $this->myUc( mb_substr( $string, 0, 1 ) ) . $this->myLc( mb_substr( $string, 1 ) ); } - private function addMappings( $mapArray, &$A2B, &$B2A, $exactCase = false, $prePat = '', $postPat = '' ) { + private function addMappings( + array $mapArray, array &$A2B, array &$B2A, bool $exactCase = false, string $prePat = '', string $postPat = '' + ) { foreach ( $mapArray as $WordA => $WordB ) { if ( !$exactCase ) { $ucA = $this->myUc( $WordA ); diff --git a/includes/languages/data/Names.php b/includes/languages/data/Names.php index e3c97ac5ce67..c2780eda3895 100644 --- a/includes/languages/data/Names.php +++ b/includes/languages/data/Names.php @@ -122,6 +122,7 @@ class Names { 'btm' => 'Batak Mandailing', # Batak Mandailing 'bto' => 'Iriga Bicolano', # Rinconada Bikol 'bug' => 'Basa Ugi', # Buginese + 'bug-bugi' => 'ᨅᨔ ᨕᨘᨁᨗ', # Buginese (Buginese script), T389916 'bxr' => 'буряад', # Buryat (Russia) 'ca' => 'català', # Catalan 'cbk-zam' => 'Chavacano de Zamboanga', # Zamboanga Chavacano, T124657 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/ParamValidator/i18n/ko.json b/includes/libs/ParamValidator/i18n/ko.json index e2027f2fb302..af5bc7caf41d 100644 --- a/includes/libs/ParamValidator/i18n/ko.json +++ b/includes/libs/ParamValidator/i18n/ko.json @@ -1,17 +1,31 @@ { "@metadata": { "authors": [ + "Tensama0415", "Ykhwong" ] }, + "paramvalidator-badexpiry": "만료 변수 \"$1\"에 유효하지 않은 값 \"$2\"이 지정되었습니다.", + "paramvalidator-badfloat": "float 변수 \"$1\"에 유효하지 않은 값 \"$2\"이 지정되었습니다.", + "paramvalidator-badfloat-notfinite": "float 변수 \"$1\"에 대한 값 \"$2\"은 너무 크거나 숫자가 아닙니다.", + "paramvalidator-badinteger": "정수 변수 \"$1\"에 유효하지 않은 값 \"$2\"이 지정되었습니다.", + "paramvalidator-badtimestamp": "시간 기록 변수 \"$1\"에 유효하지 않은 값 \"$2\"이 지정되었습니다.", + "paramvalidator-badupload-formsize": "\"$1\"에 대한 업로드 파일은 클라이언트가 지정한 최대값을 초과합니다.", + "paramvalidator-badupload-inisize": "\"$1\"에 대한 업로드 파일은 서버의 최대값($3)을 초과합니다.", + "paramvalidator-badupload-nofile": "업로드 변수 \"$1\"에 대한 파일이 제공되지 않았습니다.", "paramvalidator-badupload-partial": "\"$1\"에 대한 파일이 일부만 업로드되었습니다.", "paramvalidator-badupload-phpext": "PHP 확장 기능으로 인해 \"$1\"에 대한 파일 업로드가 차단되었습니다.", + "paramvalidator-badvalue-enumnotmulti": "변수 \"$1\"에 대한 알 수 없는 값: $2.", + "paramvalidator-deprecated-value": "\"$1\" 변수에 대한 값 \"$2\"은 구식입니다.", "paramvalidator-emptystring": "빈 문자열", "paramvalidator-missingparam": "\"$1\" 변수는 설정이 필수입니다.", + "paramvalidator-multivalue-must-be-array": "다중값 변수 \"$1\"은 반드시 배열로 주어져야 합니다.", + "paramvalidator-needstring": "변수 \"$1\"은 문자열 값만을 허용하지만, $2를 받았습니다.", "paramvalidator-outofrange-max": "\"$1\" 변수의 \"$2\" 값은 $4 미만이어야 합니다.", "paramvalidator-outofrange-minmax": "\"$1\" 변수의 \"$2\" 값은 $3에서 $4 사이여야 합니다.", "paramvalidator-outofrange-min": "\"$1\" 변수의 \"$2\" 값은 $3 보다 작아서는 안 됩니다.", "paramvalidator-param-deprecated": "\"$1\" 변수는 구식입니다.", + "paramvalidator-toomanyvalues": "\"$1\" 변수에 너무 많은 갯수의 값이 주어졌습니다. 상한은 $2입니다.", "paramvalidator-unrecognizedvalues": "\"$1\" 변수에 대해 인식할 수 없는 {{PLURAL:$4|값}}: $3", "paramvalidator-help-default": "기본값: $1", "paramvalidator-help-default-empty": "기본값: (빈 값)", 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/FileBackend.php b/includes/libs/filebackend/FileBackend.php index 048040f2903a..63b0a90f4bff 100644 --- a/includes/libs/filebackend/FileBackend.php +++ b/includes/libs/filebackend/FileBackend.php @@ -1649,7 +1649,7 @@ abstract class FileBackend implements LoggerAwareInterface { final public static function extensionFromPath( $path, $case = 'lowercase' ) { // This will treat a string starting with . as not having an extension, but store paths have // to start with 'mwstore://', so "garbage in, garbage out". - $i = strrpos( $path, '.' ); + $i = strrpos( $path ?? '', '.' ); $ext = $i ? substr( $path, $i + 1 ) : ''; if ( $case === 'lowercase' ) { 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/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/linker/LinkRenderer.php b/includes/linker/LinkRenderer.php index d7aa984dbeb8..27e5411afe26 100644 --- a/includes/linker/LinkRenderer.php +++ b/includes/linker/LinkRenderer.php @@ -189,7 +189,7 @@ class LinkRenderer { } } - private function runBeginHook( $target, &$text, &$extraAttribs, &$query ) { + private function runBeginHook( $target, &$text, array &$extraAttribs, array &$query ) { $ret = null; if ( !$this->hookRunner->onHtmlPageLinkRendererBegin( // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args diff --git a/includes/logging/LogEventsList.php b/includes/logging/LogEventsList.php index 67787ab929ab..e2d9c57e435f 100644 --- a/includes/logging/LogEventsList.php +++ b/includes/logging/LogEventsList.php @@ -27,7 +27,6 @@ namespace MediaWiki\Logging; use InvalidArgumentException; use MapCacheLRU; -use MediaWiki\Block\Block; use MediaWiki\Block\DatabaseBlockStore; use MediaWiki\ChangeTags\ChangeTags; use MediaWiki\Context\ContextSource; @@ -841,28 +840,26 @@ class LogEventsList extends ContextSource { if ( !$user ) { return null; } - $numBlocks = 0; $appliesToTitle = false; $logTargetPage = ''; $blockTargetName = ''; - foreach ( $blockStore->newListFromTarget( $user, $user ) as $block ) { - if ( $block->getType() !== Block::TYPE_AUTO ) { - $numBlocks++; - if ( $block->appliesToTitle( $title ) ) { - $appliesToTitle = true; - } - $blockTargetName = $block->getTargetName(); - $logTargetPage = $namespaceInfo->getCanonicalName( NS_USER ) . - ':' . $blockTargetName; + $blocks = $blockStore->newListFromTarget( $user, $user, false, + DatabaseBlockStore::AUTO_NONE ); + foreach ( $blocks as $block ) { + if ( $block->appliesToTitle( $title ) ) { + $appliesToTitle = true; } + $blockTargetName = $block->getTargetName(); + $logTargetPage = $namespaceInfo->getCanonicalName( NS_USER ) . + ':' . $blockTargetName; } // Show log extract if the user is sitewide blocked or is partially // blocked and not allowed to edit their user page or user talk page - if ( !$numBlocks || !$appliesToTitle ) { + if ( !count( $blocks ) || !$appliesToTitle ) { return null; } - $msgKey = $numBlocks === 1 + $msgKey = count( $blocks ) === 1 ? 'blocked-notice-logextract' : 'blocked-notice-logextract-multi'; $params = [ 'lim' => 1, @@ -870,10 +867,10 @@ class LogEventsList extends ContextSource { 'msgKey' => [ $msgKey, $user->getName(), # Support GENDER in notice - $numBlocks + count( $blocks ) ], ]; - if ( $numBlocks > 1 ) { + if ( count( $blocks ) > 1 ) { $params['footerHtmlItems'] = [ $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'BlockList' ), diff --git a/includes/logging/ProtectLogFormatter.php b/includes/logging/ProtectLogFormatter.php index 1848bb487877..cf27a53bb216 100644 --- a/includes/logging/ProtectLogFormatter.php +++ b/includes/logging/ProtectLogFormatter.php @@ -223,7 +223,7 @@ class ProtectLogFormatter extends LogFormatter { return $protectDescription; } - private function formatExpiry( $expiry ) { + private function formatExpiry( string $expiry ): string { if ( wfIsInfinity( $expiry ) ) { return $this->context->msg( 'protect-expiry-indefinite' )->text(); } diff --git a/includes/logging/RightsLogFormatter.php b/includes/logging/RightsLogFormatter.php index 9537c81f72c2..c96b9a1c4537 100644 --- a/includes/logging/RightsLogFormatter.php +++ b/includes/logging/RightsLogFormatter.php @@ -309,21 +309,14 @@ class RightsLogFormatter extends LogFormatter { return $uiLanguage->semicolonList( $formattedChanges ); } - private function formatRightsListExpiryChanged( $groups ) { + private function formatRightsListExpiryChanged( array $groups ): string { $list = []; - foreach ( $groups as $group => $expiries ) { - $oldExpiry = $expiries[0]; - $newExpiry = $expiries[1]; + foreach ( $groups as $group => [ $oldExpiry, $newExpiry ] ) { + $oldExpiryFormatted = $oldExpiry ? $this->formatDate( $oldExpiry ) : false; + $newExpiryFormatted = $newExpiry ? $this->formatDate( $newExpiry ) : false; - if ( $oldExpiry ) { - $oldExpiryFormatted = $this->formatDate( $oldExpiry ); - } - if ( $newExpiry ) { - $newExpiryFormatted = $this->formatDate( $newExpiry ); - } - - if ( $oldExpiry && $newExpiry ) { + if ( $oldExpiryFormatted && $newExpiryFormatted ) { // The expiration was changed $list[] = $this->msg( 'rightslogentry-expiry-changed' )->params( $group, @@ -334,7 +327,7 @@ class RightsLogFormatter extends LogFormatter { $oldExpiryFormatted['date'], $oldExpiryFormatted['time'] )->parse(); - } elseif ( $oldExpiry ) { + } elseif ( $oldExpiryFormatted ) { // The expiration was removed $list[] = $this->msg( 'rightslogentry-expiry-removed' )->params( $group, @@ -342,7 +335,7 @@ class RightsLogFormatter extends LogFormatter { $oldExpiryFormatted['date'], $oldExpiryFormatted['time'] )->parse(); - } elseif ( $newExpiry ) { + } elseif ( $newExpiryFormatted ) { // The expiration was added $list[] = $this->msg( 'rightslogentry-expiry-set' )->params( $group, @@ -361,7 +354,7 @@ class RightsLogFormatter extends LogFormatter { return $uiLanguage->listToText( $list ); } - private function formatRightsList( $groups ) { + private function formatRightsList( array $groups ): string { $uiLanguage = $this->context->getLanguage(); // separate arrays of temporary and permanent memberships $tempList = $permList = []; @@ -384,7 +377,7 @@ class RightsLogFormatter extends LogFormatter { return $uiLanguage->listToText( array_merge( $tempList, $permList ) ); } - private function formatDate( $date ) { + private function formatDate( string $date ): array { $uiLanguage = $this->context->getLanguage(); $uiUser = $this->context->getUser(); @@ -472,7 +465,7 @@ class RightsLogFormatter extends LogFormatter { return $ret; } - private function makeGroupArray( $group ) { + private function makeGroupArray( $group ): array { // Migrate old group params from string to array if ( $group === '' ) { $group = []; diff --git a/includes/mail/EmailNotification.php b/includes/mail/EmailNotification.php index 7074fdce181b..a46380a87eea 100644 --- a/includes/mail/EmailNotification.php +++ b/includes/mail/EmailNotification.php @@ -26,12 +26,14 @@ use MediaWiki\HookContainer\HookRunner; use MediaWiki\Mail\RecentChangeMailComposer; use MediaWiki\MainConfigNames; use MediaWiki\MediaWikiServices; +use MediaWiki\Notification\RecipientSet; use MediaWiki\Permissions\Authority; use MediaWiki\RecentChanges\RecentChange; use MediaWiki\Title\Title; use MediaWiki\User\User; use MediaWiki\User\UserArray; use MediaWiki\User\UserIdentity; +use MediaWiki\Watchlist\RecentChangeNotification; /** * Find watchers and create email notifications after a page is changed. @@ -196,6 +198,8 @@ class EmailNotification { # we use $wgPasswordSender as sender's address $mwServices = MediaWikiServices::getInstance(); $config = $mwServices->getMainConfig(); + $notifService = $mwServices->getNotificationService(); + $userFactory = $mwServices->getUserFactory(); # The following code is only run, if several conditions are met: # 1. EmailNotification for pages (other than user_talk pages) must be enabled @@ -230,9 +234,21 @@ class EmailNotification { && $title->getNamespace() === NS_USER_TALK && $this->canSendUserTalkEmail( $editor->getUser(), $title, $minorEdit ) ) { - $targetUser = User::newFromName( $title->getText() ); - $composer->compose( $targetUser, RecentChangeMailComposer::USER_TALK ); - $userTalkId = $targetUser->getId(); + $targetUser = $userFactory->newFromName( $title->getText() ); + if ( $targetUser ) { + $talkNotification = new RecentChangeNotification( + $mwServices->getUserFactory()->newFromAuthority( $editor ), + $title, + $summary, + $minorEdit, + $oldid, + $timestamp, + $pageStatus, + RecentChangeNotification::TALK_NOTIFICATION + ); + $notifService->notify( $talkNotification, new RecipientSet( [ $targetUser ] ) ); + $userTalkId = $targetUser->getId(); + } } if ( $config->get( MainConfigNames::EnotifWatchlist ) ) { @@ -265,21 +281,22 @@ class EmailNotification { // No point notifying the user that actually made the change! continue; } - $user = User::newFromName( $name ); + $user = $userFactory->newFromName( $name ); if ( $user instanceof User ) { $admins[] = $user; } - MediaWikiServices::getInstance()->getNotificationService()->notify( - new \MediaWiki\Watchlist\RecentChangeNotification( + $notifService->notify( + new RecentChangeNotification( $mwServices->getUserFactory()->newFromAuthority( $editor ), $title, $summary, $minorEdit, $oldid, $timestamp, - $pageStatus + $pageStatus, + RecentChangeNotification::ADMIN_NOTIFICATION ), - new \MediaWiki\Notification\RecipientSet( $admins ) + new RecipientSet( $admins ) ); } diff --git a/includes/media/BitmapMetadataHandler.php b/includes/media/BitmapMetadataHandler.php index 3c8aa87e845f..32a3522dcad1 100644 --- a/includes/media/BitmapMetadataHandler.php +++ b/includes/media/BitmapMetadataHandler.php @@ -283,7 +283,10 @@ class BitmapMetadataHandler { if ( file_exists( $filename ) ) { $byteOrder = self::getTiffByteOrder( $filename ); if ( !$byteOrder ) { - throw new InvalidTiffException( "Error determining byte order of $filename" ); + throw new InvalidTiffException( + 'Error determining byte order of {filename}', + [ 'filename' => $filename ] + ); } $exif = new Exif( $filename, $byteOrder ); $data = $exif->getFilteredData(); @@ -292,10 +295,16 @@ class BitmapMetadataHandler { return $data; } else { - throw new InvalidTiffException( "Could not extract data from tiff file $filename" ); + throw new InvalidTiffException( + 'Could not extract data from tiff file {filename}', + [ 'filename' => $filename ] + ); } } else { - throw new InvalidTiffException( "File doesn't exist - $filename" ); + throw new InvalidTiffException( + "File {filename} doesn't exist", + [ 'filename' => $filename ] + ); } } diff --git a/includes/media/InvalidTiffException.php b/includes/media/InvalidTiffException.php index 665b25d09101..d7a534203e50 100644 --- a/includes/media/InvalidTiffException.php +++ b/includes/media/InvalidTiffException.php @@ -1,4 +1,6 @@ <?php -class InvalidTiffException extends Exception { +use Wikimedia\NormalizedException\NormalizedException; + +class InvalidTiffException extends NormalizedException { } diff --git a/includes/page/Article.php b/includes/page/Article.php index ac0fec742a51..c6ff94f24503 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -1121,7 +1121,9 @@ class Article implements Page { } else { $specificTarget = $title->getRootText(); } - if ( $this->blockStore->newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) { + $block = $this->blockStore->newFromTarget( + $specificTarget, $vagueTarget, false, DatabaseBlockStore::AUTO_NONE ); + if ( $block instanceof DatabaseBlock ) { return [ 'index' => 'noindex', 'follow' => 'nofollow' diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 0a8d43c3ac07..d63d84924a83 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -2557,7 +2557,7 @@ class WikiPage implements Stringable, Page, PageRecord { } private static function queueBacklinksJobs( - Title $title, $mainSlotChanged, $maybeRedirectChanged, $causeAction + Title $title, bool $mainSlotChanged, bool $maybeRedirectChanged, string $causeAction ) { $services = MediaWikiServices::getInstance(); $backlinkCache = $services->getBacklinkCacheFactory()->getBacklinkCache( $title ); diff --git a/includes/parser/CoreParserFunctions.php b/includes/parser/CoreParserFunctions.php index 13311d3d64f6..0783d6477c22 100644 --- a/includes/parser/CoreParserFunctions.php +++ b/includes/parser/CoreParserFunctions.php @@ -1254,13 +1254,16 @@ class CoreParserFunctions { $processNowiki = $parser->tagNeedsNowikiStrippedInTagPF( $tagName ) ? PPFrame::PROCESS_NOWIKI : 0; if ( count( $args ) ) { - // With Fragment v2 support, the $processNoWiki flag isn't actually + // With Fragment v2+ support, the $processNoWiki flag isn't actually // required here, but it doesn't do any harm. $inner = $frame->expand( array_shift( $args ), $processNowiki ); if ( $processNowiki && - MediaWikiServices::getInstance()->getMainConfig() - ->get( MainConfigNames::ParsoidFragmentSupport ) === 'v2' + in_array( + MediaWikiServices::getInstance()->getMainConfig() + ->get( MainConfigNames::ParsoidFragmentSupport ), + [ 'v2', 'v3' ], true + ) ) { // This is the T299103 workaround for <syntaxhighlight>, // and reproduces the code in SyntaxHighlight::parserHook. diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 15530b79d3d1..c9f2f173af1b 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -333,7 +333,13 @@ class Parser { private Title $mTitle; /** Output type, one of the OT_xxx constants */ private int $mOutputType; - /** When false, suppress extension tag processing for OT_PREPROCESS */ + /** + * When true (default), extension tags are put in the general strip state + * for OT_PREPROCESS. + * When false, extension tags are skipped during OT_PREPROCESS (parsoid + * fragment v1) or put into the `exttag` strip state (parsoid fragment + * v2+) + */ private bool $mStripExtTags = true; /** * Shortcut alias, see Parser::setOutputType() @@ -2376,7 +2382,7 @@ class Parser { return $ret; } - private static function normalizeUrlComponent( $component, $unsafe ) { + private static function normalizeUrlComponent( string $component, string $unsafe ): string { $callback = static function ( $matches ) use ( $unsafe ) { $char = urldecode( $matches[0] ); $ord = ord( $char ); @@ -2926,7 +2932,8 @@ class Parser { * double-brace expansion. * @param array $options Various options used by Parsoid: * - 'stripExtTags' When true, put extension tags in general strip state; when - * false extension tags are skipped during OT_PREPROCESS + * false extension tags are skipped during OT_PREPROCESS; 'keep' + * makes no change to the stripExtTags setting. * - 'parsoidTopLevelCall' Is this coming from Parsoid for top-level templates? * This is used to set start-of-line flag to true for template expansions since that * is how Parsoid models templates. @@ -2964,14 +2971,23 @@ class Parser { if ( $options['processNowiki'] ?? false ) { $flags |= PPFrame::PROCESS_NOWIKI; } - $stripExtTags = $options['stripExtTags'] ?? true; - [ $stripExtTags, $this->mStripExtTags ] = [ $this->mStripExtTags, $stripExtTags ]; + $stripExtTags = $options['stripExtTags'] ?? 'keep'; + if ( $stripExtTags === 'keep' ) { + $stripExtTags = $this->mStripExtTags; + } else { + [ $stripExtTags, $this->mStripExtTags ] = [ $this->mStripExtTags, $stripExtTags ]; + } $text = $frame->expand( $dom, $flags ); $this->mStripExtTags = $stripExtTags; return $text; } + /** @internal */ + public function setStripExtTags( bool $val ) { + $this->mStripExtTags = $val; + } + /** * Warn the user when a parser limitation is reached * Will warn at most once the user per limitation type @@ -4108,7 +4124,10 @@ class Parser { $output = "<$name$attrText>$content$close"; } if ( !$this->mStripExtTags ) { - if ( $this->svcOptions->get( MainConfigNames::ParsoidFragmentSupport ) === 'v2' ) { + if ( in_array( + $this->svcOptions->get( MainConfigNames::ParsoidFragmentSupport ), + [ 'v2', 'v3' ], true + ) ) { $markerType = 'exttag'; } else { $markerType = 'none'; @@ -6236,18 +6255,18 @@ class Parser { return $this->mOutput->getPageProperty( 'defaultsort' ) ?? ''; } - private static function getSectionNameFromStrippedText( $text ) { + private static function getSectionNameFromStrippedText( string $text ): string { $text = Sanitizer::normalizeSectionNameWhitespace( $text ); $text = Sanitizer::decodeCharReferences( $text ); $text = self::normalizeSectionName( $text ); return $text; } - private static function makeAnchor( $sectionName ) { + private static function makeAnchor( string $sectionName ): string { return '#' . Sanitizer::escapeIdForLink( $sectionName ); } - private function makeLegacyAnchor( $sectionName ) { + private function makeLegacyAnchor( string $sectionName ): string { $fragmentMode = $this->svcOptions->get( MainConfigNames::FragmentMode ); if ( isset( $fragmentMode[1] ) && $fragmentMode[1] === 'legacy' ) { // ForAttribute() and ForLink() are the same for legacy encoding diff --git a/includes/parser/ParserCache.php b/includes/parser/ParserCache.php index 181dd9eb2a9c..68827975964c 100644 --- a/includes/parser/ParserCache.php +++ b/includes/parser/ParserCache.php @@ -368,7 +368,8 @@ class ParserCache { } if ( $page->isRedirect() ) { - // It's a redirect now + // NOTE: It is not clear why we should bail out here, see T389591. + // In any case, the behavior of get() and save() need to be consistent. $this->incrementStats( $page, 'miss', 'redirect' ); return false; } @@ -450,6 +451,15 @@ class ParserCache { $revId = null ) { $page->assertWiki( PageRecord::LOCAL ); + + if ( $page->isRedirect() ) { + // NOTE: It is not clear whether we should bail out here, see T389591. + // While that is being discussed, the behavior of get() and save() + // need to be consistent. + $this->incrementStats( $page, 'save', 'redirect' ); + return; + } + // T350538: Eventually we'll warn if the $cacheTime and $revId // parameters are non-null here, since we *should* be getting // them from the ParserOutput. diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index 576c40314e87..119a16f93e6c 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -2197,7 +2197,7 @@ class ParserOutput extends CacheTime implements ContentMetadataCollector { return $value; } - private static function getTimes( $clock = null ): array { + private static function getTimes( ?string $clock = null ): array { $ret = []; if ( !$clock || $clock === 'wall' ) { $ret['wall'] = microtime( true ); diff --git a/includes/parser/Parsoid/Config/DataAccess.php b/includes/parser/Parsoid/Config/DataAccess.php index 222203e5bdf3..c2d2eb99777b 100644 --- a/includes/parser/Parsoid/Config/DataAccess.php +++ b/includes/parser/Parsoid/Config/DataAccess.php @@ -334,6 +334,10 @@ class DataAccess extends IDataAccess { // Use the same legacy parser object for all calls to extension tag // processing, for greater compatibility. $this->parser ??= $this->parserFactory->create(); + if ( $this->config->get( MainConfigNames::ParsoidFragmentSupport ) === 'v3' ) { + // New PFragment-based support (T374616) + $this->parser->setStripExtTags( false ); + } $this->parser->startExternalParse( Title::newFromLinkTarget( $pageConfig->getLinkTarget() ), $pageConfig->getParserOptions(), @@ -402,9 +406,10 @@ class DataAccess extends IDataAccess { $wikitext = $parser->getStripState()->unstripBoth( $wikitext ); } else { // New PFragment-based support (T374616) + $stripExtTags = $this->config->get( MainConfigNames::ParsoidFragmentSupport ) === 'v3' ? 'keep' : false; $wikitext = $parser->replaceVariables( $wikitext, $this->ppFrame, false, [ - 'stripExtTags' => false, + 'stripExtTags' => $stripExtTags, 'parsoidTopLevelCall' => true, // This is implied by stripExtTags=false and // probably doesn't need to be explicitly passed diff --git a/includes/parser/Parsoid/Config/SiteConfig.php b/includes/parser/Parsoid/Config/SiteConfig.php index 24a6eef635b4..4e91f19aa96d 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 ); } diff --git a/includes/parser/Preprocessor_Hash.php b/includes/parser/Preprocessor_Hash.php index e37b8a78c5d5..11cfcba54d48 100644 --- a/includes/parser/Preprocessor_Hash.php +++ b/includes/parser/Preprocessor_Hash.php @@ -804,7 +804,7 @@ class Preprocessor_Hash extends Preprocessor { return [ [ 'root', $stack->rootAccum ] ]; } - private static function addLiteral( array &$accum, $text ) { + private static function addLiteral( array &$accum, string $text ) { $n = count( $accum ); if ( $n && is_string( $accum[$n - 1] ) ) { $accum[$n - 1] .= $text; diff --git a/includes/recentchanges/ChangesListFilter.php b/includes/recentchanges/ChangesListFilter.php index 38e85953333b..004992964459 100644 --- a/includes/recentchanges/ChangesListFilter.php +++ b/includes/recentchanges/ChangesListFilter.php @@ -449,7 +449,7 @@ abstract class ChangesListFilter { return false; } - private function hasConflictWithGroup( ChangesListFilterGroup $group ) { + private function hasConflictWithGroup( ChangesListFilterGroup $group ): bool { return in_array( $group, $this->getConflictingGroups() ); } @@ -476,7 +476,7 @@ abstract class ChangesListFilter { return false; } - private function hasConflictWithFilter( ChangesListFilter $filter ) { + private function hasConflictWithFilter( ChangesListFilter $filter ): bool { return in_array( $filter, $this->getConflictingFilters() ); } diff --git a/includes/recentchanges/RCCacheEntryFactory.php b/includes/recentchanges/RCCacheEntryFactory.php index a4ac492d1792..2353074a9e9d 100644 --- a/includes/recentchanges/RCCacheEntryFactory.php +++ b/includes/recentchanges/RCCacheEntryFactory.php @@ -166,7 +166,7 @@ class RCCacheEntryFactory { return $clink; } - private function getLogLink( $logType ) { + private function getLogLink( string $logType ): string { $logtitle = SpecialPage::getTitleFor( 'Log', $logType ); $logpage = new LogPage( $logType ); $logname = $logpage->getName()->text(); diff --git a/includes/recentchanges/RecentChange.php b/includes/recentchanges/RecentChange.php index af0865502a85..130cff0bc915 100644 --- a/includes/recentchanges/RecentChange.php +++ b/includes/recentchanges/RecentChange.php @@ -1275,7 +1275,7 @@ class RecentChange implements Taggable { return ChangesList::showCharacterDifference( $old, $new ); } - private static function checkIPAddress( $ip ) { + private static function checkIPAddress( string $ip ): string { global $wgRequest; if ( $ip ) { diff --git a/includes/registration/ExtensionRegistry.php b/includes/registration/ExtensionRegistry.php index ef4aff930736..d0fb664617eb 100644 --- a/includes/registration/ExtensionRegistry.php +++ b/includes/registration/ExtensionRegistry.php @@ -270,7 +270,7 @@ class ExtensionRegistry implements DomainEventSubscriber { return $this->cache; } - private function makeCacheKey( BagOStuff $cache, $component, ...$extra ) { + private function makeCacheKey( BagOStuff $cache, string $component, string ...$extra ): string { // Allow reusing cached ExtensionRegistry metadata between wikis (T274648) return $cache->makeGlobalKey( "registration-$component", diff --git a/includes/revisiondelete/RevisionDeleteUser.php b/includes/revisiondelete/RevisionDeleteUser.php index 9cb7a5ff0c5f..f10782be59b7 100644 --- a/includes/revisiondelete/RevisionDeleteUser.php +++ b/includes/revisiondelete/RevisionDeleteUser.php @@ -141,7 +141,9 @@ class RevisionDeleteUser { return true; } - private static function buildSetBitDeletedField( $field, $op, $value, IDatabase $dbw ) { + private static function buildSetBitDeletedField( + string $field, string $op, $value, IDatabase $dbw + ): array { return [ $field => new RawSQLValue( $op === '&' ? $dbw->bitAnd( $field, $value ) : $dbw->bitOr( $field, $value ) diff --git a/includes/search/BaseSearchResultSet.php b/includes/search/BaseSearchResultSet.php index 59b633c84903..39e314003c82 100644 --- a/includes/search/BaseSearchResultSet.php +++ b/includes/search/BaseSearchResultSet.php @@ -41,7 +41,7 @@ abstract class BaseSearchResultSet implements ISearchResultSet { $this->bcIterator()->rewind(); } - private function bcIterator() { + private function bcIterator(): ArrayIterator { if ( $this->bcIterator === null ) { // @phan-suppress-next-line PhanTypeMismatchProperty Expected $this->bcIterator = 'RECURSION'; diff --git a/includes/search/PrefixSearch.php b/includes/search/PrefixSearch.php index c40b703d958c..def316096de9 100644 --- a/includes/search/PrefixSearch.php +++ b/includes/search/PrefixSearch.php @@ -138,7 +138,9 @@ abstract class PrefixSearch { $this->handleResultFromHook( $srchres, $namespaces, $search, $limit, $offset ) ); } - private function handleResultFromHook( $srchres, $namespaces, $search, $limit, $offset ) { + private function handleResultFromHook( + array $srchres, array $namespaces, string $search, int $limit, int $offset + ): array { if ( $offset === 0 ) { // Only perform exact db match if offset === 0 // This is still far from perfect but at least we avoid returning the diff --git a/includes/search/SearchMySQL.php b/includes/search/SearchMySQL.php index ddbab89c4ac0..554450511061 100644 --- a/includes/search/SearchMySQL.php +++ b/includes/search/SearchMySQL.php @@ -143,7 +143,7 @@ class SearchMySQL extends SearchDatabase { ]; } - private function regexTerm( $string, $wildcard ) { + private function regexTerm( string $string, ?string $wildcard ): string { $regex = preg_quote( $string, '/' ); if ( MediaWikiServices::getInstance()->getContentLanguage()->hasWordBreaks() ) { if ( $wildcard ) { diff --git a/includes/search/SearchSqlite.php b/includes/search/SearchSqlite.php index 5e4bd83be001..ea89e166ed00 100644 --- a/includes/search/SearchSqlite.php +++ b/includes/search/SearchSqlite.php @@ -143,7 +143,7 @@ class SearchSqlite extends SearchDatabase { return " $field MATCH $searchon "; } - private function regexTerm( $string, $wildcard ) { + private function regexTerm( string $string, string $wildcard ): string { $regex = preg_quote( $string, '/' ); if ( MediaWikiServices::getInstance()->getContentLanguage()->hasWordBreaks() ) { if ( $wildcard ) { @@ -280,7 +280,7 @@ class SearchSqlite extends SearchDatabase { "WHERE page_id=$searchindex.rowid AND $match"; } - private function getCountQuery( $filteredTerm, $fulltext ) { + private function getCountQuery( string $filteredTerm, bool $fulltext ): string { $match = $this->parseQuery( $filteredTerm, $fulltext ); $dbr = $this->dbProvider->getReplicaDatabase(); $page = $dbr->tableName( 'page' ); diff --git a/includes/session/UserInfo.php b/includes/session/UserInfo.php index a55725625624..ece85e03dca0 100644 --- a/includes/session/UserInfo.php +++ b/includes/session/UserInfo.php @@ -59,7 +59,7 @@ final class UserInfo implements Stringable { /** @var User|null */ private $user = null; - private function __construct( ?User $user, $verified ) { + private function __construct( ?User $user, bool $verified ) { $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils(); if ( $user && $user->isAnon() && !$userNameUtils->isUsable( $user->getName() ) ) { $this->verified = true; diff --git a/includes/skins/components/SkinComponentTempUserBanner.php b/includes/skins/components/SkinComponentTempUserBanner.php index d8404801a7b8..3d3f26409b85 100644 --- a/includes/skins/components/SkinComponentTempUserBanner.php +++ b/includes/skins/components/SkinComponentTempUserBanner.php @@ -44,7 +44,7 @@ class SkinComponentTempUserBanner implements SkinComponent { $this->userpageUrl = $user->getUserPage()->getFullURL(); } - private function createLoginLink() { + private function createLoginLink(): string { return Html::element( 'a', [ 'href' => $this->loginUrl, @@ -55,7 +55,7 @@ class SkinComponentTempUserBanner implements SkinComponent { $this->localizer->msg( 'pt-login' )->text() ); } - private function createAccountLink() { + private function createAccountLink(): string { return Html::element( 'a', [ 'href' => $this->createAccountUrl, @@ -67,7 +67,7 @@ class SkinComponentTempUserBanner implements SkinComponent { ); } - private function renderBannerHTML() { + private function renderBannerHTML(): string { return Html::rawElement( 'div', [ 'class' => 'mw-temp-user-banner' ], Html::rawElement( 'p', [], $this->localizer->msg( 'temp-user-banner-description' )->escaped() . diff --git a/includes/specialpage/ContributionsSpecialPage.php b/includes/specialpage/ContributionsSpecialPage.php index cb9a1eb564ab..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). @@ -434,28 +440,26 @@ class ContributionsSpecialPage extends IncludableSpecialPage { // For IP ranges you must give DatabaseBlock::newFromTarget the CIDR string // and not a user object. if ( IPUtils::isValidRange( $userObj->getName() ) ) { - $blocks = $this->blockStore - ->newListFromTarget( $userObj->getName(), $userObj->getName() ); + $blocks = $this->blockStore->newListFromTarget( + $userObj->getName(), $userObj->getName(), false, + DatabaseBlockStore::AUTO_NONE ); } else { - $blocks = $this->blockStore->newListFromTarget( $userObj, $userObj ); + $blocks = $this->blockStore->newListFromTarget( + $userObj, $userObj, false, DatabaseBlockStore::AUTO_NONE ); } - $numBlocks = 0; $sitewide = false; $logTargetPage = ''; foreach ( $blocks as $block ) { - if ( $block->getType() !== Block::TYPE_AUTO ) { - $numBlocks++; - if ( $block->isSitewide() ) { - $sitewide = true; - } - $logTargetPage = $this->namespaceInfo->getCanonicalName( NS_USER ) . - ':' . $block->getTargetName(); + if ( $block->isSitewide() ) { + $sitewide = true; } + $logTargetPage = $this->namespaceInfo->getCanonicalName( NS_USER ) . + ':' . $block->getTargetName(); } - if ( $numBlocks ) { - if ( $numBlocks === 1 ) { + if ( count( $blocks ) ) { + if ( count( $blocks ) === 1 ) { if ( $userObj->isAnon() ) { $msgKey = $sitewide ? 'sp-contributions-blocked-notice-anon' : @@ -487,7 +491,7 @@ class ContributionsSpecialPage extends IncludableSpecialPage { 'msgKey' => [ $msgKey, $userObj->getName(), # Support GENDER in 'sp-contributions-blocked-notice' - $numBlocks + count( $blocks ) ], 'offset' => '', # don't use WebRequest parameter offset 'wrap' => Html::rawElement( diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index eb1efce26617..7389b6b02496 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -658,11 +658,12 @@ class SpecialBlock extends FormSpecialPage { // This won't be $fields['PreviousTarget']['default'] = (string)$this->target; - $block = $this->blockStore->newFromTarget( $this->target ); + $block = $this->blockStore->newFromTarget( + $this->target, null, false, DatabaseBlockStore::AUTO_NONE ); // Populate fields if there is a block that is not an autoblock; if it is a range // block, only populate the fields if the range is the same as $this->target - if ( $block instanceof DatabaseBlock && $block->getType() !== DatabaseBlock::TYPE_AUTO + if ( $block instanceof DatabaseBlock && ( !( $this->target instanceof RangeBlockTarget ) || $block->isBlocking( $this->target ) ) ) { @@ -1217,6 +1218,11 @@ class SpecialBlock extends FormSpecialPage { * @return bool|string|array|Status As documented for HTMLForm::trySubmit. */ public function onSubmit( array $data, ?HTMLForm $form = null ) { + if ( $this->useCodex ) { + // Treat as no submission for the JS-only Codex form. + // This happens if the form is submitted before any JS is loaded. + return false; + } return self::processFormInternal( $data, $this->getAuthority(), diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php index 7effcb5ec496..1293f7ab5287 100644 --- a/includes/specials/SpecialMergeHistory.php +++ b/includes/specials/SpecialMergeHistory.php @@ -337,8 +337,6 @@ class SpecialMergeHistory extends SpecialPage { $mergeLogPage = new LogPage( 'merge' ); $out->addHTML( '<h2>' . $mergeLogPage->getName()->escaped() . "</h2>\n" ); LogEventsList::showLogExtract( $out, 'merge', $this->mTargetObj ); - - return true; } /** diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 981f6db398d0..fd7b1270f652 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -347,16 +347,16 @@ class SpecialSearch extends SpecialPage { return $url ?? $title->getFullUrlForRedirect(); } - private function redirectOnExactMatch() { + private function redirectOnExactMatch(): bool { if ( !$this->getConfig()->get( MainConfigNames::SearchMatchRedirectPreference ) ) { // If the preference for whether to redirect is disabled, use the default setting - return $this->userOptionsManager->getDefaultOption( + return (bool)$this->userOptionsManager->getDefaultOption( 'search-match-redirect', $this->getUser() ); } else { // Otherwise use the user's preference - return $this->userOptionsManager->getOption( $this->getUser(), 'search-match-redirect' ); + return $this->userOptionsManager->getBoolOption( $this->getUser(), 'search-match-redirect' ); } } diff --git a/includes/tidy/RemexCompatMunger.php b/includes/tidy/RemexCompatMunger.php index a0faf73925df..90fc912823ce 100644 --- a/includes/tidy/RemexCompatMunger.php +++ b/includes/tidy/RemexCompatMunger.php @@ -134,7 +134,7 @@ class RemexCompatMunger implements TreeHandler { $this->serializer->endDocument( $pos ); } - private function getParentForInsert( $preposition, $refElement ) { + private function getParentForInsert( int $preposition, ?Element $refElement ): array { if ( $preposition === TreeBuilder::ROOT ) { return [ $this->serializer->getRootNode(), null ]; } elseif ( $preposition === TreeBuilder::BEFORE ) { @@ -210,7 +210,7 @@ class RemexCompatMunger implements TreeHandler { $length, $sourceStart, $sourceLength ); } - private function trace( $msg ) { + private function trace( string $msg ) { if ( $this->trace ) { wfDebug( "[RCM] $msg" ); } diff --git a/includes/user/TempUser/ScrambleMapping.php b/includes/user/TempUser/ScrambleMapping.php index fa2d29084834..86395fba892e 100644 --- a/includes/user/TempUser/ScrambleMapping.php +++ b/includes/user/TempUser/ScrambleMapping.php @@ -92,7 +92,7 @@ class ScrambleMapping implements SerialMapping { throw new RuntimeException( __METHOD__ . ": The index $index is too large" ); } - private function powmod( $num, $exponent, $modulus ) { + private function powmod( int $num, int $exponent, int $modulus ): int { if ( $this->hasGmp ) { return \gmp_intval( \gmp_powm( $num, $exponent, $modulus ) ); } elseif ( $this->hasBcm ) { diff --git a/includes/watchlist/RecentChangeNotification.php b/includes/watchlist/RecentChangeNotification.php index 9e5c0a6d952a..afdc1f3bfe3b 100644 --- a/includes/watchlist/RecentChangeNotification.php +++ b/includes/watchlist/RecentChangeNotification.php @@ -35,6 +35,9 @@ class RecentChangeNotification extends WikiNotification { public const TYPE = 'mediawiki.recent_change'; + public const TALK_NOTIFICATION = 'talk'; + public const ADMIN_NOTIFICATION = 'admin'; + /** * @todo Pass the RecentChange object * @@ -45,6 +48,7 @@ class RecentChangeNotification extends WikiNotification { * @param int|null $oldid * @param string $timestamp * @param string $pageStatus + * @param string $source one of types talk, admin or watchlist */ public function __construct( UserIdentity $editor, @@ -53,7 +57,8 @@ class RecentChangeNotification extends WikiNotification { bool $minorEdit, $oldid, $timestamp, - string $pageStatus + string $pageStatus, + string $source ) { parent::__construct( self::TYPE, $title, $editor, [ @@ -61,7 +66,8 @@ class RecentChangeNotification extends WikiNotification { 'minorEdit' => $minorEdit, 'oldid' => $oldid, 'timestamp' => $timestamp, - 'pageStatus' => $pageStatus + 'pageStatus' => $pageStatus, + 'source' => $source ] ); } diff --git a/includes/watchlist/WatchedItemQueryService.php b/includes/watchlist/WatchedItemQueryService.php index 15e8f6ce48e8..d6d90fcdf903 100644 --- a/includes/watchlist/WatchedItemQueryService.php +++ b/includes/watchlist/WatchedItemQueryService.php @@ -369,7 +369,7 @@ class WatchedItemQueryService { return $watchedItems; } - private function getRecentChangeFieldsFromRow( \stdClass $row ) { + private function getRecentChangeFieldsFromRow( \stdClass $row ): array { return array_filter( get_object_vars( $row ), static function ( $key ) { @@ -379,7 +379,7 @@ class WatchedItemQueryService { ); } - private function getWatchedItemsWithRCInfoQueryTables( array $options ) { + private function getWatchedItemsWithRCInfoQueryTables( array $options ): array { $tables = [ 'recentchanges', 'watchlist' ]; if ( $this->expiryEnabled ) { @@ -403,7 +403,7 @@ class WatchedItemQueryService { return $tables; } - private function getWatchedItemsWithRCInfoQueryFields( array $options ) { + private function getWatchedItemsWithRCInfoQueryFields( array $options ): array { $fields = [ 'rc_id', 'rc_namespace', @@ -466,7 +466,7 @@ class WatchedItemQueryService { IReadableDatabase $db, User $user, array $options - ) { + ): array { $watchlistOwnerId = $this->getWatchlistOwnerId( $user, $options ); $conds = [ 'wl_user' => $watchlistOwnerId ]; @@ -511,7 +511,7 @@ class WatchedItemQueryService { return $conds; } - private function getWatchlistOwnerId( UserIdentity $user, array $options ) { + private function getWatchlistOwnerId( UserIdentity $user, array $options ): int { if ( array_key_exists( 'watchlistOwner', $options ) ) { /** @var UserIdentity $watchlistOwner */ $watchlistOwner = $options['watchlistOwner']; @@ -530,7 +530,7 @@ class WatchedItemQueryService { IReadableDatabase $dbr, User $user, array $options - ) { + ): array { $conds = []; if ( in_array( self::FILTER_MINOR, $options['filters'] ) ) { @@ -588,7 +588,7 @@ class WatchedItemQueryService { return $conds; } - private function getStartEndConds( IReadableDatabase $db, array $options ) { + private function getStartEndConds( IReadableDatabase $db, array $options ): array { if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) { return []; } @@ -606,7 +606,7 @@ class WatchedItemQueryService { return $conds; } - private function getUserRelatedConds( IReadableDatabase $db, Authority $user, array $options ) { + private function getUserRelatedConds( IReadableDatabase $db, Authority $user, array $options ): array { if ( !array_key_exists( 'onlyByUser', $options ) && !array_key_exists( 'notByUser', $options ) ) { return []; } @@ -633,7 +633,7 @@ class WatchedItemQueryService { return $conds; } - private function getExtraDeletedPageLogEntryRelatedCond( IReadableDatabase $db, Authority $user ) { + private function getExtraDeletedPageLogEntryRelatedCond( IReadableDatabase $db, Authority $user ): string { // LogPage::DELETED_ACTION hides the affected page, too. So hide those // entirely from the watchlist, or someone could guess the title. $bitmask = 0; @@ -651,7 +651,7 @@ class WatchedItemQueryService { return ''; } - private function getStartFromConds( IReadableDatabase $db, array $options, array $startFrom ) { + private function getStartFromConds( IReadableDatabase $db, array $options, array $startFrom ): string { $op = $options['dir'] === self::DIR_OLDER ? '<=' : '>='; [ $rcTimestamp, $rcId ] = $startFrom; $rcTimestamp = $db->timestamp( $rcTimestamp ); @@ -708,7 +708,7 @@ class WatchedItemQueryService { ] ); } - private function getWatchedItemsWithRCInfoQueryDbOptions( array $options ) { + private function getWatchedItemsWithRCInfoQueryDbOptions( array $options ): array { $dbOptions = []; if ( array_key_exists( 'dir', $options ) ) { @@ -740,7 +740,7 @@ class WatchedItemQueryService { } } - private function getWatchedItemsWithRCInfoQueryJoinConds( array $options ) { + private function getWatchedItemsWithRCInfoQueryJoinConds( array $options ): array { $joinConds = [ 'watchlist' => [ 'JOIN', [ |