aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--RELEASE-NOTES-1.444
-rw-r--r--cache/.htaccess1
-rw-r--r--composer.json4
-rw-r--r--includes/.htaccess1
-rw-r--r--includes/Rest/Handler/CompareHandler.php4
-rw-r--r--includes/Rest/Handler/DiscoveryHandler.php2
-rw-r--r--includes/Rest/Handler/Helper/RestStatusTrait.php2
-rw-r--r--includes/Rest/Handler/LanguageLinksHandler.php2
-rw-r--r--includes/Rest/Handler/ModuleSpecHandler.php2
-rw-r--r--includes/Rest/Handler/PageHistoryCountHandler.php2
-rw-r--r--includes/Rest/Handler/ParsoidHandler.php2
-rw-r--r--includes/Rest/Router.php2
-rw-r--r--includes/Rest/i18n/ar.json1
-rw-r--r--includes/Storage/DerivedPageDataUpdater.php44
-rw-r--r--includes/Storage/PageUpdaterFactory.php3
-rw-r--r--includes/api/ApiComparePages.php3
-rw-r--r--includes/api/ApiHelpParamValueMessage.php2
-rw-r--r--includes/api/ApiOptions.php2
-rw-r--r--includes/api/ApiParse.php17
-rw-r--r--includes/api/ApiQueryBacklinksprop.php2
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php2
-rw-r--r--includes/api/ApiQueryExternalLinks.php2
-rw-r--r--includes/api/ApiQueryInfo.php2
-rw-r--r--includes/api/ApiQueryLogEvents.php2
-rw-r--r--includes/api/ApiQueryRevisionsBase.php3
-rw-r--r--includes/api/ApiQuerySiteinfo.php8
-rw-r--r--includes/api/ApiQueryUserContribs.php2
-rw-r--r--includes/api/ApiQueryWatchlist.php6
-rw-r--r--includes/api/ApiRevisionDelete.php2
-rw-r--r--includes/api/ApiStashEdit.php3
-rw-r--r--includes/api/ApiWatch.php4
-rw-r--r--includes/api/i18n/pt-br.json77
-rw-r--r--includes/auth/LocalPasswordPrimaryAuthenticationProvider.php42
-rw-r--r--includes/composer/ComposerVendorHtaccessCreator.php4
-rw-r--r--includes/filerepo/ThumbnailEntryPoint.php4
-rw-r--r--includes/filerepo/file/File.php8
-rw-r--r--includes/filerepo/file/FileSelectQueryBuilder.php10
-rw-r--r--includes/filerepo/file/OldLocalFile.php4
-rw-r--r--includes/filerepo/file/UnregisteredLocalFile.php2
-rw-r--r--includes/installer/Task/SqliteCreateDatabaseTask.php4
-rw-r--r--includes/installer/i18n/zh-hant.json6
-rw-r--r--includes/jobqueue/jobs/ThumbnailRenderJob.php2
-rw-r--r--includes/libs/ParamValidator/TypeDef/BooleanDef.php2
-rw-r--r--includes/libs/WRStats/LimitBatch.php2
-rw-r--r--includes/libs/filebackend/FSFileBackend.php3
-rw-r--r--includes/libs/filebackend/FileBackendMultiWrite.php2
-rw-r--r--includes/libs/http/MultiHttpClient.php2
-rw-r--r--includes/libs/mime/MSCompoundFileReader.php16
-rw-r--r--includes/libs/mime/XmlTypeCheck.php4
-rw-r--r--includes/libs/objectcache/BagOStuff.php11
-rw-r--r--includes/libs/objectcache/RESTBagOStuff.php2
-rw-r--r--includes/libs/objectcache/WANObjectCache.php14
-rw-r--r--includes/libs/rdbms/ChronologyProtector.php2
-rw-r--r--includes/libs/rdbms/database/Database.php4
-rw-r--r--includes/libs/rdbms/database/DatabaseMySQL.php2
-rw-r--r--includes/libs/rdbms/database/QueryBuilderFromRawSql.php2
-rw-r--r--includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php2
-rw-r--r--includes/libs/rdbms/expression/LikeValue.php2
-rw-r--r--includes/libs/rdbms/lbfactory/LBFactorySimple.php2
-rw-r--r--includes/libs/rdbms/loadbalancer/LoadBalancer.php2
-rw-r--r--includes/libs/rdbms/platform/SQLPlatform.php2
-rw-r--r--includes/media/DjVuImage.php12
-rw-r--r--includes/media/MediaHandler.php10
-rw-r--r--includes/media/SVGReader.php2
-rw-r--r--includes/parser/ParserOptions.php3
-rw-r--r--includes/parser/Parsoid/Config/SiteConfig.php7
-rw-r--r--includes/recentchanges/ChangeTrackingEventIngress.php77
-rw-r--r--includes/specialpage/ContributionsSpecialPage.php16
-rw-r--r--includes/specials/SpecialBotPasswords.php2
-rw-r--r--includes/specials/SpecialComparePages.php2
-rw-r--r--includes/specials/SpecialDoubleRedirects.php2
-rw-r--r--includes/specials/SpecialImport.php2
-rw-r--r--includes/specials/SpecialLinkSearch.php2
-rw-r--r--includes/specials/SpecialLog.php2
-rw-r--r--includes/specials/SpecialMovePage.php6
-rw-r--r--includes/specials/SpecialNewPages.php2
-rw-r--r--includes/specials/SpecialPageLanguage.php2
-rw-r--r--includes/specials/SpecialRandomPage.php4
-rw-r--r--includes/specials/SpecialRenameUser.php6
-rw-r--r--includes/specials/SpecialSpecialPages.php2
-rw-r--r--includes/specials/SpecialStatistics.php6
-rw-r--r--includes/specials/SpecialTags.php4
-rw-r--r--includes/specials/SpecialUndelete.php6
-rw-r--r--includes/specials/SpecialUserRights.php2
-rw-r--r--includes/specials/SpecialVersion.php2
-rw-r--r--includes/specials/SpecialWatchlist.php2
-rw-r--r--includes/specials/SpecialWhatLinksHere.php4
-rw-r--r--includes/specials/forms/PreferencesFormOOUI.php2
-rw-r--r--includes/specials/pagers/AllMessagesTablePager.php2
-rw-r--r--includes/specials/pagers/ImageListPager.php2
-rw-r--r--includes/upload/UploadBase.php2
-rw-r--r--includes/upload/UploadFromChunks.php2
-rw-r--r--languages/.htaccess1
-rw-r--r--languages/i18n/en.json5
-rw-r--r--languages/i18n/preferences/nia.json162
-rw-r--r--languages/i18n/qqq.json3
-rw-r--r--maintenance/.htaccess1
-rw-r--r--maintenance/SqliteMaintenance.php2
-rw-r--r--maintenance/benchmarks/benchmarkJsonCodec.php2
-rw-r--r--maintenance/benchmarks/benchmarkSettings.php2
-rw-r--r--maintenance/benchmarks/benchmarkTidy.php2
-rw-r--r--maintenance/checkDependencies.php8
-rw-r--r--maintenance/cleanupImages.php11
-rw-r--r--maintenance/cleanupPreferences.php3
-rw-r--r--maintenance/cleanupWatchlist.php2
-rw-r--r--maintenance/compareParsers.php4
-rw-r--r--maintenance/convertExtensionToRegistration.php2
-rw-r--r--maintenance/deleteAutoPatrolLogs.php4
-rw-r--r--maintenance/deleteOldRevisions.php2
-rw-r--r--maintenance/dumpUploads.php4
-rw-r--r--maintenance/getConfiguration.php2
-rw-r--r--maintenance/importDump.php12
-rw-r--r--maintenance/importImages.php2
-rw-r--r--maintenance/includes/MaintenanceParameters.php2
-rw-r--r--maintenance/includes/MaintenanceRunner.php4
-rw-r--r--maintenance/includes/SevenZipStream.php2
-rw-r--r--maintenance/includes/TextPassDumper.php4
-rw-r--r--maintenance/install.php2
-rw-r--r--maintenance/language/generateCollationData.php2
-rw-r--r--maintenance/language/generateUcfirstOverrides.php2
-rw-r--r--maintenance/language/importExtensionMessages.php10
-rw-r--r--maintenance/mcc.php2
-rw-r--r--maintenance/migrateExternallinks.php2
-rw-r--r--maintenance/migrateFileTables.php2
-rw-r--r--maintenance/migrateLinksTable.php2
-rw-r--r--maintenance/populateChangeTagDef.php2
-rw-r--r--maintenance/purgePage.php2
-rw-r--r--maintenance/rebuildImages.php11
-rw-r--r--maintenance/storage/checkStorage.php6
-rw-r--r--maintenance/storage/moveToExternal.php8
-rw-r--r--maintenance/storage/recompressTracked.php2
-rw-r--r--maintenance/storage/trackBlobs.php4
-rwxr-xr-xmaintenance/update.php2
-rw-r--r--maintenance/updateSearchIndex.php2
-rw-r--r--resources/Resources.php4
-rw-r--r--resources/src/mediawiki.page.ready/ready.js27
-rw-r--r--resources/src/mediawiki.special.block/SpecialBlock.vue9
-rw-r--r--resources/src/mediawiki.special.block/components/BlockLog.vue58
-rw-r--r--resources/src/mediawiki.special.block/stores/block.js12
-rw-r--r--sql/.htaccess1
-rw-r--r--tests/.htaccess1
-rw-r--r--tests/jest/jest.setup.js1
-rw-r--r--tests/jest/mediawiki.special.block/SpecialBlock.setup.js112
-rw-r--r--tests/jest/mediawiki.special.block/SpecialBlock.test.js22
-rw-r--r--tests/parser/ParserTestRunner.php21
-rw-r--r--tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php81
-rw-r--r--tests/phpunit/includes/Storage/PageUpdaterTest.php24
-rw-r--r--tests/phpunit/includes/api/ApiImportTest.php9
-rw-r--r--tests/phpunit/includes/filerepo/file/LocalFileTest.php8
-rw-r--r--tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php2
-rw-r--r--tests/phpunit/includes/page/MovePageTest.php10
-rw-r--r--tests/phpunit/includes/page/UndeletePageTest.php11
-rw-r--r--tests/phpunit/includes/page/WikiPageDbTest.php9
-rw-r--r--tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php28
-rw-r--r--tests/phpunit/includes/specials/SpecialContributionsTest.php42
-rw-r--r--tests/phpunit/integration/includes/page/DeletePageTest.php8
-rw-r--r--tests/phpunit/integration/includes/page/RollbackPageTest.php11
-rw-r--r--tests/phpunit/unit/includes/installer/Task/SqliteCreateDatabaseTaskTest.php2
-rw-r--r--tests/phpunit/unit/includes/libs/Stats/UnitTestingHelperTest.php6
159 files changed, 1007 insertions, 399 deletions
diff --git a/RELEASE-NOTES-1.44 b/RELEASE-NOTES-1.44
index 12e99197e235..8c3f7e05a30a 100644
--- a/RELEASE-NOTES-1.44
+++ b/RELEASE-NOTES-1.44
@@ -156,7 +156,7 @@ For notes on 1.43.x and older releases, see HISTORY.
* Updated wikimedia/minify from 2.8.0 to 2.9.0.
* Updated wikimedia/php-session-serializer from 3.0.0 to 3.0.1.
* Updated wikimedia/relpath from 4.0.1 to 4.0.2.
-* Updated wikimedia/shellbox from 4.1.1 to 4.1.2.
+* Updated wikimedia/shellbox from 4.1.1 to 4.2.0.
* Updated wikimedia/timestamp from 4.1.1 to 4.2.0.
* …
@@ -271,6 +271,8 @@ because of Phabricator reports.
been removed.
* The `MediaWiki\User\Registration\IUserRegistrationProvider` interface now
defines a required fetchRegistrationBatch() method.
+* BagOStuff::clearLastError() and WANObjectCache::clearLastError(), both
+ deprecated in 1.43, have been removed.
* The hook OutputPageMakeCategoryLinks, deprecated in 1.43, has been removed.
* ApiPageSet::getTitles(), ApiPageSet::getGoodTitles(),
ApiPageSet::getMissingTitles(), ApiPageSet::getGoodAndMissingTitles(),
diff --git a/cache/.htaccess b/cache/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/cache/.htaccess
+++ b/cache/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/composer.json b/composer.json
index bc6ca774bcf3..ac14af55c5dc 100644
--- a/composer.json
+++ b/composer.json
@@ -69,7 +69,7 @@
"wikimedia/minify": "2.9.0",
"wikimedia/normalized-exception": "2.1.1",
"wikimedia/object-factory": "5.0.1",
- "wikimedia/parsoid": "0.21.0-a23",
+ "wikimedia/parsoid": "0.21.0-a24",
"wikimedia/php-session-serializer": "3.0.1",
"wikimedia/purtle": "2.0.0",
"wikimedia/relpath": "4.0.2",
@@ -78,7 +78,7 @@
"wikimedia/running-stat": "2.1.0",
"wikimedia/scoped-callback": "5.0.0",
"wikimedia/services": "4.0.0",
- "wikimedia/shellbox": "4.1.2",
+ "wikimedia/shellbox": "4.2.0",
"wikimedia/utfnormal": "4.0.0",
"wikimedia/timestamp": "4.2.0",
"wikimedia/wait-condition-loop": "2.0.2",
diff --git a/includes/.htaccess b/includes/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/includes/.htaccess
+++ b/includes/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/includes/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/ar.json b/includes/Rest/i18n/ar.json
index db780db3ae9f..7737778374e2 100644
--- a/includes/Rest/i18n/ar.json
+++ b/includes/Rest/i18n/ar.json
@@ -141,5 +141,6 @@
"rest-schema-desc-mock-desc": "وصف وهمي.",
"rest-schema-desc-revision-metadata": "مراجعة بيانات التعريف",
"rest-schema-desc-media-file": "معلومات حول الملف",
+ "rest-schema-desc-page-history": "تاريخ مراجعة الصفحة",
"rest-schema-desc-search-results": "نتائج البحث"
}
diff --git a/includes/Storage/DerivedPageDataUpdater.php b/includes/Storage/DerivedPageDataUpdater.php
index e8e596230585..83d68b309664 100644
--- a/includes/Storage/DerivedPageDataUpdater.php
+++ b/includes/Storage/DerivedPageDataUpdater.php
@@ -26,7 +26,6 @@ use MediaWiki\ChangeTags\ChangeTags;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\Content;
-use MediaWiki\Content\ContentHandler;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Transform\ContentTransformer;
use MediaWiki\Deferred\DeferrableUpdate;
@@ -36,11 +35,9 @@ use MediaWiki\Deferred\RefreshSecondaryDataUpdate;
use MediaWiki\Deferred\SiteStatsUpdate;
use MediaWiki\DomainEvent\DomainEventDispatcher;
use MediaWiki\Edit\PreparedEdit;
-use MediaWiki\Exception\MWUnknownContentModelException;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\JobQueue\JobQueueGroup;
-use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob;
use MediaWiki\JobQueue\Jobs\ParsoidCachePrewarmJob;
use MediaWiki\Language\Language;
use MediaWiki\MainConfigNames;
@@ -160,11 +157,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
private $articleCountMethod;
/**
- * @var bool see $wgRCWatchCategoryMembership
- */
- private $rcWatchCategoryMembership = false;
-
- /**
* Stores (most of) the $options parameter of prepareUpdate().
* @see prepareUpdate()
*
@@ -535,14 +527,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
}
/**
- * @param bool $rcWatchCategoryMembership
- * @see $wgRCWatchCategoryMembership
- */
- public function setRcWatchCategoryMembership( $rcWatchCategoryMembership ) {
- $this->rcWatchCategoryMembership = $rcWatchCategoryMembership;
- }
-
- /**
* @return Title
*/
private function getTitle() {
@@ -738,16 +722,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
return $this->getRawSlot( $role )->getContent();
}
- /**
- * @param string $role slot role name
- * @return ContentHandler
- * @throws MWUnknownContentModelException
- */
- private function getContentHandler( $role ): ContentHandler {
- return $this->contentHandlerFactory
- ->getContentHandler( $this->getRawSlot( $role )->getModel() );
- }
-
private function usePrimary(): bool {
// TODO: can we just set a flag to true in prepareContent()?
return $this->wikiPage->wasLoadedFrom( IDBAccessObject::READ_LATEST );
@@ -1591,24 +1565,6 @@ class DerivedPageDataUpdater implements LoggerAwareInterface, PreparedUpdate {
'defer' => DeferredUpdates::POSTSEND
] );
- // TODO: MCR: check if *any* changed slot supports categories!
- if ( $this->rcWatchCategoryMembership
- && $this->getContentHandler( SlotRecord::MAIN )->supportsCategories() === true
- && $event->isNominalContentChange()
- && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE )
- ) {
- // Note: jobs are pushed after deferred updates, so the job should be able to see
- // the recent change entry (also done via deferred updates) and carry over any
- // bot/deletion/IP flags, ect.
- $this->jobQueueGroup->lazyPush(
- CategoryMembershipChangeJob::newSpec(
- $this->getTitle(),
- $this->revision->getTimestamp(),
- $event->hasCause( PageUpdater::CAUSE_IMPORT )
- )
- );
- }
-
$id = $this->getPageId();
$title = $this->getTitle();
$wikiPage = $this->getWikiPage();
diff --git a/includes/Storage/PageUpdaterFactory.php b/includes/Storage/PageUpdaterFactory.php
index 7bb77215d916..108bd2e3e8be 100644
--- a/includes/Storage/PageUpdaterFactory.php
+++ b/includes/Storage/PageUpdaterFactory.php
@@ -304,9 +304,6 @@ class PageUpdaterFactory {
$derivedDataUpdater->setLogger( $this->logger );
$derivedDataUpdater->setArticleCountMethod(
$this->options->get( MainConfigNames::ArticleCountMethod ) );
- $derivedDataUpdater->setRcWatchCategoryMembership(
- $this->options->get( MainConfigNames::RCWatchCategoryMembership )
- );
return $derivedDataUpdater;
}
diff --git a/includes/api/ApiComparePages.php b/includes/api/ApiComparePages.php
index e2ec95843ba5..334765d06b1d 100644
--- a/includes/api/ApiComparePages.php
+++ b/includes/api/ApiComparePages.php
@@ -38,6 +38,7 @@ use MediaWiki\Revision\SlotRoleRegistry;
use MediaWiki\Title\Title;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
+use MediaWiki\User\UserIdentity;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\RequestTimeout\TimeoutException;
@@ -671,7 +672,7 @@ class ApiComparePages extends ApiBase {
}
}
- private function getUserForPreview() {
+ private function getUserForPreview(): UserIdentity {
$user = $this->getUser();
if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
return $this->userFactory->newUnsavedTempUser(
diff --git a/includes/api/ApiHelpParamValueMessage.php b/includes/api/ApiHelpParamValueMessage.php
index 6949af8887a1..695cc7063d97 100644
--- a/includes/api/ApiHelpParamValueMessage.php
+++ b/includes/api/ApiHelpParamValueMessage.php
@@ -128,7 +128,7 @@ class ApiHelpParamValueMessage extends Message {
return $this->message;
}
- private function subMessage( $key ) {
+ private function subMessage( string $key ): string {
$msg = new Message( $key );
$msg->isInterface = $this->isInterface;
$msg->language = $this->language;
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
index a1ccea47fedf..e3ab7a3e35f7 100644
--- a/includes/api/ApiOptions.php
+++ b/includes/api/ApiOptions.php
@@ -85,7 +85,7 @@ class ApiOptions extends ApiOptionsBase {
);
}
- private function getGlobalParam() {
+ private function getGlobalParam(): string {
return $this->extractRequestParams()['global'];
}
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index 5d5bd20848aa..f147178055cf 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -59,6 +59,7 @@ use MediaWiki\Title\TitleFormatter;
use MediaWiki\Title\TitleValue;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
+use MediaWiki\User\UserIdentity;
use MediaWiki\Utils\UrlUtils;
use MediaWiki\WikiMap\WikiMap;
use Wikimedia\ParamValidator\ParamValidator;
@@ -150,7 +151,7 @@ class ApiParse extends ApiBase {
PageReference $page,
?RevisionRecord $revision,
ParserOptions $popts
- ) {
+ ): ParserOutput {
$worker = new PoolCounterWorkViaCallback( 'ApiParser', $this->getPoolKey(),
[
'doWork' => function () use ( $content, $page, $revision, $popts ) {
@@ -166,7 +167,7 @@ class ApiParse extends ApiBase {
return $worker->execute();
}
- private function getUserForPreview() {
+ private function getUserForPreview(): UserIdentity {
$user = $this->getUser();
if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
return $this->userFactory->newUnsavedTempUser(
@@ -178,7 +179,7 @@ class ApiParse extends ApiBase {
private function getPageParserOutput(
WikiPage $page,
- $revId,
+ ?int $revId,
ParserOptions $popts,
bool $suppressCache
) {
@@ -974,7 +975,7 @@ class ApiParse extends ApiBase {
return $result;
}
- private function formatCategoryLinks( $links ) {
+ private function formatCategoryLinks( array $links ): array {
$result = [];
if ( !$links ) {
@@ -1038,7 +1039,7 @@ class ApiParse extends ApiBase {
return $result;
}
- private function formatIWLinks( $iw ) {
+ private function formatIWLinks( array $iw ): array {
$result = [];
foreach ( $iw as $linkTarget ) {
$entry = [];
@@ -1055,7 +1056,7 @@ class ApiParse extends ApiBase {
return $result;
}
- private function formatHeadItems( $headItems ) {
+ private function formatHeadItems( array $headItems ): array {
$result = [];
foreach ( $headItems as $tag => $content ) {
$entry = [];
@@ -1067,7 +1068,7 @@ class ApiParse extends ApiBase {
return $result;
}
- private function formatLimitReportData( $limitReportData ) {
+ private function formatLimitReportData( array $limitReportData ): array {
$result = [];
foreach ( $limitReportData as $name => $value ) {
@@ -1084,7 +1085,7 @@ class ApiParse extends ApiBase {
return $result;
}
- private function setIndexedTagNames( &$array, $mapping ) {
+ private function setIndexedTagNames( array &$array, array $mapping ) {
foreach ( $mapping as $key => $name ) {
if ( isset( $array[$key] ) ) {
ApiResult::setIndexedTagName( $array[$key], $name );
diff --git a/includes/api/ApiQueryBacklinksprop.php b/includes/api/ApiQueryBacklinksprop.php
index 819a60896428..342972254908 100644
--- a/includes/api/ApiQueryBacklinksprop.php
+++ b/includes/api/ApiQueryBacklinksprop.php
@@ -359,7 +359,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
}
}
- private function setContinue( $row, $sortby ) {
+ private function setContinue( \stdClass $row, array $sortby ) {
$cont = [];
foreach ( $sortby as $field => $v ) {
$cont[] = $row->$field;
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
index ee28e4012cce..685ab9524884 100644
--- a/includes/api/ApiQueryExtLinksUsage.php
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -190,7 +190,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
}
}
- private function setContinue( $orderBy, $row ) {
+ private function setContinue( array $orderBy, \stdClass $row ) {
$fields = [];
foreach ( $orderBy as $field ) {
$fields[] = strtr( $row->$field, [ '%' => '%25', '|' => '%7C' ] );
diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php
index 9c47efba6342..67cb161a2582 100644
--- a/includes/api/ApiQueryExternalLinks.php
+++ b/includes/api/ApiQueryExternalLinks.php
@@ -127,7 +127,7 @@ class ApiQueryExternalLinks extends ApiQueryBase {
}
}
- private function setContinue( $orderBy, $row ) {
+ private function setContinue( array $orderBy, \stdClass $row ) {
$fields = [];
foreach ( $orderBy as $field ) {
$fields[] = strtr( $row->$field, [ '%' => '%25', '|' => '%7C' ] );
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 9382d43652c7..b5bb5bcd9bcc 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -845,7 +845,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
}
- private function getAllVariants( $text, $ns = NS_MAIN ) {
+ private function getAllVariants( string $text, int $ns = NS_MAIN ): array {
$result = [];
foreach ( $this->languageConverter->getVariants() as $variant ) {
$convertTitle = $this->languageConverter->autoConvert( $text, $variant );
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index 8d9bdfb92d9c..3d0f42aa303c 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -328,7 +328,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'item' );
}
- private function extractRowInfo( $row ) {
+ private function extractRowInfo( \stdClass $row ): array {
$logEntry = DatabaseLogEntry::newFromRow( $row );
$vals = [
ApiResult::META_TYPE => 'assoc',
diff --git a/includes/api/ApiQueryRevisionsBase.php b/includes/api/ApiQueryRevisionsBase.php
index 2e246b0519b1..7f525d836aeb 100644
--- a/includes/api/ApiQueryRevisionsBase.php
+++ b/includes/api/ApiQueryRevisionsBase.php
@@ -43,6 +43,7 @@ use MediaWiki\Revision\SlotRoleRegistry;
use MediaWiki\Title\Title;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
+use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserNameUtils;
use stdClass;
use Wikimedia\ParamValidator\ParamValidator;
@@ -788,7 +789,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
return $vals;
}
- private function getUserForPreview() {
+ private function getUserForPreview(): UserIdentity {
$user = $this->getUser();
if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
return $this->userFactory->newUnsavedTempUser(
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index a5007c560704..771d30bce773 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -950,7 +950,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
return $this->getResult()->addValue( 'query', $property, $config );
}
- private function getAutoPromoteConds() {
+ private function getAutoPromoteConds(): array {
$allowedConditions = [];
foreach ( get_defined_constants() as $constantName => $constantValue ) {
if ( strpos( $constantName, 'APCOND_' ) !== false ) {
@@ -960,7 +960,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
return $allowedConditions;
}
- private function processAutoPromote( $input, $allowedConditions ) {
+ private function processAutoPromote( array $input, array $allowedConditions ): array {
$data = [];
foreach ( $input as $groupName => $conditions ) {
$row = $this->recAutopromote( $conditions, $allowedConditions );
@@ -972,7 +972,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
return $data;
}
- private function appendAutoPromote( $property ) {
+ private function appendAutoPromote( string $property ): bool {
return $this->getResult()->addValue(
'query',
$property,
@@ -983,7 +983,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
);
}
- private function appendAutoPromoteOnce( $property ) {
+ private function appendAutoPromoteOnce( string $property ): bool {
$allowedConditions = $this->getAutoPromoteConds();
$data = [];
foreach ( $this->getConfig()->get( MainConfigNames::AutopromoteOnce ) as $key => $value ) {
diff --git a/includes/api/ApiQueryUserContribs.php b/includes/api/ApiQueryUserContribs.php
index 3ac4bc5b234f..9dced1c324f1 100644
--- a/includes/api/ApiQueryUserContribs.php
+++ b/includes/api/ApiQueryUserContribs.php
@@ -641,7 +641,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
return $vals;
}
- private function continueStr( $row ) {
+ private function continueStr( \stdClass $row ): string {
if ( $this->multiUserMode ) {
switch ( $this->orderBy ) {
case 'name':
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 5f6db828bb46..68461145baa0 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -268,7 +268,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
}
- private function getFieldsToInclude() {
+ private function getFieldsToInclude(): array {
$includeFields = [];
if ( $this->fld_flags ) {
$includeFields[] = WatchedItemQueryService::INCLUDE_FLAGS;
@@ -298,7 +298,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
return $includeFields;
}
- private function showParamsConflicting( array $show ) {
+ private function showParamsConflicting( array $show ): bool {
return ( isset( $show[WatchedItemQueryService::FILTER_MINOR] )
&& isset( $show[WatchedItemQueryService::FILTER_NOT_MINOR] ) )
|| ( isset( $show[WatchedItemQueryService::FILTER_BOT] )
@@ -315,7 +315,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
&& isset( $show[WatchedItemQueryService::FILTER_NOT_UNREAD] ) );
}
- private function extractOutputData( WatchedItem $watchedItem, array $recentChangeInfo ) {
+ private function extractOutputData( WatchedItem $watchedItem, array $recentChangeInfo ): array {
/* Determine the title of the page that has been changed. */
$target = $watchedItem->getTarget();
if ( $target instanceof LinkTarget ) {
diff --git a/includes/api/ApiRevisionDelete.php b/includes/api/ApiRevisionDelete.php
index 77bdb99dd18f..8a6df9da0636 100644
--- a/includes/api/ApiRevisionDelete.php
+++ b/includes/api/ApiRevisionDelete.php
@@ -134,7 +134,7 @@ class ApiRevisionDelete extends ApiBase {
$result->addValue( null, $this->getModuleName(), $data );
}
- private function extractStatusInfo( Status $status ) {
+ private function extractStatusInfo( Status $status ): array {
$ret = [
'status' => $status->isOK() ? 'Success' : 'Fail',
];
diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php
index f7bd10cff434..4147d3176dbe 100644
--- a/includes/api/ApiStashEdit.php
+++ b/includes/api/ApiStashEdit.php
@@ -28,6 +28,7 @@ use MediaWiki\Revision\SlotRecord;
use MediaWiki\Storage\PageEditStash;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserFactory;
+use MediaWiki\User\UserIdentity;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\Stats\StatsFactory;
@@ -208,7 +209,7 @@ class ApiStashEdit extends ApiBase {
$this->getResult()->addValue( null, $this->getModuleName(), $ret );
}
- private function getUserForPreview() {
+ private function getUserForPreview(): UserIdentity {
$user = $this->getUser();
if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
return $this->userFactory->newUnsavedTempUser(
diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php
index acb071a6a846..7d9dea1fffec 100644
--- a/includes/api/ApiWatch.php
+++ b/includes/api/ApiWatch.php
@@ -132,8 +132,8 @@ class ApiWatch extends ApiBase {
}
private function watchTitle( PageIdentity $page, User $user, array $params,
- $compatibilityMode = false
- ) {
+ bool $compatibilityMode = false
+ ): array {
$res = [ 'title' => $this->titleFormatter->getPrefixedText( $page ), 'ns' => $page->getNamespace() ];
if ( !$this->watchlistManager->isWatchable( $page ) ) {
diff --git a/includes/api/i18n/pt-br.json b/includes/api/i18n/pt-br.json
index 9dba97e178e7..e9cb4c0d51a6 100644
--- a/includes/api/i18n/pt-br.json
+++ b/includes/api/i18n/pt-br.json
@@ -32,45 +32,52 @@
"apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:Etiquette|Etiqueta & diretrizes de uso]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api.lists.wikimedia.org/ Lista de discussão]\n* [https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/ Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> A API do MediaWiki é uma interface madura e estável que é ativamente suportada e aprimorada. Por mais que tentemos evitar, ocasionalmente talvez precisemos fazer mudanças estruturais; inscreva-se na [https://lists.wikimedia.org/hyperkitty/list/mediawiki-api-announce@lists.wikimedia.org/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].</p>",
"apihelp-main-param-action": "Qual ação executar.",
"apihelp-main-param-format": "O formato da saída.",
- "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] para mais informações.",
- "apihelp-main-param-smaxage": "Define o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
- "apihelp-main-param-maxage": "Define o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
- "apihelp-main-param-assert": "Verifique se o usuário está logado se configurado para <kbd>user</kbd>, <em>not</em> logado se definido como <kbd>anon</kbd> ou tem o direito do usuário do bot se <kbd>bot</kbd>.",
- "apihelp-main-param-assertuser": "Verificar que o usuário atual é o utilizador nomeado.",
+ "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.",
+ "apihelp-main-param-smaxage": "Definir o cabeçalho HTTP de controle de cache <code>s-maxage</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
+ "apihelp-main-param-maxage": "Definir o cabeçalho HTTP de controle de cache <code>max-age</code> para esta quantidade de segundos. Erros não são armazenados em cache.",
+ "apihelp-main-param-assert": "Verificar se o usuário está logado (incluindo possivelmente como usuário temporário) quando definido como <kbd>user</kbd>, se <em>não</em> está logado quando definido como <kbd>anon</kbd>, ou se tem o direito de usuário de robô quando definido como <kbd>bot</kbd>.",
+ "apihelp-main-param-assertuser": "Verificar se o usuário atual é o usuário nomeado.",
"apihelp-main-param-requestid": "Qualquer valor dado aqui será incluído na resposta. Pode ser usado para distinguir requisições.",
- "apihelp-main-param-servedby": "Incluir nos resultados o nome do servidor que serviu o pedido.",
- "apihelp-main-param-curtimestamp": "Inclui o timestamp atual no resultado.",
- "apihelp-main-param-responselanginfo": "Inclua os idiomas usados para <var>uselang</var> e <var>errorlang</var> no resultado.",
- "apihelp-main-param-origin": "Ao acessar a API usando uma solicitação AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda solicitação ''pre-flight'', sendo portanto parte do URI da solicitação (ao invés do corpo do POST).\n\nPara solicitações autenticadas, isto deve corresponder a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida (''whitelisted''), os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara solicitações não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos para usuários tornar-se-ão restritos.",
- "apihelp-main-param-uselang": "Linguagem a ser usada para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.",
- "apihelp-main-param-errorformat": "Formato a ser usado aviso e saída de texto de erro.\n; Texto simples: Texto wiki com tags HTML removidas e entidades substituídas.\n; Wikitext: Unparsed wikitext. \n; html: HTML.\n; Bruto: chave e parâmetros da mensagem.\n; Nenhum: sem saída de texto, apenas os códigos de erro.\n; Bc: Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> são ignorados.",
+ "apihelp-main-param-servedby": "Incluir nos resultados o hostname que serviu a requisição.",
+ "apihelp-main-param-curtimestamp": "Incluir no resultado o timestamp atual.",
+ "apihelp-main-param-responselanginfo": "Incluir no resultado os idiomas usados para <var>uselang</var> e <var>errorlang</var>.",
+ "apihelp-main-param-origin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS), defina isto como o domínio de origem. Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nPara requisições autenticadas, isto deve corresponder exatamente a uma das origens no cabeçalho <code>Origin</code>, para que seja algo como <kbd>https://pt.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parâmetro não corresponder ao cabeçalho <code>Origin</code>, uma resposta 403 será retornada. Se este parâmetro corresponder ao cabeçalho <code>Origin</code> e a origem for permitida, os cabeçalhos <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serão definidos.\n\nPara requisições não autenticadas, especifique o valor <kbd>*</kbd>. Isto fará com que o cabeçalho <code>Access-Control-Allow-Origin</code> seja definido, porém o <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os dados específicos de usuário tornar-se-ão restritos.",
+ "apihelp-main-param-crossorigin": "Ao acessar a API usando uma requisição AJAX por domínio cruzado (CORS) e um provedor de sessão seguro contra ataques de falsificação de requisição entre websites (CSRF), como o provedor OAuth, use isto em vez de <code>origin=*</code> para tornar a requisição autenticada (ou seja, evitar que ela não esteja logada). Isto deve estar incluso em toda requisição “pre-flight”, sendo portanto parte do URI da requisição (ao invés do corpo do POST).\n\nNote que a maioria dos provedores de sessão, incluindo sessões padrões baseadas em cookies, não são compatíveis com CORS autenticado e, portanto, não podem ser utilizadas com este parâmetro.",
+ "apihelp-main-param-uselang": "Idioma a ser usado para traduções de mensagens. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>user</kbd> para usar a preferência de idioma do usuário atual ou especifique <kbd>content</kbd> para usar o idioma de conteúdo desta wiki.",
+ "apihelp-main-param-variant": "Variante do idioma. Funciona somente se o idioma-base for compatível com conversão para suas variantes.",
+ "apihelp-main-param-errorformat": "Formato a ser usado para textos de avisos e erros na saída",
+ "apihelp-main-paramvalue-errorformat-plaintext": "Wikitexto com marcações HTML removidas e entidades substituidas.",
"apihelp-main-paramvalue-errorformat-wikitext": "Wikitexto não analisado.",
"apihelp-main-paramvalue-errorformat-raw": "Chave e parâmetros da mensagem.",
"apihelp-main-paramvalue-errorformat-none": "Sem saída de texto, apenas os códigos de erro.",
"apihelp-main-paramvalue-errorformat-bc": "Formato usado antes do MediaWiki 1.29. <var>errorlang</var> e <var>errorsuselocal</var> serão ignorados.",
- "apihelp-main-param-errorlang": "Linguagem a utilizar para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> com <kbd>siprop=languages</kbd> retorna uma lista de códigos de idioma ou especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou especifique <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.",
- "apihelp-main-param-errorsuselocal": "Se for dado, os textos de erro usarão mensagens customizadas localmente a partir do espaço nominal {{ns: MediaWiki}}.",
+ "apihelp-main-param-errorlang": "Idioma a ser utilizado para avisos e erros. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]]</kbd> retorna uma lista de códigos de idioma. Alternativamente, especifique <kbd>content</kbd> para usar o idioma do conteúdo desta wiki ou <kbd>uselang</kbd> para usar o mesmo valor que o parâmetro <var>uselang</var>.",
+ "apihelp-main-param-errorsuselocal": "Se fornecido, os textos de erro usarão mensagens personalizadas localmente a partir do espaço nominal {{ns:MediaWiki}}.",
+ "apihelp-acquiretempusername-summary": "Adquirir um nome de usuário temporário e armazená-lo na sessão atual caso a criação de contas temporárias esteja ativada e o usuário atual não estiver logado. Se um nome já foi armazenado, retorna esse nome.",
+ "apihelp-acquiretempusername-extended-description": "Se o usuário posteriormente realizar uma ação que resulte na criação de conta temporária, o nome de usuário armazenado será utilizado para a conta criada. Ele também poderá ser utilizado em previsões. Entretanto, a conta ainda não terá sido criada, e o nome não estará visível a outros usuários.",
"apihelp-block-summary": "Bloquear um usuário.",
- "apihelp-block-param-user": "Usuário para bloquear.",
- "apihelp-block-param-userid": "Especifique <kbd>$1user=#<var>ID</var></kbd>.",
- "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (por exemplo <kbd>5 meses</kbd> ou <kbd>2 semanas</kbd>) ou absoluto (por exemplo <kbd>2014-09-18T12:34:56Z</kbd>). Se definido para <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.",
+ "apihelp-block-param-id": "O ID de bloqueio a ser modificado.",
+ "apihelp-block-param-user": "Usuário a ser bloqueado.",
+ "apihelp-block-param-userid": "Ao invés, especifique <kbd>$1user=#<var>ID</var></kbd>.",
+ "apihelp-block-param-expiry": "Tempo de expiração. Pode ser relativo (p. ex., <kbd>5 months</kbd> ou <kbd>2 weeks</kbd>) ou absoluto (p. ex., <kbd>2014-09-18T12:34:56Z</kbd>). Se definido como <kbd>infinite</kbd>, <kbd>indefinite</kbd> ou <kbd>never</kbd>, o bloqueio nunca irá expirar.",
"apihelp-block-param-reason": "Razão do bloqueio.",
- "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja. desativa edições anônimas para este endereço IP).",
+ "apihelp-block-param-anononly": "Bloqueia apenas usuários anônimos (ou seja, desativa edições anônimas para este endereço IP, incluindo edições de contas temporárias).",
"apihelp-block-param-nocreate": "Prevenir a criação de conta.",
- "apihelp-block-param-autoblock": "Bloquear automaticamente o endereço IP usado e quaisquer endereços IPs subsequentes que tentarem acessar a partir deles.",
+ "apihelp-block-param-autoblock": "Bloquear automaticamente o último endereço IP utilizado e quaisquer endereços IP subsequentes a partir dos quais houver tentativa de login.",
"apihelp-block-param-noemail": "Impedir que o usuário envie e-mails através da wiki. (Requer o direito <code>blockemail</code>).",
- "apihelp-block-param-hidename": "Oculta o nome do usuário do ''log'' de bloqueio. (Requer o direito <code>hideuser</code>).",
+ "apihelp-block-param-hidename": "Oculta o nome do usuário dos registros de bloqueio. (Requer o direito <code>hideuser</code>).",
"apihelp-block-param-allowusertalk": "Permitir que o usuário edite sua própria página de discussão (depende de <var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var>).",
- "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado, sobrescrever o bloqueio existente.",
+ "apihelp-block-param-reblock": "Se o usuário já estiver bloqueado por um único bloqueio, sobrescrever o bloqueio existente. Se o usuário estiver com múltiplos bloqueios, isso falhará; utilize o parâmetro <var>id</var> para especificar qual bloqueio será sobrescrito.",
+ "apihelp-block-param-newblock": "Adicionar outro bloqueio mesmo se o usuário já estiver bloqueado.",
"apihelp-block-param-watchuser": "Vigiar as páginas de usuário e de discussão, do usuário ou do endereço IP.",
"apihelp-block-param-watchlistexpiry": "Carimbo de data/hora de expiração da lista de páginas vigiadas. Omita esse parâmetro inteiramente para manter inalterada a validade atual.",
"apihelp-block-param-tags": "As tags de alteração a serem aplicadas à entrada no registro de bloqueio.",
- "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o site.",
- "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).",
- "apihelp-block-param-namespacerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>'partial</var>' (parcial) estiver definido como 'true' (verdadeiro).",
- "apihelp-block-param-actionrestrictions": "Lista de ações para bloquear a execução do usuário. Aplica-se apenas quando <var>parcial</var> é definido como verdadeiro.",
- "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com a razão;",
- "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com a razão e o impedir de criar nova conta e de enviar e-mails.",
+ "apihelp-block-param-partial": "Bloquear o usuário de acessar a páginas ou espaços nominais específicos, em vez de a todo o website.",
+ "apihelp-block-param-pagerestrictions": "Lista de títulos que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-param-namespacerestrictions": "Lista de IDs de espaços nominais que o bloqueio impedirá o usuário de editar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-param-actionrestrictions": "Lista de ações que o bloqueio impedirá o usuário de executar. Só se aplica quando <var>partial</var> estiver definido como true.",
+ "apihelp-block-example-ip-simple": "Bloquear endereço IP <kbd>192.0.2.5</kbd> por três dias com um motivo.",
+ "apihelp-block-example-user-complex": "Bloquear usuário <kbd>Vandal</kbd> indefinidamente com um motivo, e impedi-lo de criar nova conta e de enviar e-mails.",
"apihelp-changeauthenticationdata-summary": "Alterar os dados de autenticação para o usuário atual.",
"apihelp-changeauthenticationdata-example-password": "Tenta alterar a senha do usuário atual para <kbd>ExamplePassword</kbd>.",
"apihelp-changecontentmodel-summary": "Alterar o modelo de conteúdo de uma página",
@@ -88,7 +95,7 @@
"apihelp-checktoken-example-simple": "Testa a validade de um token <kbd>csrf</kbd>.",
"apihelp-clearhasmsg-summary": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.",
"apihelp-clearhasmsg-example-1": "Limpa a etiqueta <code>hasmsg</code> do usuário atual.",
- "apihelp-clientlogin-summary": "Faça o login no wiki usando o fluxo interativo.",
+ "apihelp-clientlogin-summary": "Entrar na wiki usando o fluxo interativo.",
"apihelp-clientlogin-example-login": "Comeca o processo de logar na wiki como usuário <kbd>Exemple</kbd> com a senha <kbd>ExamplePassword</kbd>.",
"apihelp-clientlogin-example-login2": "Continuar efetuando login após uma resposta <samp>UI</samp> para autenticação de dois fatores, fornecendo um <var>OATHToken</var> de <kbd>987654</ kbd>.",
"apihelp-compare-summary": "Obter a diferença entre duas páginas.",
@@ -197,7 +204,7 @@
"apihelp-expandtemplates-param-prop": "Quais peças de informação obter.\n\nNote que se nenhum valor for selecionado, o resultado conterá o texto wiki, mas o resultado será em um formato obsoleto.",
"apihelp-expandtemplates-paramvalue-prop-wikitext": "O texto wiki expandido.",
"apihelp-expandtemplates-paramvalue-prop-categories": "Quaisquer categorias presentes na entrada que não estão representadas na saída wikitext.",
- "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no texto wiki.",
+ "apihelp-expandtemplates-paramvalue-prop-properties": "Propriedades da página definidas por palavras mágicas expandidas no wikitexto.",
"apihelp-expandtemplates-paramvalue-prop-volatile": "Se a saída é volátil e não deve ser reutilizada em outro lugar dentro da página.",
"apihelp-expandtemplates-paramvalue-prop-ttl": "O tempo máximo após o qual os caches do resultado devem ser invalidados.",
"apihelp-expandtemplates-paramvalue-prop-modules": "Quaisquer módulos ResourceLoader que as funções do analisador solicitaram foram adicionados à saída. Contudo, <kbd>jsconfigvars</kbd> ou <kbd>encodedjsconfigvars</kbd> devem ser solicitados em conjunto com <kbd>modules</kbd>.",
@@ -370,13 +377,13 @@
"apihelp-parse-param-prop": "Qual pedaço de informação obter:",
"apihelp-parse-paramvalue-prop-text": "Fornece o texto analisado do texto wiki.",
"apihelp-parse-paramvalue-prop-langlinks": "Fornece os links de idiomas do texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-categories": "Fornece as categorias no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-categorieshtml": "Fornece a versão HTML das categorias.",
"apihelp-parse-paramvalue-prop-links": "Fornece os links internos do texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-templates": "Fornece a predefinição no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-externallinks": "Fornece os links externos no texto wiki analisado.",
- "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-templates": "Fornece as predefinições no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-images": "Fornece as imagens no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-externallinks": "Fornece as ligações externas no wikitexto analisado.",
+ "apihelp-parse-paramvalue-prop-sections": "Fornece as seções no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-revid": "Adiciona o ID da revisão da página analisada.",
"apihelp-parse-paramvalue-prop-displaytitle": "Adiciona o título do texto wiki analisado.",
"apihelp-parse-paramvalue-prop-subtitle": "Adiciona o subtítulo de página da página a que foi feita a análise sintática.",
@@ -386,9 +393,9 @@
"apihelp-parse-paramvalue-prop-jsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página. Para aplicar, use <code>mw.config.set()</code>.",
"apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Fornece as variáveis de configuração JavaScript específicas da página como uma string JSON.",
"apihelp-parse-paramvalue-prop-indicators": "Fornece o HTML de indicadores de ''status'' de página utilizados na página.",
- "apihelp-parse-paramvalue-prop-iwlinks": "Fornece links interwiki no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-iwlinks": "Fornece ligações interwiki no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-wikitext": "Fornece o texto wiki original que foi analisado.",
- "apihelp-parse-paramvalue-prop-properties": "Fornece várias propriedades definidas no texto wiki analisado.",
+ "apihelp-parse-paramvalue-prop-properties": "Fornece propriedades variadas definidas no wikitexto analisado.",
"apihelp-parse-paramvalue-prop-limitreportdata": "Fornece o relatório limite de uma forma estruturada. Não informa dado, quando<var>$1disablelimitreport</var> está definido.",
"apihelp-parse-paramvalue-prop-limitreporthtml": "Retorna a versão HTML do relatório de limite. Não retorna dados quando <var>$1disablelimitreport</var> está definido.",
"apihelp-parse-paramvalue-prop-parsetree": "A árvore de análise XML do conteúdo da revisão (requer modelo de conteúdo <code>$1</code>)",
diff --git a/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php b/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
index c37313376a44..b6d0384fd472 100644
--- a/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
+++ b/includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
@@ -21,10 +21,14 @@
namespace MediaWiki\Auth;
+use BadMethodCallException;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\MainConfigNames;
use MediaWiki\Password\InvalidPassword;
+use MediaWiki\Status\Status;
use MediaWiki\User\UserRigorOptions;
+use StatusValue;
+use stdClass;
use Wikimedia\Rdbms\DBAccessObjectUtils;
use Wikimedia\Rdbms\IConnectionProvider;
use Wikimedia\Rdbms\IDBAccessObject;
@@ -60,8 +64,8 @@ class LocalPasswordPrimaryAuthenticationProvider
* Check if the password has expired and needs a reset
*
* @param string $username
- * @param \stdClass $row A row from the user table
- * @return \stdClass|null
+ * @param stdClass $row A row from the user table
+ * @return stdClass|null
*/
protected function getPasswordResetData( $username, $row ) {
$now = (int)wfTimestamp();
@@ -74,12 +78,12 @@ class LocalPasswordPrimaryAuthenticationProvider
if ( (int)$expiration + $grace < $now ) {
$data = [
'hard' => true,
- 'msg' => \MediaWiki\Status\Status::newFatal( 'resetpass-expired' )->getMessage(),
+ 'msg' => Status::newFatal( 'resetpass-expired' )->getMessage(),
];
} else {
$data = [
'hard' => false,
- 'msg' => \MediaWiki\Status\Status::newFatal( 'resetpass-expired-soft' )->getMessage(),
+ 'msg' => Status::newFatal( 'resetpass-expired-soft' )->getMessage(),
];
}
@@ -112,7 +116,7 @@ class LocalPasswordPrimaryAuthenticationProvider
$oldRow = clone $row;
// Check for *really* old password hashes that don't even have a type
- // The old hash format was just an md5 hex hash, with no type information
+ // The old hash format was just an MD5 hex hash, with no type information
if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) {
$row->user_password = ":B:{$row->user_id}:{$row->user_password}";
}
@@ -161,7 +165,9 @@ class LocalPasswordPrimaryAuthenticationProvider
public function testUserCanAuthenticate( $username ) {
$username = $this->userNameUtils->getCanonical(
- $username, UserRigorOptions::RIGOR_USABLE );
+ $username,
+ UserRigorOptions::RIGOR_USABLE
+ );
if ( $username === false ) {
return false;
}
@@ -176,7 +182,7 @@ class LocalPasswordPrimaryAuthenticationProvider
}
// Check for *really* old password hashes that don't even have a type
- // The old hash format was just an md5 hex hash, with no type information
+ // The old hash format was just an MD5 hex hash, with no type information
if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) {
return true;
}
@@ -186,7 +192,9 @@ class LocalPasswordPrimaryAuthenticationProvider
public function testUserExists( $username, $flags = IDBAccessObject::READ_NORMAL ) {
$username = $this->userNameUtils->getCanonical(
- $username, UserRigorOptions::RIGOR_USABLE );
+ $username,
+ UserRigorOptions::RIGOR_USABLE
+ );
if ( $username === false ) {
return false;
}
@@ -205,12 +213,12 @@ class LocalPasswordPrimaryAuthenticationProvider
// We only want to blank the password if something else will accept the
// new authentication data, so return 'ignore' here.
if ( $this->loginOnly ) {
- return \StatusValue::newGood( 'ignored' );
+ return StatusValue::newGood( 'ignored' );
}
if ( get_class( $req ) === PasswordAuthenticationRequest::class ) {
if ( !$checkData ) {
- return \StatusValue::newGood();
+ return StatusValue::newGood();
}
$username = $this->userNameUtils->getCanonical( $req->username,
@@ -222,7 +230,7 @@ class LocalPasswordPrimaryAuthenticationProvider
->where( [ 'user_name' => $username ] )
->caller( __METHOD__ )->fetchRow();
if ( $row ) {
- $sv = \StatusValue::newGood();
+ $sv = StatusValue::newGood();
if ( $req->password !== null ) {
if ( $req->password !== $req->retype ) {
$sv->fatal( 'badretype' );
@@ -235,12 +243,12 @@ class LocalPasswordPrimaryAuthenticationProvider
}
}
- return \StatusValue::newGood( 'ignored' );
+ return StatusValue::newGood( 'ignored' );
}
public function providerChangeAuthenticationData( AuthenticationRequest $req ) {
- $username = $req->username !== null ?
- $this->userNameUtils->getCanonical( $req->username, UserRigorOptions::RIGOR_USABLE )
+ $username = $req->username !== null
+ ? $this->userNameUtils->getCanonical( $req->username, UserRigorOptions::RIGOR_USABLE )
: false;
if ( $username === false ) {
return;
@@ -279,7 +287,7 @@ class LocalPasswordPrimaryAuthenticationProvider
public function testForAccountCreation( $user, $creator, array $reqs ) {
$req = AuthenticationRequest::getRequestByClass( $reqs, PasswordAuthenticationRequest::class );
- $ret = \StatusValue::newGood();
+ $ret = StatusValue::newGood();
if ( !$this->loginOnly && $req && $req->username !== null && $req->password !== null ) {
if ( $req->password !== $req->retype ) {
$ret->fatal( 'badretype' );
@@ -294,7 +302,7 @@ class LocalPasswordPrimaryAuthenticationProvider
public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) {
if ( $this->accountCreationType() === self::TYPE_NONE ) {
- throw new \BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' );
+ throw new BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' );
}
$req = AuthenticationRequest::getRequestByClass( $reqs, PasswordAuthenticationRequest::class );
@@ -314,7 +322,7 @@ class LocalPasswordPrimaryAuthenticationProvider
public function finishAccountCreation( $user, $creator, AuthenticationResponse $res ) {
if ( $this->accountCreationType() === self::TYPE_NONE ) {
- throw new \BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' );
+ throw new BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' );
}
// Now that the user is in the DB, set the password on it.
diff --git a/includes/composer/ComposerVendorHtaccessCreator.php b/includes/composer/ComposerVendorHtaccessCreator.php
index e2a079b021d5..ef835fa1d898 100644
--- a/includes/composer/ComposerVendorHtaccessCreator.php
+++ b/includes/composer/ComposerVendorHtaccessCreator.php
@@ -40,6 +40,8 @@ class ComposerVendorHtaccessCreator {
return;
}
- file_put_contents( $fname, "Require all denied\n" );
+ file_put_contents( $fname,
+ "Require all denied\n" .
+ "Satisfy All\n" );
}
}
diff --git a/includes/filerepo/ThumbnailEntryPoint.php b/includes/filerepo/ThumbnailEntryPoint.php
index a1ae53359a1b..e8367ada0d2b 100644
--- a/includes/filerepo/ThumbnailEntryPoint.php
+++ b/includes/filerepo/ThumbnailEntryPoint.php
@@ -673,7 +673,7 @@ EOT;
return false;
}
- private function vary( $header ) {
+ private function vary( string $header ) {
$this->varyHeader[] = $header;
}
@@ -823,7 +823,7 @@ EOT;
return false;
}
- private function maybeEnforceRateLimits( File $img, array $params ) {
+ private function maybeEnforceRateLimits( File $img, array $params ): bool {
$authority = $this->getContext()->getAuthority();
$status = PermissionStatus::newEmpty();
diff --git a/includes/filerepo/file/File.php b/includes/filerepo/file/File.php
index 30844f5c2059..d088b87fc001 100644
--- a/includes/filerepo/file/File.php
+++ b/includes/filerepo/file/File.php
@@ -1147,7 +1147,7 @@ abstract class File implements MediaHandlerState {
return $thumbName;
}
- private function adjustThumbWidthForSteps( $params ) {
+ private function adjustThumbWidthForSteps( array $params ): array {
$thumbnailSteps = MediaWikiServices::getInstance()
->getMainConfig()->get( MainConfigNames::ThumbnailSteps );
$thumbnailStepsRatio = MediaWikiServices::getInstance()
@@ -2465,7 +2465,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string HTML
*/
public function getLongDesc() {
$handler = $this->getHandler();
@@ -2477,7 +2477,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string HTML
*/
public function getShortDesc() {
$handler = $this->getHandler();
@@ -2489,7 +2489,7 @@ abstract class File implements MediaHandlerState {
}
/**
- * @return string
+ * @return string plain text
*/
public function getDimensionsString() {
$handler = $this->getHandler();
diff --git a/includes/filerepo/file/FileSelectQueryBuilder.php b/includes/filerepo/file/FileSelectQueryBuilder.php
index a61357b00949..23cf5597a9a1 100644
--- a/includes/filerepo/file/FileSelectQueryBuilder.php
+++ b/includes/filerepo/file/FileSelectQueryBuilder.php
@@ -73,7 +73,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder {
return new FileSelectQueryBuilder( $db, 'archivedfile', $options );
}
- private function initFileOld( $options ) {
+ private function initFileOld( array $options ) {
$this->table( 'image' )
->join( 'actor', 'image_actor', 'actor_id=img_actor' )
->join(
@@ -112,7 +112,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder {
}
}
- private function initFileNew( $options ) {
+ private function initFileNew( array $options ) {
$subquery = $this->newSubquery();
$subquery->table( 'file' )
->join( 'filerevision', null, 'file_latest = fr_id' )
@@ -164,7 +164,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder {
->from( $subquery );
}
- private function initOldFileOld( $options ) {
+ private function initOldFileOld( array $options ) {
$this->table( 'oldimage' )
->join( 'actor', 'oldimage_actor', 'actor_id=oi_actor' )
->join(
@@ -204,7 +204,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder {
}
}
- private function initOldFileNew( $options ) {
+ private function initOldFileNew( array $options ) {
$subquery = $this->newSubquery();
$subquery->table( 'filerevision' )
->join( 'file', null, 'fr_file = file_id' )
@@ -253,7 +253,7 @@ class FileSelectQueryBuilder extends SelectQueryBuilder {
->from( $subquery );
}
- private function initArchivedFile( $options ) {
+ private function initArchivedFile( array $options ) {
$this->table( 'filearchive' )
->join( 'actor', 'filearchive_actor', 'actor_id=fa_actor' )
->join(
diff --git a/includes/filerepo/file/OldLocalFile.php b/includes/filerepo/file/OldLocalFile.php
index 76b624f1d5ad..8e4e7811b217 100644
--- a/includes/filerepo/file/OldLocalFile.php
+++ b/includes/filerepo/file/OldLocalFile.php
@@ -261,7 +261,9 @@ class OldLocalFile extends LocalFile {
}
}
- private function buildQueryBuilderForLoad( IReadableDatabase $dbr, $options = [ 'omit-nonlazy' ] ) {
+ private function buildQueryBuilderForLoad(
+ IReadableDatabase $dbr, array $options = [ 'omit-nonlazy' ]
+ ): FileSelectQueryBuilder {
$queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr, $options );
$queryBuilder->where( [ 'oi_name' => $this->getName() ] )
->orderBy( 'oi_timestamp', SelectQueryBuilder::SORT_DESC );
diff --git a/includes/filerepo/file/UnregisteredLocalFile.php b/includes/filerepo/file/UnregisteredLocalFile.php
index e09923859f0c..1f4ccd182794 100644
--- a/includes/filerepo/file/UnregisteredLocalFile.php
+++ b/includes/filerepo/file/UnregisteredLocalFile.php
@@ -199,7 +199,7 @@ class UnregisteredLocalFile extends File {
return $info['metadata'];
}
- private function getSizeAndMetadata() {
+ private function getSizeAndMetadata(): array {
if ( $this->sizeAndMetadata === null ) {
if ( !$this->getHandler() ) {
$this->sizeAndMetadata = [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
diff --git a/includes/installer/Task/SqliteCreateDatabaseTask.php b/includes/installer/Task/SqliteCreateDatabaseTask.php
index 204a572ed06a..4298db4547e2 100644
--- a/includes/installer/Task/SqliteCreateDatabaseTask.php
+++ b/includes/installer/Task/SqliteCreateDatabaseTask.php
@@ -130,7 +130,9 @@ EOT;
}
}
# Put a .htaccess file in case the user didn't take our advice
- file_put_contents( "$dir/.htaccess", "Require all denied\n" );
+ file_put_contents( "$dir/.htaccess",
+ "Require all denied\n" .
+ "Satisfy All\n" );
return Status::newGood();
}
diff --git a/includes/installer/i18n/zh-hant.json b/includes/installer/i18n/zh-hant.json
index c081e46efc09..0321e4873481 100644
--- a/includes/installer/i18n/zh-hant.json
+++ b/includes/installer/i18n/zh-hant.json
@@ -169,6 +169,8 @@
"config-mysql-engine": "儲存引擎:",
"config-mysql-innodb": "InnoDB(推薦)",
"config-mysql-engine-help": "由於對同時連線有較好的處理能力,<strong>InnoDB</strong> 通常是最佳的選項。\n\n<strong>MyISAM</strong> 只在單人使用或者唯讀作業的情況之下才可能有較快的處理能力。\n相較於 InnoDB,MyISAM 也較容易出現資料損毀的情況。",
+ "config-server": "URL 主機名稱:",
+ "config-server-help": "用於存取您的 wiki 與 URL 的協定和主機名稱部分。自動偵測到的預設值在一般情況下會是正確的。",
"config-site-name": "wiki 的名稱:",
"config-site-name-help": "您所填入的內容會出現在瀏覽器的標題列以及各種其他地方。",
"config-site-name-blank": "請輸入網站名稱。",
@@ -302,6 +304,7 @@
"config-install-updates": "略過執行不需要的更新",
"config-install-updates-failed": "<strong>錯誤:</strong> 插入更新鍵值至資料表失敗,並出現以下錯誤:$1",
"config-install-sysop": "正在建立管理員使用者帳號",
+ "config-install-subscribe": "訂閱 mediawiki-announce",
"config-install-subscribe-fail": "無法訂閱 [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ mediawiki-announce]:$1",
"config-install-subscribe-notpossible": "未安裝 cURL,因此無法使用 <code>allow_url_fopen</code> 設定項目。",
"config-install-subscribe-alreadysubscribed": "您已訂閱 mediawiki-announce。[https://lists.wikimedia.org/postorius/accounts/per-subscription-preferences/ 檢視或變更您的訂閱設定]。",
@@ -315,6 +318,9 @@
"config-install-done-path": "<strong>恭喜!</strong>\n您已經成功安裝MediaWiki。\n\n安裝程式已自動產生 <code>LocalSettings.php</code> 檔案,\n該檔案中包含了您所有的設定項目。\n\n您需要下載該檔案,並將其放置在 <code>$4</code> 中,下載應已自動開始。\n\n若瀏覽器沒有提示您下載,或者您取消了下載,您可以點選下方連結重新下載:\n\n$3\n\n<strong>注意:</strong>如果您現在不下載此檔案,稍後結束安裝程式之後將無法再下載設定檔。\n\n當您完成本步驟後,您可以<strong>[$2 進入您的 wiki]</strong>。",
"config-install-success": "MediaWiki 已安裝成功。您現在可以在 <$1$2> 上檢視您的 wiki。若您有任何問題,請閱讀常見問題清單:<https://www.mediawiki.org/wiki/Manual:FAQ/zh>,或是利用在頁面上所連結的支援論壇之一。",
"config-install-db-success": "資料庫設定成功",
+ "config-install-generic": "正在執行任務「$1」",
+ "config-install-external-domains": "正在建立外部資料庫",
+ "config-skip-shared-domain": "略過可能的共享網域「$1」",
"config-download-localsettings": "下載 <code>LocalSettings.php</code>",
"config-help": "說明",
"config-help-tooltip": "點選以展開",
diff --git a/includes/jobqueue/jobs/ThumbnailRenderJob.php b/includes/jobqueue/jobs/ThumbnailRenderJob.php
index 5fbdb3823bd6..f12532e27a9e 100644
--- a/includes/jobqueue/jobs/ThumbnailRenderJob.php
+++ b/includes/jobqueue/jobs/ThumbnailRenderJob.php
@@ -153,7 +153,7 @@ class ThumbnailRenderJob extends Job {
return false;
}
- private function maybeEnqueueNextPage( $transformParams ) {
+ private function maybeEnqueueNextPage( array $transformParams ) {
if (
( $this->params['enqueueNextPage'] ?? false ) &&
( $transformParams['page'] ?? 0 ) < ( $this->params['pageLimit'] ?? 0 )
diff --git a/includes/libs/ParamValidator/TypeDef/BooleanDef.php b/includes/libs/ParamValidator/TypeDef/BooleanDef.php
index e2ab0cb1460b..19ee271bde00 100644
--- a/includes/libs/ParamValidator/TypeDef/BooleanDef.php
+++ b/includes/libs/ParamValidator/TypeDef/BooleanDef.php
@@ -57,7 +57,7 @@ class BooleanDef extends TypeDef {
);
}
- private function quoteVal( $v ) {
+ private function quoteVal( string $v ): ScalarParam {
return new ScalarParam( ParamType::TEXT, "\"$v\"" );
}
diff --git a/includes/libs/WRStats/LimitBatch.php b/includes/libs/WRStats/LimitBatch.php
index 3758a40fc6e3..b83adb4cffda 100644
--- a/includes/libs/WRStats/LimitBatch.php
+++ b/includes/libs/WRStats/LimitBatch.php
@@ -72,7 +72,7 @@ class LimitBatch {
return $this;
}
- private function queueOp( $type, $entity, $amount ) {
+ private function queueOp( string $type, ?EntityKey $entity, ?int $amount ) {
$amount ??= $this->defaultAmount;
if ( isset( $this->operations[$type] ) ) {
throw new WRStatsError( 'Cannot queue multiple actions of the same type, ' .
diff --git a/includes/libs/filebackend/FSFileBackend.php b/includes/libs/filebackend/FSFileBackend.php
index 1d76f2bf9f5e..92ae176ee7e4 100644
--- a/includes/libs/filebackend/FSFileBackend.php
+++ b/includes/libs/filebackend/FSFileBackend.php
@@ -987,7 +987,8 @@ class FSFileBackend extends FileBackendStore {
* @return string
*/
protected function htaccessPrivate() {
- return "Require all denied\n";
+ return "Require all denied\n" .
+ "Satisfy All\n";
}
/**
diff --git a/includes/libs/filebackend/FileBackendMultiWrite.php b/includes/libs/filebackend/FileBackendMultiWrite.php
index 7be37112be27..7c59e161e396 100644
--- a/includes/libs/filebackend/FileBackendMultiWrite.php
+++ b/includes/libs/filebackend/FileBackendMultiWrite.php
@@ -776,7 +776,7 @@ class FileBackendMultiWrite extends FileBackend {
return $this->backends[$this->masterIndex]->getFileList( $realParams );
}
- private function getFileListForWrite( $params ) {
+ private function getFileListForWrite( array $params ): array {
$files = [];
// Get the list of thumbnails from all backends to allow
// deleting all of them. Otherwise, old thumbnails existing on
diff --git a/includes/libs/http/MultiHttpClient.php b/includes/libs/http/MultiHttpClient.php
index 4aefaf1551b5..ff0bd5f6866c 100644
--- a/includes/libs/http/MultiHttpClient.php
+++ b/includes/libs/http/MultiHttpClient.php
@@ -734,7 +734,7 @@ class MultiHttpClient implements LoggerAwareInterface {
}
}
- private function useReverseProxy( array &$req, $proxy ) {
+ private function useReverseProxy( array &$req, string $proxy ) {
$parsedProxy = parse_url( $proxy );
if ( $parsedProxy === false ) {
throw new InvalidArgumentException( "Invalid reverseProxy configured: $proxy" );
diff --git a/includes/libs/mime/MSCompoundFileReader.php b/includes/libs/mime/MSCompoundFileReader.php
index ff6afe144088..398c2bdd8e9d 100644
--- a/includes/libs/mime/MSCompoundFileReader.php
+++ b/includes/libs/mime/MSCompoundFileReader.php
@@ -170,11 +170,11 @@ class MSCompoundFileReader {
$this->valid = true;
}
- private function sectorOffset( $sectorId ) {
+ private function sectorOffset( int $sectorId ): int {
return $this->sectorLength * ( $sectorId + 1 );
}
- private function decodeClsid( $binaryClsid ) {
+ private function decodeClsid( string $binaryClsid ): string {
$parts = unpack( 'Va/vb/vc/C8d', $binaryClsid );
return sprintf( "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
$parts['a'],
@@ -220,7 +220,7 @@ class MSCompoundFileReader {
return $data;
}
- private function bin2dec( $str, $offset, $length ) {
+ private function bin2dec( string $str, int $offset, int $length ): int {
$value = 0;
for ( $i = $length - 1; $i >= 0; $i-- ) {
$value *= 256;
@@ -229,7 +229,7 @@ class MSCompoundFileReader {
return $value;
}
- private function readOffset( $offset, $length ) {
+ private function readOffset( int $offset, int $length ): string {
$this->fseek( $offset );
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
$block = @fread( $this->file, $length );
@@ -243,7 +243,7 @@ class MSCompoundFileReader {
return $block;
}
- private function readSector( $sectorId ) {
+ private function readSector( int $sectorId ): string {
return $this->readOffset( $this->sectorOffset( $sectorId ), 1 << $this->header['sector_shift'] );
}
@@ -256,7 +256,7 @@ class MSCompoundFileReader {
throw new RuntimeException( $message, $code );
}
- private function fseek( $offset ) {
+ private function fseek( int $offset ) {
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
$result = @fseek( $this->file, $offset );
if ( $result !== 0 ) {
@@ -287,14 +287,14 @@ class MSCompoundFileReader {
}
}
- private function getNextSectorIdFromFat( $sectorId ) {
+ private function getNextSectorIdFromFat( int $sectorId ): int {
$entriesPerSector = intdiv( $this->sectorLength, 4 );
$fatSectorId = intdiv( $sectorId, $entriesPerSector );
$fatSectorArray = $this->getFatSector( $fatSectorId );
return $fatSectorArray[$sectorId % $entriesPerSector];
}
- private function getFatSector( $fatSectorId ) {
+ private function getFatSector( int $fatSectorId ): array {
if ( !isset( $this->fat[$fatSectorId] ) ) {
$fat = [];
if ( !isset( $this->difat[$fatSectorId] ) ) {
diff --git a/includes/libs/mime/XmlTypeCheck.php b/includes/libs/mime/XmlTypeCheck.php
index b86fd406bef7..274bcbaafc4c 100644
--- a/includes/libs/mime/XmlTypeCheck.php
+++ b/includes/libs/mime/XmlTypeCheck.php
@@ -197,7 +197,7 @@ class XmlTypeCheck {
}
}
- private function readNext( XMLReader $reader ) {
+ private function readNext( XMLReader $reader ): bool {
set_error_handler( function ( $line, $file ) {
$this->wellFormed = false;
return true;
@@ -207,7 +207,7 @@ class XmlTypeCheck {
return $ret;
}
- private function validate( $reader ) {
+ private function validate( XMLReader $reader ) {
// First, move through anything that isn't an element, and
// handle any processing instructions with the callback
do {
diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php
index 5edb35bbdd9f..5e73704c6088 100644
--- a/includes/libs/objectcache/BagOStuff.php
+++ b/includes/libs/objectcache/BagOStuff.php
@@ -557,17 +557,6 @@ abstract class BagOStuff implements
}
/**
- * Clear the "last error" registry
- *
- * @since 1.23
- * @deprecated Since 1.38, hard deprecated in 1.43
- */
- public function clearLastError() {
- wfDeprecated( __METHOD__, '1.38' );
- $this->lastError = self::ERR_NONE;
- }
-
- /**
* Set the "last error" registry due to a problem encountered during an attempted operation
*
* @param int $error BagOStuff:ERR_* constant
diff --git a/includes/libs/objectcache/RESTBagOStuff.php b/includes/libs/objectcache/RESTBagOStuff.php
index b2600b908c86..dcc7bd96a2f9 100644
--- a/includes/libs/objectcache/RESTBagOStuff.php
+++ b/includes/libs/objectcache/RESTBagOStuff.php
@@ -226,7 +226,7 @@ class RESTBagOStuff extends MediumSpecificBagOStuff {
[ 'cacheKey' => $key ] );
}
- $this->updateOpStats( self::METRIC_OP_SET, [ $key => [ strlen( $rbody ), 0 ] ] );
+ $this->updateOpStats( self::METRIC_OP_SET, [ $key => [ strlen( $req['body'] ), 0 ] ] );
return $res;
}
diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php
index 925a2709158d..d2661d68bd7a 100644
--- a/includes/libs/objectcache/WANObjectCache.php
+++ b/includes/libs/objectcache/WANObjectCache.php
@@ -194,7 +194,7 @@ class WANObjectCache implements
protected $coalesceScheme;
/** @var TracerInterface */
- private $tracer = null;
+ private $tracer;
/** @var array<int,array> List of (key, UNIX timestamp) tuples for get() cache misses */
private $missLog;
@@ -461,6 +461,7 @@ class WANObjectCache implements
// Also, if no $info parameter is provided, then it doesn't matter how it changes here.
$legacyInfo = ( $info !== self::PASS_BY_REF );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key, $checkKeys );
$now = $this->getCurrentTime();
@@ -521,6 +522,7 @@ class WANObjectCache implements
// Also, if no $info parameter is provided, then it doesn't matter how it changes here.
$legacyInfo = ( $info !== self::PASS_BY_REF );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $keys, $checkKeys );
$curTTLs = [];
@@ -803,6 +805,7 @@ class WANObjectCache implements
* @return bool Success
*/
final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$keygroup = $this->determineKeyGroupForStats( $key );
@@ -1055,6 +1058,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
// Purge values must be stored under the value key so that WANObjectCache::set()
@@ -1116,7 +1120,9 @@ class WANObjectCache implements
* @return float UNIX timestamp
*/
final public function getCheckKeyTime( $key ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
+
return $this->getMultiCheckKeyTime( [ $key ] )[$key];
}
@@ -1182,6 +1188,7 @@ class WANObjectCache implements
* @since 1.31
*/
final public function getMultiCheckKeyTime( array $keys ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $keys );
$checkSisterKeysByKey = [];
@@ -1246,6 +1253,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP );
@@ -1298,6 +1306,7 @@ class WANObjectCache implements
* @return bool True if the item was purged or not found, false on failure
*/
public function resetCheckKey( $key ) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
$checkSisterKey = $this->makeSisterKey( $key, self::TYPE_TIMESTAMP );
@@ -1618,7 +1627,9 @@ class WANObjectCache implements
final public function getWithSetCallback(
$key, $ttl, $callback, array $opts = [], array $cbParams = []
) {
+ /** @noinspection PhpUnusedLocalVariableInspection */
$span = $this->startOperationSpan( __FUNCTION__, $key );
+
$version = $opts['version'] ?? null;
$pcTTL = $opts['pcTTL'] ?? self::TTL_UNCACHEABLE;
$pCache = ( $pcTTL >= 0 )
@@ -1828,6 +1839,7 @@ class WANObjectCache implements
$preCallbackTime = $this->getCurrentTime();
++$this->callbackDepth;
// https://github.com/phan/phan/issues/4419
+ /** @noinspection PhpUnusedLocalVariableInspection */
$value = null;
try {
$value = $callback(
diff --git a/includes/libs/rdbms/ChronologyProtector.php b/includes/libs/rdbms/ChronologyProtector.php
index 5b8783cbe047..95b9fd7d1440 100644
--- a/includes/libs/rdbms/ChronologyProtector.php
+++ b/includes/libs/rdbms/ChronologyProtector.php
@@ -591,7 +591,7 @@ class ChronologyProtector implements LoggerAwareInterface {
$this->wallClockOverride =& $time;
}
- private function marshalPositions( array $positions ) {
+ private function marshalPositions( array $positions ): array {
foreach ( $positions[ self::FLD_POSITIONS ] as $key => $pos ) {
$positions[ self::FLD_POSITIONS ][ $key ] = $pos->toArray();
}
diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php
index 6904f654ead4..4c2e9495d265 100644
--- a/includes/libs/rdbms/database/Database.php
+++ b/includes/libs/rdbms/database/Database.php
@@ -877,7 +877,9 @@ abstract class Database implements Stringable, IDatabaseForOwner, IMaintainableD
return $status;
}
- private function handleErroredQuery( QueryStatus $status, $sql, $fname, $queryRuntime, $priorSessInfo ) {
+ private function handleErroredQuery(
+ QueryStatus $status, Query $sql, string $fname, float $queryRuntime, CriticalSessionInfo $priorSessInfo
+ ): int {
$errflags = self::ERR_NONE;
$error = $status->message;
$errno = $status->code;
diff --git a/includes/libs/rdbms/database/DatabaseMySQL.php b/includes/libs/rdbms/database/DatabaseMySQL.php
index f7afce6b6c6c..2dd9cc904254 100644
--- a/includes/libs/rdbms/database/DatabaseMySQL.php
+++ b/includes/libs/rdbms/database/DatabaseMySQL.php
@@ -808,7 +808,7 @@ class DatabaseMySQL extends Database {
}
}
- private function mysqlRealEscapeString( $s ) {
+ private function mysqlRealEscapeString( $s ): string {
$conn = $this->getBindingHandle();
return $conn->real_escape_string( (string)$s );
diff --git a/includes/libs/rdbms/database/QueryBuilderFromRawSql.php b/includes/libs/rdbms/database/QueryBuilderFromRawSql.php
index 3be8e0e13f69..af268f9cc2e2 100644
--- a/includes/libs/rdbms/database/QueryBuilderFromRawSql.php
+++ b/includes/libs/rdbms/database/QueryBuilderFromRawSql.php
@@ -99,7 +99,7 @@ class QueryBuilderFromRawSql {
);
}
- private static function isWriteQuery( $rawSql ) {
+ private static function isWriteQuery( string $rawSql ): bool {
// Treat SELECT queries without FOR UPDATE queries as non-writes. This matches
// how MySQL enforces read_only (FOR SHARE and LOCK IN SHADE MODE are allowed).
// Handle (SELECT ...) UNION (SELECT ...) queries in a similar fashion.
diff --git a/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php b/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php
index 0f06ee2f4679..ab4b813bfe8c 100644
--- a/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php
+++ b/includes/libs/rdbms/dbal/DoctrineAbstractSchemaTrait.php
@@ -31,7 +31,7 @@ trait DoctrineAbstractSchemaTrait {
private AbstractPlatform $platform;
- private function addTableToSchema( Schema $schema, array $schemaSpec ) {
+ private function addTableToSchema( Schema $schema, array $schemaSpec ): Schema {
$prefix = $this->platform->getName() === 'postgresql' ? '' : '/*_*/';
$table = $schema->createTable( $prefix . $schemaSpec['name'] );
diff --git a/includes/libs/rdbms/expression/LikeValue.php b/includes/libs/rdbms/expression/LikeValue.php
index 58b732927887..95bd801defa5 100644
--- a/includes/libs/rdbms/expression/LikeValue.php
+++ b/includes/libs/rdbms/expression/LikeValue.php
@@ -59,7 +59,7 @@ class LikeValue {
return $dbQuoter->addQuotes( $s ) . ' ESCAPE ' . $dbQuoter->addQuotes( $escapeChar );
}
- private function escapeLikeInternal( $s, $escapeChar = '`' ) {
+ private function escapeLikeInternal( string $s, string $escapeChar = '`' ): string {
return str_replace(
[ $escapeChar, '%', '_' ],
[ "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_" ],
diff --git a/includes/libs/rdbms/lbfactory/LBFactorySimple.php b/includes/libs/rdbms/lbfactory/LBFactorySimple.php
index 4d90b309acd0..80f2eaedd44a 100644
--- a/includes/libs/rdbms/lbfactory/LBFactorySimple.php
+++ b/includes/libs/rdbms/lbfactory/LBFactorySimple.php
@@ -124,7 +124,7 @@ class LBFactorySimple extends LBFactory {
return $lbs;
}
- private function newLoadBalancer( string $clusterName, array $servers ) {
+ private function newLoadBalancer( string $clusterName, array $servers ): ILoadBalancerForOwner {
$lb = new LoadBalancer( array_merge(
$this->baseLoadBalancerParams(),
[
diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php
index 66e3a1ac30b8..db959970be33 100644
--- a/includes/libs/rdbms/loadbalancer/LoadBalancer.php
+++ b/includes/libs/rdbms/loadbalancer/LoadBalancer.php
@@ -233,7 +233,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
$this->defaultGroup = isset( $this->groupLoads[ $group ] ) ? $group : self::GROUP_GENERIC;
}
- private static function newTrackedConnectionsArray() {
+ private static function newTrackedConnectionsArray(): array {
// Note that CATEGORY_GAUGE connections are untracked
return [
self::CATEGORY_ROUND => [],
diff --git a/includes/libs/rdbms/platform/SQLPlatform.php b/includes/libs/rdbms/platform/SQLPlatform.php
index 6ac4f1c07865..a1bc9168485d 100644
--- a/includes/libs/rdbms/platform/SQLPlatform.php
+++ b/includes/libs/rdbms/platform/SQLPlatform.php
@@ -1596,7 +1596,7 @@ class SQLPlatform implements ISQLPlatform {
);
}
- private function scrubArray( $array, $listType = self::LIST_AND ) {
+ private function scrubArray( $array, int $listType = self::LIST_AND ): string {
if ( is_array( $array ) ) {
$scrubbedArray = [];
foreach ( $array as $key => $value ) {
diff --git a/includes/media/DjVuImage.php b/includes/media/DjVuImage.php
index 1ff80602730e..d5a18e75bd6d 100644
--- a/includes/media/DjVuImage.php
+++ b/includes/media/DjVuImage.php
@@ -97,7 +97,7 @@ class DjVuImage {
fclose( $file );
}
- private function dumpForm( $file, $length, $indent ) {
+ private function dumpForm( $file, int $length, int $indent ) {
$start = ftell( $file );
$secondary = fread( $file, 4 );
echo str_repeat( ' ', $indent * 4 ) . "($secondary)\n";
@@ -159,7 +159,7 @@ class DjVuImage {
return $info;
}
- private function readChunk( $file ) {
+ private function readChunk( $file ): array {
$header = fread( $file, 8 );
if ( strlen( $header ) < 8 ) {
return [ false, 0 ];
@@ -169,7 +169,7 @@ class DjVuImage {
return [ $arr['chunk'], $arr['length'] ];
}
- private function skipChunk( $file, $chunkLength ) {
+ private function skipChunk( $file, int $chunkLength ) {
fseek( $file, $chunkLength, SEEK_CUR );
if ( ( $chunkLength & 1 ) && !feof( $file ) ) {
@@ -178,7 +178,7 @@ class DjVuImage {
}
}
- private function getMultiPageInfo( $file, $formLength ) {
+ private function getMultiPageInfo( $file, int $formLength ) {
// For now, we'll just look for the first page in the file
// and report its information, hoping others are the same size.
$start = ftell( $file );
@@ -366,7 +366,7 @@ EOR;
return $json;
}
- private function pageTextCallback( string $match ) {
+ private function pageTextCallback( string $match ): string {
# Get rid of invalid UTF-8
$val = UtfNormal\Validator::cleanUp( stripcslashes( $match ) );
return str_replace( '�', '', $val );
@@ -434,7 +434,7 @@ EOR;
return $result;
}
- private function parseFormDjvu( $line ) {
+ private function parseFormDjvu( string $line ) {
$parentLevel = strspn( $line, ' ' );
$line = strtok( "\n" );
# Find INFO
diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php
index c5ec3c403c8b..cc7a092e3341 100644
--- a/includes/media/MediaHandler.php
+++ b/includes/media/MediaHandler.php
@@ -818,7 +818,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public function getShortDesc( $file ) {
return self::getGeneralShortDesc( $file );
@@ -830,7 +830,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public function getLongDesc( $file ) {
return self::getGeneralLongDesc( $file );
@@ -840,7 +840,7 @@ abstract class MediaHandler {
* Used instead of getShortDesc if there is no handler registered for file.
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public static function getGeneralShortDesc( $file ) {
global $wgLang;
@@ -852,7 +852,7 @@ abstract class MediaHandler {
* Used instead of getLongDesc if there is no handler registered for file.
*
* @param File $file
- * @return string
+ * @return string HTML
*/
public static function getGeneralLongDesc( $file ) {
return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
@@ -882,7 +882,7 @@ abstract class MediaHandler {
* @stable to override
*
* @param File $file
- * @return string Dimensions
+ * @return string Dimensions (plain text)
*/
public function getDimensionsString( $file ) {
return '';
diff --git a/includes/media/SVGReader.php b/includes/media/SVGReader.php
index f988456e2f66..f68f446f1ccb 100644
--- a/includes/media/SVGReader.php
+++ b/includes/media/SVGReader.php
@@ -314,7 +314,7 @@ class SVGReader {
}
}
- private function debug( $data ) {
+ private function debug( string $data ) {
if ( $this->mDebug ) {
wfDebug( "SVGReader: $data" );
}
diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php
index e364bf0ae2b0..971442f2c486 100644
--- a/includes/parser/ParserOptions.php
+++ b/includes/parser/ParserOptions.php
@@ -1099,6 +1099,7 @@ class ParserOptions {
* Get a ParserOptions object from a given user.
* Language will be taken from $wgLang.
*
+ * @since 1.13
* @param UserIdentity $user
* @return ParserOptions
*/
@@ -1109,6 +1110,7 @@ class ParserOptions {
/**
* Get a ParserOptions object from a given user and language
*
+ * @since 1.19
* @param UserIdentity $user
* @param Language $lang
* @return ParserOptions
@@ -1120,6 +1122,7 @@ class ParserOptions {
/**
* Get a ParserOptions object from a IContextSource object
*
+ * @since 1.19
* @param IContextSource $context
* @return ParserOptions
*/
diff --git a/includes/parser/Parsoid/Config/SiteConfig.php b/includes/parser/Parsoid/Config/SiteConfig.php
index 24a6eef635b4..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/recentchanges/ChangeTrackingEventIngress.php b/includes/recentchanges/ChangeTrackingEventIngress.php
index eaa85f53df74..3da2b54f4b35 100644
--- a/includes/recentchanges/ChangeTrackingEventIngress.php
+++ b/includes/recentchanges/ChangeTrackingEventIngress.php
@@ -5,10 +5,12 @@ namespace MediaWiki\RecentChanges;
use LogicException;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Config\Config;
+use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\DomainEvent\DomainEventIngress;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\JobQueue\JobQueueGroup;
+use MediaWiki\JobQueue\Jobs\CategoryMembershipChangeJob;
use MediaWiki\JobQueue\Jobs\RevertedTagUpdateJob;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
@@ -60,7 +62,8 @@ class ChangeTrackingEventIngress
'UserNameUtils',
'TalkPageNotificationManager',
'MainConfig',
- 'JobQueueGroup'
+ 'JobQueueGroup',
+ 'ContentHandlerFactory',
],
'events' => [ // see registerListeners()
PageRevisionUpdatedEvent::TYPE
@@ -75,7 +78,9 @@ class ChangeTrackingEventIngress
private UserNameUtils $userNameUtils;
private TalkPageNotificationManager $talkPageNotificationManager;
private JobQueueGroup $jobQueueGroup;
+ private IContentHandlerFactory $contentHandlerFactory;
private bool $useRcPatrol;
+ private bool $rcWatchCategoryMembership;
public function __construct(
ChangeTagsStore $changeTagsStore,
@@ -86,7 +91,8 @@ class ChangeTrackingEventIngress
UserNameUtils $userNameUtils,
TalkPageNotificationManager $talkPageNotificationManager,
Config $mainConfig,
- JobQueueGroup $jobQueueGroup
+ JobQueueGroup $jobQueueGroup,
+ IContentHandlerFactory $contentHandlerFactory
) {
// NOTE: keep in sync with self::OBJECT_SPEC
$this->changeTagsStore = $changeTagsStore;
@@ -97,8 +103,12 @@ class ChangeTrackingEventIngress
$this->userNameUtils = $userNameUtils;
$this->talkPageNotificationManager = $talkPageNotificationManager;
$this->jobQueueGroup = $jobQueueGroup;
+ $this->contentHandlerFactory = $contentHandlerFactory;
$this->useRcPatrol = $mainConfig->get( MainConfigNames::UseRCPatrol );
+ $this->rcWatchCategoryMembership = $mainConfig->get(
+ MainConfigNames::RCWatchCategoryMembership
+ );
}
public static function newForTesting(
@@ -110,7 +120,8 @@ class ChangeTrackingEventIngress
UserNameUtils $userNameUtils,
TalkPageNotificationManager $talkPageNotificationManager,
Config $mainConfig,
- JobQueueGroup $jobQueueGroup
+ JobQueueGroup $jobQueueGroup,
+ IContentHandlerFactory $contentHandlerFactory
) {
$ingress = new self(
$changeTagsStore,
@@ -121,7 +132,8 @@ class ChangeTrackingEventIngress
$userNameUtils,
$talkPageNotificationManager,
$mainConfig,
- $jobQueueGroup
+ $jobQueueGroup,
+ $contentHandlerFactory
);
$ingress->initSubscriber( self::OBJECT_SPEC );
return $ingress;
@@ -163,12 +175,16 @@ class ChangeTrackingEventIngress
);
}
- if ( $event->isEffectiveContentChange() && !$event->isImplicit() ) {
- $this->updateUserEditTrackerAfterPageUpdated(
- $event->getPerformer()
- );
+ if ( $event->isEffectiveContentChange() ) {
+ $this->generateCategoryMembershipChanges( $event );
+
+ if ( !$event->isImplicit() ) {
+ $this->updateUserEditTrackerAfterPageUpdated(
+ $event->getPerformer()
+ );
- $this->updateNewTalkAfterPageUpdated( $event );
+ $this->updateNewTalkAfterPageUpdated( $event );
+ }
}
if ( $event->isRevert() && $event->isEffectiveContentChange() ) {
@@ -176,6 +192,49 @@ class ChangeTrackingEventIngress
}
}
+ /**
+ * Create RC entries for category changes that resulted from this update
+ * if the relevant config is enabled.
+ * This should only be triggered for actual edits, not reconciliation events (T390636).
+ *
+ * @param PageRevisionUpdatedEvent $event
+ */
+ private function generateCategoryMembershipChanges( PageRevisionUpdatedEvent $event ): void {
+ if ( $this->rcWatchCategoryMembership
+ && !$event->hasCause( PageRevisionUpdatedEvent::CAUSE_UNDELETE )
+ && $this->anyChangedSlotSupportsCategories( $event )
+ ) {
+ // Note: jobs are pushed after deferred updates, so the job should be able to see
+ // the recent change entry (also done via deferred updates) and carry over any
+ // bot/deletion/IP flags, ect.
+ $this->jobQueueGroup->lazyPush(
+ CategoryMembershipChangeJob::newSpec(
+ $event->getPage(),
+ $event->getLatestRevisionAfter()->getTimestamp(),
+ $event->hasCause( PageRevisionUpdatedEvent::CAUSE_IMPORT )
+ )
+ );
+ }
+ }
+
+ /**
+ * Determine whether any slots changed in this update supports categories.
+ * @param PageRevisionUpdatedEvent $event
+ * @return bool
+ */
+ private function anyChangedSlotSupportsCategories( PageRevisionUpdatedEvent $event ): bool {
+ $slotsUpdate = $event->getSlotsUpdate();
+ foreach ( $slotsUpdate->getModifiedRoles() as $role ) {
+ $model = $slotsUpdate->getModifiedSlot( $role )->getModel();
+
+ if ( $this->contentHandlerFactory->getContentHandler( $model )->supportsCategories() ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private function updateChangeTagsAfterPageUpdated( array $tags, int $revId ) {
$this->changeTagsStore->addTags( $tags, null, $revId );
}
diff --git a/includes/specialpage/ContributionsSpecialPage.php b/includes/specialpage/ContributionsSpecialPage.php
index 7a5a43811f0f..dec9fdfe807f 100644
--- a/includes/specialpage/ContributionsSpecialPage.php
+++ b/includes/specialpage/ContributionsSpecialPage.php
@@ -126,6 +126,17 @@ class ContributionsSpecialPage extends IncludableSpecialPage {
* @inheritDoc
*/
public function execute( $par ) {
+ $request = $this->getRequest();
+ $target = $request->getText( 'target' );
+
+ if ( $target !== '' ) {
+ // Update the value in the request so that code reading it
+ // directly form the request gets the trimmed value (T378279).
+ $request->setVal( 'target', trim( $target ) );
+ }
+
+ $target = trim( $par ?? $target );
+
$this->setHeaders();
$this->outputHeader();
$this->checkPermissions();
@@ -144,11 +155,6 @@ class ContributionsSpecialPage extends IncludableSpecialPage {
] );
$this->addHelpLink( 'Help:User contributions' );
- $request = $this->getRequest();
-
- $target = $par ?? $request->getVal( 'target', '' );
- '@phan-var string $target'; // getVal does not return null here
-
// Normalize underscores that may be present in the target parameter
// if it was passed in as a path param, rather than a query param
// where HTMLForm may have already performed preprocessing (T372444).
diff --git a/includes/specials/SpecialBotPasswords.php b/includes/specials/SpecialBotPasswords.php
index e660e09b0dd2..89c33301f0b1 100644
--- a/includes/specials/SpecialBotPasswords.php
+++ b/includes/specials/SpecialBotPasswords.php
@@ -347,7 +347,7 @@ class SpecialBotPasswords extends FormSpecialPage {
return false;
}
- private function save( array $data ) {
+ private function save( array $data ): Status {
$bp = BotPassword::newUnsaved( [
'centralId' => $this->userId,
'appId' => $this->par,
diff --git a/includes/specials/SpecialComparePages.php b/includes/specials/SpecialComparePages.php
index dac7d2be5f94..241dea83c156 100644
--- a/includes/specials/SpecialComparePages.php
+++ b/includes/specials/SpecialComparePages.php
@@ -146,7 +146,7 @@ class SpecialComparePages extends SpecialPage {
}
}
- private function revOrTitle( $revision, $title ) {
+ private function revOrTitle( ?int $revision, ?string $title ): ?int {
if ( $revision ) {
return $revision;
} elseif ( $title ) {
diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php
index 88e548a9e967..0cbbfb371be8 100644
--- a/includes/specials/SpecialDoubleRedirects.php
+++ b/includes/specials/SpecialDoubleRedirects.php
@@ -71,7 +71,7 @@ class SpecialDoubleRedirects extends QueryPage {
return $this->msg( 'doubleredirectstext' )->parseAsBlock();
}
- private function reallyGetQueryInfo( $namespace = null, $title = null ) {
+ private function reallyGetQueryInfo( ?int $namespace = null, ?string $title = null ): array {
$limitToTitle = !( $namespace === null && $title === null );
$retval = [
'tables' => [
diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php
index 526c4ade508a..cdfd5dd707a9 100644
--- a/includes/specials/SpecialImport.php
+++ b/includes/specials/SpecialImport.php
@@ -276,7 +276,7 @@ class SpecialImport extends SpecialPage {
}
}
- private function getMappingFormPart( $sourceName ) {
+ private function getMappingFormPart( string $sourceName ): array {
$defaultNamespace = $this->getConfig()->get( MainConfigNames::ImportTargetNamespace );
return [
'mapping' => [
diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php
index 450c72d30f23..48e34d52cb10 100644
--- a/includes/specials/SpecialLinkSearch.php
+++ b/includes/specials/SpecialLinkSearch.php
@@ -54,7 +54,7 @@ class SpecialLinkSearch extends QueryPage {
private UrlUtils $urlUtils;
- private function setParams( $params ) {
+ private function setParams( array $params ) {
$this->mQuery = $params['query'];
$this->mNs = $params['namespace'];
$this->mProt = $params['protocol'];
diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php
index c4b1cdd800ea..fd7298bea7e3 100644
--- a/includes/specials/SpecialLog.php
+++ b/includes/specials/SpecialLog.php
@@ -321,7 +321,7 @@ class SpecialLog extends SpecialPage {
}
}
- private function getActionButtons( $formcontents ) {
+ private function getActionButtons( string $formcontents ): string {
$canRevDelete = $this->getAuthority()
->isAllowedAll( 'deletedhistory', 'deletelogentry' );
$showTagEditUI = ChangeTags::showTagEditingUI( $this->getAuthority() );
diff --git a/includes/specials/SpecialMovePage.php b/includes/specials/SpecialMovePage.php
index 94e44f0d61e9..2a4000ab2754 100644
--- a/includes/specials/SpecialMovePage.php
+++ b/includes/specials/SpecialMovePage.php
@@ -964,7 +964,7 @@ class SpecialMovePage extends UnlistedSpecialPage {
$this->watchlistManager->setWatch( $this->watch, $this->getAuthority(), $nt );
}
- private function showLogFragment( $title ) {
+ private function showLogFragment( Title $title ) {
$moveLogPage = new LogPage( 'move' );
$out = $this->getOutput();
$out->addHTML( Xml::element( 'h2', null, $moveLogPage->getName()->text() ) );
@@ -1010,7 +1010,9 @@ class SpecialMovePage extends UnlistedSpecialPage {
}
}
- private function showSubpagesList( $subpages, $pagecount, $msg, $truncatedMsg, $noSubpageMsg = false ) {
+ private function showSubpagesList(
+ TitleArrayFromResult $subpages, int $pagecount, string $msg, string $truncatedMsg, bool $noSubpageMsg = false
+ ) {
$out = $this->getOutput();
# No subpages.
diff --git a/includes/specials/SpecialNewPages.php b/includes/specials/SpecialNewPages.php
index 0569d9752df7..eb2fdedb34cb 100644
--- a/includes/specials/SpecialNewPages.php
+++ b/includes/specials/SpecialNewPages.php
@@ -380,7 +380,7 @@ class SpecialNewPages extends IncludableSpecialPage {
$out->addModuleStyles( 'mediawiki.special' );
}
- private function getNewPagesPager() {
+ private function getNewPagesPager(): NewPagesPager {
return new NewPagesPager(
$this->getContext(),
$this->getLinkRenderer(),
diff --git a/includes/specials/SpecialPageLanguage.php b/includes/specials/SpecialPageLanguage.php
index 0272f2c34ce5..57f3d58a284d 100644
--- a/includes/specials/SpecialPageLanguage.php
+++ b/includes/specials/SpecialPageLanguage.php
@@ -308,7 +308,7 @@ class SpecialPageLanguage extends FormSpecialPage {
$this->getOutput()->redirect( $this->goToUrl );
}
- private function showLogFragment( $title ) {
+ private function showLogFragment( string $title ): string {
$moveLogPage = new LogPage( 'pagelang' );
$out1 = Xml::element( 'h2', null, $moveLogPage->getName()->text() );
$out2 = '';
diff --git a/includes/specials/SpecialRandomPage.php b/includes/specials/SpecialRandomPage.php
index 29babcfd45aa..c4049c8380a5 100644
--- a/includes/specials/SpecialRandomPage.php
+++ b/includes/specials/SpecialRandomPage.php
@@ -61,7 +61,7 @@ class SpecialRandomPage extends SpecialPage {
$this->namespaces = [ $ns ];
}
- private function isValidNS( $ns ) {
+ private function isValidNS( $ns ): bool {
return $ns !== false && $ns >= 0;
}
@@ -208,7 +208,7 @@ class SpecialRandomPage extends SpecialPage {
];
}
- private function selectRandomPageFromDB( $randstr, $fname ) {
+ private function selectRandomPageFromDB( $randstr, string $fname ) {
$dbr = $this->dbProvider->getReplicaDatabase();
$query = $this->getQueryInfo( $randstr );
diff --git a/includes/specials/SpecialRenameUser.php b/includes/specials/SpecialRenameUser.php
index 040f19055861..a048d621f797 100644
--- a/includes/specials/SpecialRenameUser.php
+++ b/includes/specials/SpecialRenameUser.php
@@ -251,7 +251,7 @@ class SpecialRenameUser extends SpecialPage {
}
}
- private function getWarnings( $oldName, $newName ) {
+ private function getWarnings( string $oldName, string $newName ): array {
$warnings = [];
$oldUser = $this->userFactory->newFromName( $oldName, $this->userFactory::RIGOR_NONE );
if ( $oldUser && !$oldUser->isTemp() && $oldUser->getBlock() ) {
@@ -264,7 +264,9 @@ class SpecialRenameUser extends SpecialPage {
return $warnings;
}
- private function showForm( $oldName, $newName, $warnings, $reason, $moveChecked, $suppressChecked ) {
+ private function showForm(
+ ?string $oldName, ?string $newName, array $warnings, string $reason, bool $moveChecked, bool $suppressChecked
+ ) {
$performer = $this->getUser();
$formDescriptor = [
diff --git a/includes/specials/SpecialSpecialPages.php b/includes/specials/SpecialSpecialPages.php
index f41939a4e625..68240ac1d3a6 100644
--- a/includes/specials/SpecialSpecialPages.php
+++ b/includes/specials/SpecialSpecialPages.php
@@ -97,7 +97,7 @@ class SpecialSpecialPages extends UnlistedSpecialPage {
return $groups;
}
- private function outputPageList( $groups ) {
+ private function outputPageList( array $groups ) {
$out = $this->getOutput();
// Legend
diff --git a/includes/specials/SpecialStatistics.php b/includes/specials/SpecialStatistics.php
index 9efc01638d93..5ce81a8f2bd5 100644
--- a/includes/specials/SpecialStatistics.php
+++ b/includes/specials/SpecialStatistics.php
@@ -168,7 +168,7 @@ class SpecialStatistics extends SpecialPage {
return $pageStatsHtml;
}
- private function getEditStats() {
+ private function getEditStats(): string {
return Html::rawElement( 'tr', [],
Xml::tags( 'th', [ 'colspan' => '2' ],
$this->msg( 'statistics-header-edits' )->parse()
@@ -184,7 +184,7 @@ class SpecialStatistics extends SpecialPage {
);
}
- private function getUserStats() {
+ private function getUserStats(): string {
return Html::rawElement( 'tr', [],
Xml::tags( 'th', [ 'colspan' => '2' ],
$this->msg( 'statistics-header-users' )->parse()
@@ -210,7 +210,7 @@ class SpecialStatistics extends SpecialPage {
);
}
- private function getGroupStats() {
+ private function getGroupStats(): string {
$linkRenderer = $this->getLinkRenderer();
$lang = $this->getLanguage();
$text = '';
diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php
index 625c82b97b79..159f02d5f9cf 100644
--- a/includes/specials/SpecialTags.php
+++ b/includes/specials/SpecialTags.php
@@ -193,8 +193,8 @@ class SpecialTags extends SpecialPage {
}
private function doTagRow(
- $tag, $hitcount, $showManageActions, $showDeleteActions, $showEditLinks
- ) {
+ string $tag, int $hitcount, bool $showManageActions, bool $showDeleteActions, bool $showEditLinks
+ ): string {
$newRow = '';
$newRow .= Xml::tags( 'td', null, Xml::element( 'code', null, $tag ) );
diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php
index 930c03e39944..e3486316e8ce 100644
--- a/includes/specials/SpecialUndelete.php
+++ b/includes/specials/SpecialUndelete.php
@@ -198,7 +198,7 @@ class SpecialUndelete extends SpecialPage {
return true;
}
- private function loadRequest( $par ) {
+ private function loadRequest( ?string $par ) {
$request = $this->getRequest();
$user = $this->getUser();
@@ -551,7 +551,7 @@ class SpecialUndelete extends SpecialPage {
return true;
}
- private function showRevision( $timestamp ) {
+ private function showRevision( string $timestamp ) {
if ( !preg_match( '/[0-9]{14}/', $timestamp ) ) {
return;
}
@@ -1416,7 +1416,7 @@ class SpecialUndelete extends SpecialPage {
return Xml::tags( 'li', $attribs, $revisionRow ) . "\n";
}
- private function formatFileRow( $row ) {
+ private function formatFileRow( \stdClass $row ): string {
$file = ArchivedFile::newFromRow( $row );
$ts = wfTimestamp( TS_MW, $row->fa_timestamp );
$user = $this->getUser();
diff --git a/includes/specials/SpecialUserRights.php b/includes/specials/SpecialUserRights.php
index 79018cd27eb0..0fa6b38273be 100644
--- a/includes/specials/SpecialUserRights.php
+++ b/includes/specials/SpecialUserRights.php
@@ -291,7 +291,7 @@ class SpecialUserRights extends SpecialPage {
}
}
- private function getSuccessURL() {
+ private function getSuccessURL(): string {
return $this->getPageTitle( $this->mTarget )->getFullURL();
}
diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php
index 6541721995e0..8584e96567dc 100644
--- a/includes/specials/SpecialVersion.php
+++ b/includes/specials/SpecialVersion.php
@@ -1227,7 +1227,7 @@ class SpecialVersion extends SpecialPage {
return implode( "\n", $ret );
}
- private function openExtType( ?string $text = null, ?string $name = null ) {
+ private function openExtType( ?string $text = null, ?string $name = null ): string {
$out = '';
$opt = [ 'class' => 'wikitable plainlinks mw-installed-software' ];
diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php
index 654e545d191e..4b4eb716770b 100644
--- a/includes/specials/SpecialWatchlist.php
+++ b/includes/specials/SpecialWatchlist.php
@@ -849,7 +849,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
$this->setBottomText( $opts );
}
- private function cutoffselector( $options ) {
+ private function cutoffselector( FormOptions $options ): string {
$selected = (float)$options['days'];
$maxDays = $this->getConfig()->get( MainConfigNames::RCMaxAge ) / ( 3600 * 24 );
if ( $selected <= 0 ) {
diff --git a/includes/specials/SpecialWhatLinksHere.php b/includes/specials/SpecialWhatLinksHere.php
index 81c95775248b..e2203c993e18 100644
--- a/includes/specials/SpecialWhatLinksHere.php
+++ b/includes/specials/SpecialWhatLinksHere.php
@@ -493,7 +493,7 @@ class SpecialWhatLinksHere extends FormSpecialPage {
return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) );
}
- private function listItem( stdClass $row, PageIdentity $nt, LinkTarget $target, bool $notClose = false ) {
+ private function listItem( stdClass $row, PageIdentity $nt, LinkTarget $target, bool $notClose = false ): string {
$legacyTitle = $this->titleFactory->newFromPageIdentity( $nt );
if ( $row->rd_from || $row->page_is_redirect ) {
@@ -594,7 +594,7 @@ class SpecialWhatLinksHere extends FormSpecialPage {
return $this->getLanguage()->pipeList( $links );
}
- private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ) {
+ private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ): string {
$navBuilder = new PagerNavigationBuilder( $this->getContext() );
$navBuilder
diff --git a/includes/specials/forms/PreferencesFormOOUI.php b/includes/specials/forms/PreferencesFormOOUI.php
index 6ddf2793d288..e9642a18f263 100644
--- a/includes/specials/forms/PreferencesFormOOUI.php
+++ b/includes/specials/forms/PreferencesFormOOUI.php
@@ -141,7 +141,7 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
return $layout;
}
- private function isMobileLayout() {
+ private function isMobileLayout(): bool {
if ( $this->useMobileLayout === null ) {
$skin = $this->getSkin();
$this->useMobileLayout = false;
diff --git a/includes/specials/pagers/AllMessagesTablePager.php b/includes/specials/pagers/AllMessagesTablePager.php
index c06fdf99443e..01d9626f2b93 100644
--- a/includes/specials/pagers/AllMessagesTablePager.php
+++ b/includes/specials/pagers/AllMessagesTablePager.php
@@ -124,7 +124,7 @@ class AllMessagesTablePager extends TablePager {
}
}
- private function getAllMessages( $descending ) {
+ private function getAllMessages( bool $descending ): array {
$messageNames = $this->localisationCache->getSubitemList( 'en', 'messages' );
// Normalise message names so they look like page titles and sort correctly - T86139
diff --git a/includes/specials/pagers/ImageListPager.php b/includes/specials/pagers/ImageListPager.php
index 0830e316e89e..7e78f7087bfc 100644
--- a/includes/specials/pagers/ImageListPager.php
+++ b/includes/specials/pagers/ImageListPager.php
@@ -190,7 +190,7 @@ class ImageListPager extends TablePager {
return $conds + $this->mQueryConds;
}
- private function buildQueryConds() {
+ private function buildQueryConds(): array {
$conds = [
'file_deleted' => 0,
'fr_deleted' => 0,
diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php
index 48a8f1240273..17f97ee56fba 100644
--- a/includes/upload/UploadBase.php
+++ b/includes/upload/UploadBase.php
@@ -918,7 +918,7 @@ abstract class UploadBase {
return $warnings;
}
- private function checkLocalFileWasDeleted( LocalFile $localFile ) {
+ private function checkLocalFileWasDeleted( LocalFile $localFile ): bool {
return $localFile->wasDeleted() && !$localFile->exists();
}
diff --git a/includes/upload/UploadFromChunks.php b/includes/upload/UploadFromChunks.php
index 85a486dbeaf7..d484a3516022 100644
--- a/includes/upload/UploadFromChunks.php
+++ b/includes/upload/UploadFromChunks.php
@@ -449,7 +449,7 @@ class UploadFromChunks extends UploadFromFile {
return $storeStatus;
}
- private function getChunkFileKey( $index = null ) {
+ private function getChunkFileKey( ?int $index = null ): string {
return $this->mFileKey . '.' . ( $index ?? $this->getChunkIndex() );
}
diff --git a/languages/.htaccess b/languages/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/languages/.htaccess
+++ b/languages/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/languages/i18n/en.json b/languages/i18n/en.json
index 718828a07c33..b75303234f09 100644
--- a/languages/i18n/en.json
+++ b/languages/i18n/en.json
@@ -2655,9 +2655,11 @@
"block-confirm-yes": "Yes",
"block-confirm-no": "No",
"block-submit": "Submit",
- "block-success": "[[Special:Contributions/$1|{{GENDER:$1|$1}}]] has been blocked. See the [[Special:BlockList|block list]] to review blocks.",
+ "block-success": "[[Special:Contributions/$1|{{GENDER:$1|$1}}]] ([[{{ns:user_talk}}:$1|talk]]) has been blocked. See the [[Special:BlockList|block list]] to review blocks.",
"block-user-active-blocks": "Active blocks",
"block-user-no-active-blocks": "No active blocks found",
+ "block-user-active-range-blocks": "Active range blocks",
+ "block-user-no-active-range-blocks": "No active range blocks found",
"block-user-previous-blocks": "Block log",
"block-user-no-previous-blocks": "No previous blocks found",
"block-user-label-count-exceeds-limit": "$1+",
@@ -2679,6 +2681,7 @@
"block-javascript-required": "JavaScript is required to use the Special:Block interface.",
"block-removed": "Block has been removed.",
"block-reblock-multi-legacy": "This user is blocked multiple times already and cannot be reblocked with this form.",
+ "block-view-target": "View",
"unblockip": "Unblock user",
"unblockiptext": "Use the form below to restore write access to a previously blocked IP address or username.",
"unblock-target": "Unblock target",
diff --git a/languages/i18n/preferences/nia.json b/languages/i18n/preferences/nia.json
index 34cdac36382a..7d703aa975a7 100644
--- a/languages/i18n/preferences/nia.json
+++ b/languages/i18n/preferences/nia.json
@@ -4,7 +4,30 @@
"Slaia"
]
},
+ "preferences": "Preferensi",
+ "prefsnologintext2": "Ae bakha ena'ö tola ö'atulö'ö preferensi khöu.",
+ "prefsnologintext2-for-temp-user": "Fazökhi akun ena'ö tola ö'a'azökhi preferensi.",
+ "searchprefs": "Alui preferensi",
+ "searchprefs-noresults": "Lö nisöndra",
+ "searchprefs-results": "$1 {{PLURAL:$1|nisöndra}}",
+ "saveprefs": "Irö'ö",
+ "tooltip-preferences-save": "Irö'ö preferensi",
+ "savedprefs": "No te'irö'ö khöu preferensi.",
+ "prefs-back-title": "Mangawuli ba preferensi",
+ "prefs-tabs-navigation-hint": "Kie: Tola ö'oguna'ö dandra nono wana ba we'amöi ba tab tanö bö'ö si so ba gangolifa tab.",
+ "prefs-sections-navigation-hint": "Kie: Tola ö'oguna'ö hagu Tab awö Shift+Tab ba we'amöi ba ngawalö waosatö preferensi.",
+ "prefs-personal": "Profil zangoguna",
+ "prefs-description-personal": "Atulö'ö hewisa oroma, tefakhai ba fahuhuo ndra'ugö.",
+ "prefs-info": "Informasi danefa",
+ "username": "{{GENDER:$1|Töi zangoguna}}:",
+ "prefs-memberingroups": "{{GENDER:$2|Sifao awö}} ba {{PLURAL:$1|grup}}:",
+ "group-membership-link-with-expiry": "$1 (irege $2)",
+ "prefs-edits": "Fa'oya nibulö'ö:",
+ "prefs-registration": "Inötö tefasura'ö döi:",
+ "yourrealname": "Töi sindruhu:",
+ "prefs-help-realname": "Tola so tola lö'ö döi sindruhu.\nNa öbe döimö sindruhu, tola dania latötöi döimö ba ngawalö kontribusi si no öfalua.",
"yourpassword": "Ono kusi:",
+ "prefs-resetpass": "Falali nonokusi",
"passwordtooshort": "Si lö'ö-lö'önia so {{PLURAL:$1|1 hurufo|$1 hurufo}} nono kusi.",
"passwordtoolong": "Ono kusi tebai töra moroi {{PLURAL:$1|1 hurufo|$1 hurufo}}.",
"password-substring-username-match": "Böi te'oroma'ö nono kusi-mö ba döi zangoguna khöu.",
@@ -13,46 +36,164 @@
"passwordincommonlist": "Ato zangoguna'ö ono kusi andrö khöu. Alui nono kusi bö'ö.",
"prefs-help-yourpassword": "Famangawuli akun aktif. Faigi setelan onönöta ba $1",
"tog-prefershttps": "Oguna'ö ''secure connection'' götö we'aso bakha ba gu'ö",
+ "prefs-help-prefershttps": "Awena dania mufasa preferensi da'a ba ginötö möi'ö zui bakha dania.",
+ "prefs-user-downloaddata-label": "Faigi data akun:",
+ "prefs-user-downloaddata-info": "Data akun khögu ba proyek andre",
+ "prefs-user-restoreprefs-label": "Fangawuli furi stelan:",
+ "prefs-user-restoreprefs-info": "Fangawuli furi ba preferensi si föföna (ba fefu waosatö)",
+ "prefs-i18n": "Internasionalisasi",
+ "yourlanguage": "Li:",
+ "yourgender": "Hewisa lakaoni khöu?",
+ "gender-notknown": "Labulö'ö nga'örö wiki",
+ "gender-unknown": "Ba ginötö latötöi ndra'ugö, heza tola, i'oguna'ö ngawua wehede netral molo'ö jender software andre",
+ "gender-female": "Ibulö'ö nga'örö wiki",
+ "gender-male": "Ibulö'ö nga'örö wiki",
+ "prefs-help-gender": "Tola te'atulö'ö ma zui lö'ö preferensi andre.\nI'oguna'ö ni'atulö'ö bakha ba da'ö dania software andre ba wanötöi ya'ugö ma zui niha bö'ö si faudu ba jender.\nOroma dania ba zato informasi andre.",
+ "yourvariant": "Variasi li nösi:",
+ "prefs-help-variant": "Varian ma zui ortografi ni'atulö'ömö ba wangoroma'ö nösi nga'örö wiki andre.",
+ "prefs-signature": "Tandratanga",
"tog-oldsig": "Tandra tanga si no so:",
+ "yournick": "Tandratanga si bohou:",
"tog-fancysig": "Sura dandra tanga hulö teks wiki (lö khai-khai otomatis)",
- "tog-requireemail": "Fa'ohe'ö surel wamulö'ö nono kusi (password) ha na so alamat surel ba töi zangoguna.",
+ "prefs-help-signature": "Fanemali ba nga'örö wahuhuosa silötolalö'ö mutandratangaisi faoma \"<nowiki>~~~~</nowiki>\", sedöna dania tobali tandratangamö ba inötö tesura ia.",
+ "prefs-signature-invalid-warning": "Tandratanga khö tola fatundraha ba mato ha'uga wakake.",
+ "prefs-signature-invalid-new": "Lö atulö dandratanga si no so khöu. Lö tola ö'oguna'ö ia irege ö'atulö'ö ia.",
+ "prefs-signature-invalid-disallow": "Lö atulö dandratanga si no so khöu. Na lö ö'atulö'ö ia, ba te'oguna'ö manö dania dandratanga si föföna so ba ginötö latandratangaisi wanemali.",
+ "prefs-signature-highlight-error": "Oroma'ö heza alua zi fasala",
+ "prefs-signature-error-details": "Faigi gonönötania",
+ "badsig": "Tandratanga si so zi fasala.\nFareso kode HTML.",
+ "badsiglength": "Anau sibai dandratanga khöu.\nTebai töra ia moroi ba $1 {{PLURAL:$1|hurufo}}.",
+ "badsigsubst": "No oi faruka dandratanga andre khöu (duma-dumania <code>subst:</code> ma <code><nowiki>~~~~</nowiki></code>).",
+ "badsightml": "So kode HTML si lö atulö bakha ba dandratangamö andre:",
+ "badsiglinks": "Silötolalö'ö so khai-khai ba nga'örö zangoguna khöu ma zui ba kontribusi bakha ba dandratanga. Nönö ia, duma-dumania: <code>$1</code>.",
+ "badsiglinebreak": "Silötolalö'ö ha sara khoi teks wiki manö dandratanga.",
+ "prefs-email": "Opsi sura elektronis",
+ "youremail": "Email:",
+ "prefs-setemail": "Sura alamat email",
+ "prefs-changeemail": "Falali ma zui heta alamat email",
+ "prefs-help-email": "Tola so tola lö'ö alamat email, bahiza moguna ia na ö'andrö ena'ö nonokusi (password) sibohou, ba ginötö olifu ndra'ugö nonokusi si no so.",
+ "prefs-help-email-required": "Moguna mube'e alamat email.",
+ "prefs-help-email-others": "Tola göi öbe ena'ö lafa'ohe'ö khöu duria ira sangoguna bö'ö faoma khai-khai si so ba nga'örö huhuo zangoguna khöu. Lö sa te'oroma'ö khöra alamat emailmö.",
+ "tog-requireemail": "Fa'ohe'ö surel wamulö'ö nono kusi (password) ha na so alamat surel awö döi zangoguna.",
+ "prefs-help-requireemail": "Moguna da'a ena'ö terorogö privasi ba teba'agö email si lö faudu.",
"noemailprefs": "Be alamat email bakha ba preferensi ena'ö tola te'oguna'ö fitur andrö.",
"emailnotauthenticated": "Lö nasa tefaduhu'ö alamat email-mö.\nBörö da'ö lö tefa'ohe'ö khöu duria sanandrösa ba da'e tou.",
"emailconfirmlink": "Faduhu'ö alamat email-mö",
+ "prefs-emailconfirm-label": "Faduhu'ö email",
"emailauthenticated": "No tefaduhu'ö alamat email-mö ba $2 bözi $3.",
+ "allowemail": "Tehegö lafa'ohe'ö khögu email sangoguna bö'ö",
+ "email-allow-new-users-label": "Tehegö email moroi ba zangoguna sibohou sibai",
+ "prefs-help-email-allow-new-users": "Na auri da'a, tola dania lafa'ohe'ö khöu email ya'ira sangoguna si lö otomatis [[{{MediaWiki:grouppage-autoconfirmed}}|tefaduhugö]].",
"tog-ccmeonemails": "Fa'ohe'ö khögu kopi surel nifa'ohe'ögu khö niha bö'ö",
+ "email-mutelist-label": "Böi tehegö ndra sangoguna andre ba wama'ohe'ö khögu email:",
"tog-enotifwatchlistpages": "Faohe'ö khögu surel na tebulö sambua nga'örö ma berkas si so ba gangolita nihörögögu",
"tog-enotifusertalkpages": "Fa'ohe'ö khögu surel na labulö'ö nga'örö wahuhuosa khögu",
"tog-enotifminoredits": "Fa'ohe'ö göi khögu surel ba wamulö'ö side-ide",
"tog-enotifrevealaddr": "Oroma'ö alamat surelgu ba notifikasi surel",
+ "prefs-user-pages": "Nga'örö zangoguna",
+ "prefs-rendering": "Khala-khala",
+ "prefs-description-rendering": "Atulö'ö guli, fa'ebolo, ba lala wombaso.",
+ "prefs-skin": "Uli",
+ "skin-preview": "Khala-khala",
+ "prefs-common-config": "CSS/JavaScript si faoma te'oguna'ö ba fefu guli:",
+ "prefs-custom-css": "CSS nifa'anö samösa",
+ "prefs-custom-js": "JavaScript nifa'anö samösa",
+ "prefs-custom-cssjs-safemode": "Lö hadöi CSS/JavaScript nifa'anö samösa börö auri iada'a \"safe mode\". Faigi hewisa [[#mw-input-wpforcesafemode|wamunu \"safe mode\"]] ena'ö tola auri CSS/JavaScript nifa'anö samösa.",
+ "prefs-skin-prefs": "Preferensi guli",
+ "prefs-skin-responsive": "Orifi mode responsif",
+ "prefs-help-skin-responsive": "Adaptasi layout ba wa'ebolo layar HP.",
+ "prefs-dateformat": "Format ginötö",
+ "datedefault": "Lö preferensi",
+ "prefs-timeoffset": "Inötö otalua",
+ "servertime": "Inötö server:",
+ "localtime": "Inötö lokal:",
+ "timezonelegend": "Zona ginötö:",
+ "timezoneuseserverdefault": "Oguna'ö zi tohöna ba wiki ($1)",
+ "timezoneuseoffset": "Bö'önia (otalua ginötö te'erai moroi ba UTC)",
+ "timezone-useoffset-placeholder": "Duma-dumania: \"-07:00\" or \"01:00\"",
+ "timezone-invalid": "No fasala zona ginötö ma otalua ginötö.",
+ "guesstimezone": "Fo'ösi moroi ba mbolokha gu'ö (browser)",
+ "timezoneregion-africa": "Afrika",
+ "timezoneregion-america": "Amerika",
+ "timezoneregion-antarctica": "Antartika",
+ "timezoneregion-arctic": "Arktik",
+ "timezoneregion-asia": "Asia",
+ "timezoneregion-atlantic": "Samudra Atlantik",
+ "timezoneregion-australia": "Australia",
+ "timezoneregion-europe": "Europa",
+ "timezoneregion-indian": "Samudera Hindia",
+ "timezoneregion-pacific": "Samudera Pasifik",
+ "prefs-files": "Berkas",
+ "imagemaxsize": "Fondrege wa'ebua gambara ba nga'örö wamotokhi:",
+ "thumbsize": "Fa'ebua gambara side-ide:",
+ "prefs-diffs": "Sifabö'ö",
"tog-diffonly": "Böi oroma'ö nösi nga'örö tou diffs",
"tog-norollbackdiff": "Böi oroma'ö diff aefa tefalua ''rollback''",
+ "prefs-advancedrendering": "Opsi bö'ö",
"tog-underline": "Lala wanura khai-khai",
"underline-default": "Uli ma default wuka gu'ö",
"underline-never": "Lö'ö",
"underline-always": "Selalu",
"tog-showhiddencats": "Oroma'ö kategori si tobini",
"tog-showrollbackconfirmation": "Oroma'ö konfirmasi na tehöndrögö khai-khai ''rollback''",
+ "tog-forcesafemode": "Lö mamalö orifi [[mw:Manual:Safemode|\"safe mode\"]]",
+ "prefs-help-forcesafemode": "Bunu skip awö lembar gaya ba wiki",
+ "prefs-editing": "Mamulö'ö",
+ "prefs-description-editing": "Atulö'ö hewisa öfalua, ö'osisi'ö ba öfaigi wamulö'ö.",
+ "prefs-advancedediting": "Opsi umum",
"tog-editsectiononrightclick": "Be tola mubulö'ö seksi faoma fangöhöndrögö kambölö ba högö seksi",
"tog-editondblclick": "Bulö'ö nga'örö na mendrua lahöndrögö",
+ "prefs-editor": "Samulö'ö",
"editfont-style": "Ngawalö hurufo nahia wamulö'ö",
"editfont-monospace": "Hurufo ''monospace''",
"editfont-sansserif": "Hurufo ''sans-serif''",
"editfont-serif": "Hurufo ''serif''",
"tog-minordefault": "Tandrai fefu nibulö'ö otomatis side-ide",
"tog-forceeditsummary": "Oroma'ö khögu na urugi nahia wanura fangadogo'ö si lö ösi (ma zui sura fangadogo'ö default)",
+ "tog-editrecovery": "Orifi fitur [[Special:EditRecovery|{{int:editrecovery}}]]",
+ "tog-editrecovery-help": "Tola öbe'e zöndrau ba [$1 nga'örö wahuhuosa proyek].",
"tog-useeditwarning": "Oroma'ö na uröi sambua nga'örö si so nibulö'ö si lö ni'irö'ö",
+ "prefs-preview": "Khala-khala",
"tog-previewonfirst": "Oroma'ö khala-khala na tebörögö wamulö'ö",
"tog-previewontop": "Oroma'ö khala-khala föna kota bulö'ö",
"tog-uselivepreview": "Oroma'ö khala-khala ba böi fuli halö nga'örö",
+ "prefs-discussion": "Nga'örö wahuhuosa",
+ "prefs-developertools": "Fakake ndra samazökhi software",
+ "prefs-rc": "Safuria tebulö",
+ "prefs-description-rc": "Atulö'ö gangolifa zafuria tebulö.",
+ "prefs-displayrc": "Opsi wangoroma'ö",
+ "recentchangesdays": "Fa'ara zafuria tebulö sedöna mu'oroma'ö:",
+ "recentchangesdays-max": "Fondrege ara $1 {{PLURAL:$1|ngaluo}}",
+ "recentchangescount": "Fa'oya wamulö'ö ni'oroma'ö ba zafuria tebulö, waö-waö nga'örö ba ba lahe wamulö'ö molo'ö standar:",
+ "prefs-help-recentchangescount": "Fondrege wa'oya: 1000",
+ "prefs-advancedrc": "Opsi bö'ö",
"tog-usenewrc": "Sifabö'ö molo'ö nga'örö ba zifaö'ö faoma nihörögö safuria",
+ "rcfilters-preference-label": "Oguna'ö wangoroma'ö si lö JavaScript",
+ "rcfilters-preference-help": "Oroma'ö [[{{#special:RecentChanges}}|zafuria tebulö]] awö [[{{#special:RecentChangesLinked}}|si tebulö si fakhai]] si lö mutafi ma zui mutandrai.",
+ "prefs-changesrc": "Si tebulö ni'oroma'ö",
"tog-hideminor": "Bini'ö wamulö'ö side-ide moroi ba zifabö'ö safuria",
"tog-hidecategorization": "Bini'ö kategorisasi nga'örö",
"tog-hidepatrolled": "Bini'ö nibulö'ö nitandrai mufareso moroi ba zifabö'ö safuria",
"tog-newpageshidepatrolled": "Bini'ö nga'örö nitandrai mufareso moroi ba gangolifa nga'örö si bohou",
"tog-shownumberswatching": "Oroma'ö wa'oya zangoguna sangohörögö",
+ "prefs-watchlist": "Nihörögö",
+ "prefs-description-watchlist": "Atulö'ö gangolifa nga'örö somasi'ö öhörögö.",
+ "prefs-editwatchlist": "Bulö'ö gangolifa nihörögö",
+ "prefs-editwatchlist-label": "Bulö'ö nösi si so ba gangolifa nihörögö khöu:",
+ "prefs-editwatchlist-edit": "Faigi ba heta nga'örö si so ba gangolifa nihörögö khöu",
+ "prefs-editwatchlist-raw": "Bulö'ö gangolifa nihörögö si lö na ö'ohalöŵögöigö",
+ "prefs-editwatchlist-clear": "Heta fefu nösi gangolita nihörögö khöu",
+ "prefs-displaywatchlist": "Opsi wamongoroma'ö",
+ "prefs-watchlist-days": "Ndrege wa'oya ngaluo ni'oroma'ö ba gangolifa nihörögö",
+ "prefs-watchlist-days-max": "Fondregenia $1 {{PLURAL:$1|ngaluo}}",
+ "prefs-watchlist-edits": "Ndrege wa'oya wamulö'ö ni'oroma'ö ba gangolita nihörögö:",
+ "prefs-watchlist-edits-max": "Fondrege wa'oya: 1000",
+ "prefs-advancedwatchlist": "Opsi bö'ö",
"tog-extendwatchlist": "Oroma'ö fefu zifabö'ö, tenga ha safuria",
"tog-watchlistunwatchlinks": "Nönö dandra wangöhörögö ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) ena'ö te'oroma'ö nga'örö si no tebulö (moguna te'orifi JavaScript)",
+ "rcfilters-watchlist-preference-label": "Oguna'ö wangoroma'ö si lö JavaScript",
+ "rcfilters-watchlist-preference-help": "Oroma'ö [[{{#special:Watchlist}}|nga'örö nihörögö]] si lö mutafi'ö ma zui mutandrai.",
+ "prefs-changeswatchlist": "Si tebulö ni'oroma'ö",
"tog-watchlisthideminor": "Bini'ö nibulö'ö side-ide moroi ba gangolita nihörögö",
"tog-watchlisthidebots": "Bini'ö nibulö'ö robot moroi ba gangolita nihörögö",
"tog-watchlisthideown": "Bini'ö nibulö'ögu moroi ba gangolita nihörögö",
@@ -61,10 +202,27 @@
"tog-watchlistreloadautomatically": "Fuli halö gangolita nihörögö ero tebulö wanafi (moguna JavaScript)",
"tog-watchlisthidecategorization": "Bini'ö kategorisasi nga'örö",
"tog-watchlisthidepatrolled": "Bini'ö nibulö'ö si no mutandrai moroi ba gangolita nihörögö",
+ "prefs-pageswatchlist": "Nga'örö nihörögö",
"tog-watchdefault": "Nönögö nga'örö ba berkas nibulö'ögu ba gangolita nihörögögu",
"tog-watchmoves": "Nönögö nga'örö ba berkas nifawu'agu ba gangolita nihörögögu",
"tog-watchdeletion": "Nönögö nga'örö ba berkas nitibo'ögu ba gangolita nihörögögu",
"tog-watchcreations": "Nönögö nga'örö nifa'anögu ba berkas nifairögu ba gangolita nihörögö",
"tog-watchuploads": "Nönögö berkas si bohou ufairö ba gangolita nihörögögu",
- "tog-watchrollback": "Nönögö nga'örö nifangawuligögu (''rollback'') ba gangolita nihörögögu"
+ "tog-watchrollback": "Nönögö nga'örö nifangawuligögu (''rollback'') ba gangolita nihörögögu",
+ "prefs-tokenwatchlist": "Token",
+ "prefs-watchlist-token": "Token gangolita nihörögö:",
+ "prefs-watchlist-managetokens": "Atulö'ö token",
+ "prefs-help-tokenmanagement": "Tola öfaigi ba öfangawuli furi nonokusi akun-mö ena'ö tola öfaigi nga'örö gu'ö si so ba gangolita nihörögö khöu. Böi ombakha'ö nonokusi andrö, ena'ö böi la'ila lafaigi gangolita nihörögö khöu niha bö'ö.",
+ "prefs-searchoptions": "Alui",
+ "prefs-description-searchoptions": "Atulö'ö hewisa tefalua wamo'ösi otomatis awö lua-luania.",
+ "prefs-searchmisc": "Umum",
+ "searchlimit-label": "Fa'oya nisöndra ni'oroma'ö ero nga'örö:",
+ "searchlimit-help": "Fondrege wa'oya: $1",
+ "search-thumbnail-extra-namespaces-label": "Oroma'ö gambara side-ide ba Special:Search on Desktop",
+ "search-thumbnail-extra-namespaces-message": "Oroma'ö gambara side-ide $1 {{PLURAL:$2|ruang nama}} ba nga'örö {{#special:search}}",
+ "prefs-advancedsearchoptions": "Opsi bö'ö",
+ "prefs-misc": "Tanöbö'önia",
+ "prefs-reset-intro": "Ba nga'örö andre tola öfangawuli furi preferensi khöu ba wangatulö'ö si tohöna",
+ "prefs-reset-confirm": "Lau, omasido ufangawuli furi preferensi khögu.",
+ "restoreprefs": "Fangawuli fefu ba stelan si tohöna"
}
diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json
index 086df3bd8bcd..ab1f44781fb6 100644
--- a/languages/i18n/qqq.json
+++ b/languages/i18n/qqq.json
@@ -2925,6 +2925,8 @@
"block-success": "Message shown when a block is successfully applied on [[Special:Block]]. Similar to {{msg-mw|blockipsuccesstext}} except only basic wikitext is allowed.\n\nParameters:\n* $1 - target username",
"block-user-active-blocks": "Header for the active blocks table",
"block-user-no-active-blocks": "Message shown when there are no active blocks for the user",
+ "block-user-active-range-blocks": "Header for the 'Active range blocks' table shown at [[Special:Block]].",
+ "block-user-no-active-range-blocks": "Message shown at [[Special:Block]] when there are no active range blocks for an IP address.",
"block-user-previous-blocks": "Header for the previous blocks table",
"block-user-no-previous-blocks": "Message shown when there are no previous block log entries for the user",
"block-user-label-count-exceeds-limit": "{{optional}}\nLabel displaying the total number of blocks or block events, with a '+' sign to indicate additional entries.\n\nParameters:\n* $1 - total number of blocks or log events",
@@ -2946,6 +2948,7 @@
"block-javascript-required": "Error message shown when JavaScript is disabled on [[Special:Block]]",
"block-removed": "Success message shown after a block has been removed.",
"block-reblock-multi-legacy": "Error message shown on [[Special:Block]].",
+ "block-view-target": "Message for link to load an IP range in the [[Special:Block]] interface.\n{{Identical|View}}",
"unblockip": "Used as title and legend for the form in [[Special:Unblock]].",
"unblockiptext": "Used in the {{msg-mw|Unblockip}} form on [[Special:Unblock]].",
"unblock-target": "Used as a title and legend for the form in [[Special:Unblock]]",
diff --git a/maintenance/.htaccess b/maintenance/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/maintenance/.htaccess
+++ b/maintenance/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/maintenance/SqliteMaintenance.php b/maintenance/SqliteMaintenance.php
index da9a3ab59485..609c4cc18387 100644
--- a/maintenance/SqliteMaintenance.php
+++ b/maintenance/SqliteMaintenance.php
@@ -109,7 +109,7 @@ class SqliteMaintenance extends Maintenance {
}
}
- private function backup( DBConnRef $dbw, $fileName ) {
+ private function backup( DBConnRef $dbw, string $fileName ) {
$this->output( "Backing up database:\n Locking..." );
$dbw->query( 'BEGIN IMMEDIATE TRANSACTION', __METHOD__ );
$ourFile = $dbw->__call( 'getDbFilePath', [] );
diff --git a/maintenance/benchmarks/benchmarkJsonCodec.php b/maintenance/benchmarks/benchmarkJsonCodec.php
index 7ab1d0bd67b4..d5ec449955fe 100644
--- a/maintenance/benchmarks/benchmarkJsonCodec.php
+++ b/maintenance/benchmarks/benchmarkJsonCodec.php
@@ -76,7 +76,7 @@ class BenchmarkJsonCodec extends Benchmarker {
] );
}
- private function loadData( $file ) {
+ private function loadData( string $file ) {
if ( str_ends_with( $file, '.php' ) ) {
$data = include $file;
if ( !$data ) {
diff --git a/maintenance/benchmarks/benchmarkSettings.php b/maintenance/benchmarks/benchmarkSettings.php
index 2f49284c0860..66b12df55e82 100644
--- a/maintenance/benchmarks/benchmarkSettings.php
+++ b/maintenance/benchmarks/benchmarkSettings.php
@@ -46,7 +46,7 @@ class BenchmarkSettings extends Benchmarker {
$this->addDescription( 'Benchmark loading settings files.' );
}
- private function newSettingsBuilder() {
+ private function newSettingsBuilder(): SettingsBuilder {
$extReg = new ExtensionRegistry();
$configBuilder = new ArrayConfigBuilder();
$phpIniSink = new NullIniSink();
diff --git a/maintenance/benchmarks/benchmarkTidy.php b/maintenance/benchmarks/benchmarkTidy.php
index c193ee5dcc28..6ed3b3ccf58d 100644
--- a/maintenance/benchmarks/benchmarkTidy.php
+++ b/maintenance/benchmarks/benchmarkTidy.php
@@ -41,7 +41,7 @@ class BenchmarkTidy extends Benchmarker {
$this->benchmark( $html );
}
- private function benchmark( $html ) {
+ private function benchmark( string $html ) {
$services = $this->getServiceContainer();
$contLang = $services->getContentLanguage();
$tidy = $services->getTidy();
diff --git a/maintenance/checkDependencies.php b/maintenance/checkDependencies.php
index 69a646382d71..25fd10eb070e 100644
--- a/maintenance/checkDependencies.php
+++ b/maintenance/checkDependencies.php
@@ -86,7 +86,7 @@ class CheckDependencies extends Maintenance {
}
}
- private function loadThing( &$dependencies, $name, $extensions, $skins ) {
+ private function loadThing( array &$dependencies, string $name, array $extensions, array $skins ) {
$extDir = $this->getConfig()->get( MainConfigNames::ExtensionDirectory );
$styleDir = $this->getConfig()->get( MainConfigNames::StyleDirectory );
$queue = [];
@@ -154,8 +154,8 @@ class CheckDependencies extends Maintenance {
$this->addToDependencies( $dependencies, $extensions, $skins, $name );
}
- private function addToDependencies( &$dependencies, $extensions, $skins,
- $why = null, $status = null, $message = null
+ private function addToDependencies( array &$dependencies, array $extensions, array $skins,
+ ?string $why = null, ?string $status = null, ?string $message = null
) {
$mainRegistry = ExtensionRegistry::getInstance();
$iter = [ 'extensions' => $extensions, 'skins' => $skins ];
@@ -186,7 +186,7 @@ class CheckDependencies extends Maintenance {
}
}
- private function formatForHumans( $dependencies ) {
+ private function formatForHumans( array $dependencies ): string {
$text = '';
foreach ( $dependencies as $type => $things ) {
$text .= ucfirst( $type ) . "\n" . str_repeat( '=', strlen( $type ) ) . "\n";
diff --git a/maintenance/cleanupImages.php b/maintenance/cleanupImages.php
index b4634e12cb05..d471e97039cf 100644
--- a/maintenance/cleanupImages.php
+++ b/maintenance/cleanupImages.php
@@ -28,6 +28,7 @@
use MediaWiki\FileRepo\LocalRepo;
use MediaWiki\Parser\Sanitizer;
use MediaWiki\Title\Title;
+use Wikimedia\Rdbms\IReadableDatabase;
// @codeCoverageIgnoreStart
require_once __DIR__ . '/TableCleanup.php';
@@ -137,7 +138,7 @@ class CleanupImages extends TableCleanup {
return $this->repo->getRootDirectory() . '/' . $this->repo->getHashPath( $name ) . $name;
}
- private function imageExists( $name, $db ) {
+ private function imageExists( string $name, IReadableDatabase $db ): bool {
return (bool)$db->newSelectQueryBuilder()
->select( '1' )
->from( 'image' )
@@ -146,7 +147,7 @@ class CleanupImages extends TableCleanup {
->fetchField();
}
- private function pageExists( $name, $db ) {
+ private function pageExists( string $name, IReadableDatabase $db ): bool {
return (bool)$db->newSelectQueryBuilder()
->select( '1' )
->from( 'page' )
@@ -158,7 +159,7 @@ class CleanupImages extends TableCleanup {
->fetchField();
}
- private function pokeFile( $orig, $new ) {
+ private function pokeFile( string $orig, string $new ) {
$path = $this->filePath( $orig );
if ( !file_exists( $path ) ) {
$this->output( "missing file: $path\n" );
@@ -232,12 +233,12 @@ class CleanupImages extends TableCleanup {
}
}
- private function appendTitle( $name, $suffix ) {
+ private function appendTitle( string $name, string $suffix ): string {
return preg_replace( '/^(.*)(\..*?)$/',
"\\1$suffix\\2", $name );
}
- private function buildSafeTitle( $name ) {
+ private function buildSafeTitle( string $name ) {
$x = preg_replace_callback(
'/([^' . Title::legalChars() . ']|~)/',
[ $this, 'hexChar' ],
diff --git a/maintenance/cleanupPreferences.php b/maintenance/cleanupPreferences.php
index 0b4707b4411c..afd85bd4fe7c 100644
--- a/maintenance/cleanupPreferences.php
+++ b/maintenance/cleanupPreferences.php
@@ -32,6 +32,7 @@ use MediaWiki\MainConfigNames;
use MediaWiki\Maintenance\Maintenance;
use MediaWiki\User\Options\UserOptionsLookup;
use Wikimedia\Rdbms\IExpression;
+use Wikimedia\Rdbms\IReadableDatabase;
use Wikimedia\Rdbms\LikeValue;
/**
@@ -99,7 +100,7 @@ class CleanupPreferences extends Maintenance {
}
}
- private function deleteByWhere( $dbr, $startMessage, $where ) {
+ private function deleteByWhere( IReadableDatabase $dbr, string $startMessage, array $where ) {
$this->output( $startMessage . "...\n" );
$dryRun = $this->hasOption( 'dry-run' );
diff --git a/maintenance/cleanupWatchlist.php b/maintenance/cleanupWatchlist.php
index 61ef59bb2e36..f9a9dba53e65 100644
--- a/maintenance/cleanupWatchlist.php
+++ b/maintenance/cleanupWatchlist.php
@@ -80,7 +80,7 @@ class CleanupWatchlist extends TableCleanup {
$this->progress( 0 );
}
- private function removeWatch( $row ) {
+ private function removeWatch( \stdClass $row ): int {
if ( !$this->dryrun && $this->hasOption( 'fix' ) ) {
$dbw = $this->getPrimaryDB();
$dbw->newDeleteQueryBuilder()
diff --git a/maintenance/compareParsers.php b/maintenance/compareParsers.php
index 4e97cc03815d..09a683090c7e 100644
--- a/maintenance/compareParsers.php
+++ b/maintenance/compareParsers.php
@@ -123,7 +123,7 @@ class CompareParsers extends DumpIterator {
}
}
- private function stripParameters( $text ) {
+ private function stripParameters( string $text ): string {
if ( !$this->stripParametersEnabled ) {
return $text;
}
@@ -191,7 +191,7 @@ class CompareParsers extends DumpIterator {
}
}
- private static function checkParserLocally( $parserName ) {
+ private static function checkParserLocally( string $parserName ) {
/* Look for the parser in a file appropriately named in the current folder */
if ( !class_exists( $parserName ) && file_exists( "$parserName.php" ) ) {
global $wgAutoloadClasses;
diff --git a/maintenance/convertExtensionToRegistration.php b/maintenance/convertExtensionToRegistration.php
index 3c2f5c2975f8..bed732a5092a 100644
--- a/maintenance/convertExtensionToRegistration.php
+++ b/maintenance/convertExtensionToRegistration.php
@@ -226,7 +226,7 @@ class ConvertExtensionToRegistration extends Maintenance {
}
}
- private function stripPath( $val, $dir ) {
+ private function stripPath( string $val, string $dir ): string {
if ( $val === $dir ) {
$val = '';
} elseif ( strpos( $val, $dir ) === 0 ) {
diff --git a/maintenance/deleteAutoPatrolLogs.php b/maintenance/deleteAutoPatrolLogs.php
index 00e5a5908c39..35949641e414 100644
--- a/maintenance/deleteAutoPatrolLogs.php
+++ b/maintenance/deleteAutoPatrolLogs.php
@@ -100,7 +100,7 @@ class DeleteAutoPatrolLogs extends Maintenance {
}
}
- private function getRows( $fromId ) {
+ private function getRows( ?int $fromId ): array {
$dbr = $this->getReplicaDB();
$before = $this->getOption( 'before', false );
@@ -127,7 +127,7 @@ class DeleteAutoPatrolLogs extends Maintenance {
->fetchFieldValues();
}
- private function getRowsOld( $fromId ) {
+ private function getRowsOld( ?int $fromId ): ?array {
$dbr = $this->getReplicaDB();
$batchSize = $this->getBatchSize();
$before = $this->getOption( 'before', false );
diff --git a/maintenance/deleteOldRevisions.php b/maintenance/deleteOldRevisions.php
index ff62cdf24b86..0aeca0ae6531 100644
--- a/maintenance/deleteOldRevisions.php
+++ b/maintenance/deleteOldRevisions.php
@@ -46,7 +46,7 @@ class DeleteOldRevisions extends Maintenance {
$this->doDelete( $this->hasOption( 'delete' ), $this->getArgs( 'page_id' ) );
}
- private function doDelete( $delete = false, $pageIds = [] ) {
+ private function doDelete( bool $delete = false, array $pageIds = [] ) {
# Data should come off the master, wrapped in a transaction
$dbw = $this->getPrimaryDB();
$this->beginTransaction( $dbw, __METHOD__ );
diff --git a/maintenance/dumpUploads.php b/maintenance/dumpUploads.php
index c476fa594e0c..901da886a543 100644
--- a/maintenance/dumpUploads.php
+++ b/maintenance/dumpUploads.php
@@ -115,7 +115,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
}
}
- private function outputItem( $name, $shared ) {
+ private function outputItem( string $name, bool $shared ) {
$file = $this->getServiceContainer()->getRepoGroup()->findFile( $name );
if ( $file && $this->filterItem( $file, $shared ) ) {
$filename = $file->getLocalRefPath();
@@ -126,7 +126,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
}
}
- private function filterItem( $file, $shared ) {
+ private function filterItem( File $file, bool $shared ): bool {
return $shared || $file->isLocal();
}
}
diff --git a/maintenance/getConfiguration.php b/maintenance/getConfiguration.php
index 22fcc8b3b8ec..52b27a8a15d3 100644
--- a/maintenance/getConfiguration.php
+++ b/maintenance/getConfiguration.php
@@ -187,7 +187,7 @@ class GetConfiguration extends Maintenance {
return trim( $ret, "\n" );
}
- private function isAllowedVariable( $value ) {
+ private function isAllowedVariable( $value ): bool {
if ( is_array( $value ) ) {
foreach ( $value as $v ) {
if ( !$this->isAllowedVariable( $v ) ) {
diff --git a/maintenance/importDump.php b/maintenance/importDump.php
index ee3ff3ff1999..e7a6ee26d763 100644
--- a/maintenance/importDump.php
+++ b/maintenance/importDump.php
@@ -159,7 +159,7 @@ TEXT
$this->nsFilter = array_unique( array_map( [ $this, 'getNsIndex' ], $namespaces ) );
}
- private function getNsIndex( $namespace ) {
+ private function getNsIndex( string $namespace ): int {
$contLang = $this->getServiceContainer()->getContentLanguage();
$result = $contLang->getNsIndex( $namespace );
if ( $result !== false ) {
@@ -249,7 +249,7 @@ TEXT
}
}
- private function report( $final = false ) {
+ private function report( bool $final = false ) {
if ( $final xor ( $this->pageCount % $this->reportingInterval == 0 ) ) {
$this->showReport();
}
@@ -275,11 +275,11 @@ TEXT
$this->waitForReplication();
}
- private function progress( $string ) {
+ private function progress( string $string ) {
fwrite( $this->stderr, $string . "\n" );
}
- private function importFromFile( $filename ) {
+ private function importFromFile( string $filename ): bool {
if ( preg_match( '/\.gz$/', $filename ) ) {
$filename = 'compress.zlib://' . $filename;
} elseif ( preg_match( '/\.bz2$/', $filename ) ) {
@@ -296,7 +296,7 @@ TEXT
return $this->importFromHandle( $file );
}
- private function importFromStdin() {
+ private function importFromStdin(): bool {
$file = fopen( 'php://stdin', 'rt' );
if ( self::posix_isatty( $file ) ) {
$this->maybeHelp( true );
@@ -305,7 +305,7 @@ TEXT
return $this->importFromHandle( $file );
}
- private function importFromHandle( $handle ) {
+ private function importFromHandle( $handle ): bool {
$this->startTime = microtime( true );
$user = User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [ 'steal' => true ] );
diff --git a/maintenance/importImages.php b/maintenance/importImages.php
index c9a831dec084..aab6d5215d9b 100644
--- a/maintenance/importImages.php
+++ b/maintenance/importImages.php
@@ -523,7 +523,7 @@ class ImportImages extends Maintenance {
return html_entity_decode( $matches[1] );
}
- private function getFileUserFromSourceWiki( $wiki_host, $file ) {
+ private function getFileUserFromSourceWiki( string $wiki_host, string $file ) {
$url = $wiki_host . '/api.php?action=query&format=xml&titles=File:'
. rawurlencode( $file ) . '&prop=imageinfo&&iiprop=user';
$body = $this->getServiceContainer()->getHttpRequestFactory()->get( $url, [], __METHOD__ );
diff --git a/maintenance/includes/MaintenanceParameters.php b/maintenance/includes/MaintenanceParameters.php
index 8d5855dc7904..a03b76935958 100644
--- a/maintenance/includes/MaintenanceParameters.php
+++ b/maintenance/includes/MaintenanceParameters.php
@@ -635,7 +635,7 @@ class MaintenanceParameters {
return implode( '', $output );
}
- private function formatHelpItems( array $items, $heading, $descWidth, $tab ) {
+ private function formatHelpItems( array $items, string $heading, int $descWidth, string $tab ): string {
if ( $items === [] ) {
return '';
}
diff --git a/maintenance/includes/MaintenanceRunner.php b/maintenance/includes/MaintenanceRunner.php
index 625fac947a9f..43f3441267d4 100644
--- a/maintenance/includes/MaintenanceRunner.php
+++ b/maintenance/includes/MaintenanceRunner.php
@@ -64,7 +64,7 @@ class MaintenanceRunner {
$this->addDefaultParams();
}
- private function getConfig() {
+ private function getConfig(): Config {
if ( $this->config === null ) {
$this->config = $this->getServiceContainer()->getMainConfig();
}
@@ -222,7 +222,7 @@ class MaintenanceRunner {
}
}
- private static function isAbsolutePath( $path ) {
+ private static function isAbsolutePath( string $path ): bool {
if ( str_starts_with( $path, '/' ) ) {
return true;
}
diff --git a/maintenance/includes/SevenZipStream.php b/maintenance/includes/SevenZipStream.php
index a133c5f01832..d1dd4520468f 100644
--- a/maintenance/includes/SevenZipStream.php
+++ b/maintenance/includes/SevenZipStream.php
@@ -50,7 +50,7 @@ class SevenZipStream {
}
}
- private function stripPath( $path ) {
+ private function stripPath( string $path ): string {
$prefix = 'mediawiki.compress.7z://';
return substr( $path, strlen( $prefix ) );
diff --git a/maintenance/includes/TextPassDumper.php b/maintenance/includes/TextPassDumper.php
index 0b82b17944ee..2703116d3614 100644
--- a/maintenance/includes/TextPassDumper.php
+++ b/maintenance/includes/TextPassDumper.php
@@ -376,7 +376,7 @@ TEXT
$this->timeExceeded = true;
}
- private function checkIfTimeExceeded() {
+ private function checkIfTimeExceeded(): bool {
if ( $this->maxTimeAllowed
&& ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed )
) {
@@ -1042,7 +1042,7 @@ TEXT
}
}
- private function isValidTextId( $id ) {
+ private function isValidTextId( string $id ): bool {
if ( preg_match( '/:/', $id ) ) {
return $id !== 'tt:0';
} elseif ( preg_match( '/^\d+$/', $id ) ) {
diff --git a/maintenance/install.php b/maintenance/install.php
index 2a4234e96167..85dc6c012e3c 100644
--- a/maintenance/install.php
+++ b/maintenance/install.php
@@ -230,7 +230,7 @@ class CommandLineInstaller extends Maintenance {
return true;
}
- private function generateStrongPassword() {
+ private function generateStrongPassword(): string {
$strongPassword = '';
$strongPasswordLength = 20;
$strongPasswordChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-={}|;:,.<>?';
diff --git a/maintenance/language/generateCollationData.php b/maintenance/language/generateCollationData.php
index a9047f73f986..ba55d34683be 100644
--- a/maintenance/language/generateCollationData.php
+++ b/maintenance/language/generateCollationData.php
@@ -117,7 +117,7 @@ class GenerateCollationData extends Maintenance {
$uxr->readChars( [ $this, 'charCallback' ] );
}
- private function charCallback( $data ) {
+ private function charCallback( array $data ) {
// Skip non-printable characters,
// but do not skip a normal space (U+0020) since
// people like to use that as a fake no header symbol.
diff --git a/maintenance/language/generateUcfirstOverrides.php b/maintenance/language/generateUcfirstOverrides.php
index d6698eec9a27..7be7ca05edb7 100644
--- a/maintenance/language/generateUcfirstOverrides.php
+++ b/maintenance/language/generateUcfirstOverrides.php
@@ -74,7 +74,7 @@ class GenerateUcfirstOverrides extends Maintenance {
);
}
- private function loadJson( $filename ) {
+ private function loadJson( string $filename ) {
$data = file_get_contents( $filename );
if ( $data === false ) {
$msg = sprintf( "Could not load data from file '%s'\n", $filename );
diff --git a/maintenance/language/importExtensionMessages.php b/maintenance/language/importExtensionMessages.php
index c5ebd5b7df00..2d11e19a7a4a 100644
--- a/maintenance/language/importExtensionMessages.php
+++ b/maintenance/language/importExtensionMessages.php
@@ -60,7 +60,7 @@ class ImportExtensionMessages extends Maintenance {
$this->extensionDir = $config->get( MainConfigNames::ExtensionDirectory );
}
- private function getMessagesDirs( $extData ) {
+ private function getMessagesDirs( array $extData ): array {
if ( isset( $extData['MessagesDirs'] ) ) {
$messagesDirs = [];
foreach ( $extData['MessagesDirs'] as $dirs ) {
@@ -78,7 +78,7 @@ class ImportExtensionMessages extends Maintenance {
return $messagesDirs;
}
- private function processDir( $dir ) {
+ private function processDir( string $dir ) {
$path = $this->extensionDir . "/{$this->extName}/$dir";
foreach ( new DirectoryIterator( $path ) as $file ) {
@@ -91,7 +91,7 @@ class ImportExtensionMessages extends Maintenance {
}
}
- private function processFile( $lang, $extI18nPath ) {
+ private function processFile( string $lang, string $extI18nPath ) {
$extJson = file_get_contents( $extI18nPath );
if ( $extJson === false ) {
$this->error( "Unable to read i18n file \"$extI18nPath\"" );
@@ -126,7 +126,7 @@ class ImportExtensionMessages extends Maintenance {
$this->setCoreData( $lang, $coreData );
}
- private function getCoreData( $lang ) {
+ private function getCoreData( string $lang ) {
if ( !isset( $this->coreDataCache[$lang] ) ) {
$corePath = MW_INSTALL_PATH . "/languages/i18n/$lang.json";
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
@@ -142,7 +142,7 @@ class ImportExtensionMessages extends Maintenance {
return $this->coreDataCache[$lang];
}
- private function setCoreData( $lang, $data ) {
+ private function setCoreData( string $lang, $data ) {
if ( !isset( $this->coreDataCache[$lang] ) ) {
// Non-existent file, do not create
return;
diff --git a/maintenance/mcc.php b/maintenance/mcc.php
index 99766423f96e..8b23ca428fcd 100644
--- a/maintenance/mcc.php
+++ b/maintenance/mcc.php
@@ -185,7 +185,7 @@ class Mcc extends Maintenance {
} while ( !$quit );
}
- private function mccGetHelp( $command ) {
+ private function mccGetHelp( $command ): string {
$output = '';
$commandList = [
'get' => 'grabs something',
diff --git a/maintenance/migrateExternallinks.php b/maintenance/migrateExternallinks.php
index 21f8cf5f6468..683c063af1cb 100644
--- a/maintenance/migrateExternallinks.php
+++ b/maintenance/migrateExternallinks.php
@@ -70,7 +70,7 @@ class MigrateExternallinks extends LoggedUpdateMaintenance {
return true;
}
- private function handleBatch( $lowId ) {
+ private function handleBatch( int $lowId ): int {
$batchSize = $this->getBatchSize();
// range is inclusive, let's subtract one.
$highId = $lowId + $batchSize - 1;
diff --git a/maintenance/migrateFileTables.php b/maintenance/migrateFileTables.php
index d40205927344..c48a7c0352ce 100644
--- a/maintenance/migrateFileTables.php
+++ b/maintenance/migrateFileTables.php
@@ -141,7 +141,7 @@ class MigrateFileTables extends Maintenance {
. "$totalRowsInserted rows have been inserted into filerevision table.\n" );
}
- private function handleFile( stdClass $row ) {
+ private function handleFile( stdClass $row ): int {
$repo = $this->getServiceContainer()->getRepoGroup()
->newCustomLocalRepo();
$dbw = $this->getPrimaryDB();
diff --git a/maintenance/migrateLinksTable.php b/maintenance/migrateLinksTable.php
index 5eb0e2c20819..43e29d3aa695 100644
--- a/maintenance/migrateLinksTable.php
+++ b/maintenance/migrateLinksTable.php
@@ -94,7 +94,7 @@ class MigrateLinksTable extends LoggedUpdateMaintenance {
return true;
}
- private function handlePageBatch( $lowPageId, $mapping, $table ) {
+ private function handlePageBatch( int $lowPageId, array $mapping, string $table ) {
$batchSize = $this->getBatchSize();
$targetColumn = $mapping[$table]['target_id'];
$pageIdColumn = $mapping[$table]['page_id'];
diff --git a/maintenance/populateChangeTagDef.php b/maintenance/populateChangeTagDef.php
index aa8b36cd491d..f75b3b64938c 100644
--- a/maintenance/populateChangeTagDef.php
+++ b/maintenance/populateChangeTagDef.php
@@ -187,7 +187,7 @@ class PopulateChangeTagDef extends LoggedUpdateMaintenance {
}
}
- private function backpopulateChangeTagPerTag( $tagName, $tagId ) {
+ private function backpopulateChangeTagPerTag( string $tagName, int $tagId ) {
$dbr = $this->getReplicaDB();
$dbw = $this->getPrimaryDB();
$sleep = (int)$this->getOption( 'sleep', 0 );
diff --git a/maintenance/purgePage.php b/maintenance/purgePage.php
index 1f533bef1f30..8053e9366530 100644
--- a/maintenance/purgePage.php
+++ b/maintenance/purgePage.php
@@ -51,7 +51,7 @@ class PurgePage extends Maintenance {
}
}
- private function purge( $titleText ) {
+ private function purge( string $titleText ) {
$title = Title::newFromText( $titleText );
if ( $title === null ) {
diff --git a/maintenance/rebuildImages.php b/maintenance/rebuildImages.php
index fc506efe63a3..7983cbcdf8dd 100644
--- a/maintenance/rebuildImages.php
+++ b/maintenance/rebuildImages.php
@@ -40,6 +40,7 @@ use MediaWiki\Maintenance\Maintenance;
use MediaWiki\Specials\SpecialUpload;
use MediaWiki\User\User;
use Wikimedia\Rdbms\IMaintainableDatabase;
+use Wikimedia\Rdbms\SelectQueryBuilder;
/**
* Maintenance script to update image metadata records.
@@ -128,7 +129,7 @@ class ImageBuilder extends Maintenance {
$this->table = $table;
}
- private function progress( $updated ) {
+ private function progress( int $updated ) {
$this->updated += $updated;
$this->processed++;
if ( $this->processed % 100 != 0 ) {
@@ -155,7 +156,7 @@ class ImageBuilder extends Maintenance {
flush();
}
- private function buildTable( $table, $queryBuilder, $callback ) {
+ private function buildTable( string $table, SelectQueryBuilder $queryBuilder, callable $callback ) {
$count = $this->dbw->newSelectQueryBuilder()
->select( 'count(*)' )
->from( $table )
@@ -181,7 +182,7 @@ class ImageBuilder extends Maintenance {
$this->buildTable( 'image', FileSelectQueryBuilder::newForFile( $this->getReplicaDB() ), $callback );
}
- private function imageCallback( $row ) {
+ private function imageCallback( \stdClass $row ): bool {
// Create a File object from the row
// This will also upgrade it
$file = $this->getRepo()->newFileFromRow( $row );
@@ -194,7 +195,7 @@ class ImageBuilder extends Maintenance {
[ $this, 'oldimageCallback' ] );
}
- private function oldimageCallback( $row ) {
+ private function oldimageCallback( \stdClass $row ): bool {
// Create a File object from the row
// This will also upgrade it
if ( $row->oi_archive_name == '' ) {
@@ -225,7 +226,7 @@ class ImageBuilder extends Maintenance {
}
}
- private function addMissingImage( $filename, $fullpath ) {
+ private function addMissingImage( string $filename, string $fullpath ) {
$timestamp = $this->dbw->timestamp( $this->getRepo()->getFileTimestamp( $fullpath ) );
$services = $this->getServiceContainer();
diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php
index 54add8a7334a..384ab296a1b1 100644
--- a/maintenance/storage/checkStorage.php
+++ b/maintenance/storage/checkStorage.php
@@ -400,7 +400,7 @@ class CheckStorage extends Maintenance {
}
}
- private function addError( $type, $msg, $ids ) {
+ private function addError( string $type, string $msg, $ids ) {
if ( is_array( $ids ) && count( $ids ) == 1 ) {
$ids = reset( $ids );
}
@@ -423,7 +423,7 @@ class CheckStorage extends Maintenance {
$this->errors[$type] += array_fill_keys( $revIds, true );
}
- private function checkExternalConcatBlobs( $externalConcatBlobs ) {
+ private function checkExternalConcatBlobs( array $externalConcatBlobs ) {
if ( !count( $externalConcatBlobs ) ) {
return;
}
@@ -465,7 +465,7 @@ class CheckStorage extends Maintenance {
}
}
- private function restoreText( $revIds, $xml ) {
+ private function restoreText( array $revIds, string $xml ) {
global $wgDBname;
$tmpDir = wfTempDir();
diff --git a/maintenance/storage/moveToExternal.php b/maintenance/storage/moveToExternal.php
index 8f66c7061c07..b27e33014700 100644
--- a/maintenance/storage/moveToExternal.php
+++ b/maintenance/storage/moveToExternal.php
@@ -129,7 +129,7 @@ class MoveToExternal extends Maintenance {
return $this->doMoveToExternal();
}
- private function doMoveToExternal() {
+ private function doMoveToExternal(): bool {
$success = true;
$dbr = $this->getReplicaDB();
@@ -253,7 +253,7 @@ class MoveToExternal extends Maintenance {
return $success;
}
- private function compress( $text, $flags ) {
+ private function compress( string $text, array $flags ): array {
if ( $this->gzip && !in_array( 'gzip', $flags ) ) {
$flags[] = 'gzip';
$text = gzdeflate( $text );
@@ -261,7 +261,7 @@ class MoveToExternal extends Maintenance {
return [ $text, $flags ];
}
- private function resolveLegacyEncoding( $text, $flags ) {
+ private function resolveLegacyEncoding( string $text, array $flags ): array {
if ( $this->legacyEncoding !== null
&& !in_array( 'utf-8', $flags )
&& !in_array( 'utf8', $flags )
@@ -290,7 +290,7 @@ class MoveToExternal extends Maintenance {
return [ $text, $flags ];
}
- private function resolveStubs( $stubIDs ) {
+ private function resolveStubs( array $stubIDs ) {
if ( $this->dryRun ) {
print "Note: resolving stubs in dry run mode is expected to fail, " .
"because the main blobs have not been moved to external storage.\n";
diff --git a/maintenance/storage/recompressTracked.php b/maintenance/storage/recompressTracked.php
index 691cc43426c7..b6f39493704d 100644
--- a/maintenance/storage/recompressTracked.php
+++ b/maintenance/storage/recompressTracked.php
@@ -174,7 +174,7 @@ class RecompressTracked {
}
}
- private function logToFile( $msg, $file ) {
+ private function logToFile( string $msg, string $file ) {
$header = '[' . date( 'd\TH:i:s' ) . '] ' . wfHostname() . ' ' . posix_getpid();
if ( $this->childId !== false ) {
$header .= "({$this->childId})";
diff --git a/maintenance/storage/trackBlobs.php b/maintenance/storage/trackBlobs.php
index 8f6d83ee5320..5eef62f9cd75 100644
--- a/maintenance/storage/trackBlobs.php
+++ b/maintenance/storage/trackBlobs.php
@@ -111,7 +111,7 @@ class TrackBlobs extends Maintenance {
$dbw->sourceFile( __DIR__ . '/blob_tracking.sql' );
}
- private function getTextClause() {
+ private function getTextClause(): IExpression {
if ( !$this->textClause ) {
$dbr = $this->getReplicaDB();
$conds = [];
@@ -128,7 +128,7 @@ class TrackBlobs extends Maintenance {
return $this->textClause;
}
- private function interpretPointer( $text ) {
+ private function interpretPointer( string $text ) {
if ( !preg_match( '!^DB://(\w+)/(\d+)(?:/([0-9a-fA-F]+)|)$!', $text, $m ) ) {
return false;
}
diff --git a/maintenance/update.php b/maintenance/update.php
index 4a748ba55138..209fc691447b 100755
--- a/maintenance/update.php
+++ b/maintenance/update.php
@@ -261,7 +261,7 @@ class UpdateMediaWiki extends Maintenance {
parent::validateParamsAndArgs();
}
- private function formatWarnings( array $warnings ) {
+ private function formatWarnings( array $warnings ): string {
$text = '';
foreach ( $warnings as $warning ) {
$warning = wordwrap( $warning, 75, "\n " );
diff --git a/maintenance/updateSearchIndex.php b/maintenance/updateSearchIndex.php
index 21ca64d5d67c..fbec1c183144 100644
--- a/maintenance/updateSearchIndex.php
+++ b/maintenance/updateSearchIndex.php
@@ -89,7 +89,7 @@ class UpdateSearchIndex extends Maintenance {
}
}
- private function doUpdateSearchIndex( $start, $end ) {
+ private function doUpdateSearchIndex( string $start, string $end ) {
global $wgDisableSearchUpdate;
$wgDisableSearchUpdate = false;
diff --git a/resources/Resources.php b/resources/Resources.php
index d70593e97cbe..2883843e5bd6 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -2350,6 +2350,7 @@ return [
'mediawiki.Title',
],
'messages' => [
+ 'api-error-unknownerror',
'blanknamespace',
'block-actions',
'block-added-message',
@@ -2400,12 +2401,15 @@ return [
'block-update',
'block-updated-message',
'block-user-active-blocks',
+ 'block-user-active-range-blocks',
'block-user-label-count-exceeds-limit',
'block-user-no-active-blocks',
+ 'block-user-no-active-range-blocks',
'block-user-no-previous-blocks',
'block-user-no-suppressed-blocks',
'block-user-previous-blocks',
'block-user-suppressed-blocks',
+ 'block-view-target',
'blockipsuccesssub',
'blocklist-actions-header',
'blocklist-by',
diff --git a/resources/src/mediawiki.page.ready/ready.js b/resources/src/mediawiki.page.ready/ready.js
index 999230f66ce4..51bf137df07b 100644
--- a/resources/src/mediawiki.page.ready/ready.js
+++ b/resources/src/mediawiki.page.ready/ready.js
@@ -245,33 +245,8 @@ function isSearchInput( element ) {
* @param {string} moduleName Name of a module
*/
function loadSearchModule( moduleName ) {
- // T251544: Collect search performance metrics to compare Vue search with
- // mediawiki.searchSuggest performance. Marks and Measures will only be
- // recorded on the Vector skin.
- //
- // Vue search isn't loaded through this function so we are only collecting
- // legacy search performance metrics here.
-
- const shouldTestSearch = !!( moduleName === 'mediawiki.searchSuggest' &&
- mw.config.get( 'skin' ) === 'vector' &&
- window.performance &&
- performance.mark &&
- performance.measure &&
-
- performance.getEntriesByName ),
- loadStartMark = 'mwVectorLegacySearchLoadStart',
- loadEndMark = 'mwVectorLegacySearchLoadEnd';
-
function requestSearchModule() {
- if ( shouldTestSearch ) {
- performance.mark( loadStartMark );
- }
- mw.loader.using( moduleName, () => {
- if ( shouldTestSearch && performance.getEntriesByName( loadStartMark ).length ) {
- performance.mark( loadEndMark );
- performance.measure( 'mwVectorLegacySearchLoadStartToLoadEnd', loadStartMark, loadEndMark );
- }
- } );
+ mw.loader.using( moduleName );
}
// Load the module once a search input is focussed.
diff --git a/resources/src/mediawiki.special.block/SpecialBlock.vue b/resources/src/mediawiki.special.block/SpecialBlock.vue
index 54a0740bc1c3..fe9b4e8105de 100644
--- a/resources/src/mediawiki.special.block/SpecialBlock.vue
+++ b/resources/src/mediawiki.special.block/SpecialBlock.vue
@@ -57,6 +57,12 @@
@remove-block="onRemoveBlock"
></block-log>
<block-log
+ v-if="mw.util.isIPAddress( store.targetUser, true )"
+ :key="`${submitCount}-active-ranges`"
+ :can-delete-log-entry="false"
+ block-log-type="active-ranges"
+ ></block-log>
+ <block-log
:key="`${submitCount}-recent`"
block-log-type="recent"
:can-delete-log-entry="canDeleteLogEntry"
@@ -483,7 +489,8 @@ module.exports = exports = defineComponent( {
doRemoveBlock,
showBlockLogs,
formVisible,
- wasRedirected
+ wasRedirected,
+ mw
};
}
} );
diff --git a/resources/src/mediawiki.special.block/components/BlockLog.vue b/resources/src/mediawiki.special.block/components/BlockLog.vue
index b526a355f728..f6aa3ffff805 100644
--- a/resources/src/mediawiki.special.block/components/BlockLog.vue
+++ b/resources/src/mediawiki.special.block/components/BlockLog.vue
@@ -1,7 +1,7 @@
<template>
<cdx-accordion
:class="`mw-block-log mw-block-log__type-${ blockLogType }`"
- :open="open || ( blockLogType === 'active' && alreadyBlocked )"
+ :open="open || ( blockLogType.includes( 'active' ) && alreadyBlocked )"
>
<template #title>
{{ title }}
@@ -37,6 +37,12 @@
<!-- Block parameters -->
<td class="mw-block-log__parameters">
<ul v-if="item.action !== 'unblock'">
+ <!-- target -->
+ <li v-if="blockLogType === 'active-ranges'">
+ <a :href="mw.Title.makeTitle( -1, `Contributions/${ item.target }` ).getUrl()">
+ {{ item.target }}
+ </a>
+ </li>
<!-- block duration -->
<li v-if="!!item.duration">
{{ item.duration }}
@@ -160,7 +166,16 @@
</cdx-button>
</span>
</td>
- <td v-else-if="blockLogType !== 'active' && canDeleteLogEntry">
+ <td v-else-if="blockLogType === 'active-ranges'">
+ <a
+ class="mw-block-log__actions"
+ :href="mw.util.getUrl( 'Special:Block', { wpTarget: item.target } )"
+ @click="( e ) => onViewIPRange( e, item.target )"
+ >
+ {{ $i18n( 'block-view-target' ).text() }}
+ </a>
+ </td>
+ <td v-else-if="!blockLogType.includes( 'active' ) && canDeleteLogEntry">
<a
class="mw-block-log__actions"
:href="mw.util.getUrl( 'Special:RevisionDelete', { type: 'logging', [`ids[${ item.logid }]`]: 1 } )"
@@ -222,13 +237,16 @@ module.exports = exports = defineComponent( {
const store = useBlockStore();
const { alreadyBlocked, blockId, targetUser } = storeToRefs( store );
let title = mw.message( 'block-user-previous-blocks' ).text();
- let emptyState = mw.message( 'block-user-no-previous-blocks' ).text();
+ const emptyState = ref( mw.message( 'block-user-no-previous-blocks' ).text() );
if ( props.blockLogType === 'active' ) {
title = mw.message( 'block-user-active-blocks' ).text();
- emptyState = mw.message( 'block-user-no-active-blocks' ).text();
+ emptyState.value = mw.message( 'block-user-no-active-blocks' ).text();
+ } else if ( props.blockLogType === 'active-ranges' ) {
+ title = mw.message( 'block-user-active-range-blocks' ).text();
+ emptyState.value = mw.message( 'block-user-no-active-range-blocks' ).text();
} else if ( props.blockLogType === 'suppress' ) {
title = mw.message( 'block-user-suppressed-blocks' ).text();
- emptyState = mw.message( 'block-user-no-suppressed-blocks' ).text();
+ emptyState.value = mw.message( 'block-user-no-suppressed-blocks' ).text();
}
const columns = [];
@@ -243,9 +261,9 @@ module.exports = exports = defineComponent( {
{ id: 'reason', label: mw.message( 'blocklist-reason' ).text() },
{ id: 'blockedby', label: mw.message( 'blocklist-by' ).text() },
{ id: 'timestamp', label: mw.message( 'blocklist-timestamp' ).text(), width: '15%' },
- ...( props.blockLogType === 'active' || props.canDeleteLogEntry ) ?
+ ...( props.blockLogType.includes( 'active' ) || props.canDeleteLogEntry ) ?
[ {
- id: props.blockLogType === 'active' ? 'modify' : 'hide',
+ id: 'actions',
label: mw.message( 'blocklist-actions-header' )
} ] :
[]
@@ -309,6 +327,18 @@ module.exports = exports = defineComponent( {
} ) );
}
+ /**
+ * Load the given IP range as the target user and reset the form.
+ *
+ * @param {Event} e
+ * @param {string} target
+ */
+ function onViewIPRange( e, target ) {
+ e.preventDefault();
+ targetUser.value = target;
+ store.resetForm();
+ }
+
watch( targetUser, ( newValue ) => {
if ( newValue ) {
store.getBlockLogData( props.blockLogType ).then( ( responses ) => {
@@ -332,6 +362,15 @@ module.exports = exports = defineComponent( {
} else {
// List of active blocks.
for ( const block of data.blocks ) {
+ const isRangeBlock = !!block.rangestart;
+ const isCurrentTarget = block.user === targetUser.value;
+ // Skip range blocks for 'active', or non-range blocks for 'active-ranges'.
+ if ( ( props.blockLogType === 'active' && isRangeBlock && !isCurrentTarget ) ||
+ ( props.blockLogType === 'active-ranges' && ( !isRangeBlock || isCurrentTarget ) )
+ ) {
+ continue;
+ }
+
newData.push( {
// Store the entire API response, for passing in when editing the block.
modify: block,
@@ -358,6 +397,8 @@ module.exports = exports = defineComponent( {
}
logEntries.value = newData;
+ } ).catch( ( error ) => {
+ emptyState.value = mw.message( 'api-error-unknownerror', error ).text();
} );
} else {
moreBlocks.value = false;
@@ -381,7 +422,8 @@ module.exports = exports = defineComponent( {
logEntriesCount,
infoChip,
shouldBlockFlagBeVisible,
- mwNamespaces
+ mwNamespaces,
+ onViewIPRange
};
}
} );
diff --git a/resources/src/mediawiki.special.block/stores/block.js b/resources/src/mediawiki.special.block/stores/block.js
index e8c67e8d12f4..15324052a89f 100644
--- a/resources/src/mediawiki.special.block/stores/block.js
+++ b/resources/src/mediawiki.special.block/stores/block.js
@@ -495,7 +495,11 @@ module.exports = exports = defineStore( 'block', () => {
}
let target = targetUser.value;
- if ( mw.util.isIPAddress( target, true ) ) {
+ const isValidIpOrRange = mw.util.isIPAddress( target, true );
+ const isIpRange = isValidIpOrRange && !mw.util.isIPAddress( target, false );
+
+ // Sanitize IP ranges for block log queries.
+ if ( isIpRange ) {
target = util.sanitizeRange( target );
}
@@ -523,7 +527,11 @@ module.exports = exports = defineStore( 'block', () => {
params.list = 'logevents|blocks';
params.letype = 'block';
params.bkprop = 'id|user|by|timestamp|expiry|reason|parsedreason|range|flags|restrictions';
- params.bkusers = target;
+ if ( isValidIpOrRange ) {
+ params.bkip = target;
+ } else {
+ params.bkusers = target;
+ }
const actualPromise = api.get( params );
actualPromise.then( ( data ) => {
diff --git a/sql/.htaccess b/sql/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/sql/.htaccess
+++ b/sql/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/tests/.htaccess b/tests/.htaccess
index b66e80882967..2e5c00314d2f 100644
--- a/tests/.htaccess
+++ b/tests/.htaccess
@@ -1 +1,2 @@
Require all denied
+Satisfy All
diff --git a/tests/jest/jest.setup.js b/tests/jest/jest.setup.js
index 51880fb52bf6..9dc109d12649 100644
--- a/tests/jest/jest.setup.js
+++ b/tests/jest/jest.setup.js
@@ -83,6 +83,7 @@ const mw = {
getUrl: jest.fn( ( pageName ) => '/wiki/' + pageName ),
isIPAddress: jest.fn(),
isInfinity: jest.fn(),
+ sanitizeIP: jest.fn(),
getParamValue: jest.fn().mockReturnValue( null )
},
Rest: RestMock
diff --git a/tests/jest/mediawiki.special.block/SpecialBlock.setup.js b/tests/jest/mediawiki.special.block/SpecialBlock.setup.js
index 58c604dc6b88..66598823b700 100644
--- a/tests/jest/mediawiki.special.block/SpecialBlock.setup.js
+++ b/tests/jest/mediawiki.special.block/SpecialBlock.setup.js
@@ -603,6 +603,118 @@ function mockMwApiGet( additionalMocks = [] ) {
}
}
},
+ // IP with range blocks (T389987)
+ {
+ params: {
+ list: 'logevents|blocks',
+ letype: 'block',
+ letitle: 'User:1.2.3.20'
+ },
+ response: {
+ query: {
+ blocks: [
+ {
+ id: 2000,
+ user: '1.2.3.20',
+ by: 'Admin',
+ timestamp: '2025-03-31T23:40:04Z',
+ expiry: '2025-04-03T23:40:04Z',
+ 'duration-l10n': '3 days',
+ reason: 'Vandalism',
+ parsedreason: 'Vandalism',
+ automatic: false,
+ anononly: true,
+ nocreate: true,
+ autoblock: false,
+ noemail: false,
+ hidden: false,
+ allowusertalk: false,
+ partial: false,
+ restrictions: []
+ }, {
+ id: 2001,
+ user: '1.2.0.0/18',
+ by: 'Admin',
+ timestamp: '2025-03-31T23:24:54Z',
+ expiry: '2025-10-01T23:24:54Z',
+ 'duration-l10n': '6 months',
+ reason: 'Spamming links to external sites',
+ parsedreason: 'Spamming links to external sites',
+ rangestart: '1.2.0.0',
+ rangeend: '1.2.63.255',
+ automatic: false,
+ anononly: true,
+ nocreate: true,
+ autoblock: false,
+ noemail: false,
+ hidden: false,
+ allowusertalk: true,
+ partial: false,
+ restrictions: []
+ }, {
+ id: 2002,
+ user: '1.2.3.20/30',
+ by: 'Admin',
+ timestamp: '2025-03-31T23:22:16Z',
+ expiry: '2027-03-01T23:22:16Z',
+ 'duration-l10n': '1 year, 10 months and 29 days',
+ reason: 'Inserting false information',
+ parsedreason: 'Inserting false information',
+ rangestart: '1.2.3.20',
+ rangeend: '1.2.3.23',
+ automatic: false,
+ anononly: true,
+ nocreate: true,
+ autoblock: false,
+ noemail: false,
+ hidden: false,
+ allowusertalk: true,
+ partial: false,
+ restrictions: []
+ }
+ ],
+ logevents: [
+ {
+ logid: 3000,
+ ns: 2,
+ title: 'User:1.2.3.20',
+ pageid: 0,
+ logpage: 0,
+ params: {
+ duration: '3 days',
+ flags: [
+ 'anononly',
+ 'nocreate',
+ 'nousertalk'
+ ],
+ blockId: 827,
+ sitewide: true,
+ expiry: '2025-04-03T23:40:04Z',
+ 'duration-l10n': '3 days'
+ },
+ type: 'block',
+ action: 'block',
+ user: 'Admin',
+ timestamp: '2025-03-31T23:40:04Z',
+ parsedcomment: 'Unacceptable username'
+ }
+ ]
+ }
+ }
+ },
+ {
+ params: {
+ list: 'logevents|blocks',
+ letype: 'block',
+ letitle: 'User:192.168.0.1'
+ },
+ response: {
+ query: {
+ blocks: [],
+ logevents: []
+ }
+ }
+ },
// Used in UserLookup
{
params: {
diff --git a/tests/jest/mediawiki.special.block/SpecialBlock.test.js b/tests/jest/mediawiki.special.block/SpecialBlock.test.js
index 9970666ea6dd..5bbe50e65cc2 100644
--- a/tests/jest/mediawiki.special.block/SpecialBlock.test.js
+++ b/tests/jest/mediawiki.special.block/SpecialBlock.test.js
@@ -371,6 +371,28 @@ describe( 'SpecialBlock', () => {
expect( wrapper.find( '.mw-block-success' ).exists() ).toBeTruthy();
} );
+ it( 'should show "Active blocks" and "Active range blocks" on the given IP', async () => {
+ mw.util.isIPAddress = jest.fn().mockReturnValue( true );
+ wrapper = getSpecialBlock( { blockTargetUser: '1.2.3.20', blockTargetExists: true } );
+ await flushPromises();
+ expect( wrapper.find( '.mw-block-log__type-active' ).exists() ).toBeTruthy();
+ expect( wrapper.findAll( '.mw-block-log__type-active tbody tr' ) ).toHaveLength( 1 );
+ expect( wrapper.find( '.mw-block-log__type-active-ranges' ).exists() ).toBeTruthy();
+ expect( wrapper.findAll( '.mw-block-log__type-active-ranges tbody tr' ) ).toHaveLength( 2 );
+ } );
+
+ it( 'should show an empty "Active range blocks" for an IP with no range blocks', async () => {
+ mw.util.isIPAddress = jest.fn().mockReturnValue( true );
+ wrapper = getSpecialBlock( { blockTargetUser: '192.168.0.1', blockTargetExists: true } );
+ await flushPromises();
+ expect( wrapper.find( '.mw-block-log__type-active' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.mw-block-log__type-active tbody' ).text() )
+ .toStrictEqual( 'block-user-no-active-blocks' );
+ expect( wrapper.find( '.mw-block-log__type-active-ranges' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.mw-block-log__type-active-ranges tbody' ).text() )
+ .toStrictEqual( 'block-user-no-active-range-blocks' );
+ } );
+
afterEach( () => {
wrapper.unmount();
} );
diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php
index e76c22e52f99..9e66e5a13a04 100644
--- a/tests/parser/ParserTestRunner.php
+++ b/tests/parser/ParserTestRunner.php
@@ -24,9 +24,6 @@
* @todo Make this more independent of the configuration (and if possible the database)
* @file
* @ingroup Testing
- * @phan-file-suppress UnusedPluginSuppression,UnusedPluginFileSuppression
- * Prevent phan from crashing CI if
- * ParsoidParserHook::getParserTestConfigFileName() happens to exist
*/
use MediaWiki\Content\WikitextContent;
@@ -501,10 +498,8 @@ class ParserTestRunner {
];
// Add magic words used in Parsoid-native extension modules
if ( method_exists( ParsoidParserHook::class, 'getParserTestConfigFileName' ) ) {
- // https://github.com/phan/phan/issues/2628
- // @phan-suppress-next-line PhanUndeclaredStaticMethod
$filename = ParsoidParserHook::getParserTestConfigFileName();
- if ( $filename !== null && file_exists( $filename ) ) {
+ if ( file_exists( $filename ) ) {
$config = json_decode( file_get_contents( $filename ), true );
$extraMagicWords = array_merge(
$extraMagicWords, $config['magicwords'] ?? []
@@ -1387,10 +1382,10 @@ class ParserTestRunner {
}
if ( isset( $opts['maxincludesize'] ) ) {
- $options->setMaxIncludeSize( $opts['maxincludesize'] );
+ $options->setMaxIncludeSize( (int)$opts['maxincludesize'] );
}
if ( isset( $opts['maxtemplatedepth'] ) ) {
- $options->setMaxTemplateDepth( $opts['maxtemplatedepth'] );
+ $options->setMaxTemplateDepth( (int)$opts['maxtemplatedepth'] );
}
return [ $title, $options, $revProps['revid'] ];
@@ -1510,8 +1505,10 @@ class ParserTestRunner {
$section = $opts['section'];
$out = $parser->getSection( $wikitext, $section );
} elseif ( isset( $opts['replace'] ) ) {
- $section = $opts['replace'][0];
- $replace = $opts['replace'][1];
+ $o = $opts['replace'];
+ '@phan-var array $o'; // Phan gets confused about types
+ $section = $o[0];
+ $replace = $o[1];
$out = $parser->replaceSection( $wikitext, $section, $replace );
} elseif ( isset( $opts['comment'] ) ) {
$out = MediaWikiServices::getInstance()->getCommentFormatter()->format( $wikitext, $title, $local );
@@ -2063,6 +2060,7 @@ class ParserTestRunner {
private function runSelserEditTest(
Parsoid $parsoid, PageConfig $pageConfig, ParserTest $test, ParserTestMode $mode, Document $doc
): array {
+ // @phan-suppress-next-line PhanTypeMismatchArgumentNullable $test->changetree is non-null here
$test->applyChanges( [], $doc, $test->changetree );
$editedHTML = ContentUtils::toXML( DOMCompat::getBody( $doc ) );
@@ -2159,7 +2157,7 @@ class ParserTestRunner {
$doc = $this->fetchCachedDoc( $parsoid, $pageConfig, $test );
$test->seed = $i . '';
$test->changetree = $test->generateChanges( $doc );
- if ( $test->changetree ) {
+ if ( $test->changetree ) { // testing for [] not null
// new mode with the generated changetree
$nmode = new ParserTestMode( 'selser', $test->changetree );
[ $out, $expected ] = $this->runSelserEditTest( $parsoid, $pageConfig, $test, $nmode, $doc );
@@ -2171,6 +2169,7 @@ class ParserTestRunner {
}
// $test->changetree can be [] which is a NOP for testing
// but not a NOP for duplicate change tree tests.
+ // @phan-suppress-next-line PhanTypeMismatchArgumentNullable $test->changetree is non-null here
if ( $test->isDuplicateChangeTree( $test->changetree ) ) {
// Once we get a duplicate change tree, we can no longer
// generate and run new tests. So, be done now!
diff --git a/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php b/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
index e292eea0a8d9..91614f56649d 100644
--- a/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
+++ b/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
@@ -2,6 +2,7 @@
namespace MediaWiki\Tests\Storage;
+use ArrayUtils;
use DummyContentHandlerForTesting;
use MediaWiki\CommentStore\CommentStoreComment;
use MediaWiki\Config\ServiceOptions;
@@ -134,7 +135,7 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
}
$this->getDerivedPageDataUpdater( $page ); // flush cached instance after.
- $this->runJobs(); // flush pending updates
+ $this->runJobs( [ 'minJobs' => 0 ] ); // flush pending updates
return $rev;
}
@@ -1123,14 +1124,19 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
}
/**
+ * @dataProvider provideDoUpdatesParams
+ *
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doUpdates()
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doSecondaryDataUpdates()
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
*/
- public function testDoUpdates() {
+ public function testDoUpdates(
+ bool $simulateNullEdit,
+ bool $simulatePageCreation
+ ) {
$page = $this->getPage( __METHOD__ );
- $content = [ SlotRecord::MAIN => new WikitextContent( 'first [[main]]' ) ];
+ $content = [ SlotRecord::MAIN => new WikitextContent( 'current [[main]]' ) ];
$content['aux'] = new WikitextContent( 'Aux [[Nix]]' );
@@ -1142,7 +1148,29 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
);
}
- $rev = $this->createRevision( $page, 'first', $content );
+ // If simulating a null edit, set up a previous revision with the same content as our change.
+ // Otherwise, initialize the previous revision with different content unless simulating page creation,
+ // in which case no previous revision should be created at all.
+ if ( !$simulatePageCreation ) {
+ $oldContent = $simulateNullEdit ? $content : [ SlotRecord::MAIN => new WikitextContent( 'first [[main]]' ) ];
+ $firstRev = $this->createRevision( $page, 'first', $oldContent );
+ } else {
+ $firstRev = null;
+ }
+
+ $slotsUpdate = RevisionSlotsUpdate::newFromContent( $content );
+
+ $updater = $this->getServiceContainer()
+ ->getPageUpdaterFactory()
+ ->newDerivedPageDataUpdater( $page );
+ $updater->prepareContent( $this->getTestUser()->getUserIdentity(), $slotsUpdate );
+
+ // Don't create a new revision if simulating a null edit.
+ if ( $simulateNullEdit ) {
+ $rev = $firstRev;
+ } else {
+ $rev = $this->createRevision( $page, 'current', $content );
+ }
$pageId = $page->getId();
$listenerCalled = 0;
@@ -1169,10 +1197,10 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
$pcache = $this->getServiceContainer()->getParserCache();
$pcache->deleteOptionsKey( $page );
- $updater = $this->getDerivedPageDataUpdater( $page, $rev );
$updater->setArticleCountMethod( 'link' );
$options = []; // TODO: test *all* the options...
+
$updater->prepareUpdate( $rev, $options );
$updater->doUpdates();
@@ -1207,9 +1235,19 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
->from( 'site_stats' )
->where( '1=1' )
->fetchRow();
- $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
- $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
- $this->assertSame( $oldStats->ss_good_articles + 1, (int)$stats->ss_good_articles );
+ if ( $simulatePageCreation ) {
+ $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
+ $this->assertSame( $oldStats->ss_good_articles + 1, (int)$stats->ss_good_articles );
+ } else {
+ $this->assertSame( $oldStats->ss_total_pages, $stats->ss_total_pages );
+ $this->assertSame( $oldStats->ss_good_articles, $stats->ss_good_articles );
+ }
+
+ if ( !$simulateNullEdit ) {
+ $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
+ } else {
+ $this->assertSame( $oldStats->ss_total_edits, $stats->ss_total_edits );
+ }
$this->runDeferredUpdates();
$this->assertSame( 1, $listenerCalled, 'PageRevisionUpdatedEvent listener' );
@@ -1222,7 +1260,32 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
// TODO: test newtalk update
// TODO: test search update
// TODO: test site stats good_articles while turning the page into (or back from) a redir.
- // TODO: test category membership update (with setRcWatchCategoryMembership())
+ }
+
+ public static function provideDoUpdatesParams(): iterable {
+ $testCases = ArrayUtils::cartesianProduct(
+ // null or non-null edit
+ [ true, false ],
+ // page creation
+ [ true, false ]
+ );
+
+ foreach ( $testCases as $params ) {
+ [ $simulateNullEdit, $simulatePageCreation ] = $params;
+
+ if ( $simulateNullEdit && $simulatePageCreation ) {
+ // Page creations cannot be null edits, so don't simulate an impossible scenario
+ continue;
+ }
+
+ $description = sprintf(
+ '%s edit%s',
+ $simulateNullEdit ? 'null' : 'non-null',
+ $simulatePageCreation ? ', page creation, ' : ''
+ );
+
+ yield $description => $params;
+ }
}
/**
diff --git a/tests/phpunit/includes/Storage/PageUpdaterTest.php b/tests/phpunit/includes/Storage/PageUpdaterTest.php
index 7c8afdbae1da..904ac1069139 100644
--- a/tests/phpunit/includes/Storage/PageUpdaterTest.php
+++ b/tests/phpunit/includes/Storage/PageUpdaterTest.php
@@ -10,6 +10,7 @@ use MediaWiki\Content\TextContent;
use MediaWiki\Content\WikitextContent;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Json\FormatJson;
+use MediaWiki\MainConfigNames;
use MediaWiki\Message\Message;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
use MediaWiki\Page\PageIdentity;
@@ -49,6 +50,10 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
protected function setUp(): void {
parent::setUp();
+ // Force enable RC entry creation for category changes
+ // so that tests can verify whether CategoryMembershipChangeJobs get enqueued.
+ $this->overrideConfigValue( MainConfigNames::RCWatchCategoryMembership, true );
+
$slotRoleRegistry = $this->getServiceContainer()->getSlotRoleRegistry();
if ( !$slotRoleRegistry->isDefinedRole( 'aux' ) ) {
@@ -69,6 +74,13 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// protect against service container resets
$this->setService( 'SlotRoleRegistry', $slotRoleRegistry );
+
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'LinksUpdateComplete',
+ ] );
}
private function getDummyTitle( $method ) {
@@ -707,7 +719,8 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
$this->expectChangeTrackingUpdates(
1, 0, 1,
- $page->getNamespace() === NS_USER_TALK ? 1 : 0
+ $page->getNamespace() === NS_USER_TALK ? 1 : 0,
+ 1
);
$this->expectSearchUpdates( 1 );
@@ -751,7 +764,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Null edits should not go into recentchanges, should not
// increment counters, and should not trigger talk page notifications.
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
// Update derived data on null edits
$this->expectSearchUpdates( 1 );
@@ -796,7 +809,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Silent dummy revisions should not go into recentchanges,
// should not increment counters, and should not trigger talk page
// notifications.
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
// Do not update derived data on dummy revisions!
$this->expectSearchUpdates( 0 );
@@ -1262,7 +1275,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Clear pending jobs so the spies don't get confused
$this->runJobs();
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
$this->expectSearchUpdates( 0 );
$updater = $page->newPageUpdater( $user );
@@ -1420,7 +1433,8 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
$services->getUserNameUtils(),
$services->getTalkPageNotificationManager(),
$services->getMainConfig(),
- $services->getJobQueueGroup()
+ $services->getJobQueueGroup(),
+ $services->getContentHandlerFactory()
);
$services->getDomainEventSource()
diff --git a/tests/phpunit/includes/api/ApiImportTest.php b/tests/phpunit/includes/api/ApiImportTest.php
index a3a3203f26d6..4c6c26e26a1a 100644
--- a/tests/phpunit/includes/api/ApiImportTest.php
+++ b/tests/phpunit/includes/api/ApiImportTest.php
@@ -30,6 +30,13 @@ class ApiImportTest extends ApiUploadTestCase {
}
public function testImport() {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'LinksUpdateComplete',
+ ] );
+
$title = $this->getNonexistingTestPage()->getTitle();
// We expect two PageRevisionUpdated events, one triggered by
@@ -89,7 +96,7 @@ class ApiImportTest extends ApiUploadTestCase {
// Expect only non-edit recent changes entry, but no edit count
// or user talk.
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 1 );
// Expect search updates to be triggered
$this->expectSearchUpdates( 1 );
diff --git a/tests/phpunit/includes/filerepo/file/LocalFileTest.php b/tests/phpunit/includes/filerepo/file/LocalFileTest.php
index 5a9b0a9ae327..8bba9ec1dfd2 100644
--- a/tests/phpunit/includes/filerepo/file/LocalFileTest.php
+++ b/tests/phpunit/includes/filerepo/file/LocalFileTest.php
@@ -896,8 +896,14 @@ class LocalFileTest extends MediaWikiIntegrationTestCase {
* @covers \LocalFile
*/
public function testUpload_updatePropagation() {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ ] );
+
// Expect two non-edit recent changes entries but only one edit count.
- $this->expectChangeTrackingUpdates( 0, 2, 1, 0 );
+ $this->expectChangeTrackingUpdates( 0, 2, 1, 0, 1 );
// Expect only one search update, the re-upload doesn't change the page.
$this->expectSearchUpdates( 1 );
diff --git a/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php b/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
index f974eec26f7e..ed064e19ed99 100644
--- a/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
+++ b/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
@@ -190,7 +190,7 @@ class ImportableOldRevisionImporterTest extends MediaWikiIntegrationTestCase {
public function testUpdatePropagation( PageIdentity $title ) {
$revision = $this->getWikiRevision( Title::castFromPageIdentity( $title ) );
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 1 );
$this->expectSearchUpdates( 1 );
$this->expectLocalizationUpdate( $title->getNamespace() === NS_MEDIAWIKI ? 1 : 0 );
diff --git a/tests/phpunit/includes/page/MovePageTest.php b/tests/phpunit/includes/page/MovePageTest.php
index be3043578b90..07aaf63a09f9 100644
--- a/tests/phpunit/includes/page/MovePageTest.php
+++ b/tests/phpunit/includes/page/MovePageTest.php
@@ -741,6 +741,14 @@ class MovePageTest extends MediaWikiIntegrationTestCase {
* @dataProvider provideUpdatePropagation
*/
public function testUpdatePropagation( $old, $new, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'PageMoveComplete',
+ 'LinksUpdateComplete',
+ ] );
+
$old = Title::newFromText( $old );
$new = Title::newFromText( $new );
@@ -754,7 +762,7 @@ class MovePageTest extends MediaWikiIntegrationTestCase {
// Should be counted as user contributions (T163966)
// Should generate an RC entry for the move log, but not for
// the dummy revision or redirect page.
- $this->expectChangeTrackingUpdates( 0, 1, 1, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 1, 0, 1 );
// The moved page and the redirect should both get re-indexed.
$this->expectSearchUpdates( 2 );
diff --git a/tests/phpunit/includes/page/UndeletePageTest.php b/tests/phpunit/includes/page/UndeletePageTest.php
index a33201382924..7a471a748db5 100644
--- a/tests/phpunit/includes/page/UndeletePageTest.php
+++ b/tests/phpunit/includes/page/UndeletePageTest.php
@@ -76,6 +76,9 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
$this->fail( $updater->getStatus()->getWikiText() );
}
+ // Run jobs that were enqueued by page creation now, since they might expect the page to exist.
+ $this->runJobs( [ 'minJobs' => 0 ] );
+
$this->pages[] = [ 'page' => $page, 'revId' => $revisionRecord->getId() ];
$this->deletePage( $page, '', $performer );
}
@@ -223,6 +226,12 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
*/
public function testUpdatePropagation( ProperPageIdentity $page, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'LinksUpdateComplete',
+ 'PageUndeleteComplete',
+ ] );
+
$content ??= new WikitextContent( 'hi' );
$this->setupPage( $page->getDBkey(), $page->getNamespace(), $content );
$this->runJobs();
@@ -231,7 +240,7 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
// Should generate an RC entry for undeletion,
// but not a regular page edit.
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 0 );
$this->expectSearchUpdates( 1 );
$this->expectLocalizationUpdate( $page->getNamespace() === NS_MEDIAWIKI ? 1 : 0 );
diff --git a/tests/phpunit/includes/page/WikiPageDbTest.php b/tests/phpunit/includes/page/WikiPageDbTest.php
index 3efff6e309d0..d4e30e48fac2 100644
--- a/tests/phpunit/includes/page/WikiPageDbTest.php
+++ b/tests/phpunit/includes/page/WikiPageDbTest.php
@@ -1777,6 +1777,13 @@ more stuff
PageIdentity $title,
?Content $content = null
) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ 'ArticleProtectComplete',
+ ] );
+
$content ??= new TextContent( 'Lorem Ipsum' );
$page = $this->createPage( $title, $content );
@@ -1784,7 +1791,7 @@ more stuff
$this->runJobs();
// Expect only non-edit recent changes entry
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 0 );
// Expect no resource module purges on protection
$this->expectResourceLoaderUpdates( 0 );
diff --git a/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php b/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
index d7df06882f7a..dd31a56f75d5 100644
--- a/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
+++ b/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
@@ -2,6 +2,8 @@
namespace MediaWiki\Tests\recentchanges;
+use MediaWiki\JobQueue\JobQueueGroup;
+use MediaWiki\MainConfigNames;
use MediaWiki\RecentChanges\RecentChange;
use MediaWiki\User\TalkPageNotificationManager;
use MediaWiki\User\UserEditTracker;
@@ -25,8 +27,13 @@ trait ChangeTrackingUpdateSpyTrait {
int $rcEdit,
int $rcOther,
int $userEditCount,
- int $talkPageNotifications
+ int $talkPageNotifications,
+ int $categoryMembershipChangeJobs
) {
+ // Force enable RC entry creation for category changes
+ // to verify CategoryMembershipChangeJobs get enqueued irrespective of local configuration.
+ $this->overrideConfigValue( MainConfigNames::RCWatchCategoryMembership, true );
+
// Hack for spying on RC insertions
$rcEditStatus = $this->createMock( StatusValue::class );
$rcEditStatus->expects( $this->exactly( $rcEdit ) )
@@ -75,6 +82,25 @@ trait ChangeTrackingUpdateSpyTrait {
->method( 'removeUserHasNewMessages' );
$this->setService( 'TalkPageNotificationManager', $talkPageNotificationManager );
+
+ $categoryMembershipChangeJobStatus = $this->createMock( StatusValue::class );
+ $categoryMembershipChangeJobStatus->expects( $this->exactly( $categoryMembershipChangeJobs ) )
+ ->method( 'setOK' );
+
+ $jobQueueGroup = $this->createMock( JobQueueGroup::class );
+ $jobQueueGroup->method( $this->logicalOr( 'push', 'lazyPush' ) )
+ ->willReturnCallback(
+ static function ( $specs ) use ( $categoryMembershipChangeJobStatus ): void {
+ $specs = is_array( $specs ) ? $specs : [ $specs ];
+ foreach ( $specs as $spec ) {
+ if ( $spec->getType() === 'categoryMembershipChange' ) {
+ $categoryMembershipChangeJobStatus->setOK( true );
+ }
+ }
+ }
+ );
+
+ $this->setService( 'JobQueueGroup', $jobQueueGroup );
}
}
diff --git a/tests/phpunit/includes/specials/SpecialContributionsTest.php b/tests/phpunit/includes/specials/SpecialContributionsTest.php
index c5f95739d3ac..67681a2d1dc6 100644
--- a/tests/phpunit/includes/specials/SpecialContributionsTest.php
+++ b/tests/phpunit/includes/specials/SpecialContributionsTest.php
@@ -106,6 +106,48 @@ class SpecialContributionsTest extends SpecialPageTestBase {
$this->assertStringContainsString( 'mw-pager-body', $html );
}
+ /**
+ * @dataProvider executeForUserWithWhitespacesDataProvider
+ */
+ public function testExecuteForUserWithWhitespaces(
+ string $expected,
+ string $target
+ ): void {
+ [ $html ] = $this->executeSpecialPage( $target );
+
+ // Assert that the Javascript code in the page contains the quoted
+ // username with the whitespaces removed.
+ $this->assertStringContainsString(
+ sprintf( '"%s"', $expected ),
+ $html
+ );
+ }
+
+ public function executeForUserWithWhitespacesDataProvider(): array {
+ return [
+ 'With an empty target' => [
+ 'expected' => '',
+ 'target' => ''
+ ],
+ 'With a target with no whitespaces' => [
+ 'expected' => 'TopUser',
+ 'target' => 'TopUser'
+ ],
+ 'With a target with leading whitespaces' => [
+ 'expected' => 'TopUser',
+ 'target' => ' TopUser'
+ ],
+ 'With a target with trailing whitespaces' => [
+ 'expected' => 'TopUser',
+ 'target' => 'TopUser '
+ ],
+ 'With a target with whitespaces at both sides' => [
+ 'expected' => 'TopUser',
+ 'target' => ' TopUser '
+ ],
+ ];
+ }
+
public function testExecuteEmptyTarget() {
[ $html ] = $this->executeSpecialPage();
// This 'topOnly' filter should always be added to Special:Contributions
diff --git a/tests/phpunit/integration/includes/page/DeletePageTest.php b/tests/phpunit/integration/includes/page/DeletePageTest.php
index 78e35dcaead9..c6f7f46ec397 100644
--- a/tests/phpunit/integration/includes/page/DeletePageTest.php
+++ b/tests/phpunit/integration/includes/page/DeletePageTest.php
@@ -478,6 +478,11 @@ class DeletePageTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
*/
public function testUpdatePropagation( $name, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageDeleteComplete',
+ ] );
+
$content ??= new WikitextContent( self::PAGE_TEXT );
$deleterUser = static::getTestSysop()->getUser();
$deleter = new UltimateAuthority( $deleterUser );
@@ -489,7 +494,8 @@ class DeletePageTest extends MediaWikiIntegrationTestCase {
// but not a regular page edit.
$this->expectChangeTrackingUpdates(
0, 1, 0,
- $page->getNamespace() === NS_USER_TALK ? -1 : 0
+ $page->getNamespace() === NS_USER_TALK ? -1 : 0,
+ 0
);
// TODO: Assert that the search index is updated after deletion.
diff --git a/tests/phpunit/integration/includes/page/RollbackPageTest.php b/tests/phpunit/integration/includes/page/RollbackPageTest.php
index 7ef37b73a795..973febea3eb3 100644
--- a/tests/phpunit/integration/includes/page/RollbackPageTest.php
+++ b/tests/phpunit/integration/includes/page/RollbackPageTest.php
@@ -588,6 +588,12 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
?Content $content1 = null,
?Content $content2 = null
) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ ] );
+
$page = $this->getServiceContainer()->getWikiPageFactory()
->newFromTitle( Title::newFromText( __METHOD__ ) );
@@ -602,7 +608,8 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
// Should generate an RC entry for rollback
$this->expectChangeTrackingUpdates(
1, 0, 1,
- $page->getNamespace() === NS_USER_TALK ? 1 : 0
+ $page->getNamespace() === NS_USER_TALK ? 1 : 0,
+ 1
);
$this->expectSearchUpdates( 1 );
@@ -617,5 +624,7 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
->newRollbackPage( $page, $admin, $user1 )
->rollbackIfAllowed();
$this->assertStatusGood( $rollbackResult );
+
+ $this->runDeferredUpdates();
}
}
diff --git a/tests/phpunit/unit/includes/installer/Task/SqliteCreateDatabaseTaskTest.php b/tests/phpunit/unit/includes/installer/Task/SqliteCreateDatabaseTaskTest.php
index f21544904400..fea3fec31c1b 100644
--- a/tests/phpunit/unit/includes/installer/Task/SqliteCreateDatabaseTaskTest.php
+++ b/tests/phpunit/unit/includes/installer/Task/SqliteCreateDatabaseTaskTest.php
@@ -29,7 +29,7 @@ class SqliteCreateDatabaseTaskTest extends MediaWikiUnitTestCase {
$dir = sys_get_temp_dir() . '/' . uniqid( 'MediaWikiTest' );
$status = $task->createDataDir( $dir );
$this->assertStatusGood( $status );
- $this->assertSame( "Require all denied\n", file_get_contents( "$dir/.htaccess" ) );
+ $this->assertSame( "Require all denied\nSatisfy All\n", file_get_contents( "$dir/.htaccess" ) );
unlink( "$dir/.htaccess" );
rmdir( $dir );
}
diff --git a/tests/phpunit/unit/includes/libs/Stats/UnitTestingHelperTest.php b/tests/phpunit/unit/includes/libs/Stats/UnitTestingHelperTest.php
index 38c5a71ffb9d..ff704b3fd2ca 100644
--- a/tests/phpunit/unit/includes/libs/Stats/UnitTestingHelperTest.php
+++ b/tests/phpunit/unit/includes/libs/Stats/UnitTestingHelperTest.php
@@ -95,11 +95,9 @@ class UnitTestingHelperTest extends TestCase {
public function testMin() {
$actual = $this->statsHelper->min( 'test{a="a"}' );
- // phpcs:ignore MediaWiki.PHPUnit.AssertEquals.Int
- $this->assertEquals( 1, $actual );
+ $this->assertSame( 1.0, $actual );
$actual = $this->statsHelperWithComponent->min( 'test{a="a"}' );
- //phpcs:ignore MediaWiki.PHPUnit.AssertEquals.Int
- $this->assertEquals( 1, $actual );
+ $this->assertSame( 1.0, $actual );
}
public function testNoFilter() {