From: jenkins-bot Date: Tue, 10 Sep 2019 19:00:47 +0000 (+0000) Subject: Merge "Allow partially blocked users to tag unrelated revisions" X-Git-Tag: 1.34.0-rc.0~276 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=5a296a1113d1243962c6459281a0307dd3efb80f;hp=3224bdabc08f522da81478a63971c949cee40bc6 Merge "Allow partially blocked users to tag unrelated revisions" --- diff --git a/.phpcs.xml b/.phpcs.xml index 9f11ebcf5c..f971881f38 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -70,47 +70,12 @@ Whitelist existing violations, but enable the sniff to prevent any new occurrences. --> - */includes/specials/SpecialMostinterwikis\.php - */includes/specials/SpecialAncientpages\.php - */includes/specials/SpecialBrokenRedirects\.php - */includes/specials/SpecialConfirmemail\.php - */includes/specials/SpecialDeadendpages\.php - */includes/specials/SpecialDeletedContributions\.php - */includes/specials/SpecialDoubleRedirects\.php - */includes/specials/SpecialEmailInvalidate\.php - */includes/specials/SpecialFewestrevisions\.php - */includes/specials/SpecialFileDuplicateSearch\.php - */includes/specials/SpecialLinkSearch\.php - */includes/specials/SpecialListDuplicatedFiles\.php - */includes/specials/SpecialListredirects\.php - */includes/specials/SpecialLonelypages\.php - */includes/specials/SpecialLongpages\.php - */includes/specials/SpecialMIMEsearch\.php - */includes/specials/SpecialMediaStatistics\.php - */includes/specials/SpecialMostcategories\.php */includes/specials/SpecialMostimages\.php - */includes/specials/SpecialMostlinked\.php - */includes/specials/SpecialMostlinkedcategories\.php - */includes/specials/SpecialMostlinkedtemplates\.php - */includes/specials/SpecialMostrevisions\.php */includes/specials/SpecialMovepage\.php - */includes/specials/SpecialNewimages\.php */includes/specials/SpecialRandompage\.php - */includes/specials/SpecialShortpages\.php - */includes/specials/SpecialUncategorizedcategories\.php - */includes/specials/SpecialUncategorizedimages\.php - */includes/specials/SpecialUncategorizedpages\.php - */includes/specials/SpecialUncategorizedtemplates\.php - */includes/specials/SpecialUnusedcategories\.php - */includes/specials/SpecialUnusedimages\.php - */includes/specials/SpecialUnusedtemplates\.php - */includes/specials/SpecialUnwatchedpages\.php */includes/specials/SpecialUserrights\.php - */includes/specials/SpecialWantedcategories\.php */includes/specials/SpecialWantedfiles\.php */includes/specials/SpecialWantedpages\.php - */includes/specials/SpecialWantedtemplates\.php - */includes/specials/SpecialWithoutinterwiki\.php */maintenance/CodeCleanerGlobalsPass.inc */maintenance/archives/upgradeLogging\.php */maintenance/benchmarks/bench_HTTP_HTTPS\.php diff --git a/RELEASE-NOTES-1.34 b/RELEASE-NOTES-1.34 index 2e220afa3c..b8c9102d82 100644 --- a/RELEASE-NOTES-1.34 +++ b/RELEASE-NOTES-1.34 @@ -190,6 +190,34 @@ because of Phabricator reports. * CryptRand class * CryptRand service * Functions of the MWCryptRand class: singleton(), wasStrong() and generate(). +* Various Special Page PHP Classes were renamed (mostly casing changes): + * SpecialAncientpages => SpecialAncientPages + * SpecialConfirmemail => SpecialConfirmEmail + * SpecialDeadendpages => SpecialDeadendPages + * SpecialFewestrevisions => SpecialFewestRevisions + * SpecialListredirects => SpecialListRedirects + * SpecialLonelypages => SpecialLonelyPages + * SpecialLongpages => SpecialLongPages + * SpecialMIMEsearch => SpecialMIMESearch + * SpecialMostcategories => SpecialMostCategories + * SpecialMostinterwikis => SpecialMostInterwikis + * SpecialMostlinked => SpecialMostLinked + * SpecialMostlinkedcategories => SpecialMostLinkedCategories + * SpecialMostlinkedtemplates => SpecialMostLinkedTemplates + * SpecialMostrevisions => SpecialMostRevisions + * SpecialNewimages => SpecialNewFiles + * SpecialShortpages => SpecialShortPages + * SpecialUncategorizedcategories => SpecialUncategorizedCategories + * SpecialUncategorizedimages => SpecialUncategorizedImages + * SpecialUncategorizedpages => SpecialUncategorizedPages + * SpecialUncategorizedtemplates => SpecialUncategorizedTemplates + * SpecialUnusedcategories => SpecialUnusedCategories + * SpecialUnusedimages => SpecialUnusedImages + * SpecialUnusedtemplates => SpecialUnusedTemplates + * SpecialUnwatchedpages => SpecialUnwatchedPages + * SpecialWantedcategories => SpecialWantedCategories + * SpecialWantedtemplates => SpecialWantedTemplates + * SpecialWithoutinterwiki => SpecialWithoutInterwiki * Language::setCode, deprecated in 1.32, was removed. Use Language::factory to create a new Language object with a different language code. * MWNamespace::clearCaches() has been removed. So has the $rebuild parameter @@ -532,6 +560,8 @@ because of Phabricator reports. be used instead. * The UserIsHidden hook is deprecated. Use GetUserBlock instead, and add a system block that hides the user. +* The GetBlockedStatus hook is deprecated. Use GetUserBlock instead, to add or + remove a block. === Other changes in 1.34 === * … diff --git a/autoload.php b/autoload.php index 87b2bb981a..f95e0015a7 100644 --- a/autoload.php +++ b/autoload.php @@ -20,7 +20,6 @@ $wgAutoloadLocalClasses = [ 'AllMessagesTablePager' => __DIR__ . '/includes/specials/pagers/AllMessagesTablePager.php', 'AllTrans' => __DIR__ . '/maintenance/language/alltrans.php', 'AlphabeticPager' => __DIR__ . '/includes/pager/AlphabeticPager.php', - 'AncientPagesPage' => __DIR__ . '/includes/specials/SpecialAncientpages.php', 'AnsiTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php', 'ApiAMCreateAccount' => __DIR__ . '/includes/api/ApiAMCreateAccount.php', 'ApiAuthManagerHelper' => __DIR__ . '/includes/api/ApiAuthManagerHelper.php', @@ -215,7 +214,6 @@ $wgAutoloadLocalClasses = [ 'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php', 'BmpHandler' => __DIR__ . '/includes/media/BmpHandler.php', 'BotPassword' => __DIR__ . '/includes/user/BotPassword.php', - 'BrokenRedirectsPage' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php', 'BufferingStatsdDataFactory' => __DIR__ . '/includes/libs/stats/BufferingStatsdDataFactory.php', 'CLIParser' => __DIR__ . '/maintenance/parse.php', 'CSSMin' => __DIR__ . '/includes/libs/CSSMin.php', @@ -366,7 +364,6 @@ $wgAutoloadLocalClasses = [ 'DateFormats' => __DIR__ . '/maintenance/language/date-formats.php', 'DateFormatter' => __DIR__ . '/includes/parser/DateFormatter.php', 'DateFormatterFactory' => __DIR__ . '/includes/parser/DateFormatterFactory.php', - 'DeadendPagesPage' => __DIR__ . '/includes/specials/SpecialDeadendpages.php', 'DeduplicateArchiveRevId' => __DIR__ . '/maintenance/deduplicateArchiveRevId.php', 'DeferrableCallback' => __DIR__ . '/includes/deferred/DeferrableCallback.php', 'DeferrableUpdate' => __DIR__ . '/includes/deferred/DeferrableUpdate.php', @@ -388,7 +385,6 @@ $wgAutoloadLocalClasses = [ 'DeletePageJob' => __DIR__ . '/includes/jobqueue/jobs/DeletePageJob.php', 'DeleteSelfExternals' => __DIR__ . '/maintenance/deleteSelfExternals.php', 'DeletedContribsPager' => __DIR__ . '/includes/specials/pagers/DeletedContribsPager.php', - 'DeletedContributionsPage' => __DIR__ . '/includes/specials/SpecialDeletedContributions.php', 'DependencyWrapper' => __DIR__ . '/includes/cache/dependency/DependencyWrapper.php', 'DeprecatedGlobal' => __DIR__ . '/includes/DeprecatedGlobal.php', 'DeprecatedInterfaceFinder' => __DIR__ . '/maintenance/findDeprecated.php', @@ -414,7 +410,6 @@ $wgAutoloadLocalClasses = [ 'DjVuImage' => __DIR__ . '/includes/media/DjVuImage.php', 'DnsSrvDiscoverer' => __DIR__ . '/includes/libs/DnsSrvDiscoverer.php', 'DoubleRedirectJob' => __DIR__ . '/includes/jobqueue/jobs/DoubleRedirectJob.php', - 'DoubleRedirectsPage' => __DIR__ . '/includes/specials/SpecialDoubleRedirects.php', 'DummyLinker' => __DIR__ . '/includes/DummyLinker.php', 'DummySearchIndexFieldDefinition' => __DIR__ . '/includes/search/DummySearchIndexFieldDefinition.php', 'DummyTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php', @@ -447,8 +442,6 @@ $wgAutoloadLocalClasses = [ 'EditPage' => __DIR__ . '/includes/EditPage.php', 'EditWatchlistCheckboxSeriesField' => __DIR__ . '/includes/specials/formfields/EditWatchlistCheckboxSeriesField.php', 'EditWatchlistNormalHTMLForm' => __DIR__ . '/includes/specials/forms/EditWatchlistNormalHTMLForm.php', - 'EmailConfirmation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php', - 'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialEmailInvalidate.php', 'EmailNotification' => __DIR__ . '/includes/mail/EmailNotification.php', 'EmaillingJob' => __DIR__ . '/includes/jobqueue/jobs/EmaillingJob.php', 'EmptyBagOStuff' => __DIR__ . '/includes/libs/objectcache/EmptyBagOStuff.php', @@ -505,7 +498,6 @@ $wgAutoloadLocalClasses = [ 'FeedItem' => __DIR__ . '/includes/changes/FeedItem.php', 'FeedUtils' => __DIR__ . '/includes/FeedUtils.php', 'FetchText' => __DIR__ . '/maintenance/fetchText.php', - 'FewestrevisionsPage' => __DIR__ . '/includes/specials/SpecialFewestrevisions.php', 'Field' => __DIR__ . '/includes/libs/rdbms/field/Field.php', 'File' => __DIR__ . '/includes/filerepo/file/File.php', 'FileAwareNodeVisitor' => __DIR__ . '/maintenance/findDeprecated.php', @@ -525,7 +517,6 @@ $wgAutoloadLocalClasses = [ 'FileContentsHasher' => __DIR__ . '/includes/utils/FileContentsHasher.php', 'FileDeleteForm' => __DIR__ . '/includes/FileDeleteForm.php', 'FileDependency' => __DIR__ . '/includes/cache/dependency/FileDependency.php', - 'FileDuplicateSearchPage' => __DIR__ . '/includes/specials/SpecialFileDuplicateSearch.php', 'FileJournal' => __DIR__ . '/includes/libs/filebackend/filejournal/FileJournal.php', 'FileOp' => __DIR__ . '/includes/libs/filebackend/fileop/FileOp.php', 'FileOpBatch' => __DIR__ . '/includes/libs/filebackend/FileOpBatch.php', @@ -785,14 +776,11 @@ $wgAutoloadLocalClasses = [ 'LinkCache' => __DIR__ . '/includes/cache/LinkCache.php', 'LinkFilter' => __DIR__ . '/includes/LinkFilter.php', 'LinkHolderArray' => __DIR__ . '/includes/parser/LinkHolderArray.php', - 'LinkSearchPage' => __DIR__ . '/includes/specials/SpecialLinkSearch.php', 'Linker' => __DIR__ . '/includes/Linker.php', 'LinksDeletionUpdate' => __DIR__ . '/includes/deferred/LinksDeletionUpdate.php', 'LinksUpdate' => __DIR__ . '/includes/deferred/LinksUpdate.php', - 'ListDuplicatedFilesPage' => __DIR__ . '/includes/specials/SpecialListDuplicatedFiles.php', 'ListToggle' => __DIR__ . '/includes/ListToggle.php', 'ListVariants' => __DIR__ . '/maintenance/language/listVariants.php', - 'ListredirectsPage' => __DIR__ . '/includes/specials/SpecialListredirects.php', 'LoadBalancer' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancer.php', 'LoadBalancerSingle' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php', 'LocalFile' => __DIR__ . '/includes/filerepo/file/LocalFile.php', @@ -818,9 +806,6 @@ $wgAutoloadLocalClasses = [ 'LoggedUpdateMaintenance' => __DIR__ . '/maintenance/Maintenance.php', 'LoginHelper' => __DIR__ . '/includes/specials/helpers/LoginHelper.php', 'LoginSignupSpecialPage' => __DIR__ . '/includes/specialpage/LoginSignupSpecialPage.php', - 'LonelyPagesPage' => __DIR__ . '/includes/specials/SpecialLonelypages.php', - 'LongPagesPage' => __DIR__ . '/includes/specials/SpecialLongpages.php', - 'MIMEsearchPage' => __DIR__ . '/includes/specials/SpecialMIMEsearch.php', 'MSCompoundFileReader' => __DIR__ . '/includes/libs/mime/MSCompoundFileReader.php', 'MWCallableUpdate' => __DIR__ . '/includes/deferred/MWCallableUpdate.php', 'MWCallbackStream' => __DIR__ . '/includes/http/MWCallbackStream.php', @@ -867,7 +852,6 @@ $wgAutoloadLocalClasses = [ 'McrUndoAction' => __DIR__ . '/includes/actions/McrUndoAction.php', 'MediaHandler' => __DIR__ . '/includes/media/MediaHandler.php', 'MediaHandlerFactory' => __DIR__ . '/includes/media/MediaHandlerFactory.php', - 'MediaStatisticsPage' => __DIR__ . '/includes/specials/SpecialMediaStatistics.php', 'MediaTransformError' => __DIR__ . '/includes/media/MediaTransformError.php', 'MediaTransformInvalidParametersException' => __DIR__ . '/includes/media/MediaTransformInvalidParametersException.php', 'MediaTransformOutput' => __DIR__ . '/includes/media/MediaTransformOutput.php', @@ -1002,13 +986,7 @@ $wgAutoloadLocalClasses = [ 'MigrateUserGroup' => __DIR__ . '/maintenance/migrateUserGroup.php', 'MimeAnalyzer' => __DIR__ . '/includes/libs/mime/MimeAnalyzer.php', 'MinifyScript' => __DIR__ . '/maintenance/minify.php', - 'MostcategoriesPage' => __DIR__ . '/includes/specials/SpecialMostcategories.php', 'MostimagesPage' => __DIR__ . '/includes/specials/SpecialMostimages.php', - 'MostinterwikisPage' => __DIR__ . '/includes/specials/SpecialMostinterwikis.php', - 'MostlinkedCategoriesPage' => __DIR__ . '/includes/specials/SpecialMostlinkedcategories.php', - 'MostlinkedPage' => __DIR__ . '/includes/specials/SpecialMostlinked.php', - 'MostlinkedTemplatesPage' => __DIR__ . '/includes/specials/SpecialMostlinkedtemplates.php', - 'MostrevisionsPage' => __DIR__ . '/includes/specials/SpecialMostrevisions.php', 'MoveBatch' => __DIR__ . '/maintenance/moveBatch.php', 'MoveFileOp' => __DIR__ . '/includes/libs/filebackend/fileop/MoveFileOp.php', 'MoveLogFormatter' => __DIR__ . '/includes/logging/MoveLogFormatter.php', @@ -1331,7 +1309,6 @@ $wgAutoloadLocalClasses = [ 'SerializedValueContainer' => __DIR__ . '/includes/libs/objectcache/serialized/SerializedValueContainer.php', 'SevenZipStream' => __DIR__ . '/maintenance/includes/SevenZipStream.php', 'ShiConverter' => __DIR__ . '/languages/classes/LanguageShi.php', - 'ShortPagesPage' => __DIR__ . '/includes/specials/SpecialShortpages.php', 'ShowJobs' => __DIR__ . '/maintenance/showJobs.php', 'ShowSiteStats' => __DIR__ . '/maintenance/showSiteStats.php', 'Site' => __DIR__ . '/includes/site/Site.php', @@ -1358,6 +1335,7 @@ $wgAutoloadLocalClasses = [ 'SpecialAllMessages' => __DIR__ . '/includes/specials/SpecialAllMessages.php', 'SpecialAllMyUploads' => __DIR__ . '/includes/specials/redirects/SpecialAllMyUploads.php', 'SpecialAllPages' => __DIR__ . '/includes/specials/SpecialAllPages.php', + 'SpecialAncientPages' => __DIR__ . '/includes/specials/SpecialAncientPages.php', 'SpecialApiHelp' => __DIR__ . '/includes/specials/SpecialApiHelp.php', 'SpecialApiSandbox' => __DIR__ . '/includes/specials/SpecialApiSandbox.php', 'SpecialAutoblockList' => __DIR__ . '/includes/specials/SpecialAutoblockList.php', @@ -1366,6 +1344,7 @@ $wgAutoloadLocalClasses = [ 'SpecialBlockList' => __DIR__ . '/includes/specials/SpecialBlockList.php', 'SpecialBookSources' => __DIR__ . '/includes/specials/SpecialBookSources.php', 'SpecialBotPasswords' => __DIR__ . '/includes/specials/SpecialBotPasswords.php', + 'SpecialBrokenRedirects' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php', 'SpecialCachedPage' => __DIR__ . '/includes/specials/SpecialCachedPage.php', 'SpecialCategories' => __DIR__ . '/includes/specials/SpecialCategories.php', 'SpecialChangeContentModel' => __DIR__ . '/includes/specials/SpecialChangeContentModel.php', @@ -1373,35 +1352,55 @@ $wgAutoloadLocalClasses = [ 'SpecialChangeEmail' => __DIR__ . '/includes/specials/SpecialChangeEmail.php', 'SpecialChangePassword' => __DIR__ . '/includes/specials/SpecialChangePassword.php', 'SpecialComparePages' => __DIR__ . '/includes/specials/SpecialComparePages.php', + 'SpecialConfirmEmail' => __DIR__ . '/includes/specials/SpecialConfirmEmail.php', 'SpecialContributions' => __DIR__ . '/includes/specials/SpecialContributions.php', 'SpecialCreateAccount' => __DIR__ . '/includes/specials/SpecialCreateAccount.php', + 'SpecialDeadendPages' => __DIR__ . '/includes/specials/SpecialDeadendPages.php', + 'SpecialDeletedContributions' => __DIR__ . '/includes/specials/SpecialDeletedContributions.php', 'SpecialDiff' => __DIR__ . '/includes/specials/SpecialDiff.php', + 'SpecialDoubleRedirects' => __DIR__ . '/includes/specials/SpecialDoubleRedirects.php', 'SpecialEditTags' => __DIR__ . '/includes/specials/SpecialEditTags.php', 'SpecialEditWatchlist' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php', + 'SpecialEmailInvalidate' => __DIR__ . '/includes/specials/SpecialEmailInvalidate.php', 'SpecialEmailUser' => __DIR__ . '/includes/specials/SpecialEmailUser.php', 'SpecialExpandTemplates' => __DIR__ . '/includes/specials/SpecialExpandTemplates.php', 'SpecialExport' => __DIR__ . '/includes/specials/SpecialExport.php', + 'SpecialFewestRevisions' => __DIR__ . '/includes/specials/SpecialFewestRevisions.php', + 'SpecialFileDuplicateSearch' => __DIR__ . '/includes/specials/SpecialFileDuplicateSearch.php', 'SpecialFilepath' => __DIR__ . '/includes/specials/SpecialFilepath.php', 'SpecialGoToInterwiki' => __DIR__ . '/includes/specials/SpecialGoToInterwiki.php', 'SpecialImport' => __DIR__ . '/includes/specials/SpecialImport.php', 'SpecialJavaScriptTest' => __DIR__ . '/includes/specials/SpecialJavaScriptTest.php', 'SpecialLinkAccounts' => __DIR__ . '/includes/specials/SpecialLinkAccounts.php', + 'SpecialLinkSearch' => __DIR__ . '/includes/specials/SpecialLinkSearch.php', 'SpecialListAdmins' => __DIR__ . '/includes/specials/redirects/SpecialListAdmins.php', 'SpecialListBots' => __DIR__ . '/includes/specials/redirects/SpecialListBots.php', + 'SpecialListDuplicatedFiles' => __DIR__ . '/includes/specials/SpecialListDuplicatedFiles.php', 'SpecialListFiles' => __DIR__ . '/includes/specials/SpecialListFiles.php', 'SpecialListGrants' => __DIR__ . '/includes/specials/SpecialListGrants.php', 'SpecialListGroupRights' => __DIR__ . '/includes/specials/SpecialListGroupRights.php', + 'SpecialListRedirects' => __DIR__ . '/includes/specials/SpecialListRedirects.php', 'SpecialListUsers' => __DIR__ . '/includes/specials/SpecialListUsers.php', 'SpecialLockdb' => __DIR__ . '/includes/specials/SpecialLockdb.php', 'SpecialLog' => __DIR__ . '/includes/specials/SpecialLog.php', + 'SpecialLonelyPages' => __DIR__ . '/includes/specials/SpecialLonelyPages.php', + 'SpecialLongPages' => __DIR__ . '/includes/specials/SpecialLongPages.php', + 'SpecialMIMESearch' => __DIR__ . '/includes/specials/SpecialMIMESearch.php', + 'SpecialMediaStatistics' => __DIR__ . '/includes/specials/SpecialMediaStatistics.php', 'SpecialMergeHistory' => __DIR__ . '/includes/specials/SpecialMergeHistory.php', + 'SpecialMostCategories' => __DIR__ . '/includes/specials/SpecialMostCategories.php', + 'SpecialMostInterwikis' => __DIR__ . '/includes/specials/SpecialMostInterwikis.php', + 'SpecialMostLinked' => __DIR__ . '/includes/specials/SpecialMostLinked.php', + 'SpecialMostLinkedCategories' => __DIR__ . '/includes/specials/SpecialMostLinkedCategories.php', + 'SpecialMostLinkedTemplates' => __DIR__ . '/includes/specials/SpecialMostLinkedTemplates.php', + 'SpecialMostRevisions' => __DIR__ . '/includes/specials/SpecialMostRevisions.php', 'SpecialMute' => __DIR__ . '/includes/specials/SpecialMute.php', 'SpecialMyLanguage' => __DIR__ . '/includes/specials/SpecialMyLanguage.php', 'SpecialMycontributions' => __DIR__ . '/includes/specials/redirects/SpecialMycontributions.php', 'SpecialMypage' => __DIR__ . '/includes/specials/redirects/SpecialMypage.php', 'SpecialMytalk' => __DIR__ . '/includes/specials/redirects/SpecialMytalk.php', 'SpecialMyuploads' => __DIR__ . '/includes/specials/redirects/SpecialMyuploads.php', - 'SpecialNewFiles' => __DIR__ . '/includes/specials/SpecialNewimages.php', + 'SpecialNewFiles' => __DIR__ . '/includes/specials/SpecialNewFiles.php', 'SpecialNewSection' => __DIR__ . '/includes/specials/SpecialNewSection.php', 'SpecialNewpages' => __DIR__ . '/includes/specials/SpecialNewpages.php', 'SpecialPage' => __DIR__ . '/includes/specialpage/SpecialPage.php', @@ -1429,22 +1428,34 @@ $wgAutoloadLocalClasses = [ 'SpecialRevisionDelete' => __DIR__ . '/includes/specials/SpecialRevisionDelete.php', 'SpecialRunJobs' => __DIR__ . '/includes/specials/SpecialRunJobs.php', 'SpecialSearch' => __DIR__ . '/includes/specials/SpecialSearch.php', + 'SpecialShortPages' => __DIR__ . '/includes/specials/SpecialShortPages.php', 'SpecialSpecialpages' => __DIR__ . '/includes/specials/SpecialSpecialpages.php', 'SpecialStatistics' => __DIR__ . '/includes/specials/SpecialStatistics.php', 'SpecialTags' => __DIR__ . '/includes/specials/SpecialTags.php', 'SpecialTrackingCategories' => __DIR__ . '/includes/specials/SpecialTrackingCategories.php', 'SpecialUnblock' => __DIR__ . '/includes/specials/SpecialUnblock.php', + 'SpecialUncategorizedCategories' => __DIR__ . '/includes/specials/SpecialUncategorizedCategories.php', + 'SpecialUncategorizedImages' => __DIR__ . '/includes/specials/SpecialUncategorizedImages.php', + 'SpecialUncategorizedPages' => __DIR__ . '/includes/specials/SpecialUncategorizedPages.php', + 'SpecialUncategorizedTemplates' => __DIR__ . '/includes/specials/SpecialUncategorizedTemplates.php', 'SpecialUndelete' => __DIR__ . '/includes/specials/SpecialUndelete.php', 'SpecialUnlinkAccounts' => __DIR__ . '/includes/specials/SpecialUnlinkAccounts.php', 'SpecialUnlockdb' => __DIR__ . '/includes/specials/SpecialUnlockdb.php', + 'SpecialUnusedCategories' => __DIR__ . '/includes/specials/SpecialUnusedCategories.php', + 'SpecialUnusedImages' => __DIR__ . '/includes/specials/SpecialUnusedImages.php', + 'SpecialUnusedTemplates' => __DIR__ . '/includes/specials/SpecialUnusedTemplates.php', + 'SpecialUnwatchedPages' => __DIR__ . '/includes/specials/SpecialUnwatchedPages.php', 'SpecialUpload' => __DIR__ . '/includes/specials/SpecialUpload.php', 'SpecialUploadStash' => __DIR__ . '/includes/specials/SpecialUploadStash.php', 'SpecialUploadStashTooLargeException' => __DIR__ . '/includes/specials/exception/SpecialUploadStashTooLargeException.php', 'SpecialUserLogin' => __DIR__ . '/includes/specials/SpecialUserLogin.php', 'SpecialUserLogout' => __DIR__ . '/includes/specials/SpecialUserLogout.php', 'SpecialVersion' => __DIR__ . '/includes/specials/SpecialVersion.php', + 'SpecialWantedCategories' => __DIR__ . '/includes/specials/SpecialWantedCategories.php', + 'SpecialWantedTemplates' => __DIR__ . '/includes/specials/SpecialWantedTemplates.php', 'SpecialWatchlist' => __DIR__ . '/includes/specials/SpecialWatchlist.php', 'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatLinksHere.php', + 'SpecialWithoutInterwiki' => __DIR__ . '/includes/specials/SpecialWithoutInterwiki.php', 'SqlBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php', 'SqlSearchResult' => __DIR__ . '/includes/search/SqlSearchResult.php', 'SqlSearchResultSet' => __DIR__ . '/includes/search/SqlSearchResultSet.php', @@ -1517,10 +1528,6 @@ $wgAutoloadLocalClasses = [ 'UDPTransport' => __DIR__ . '/includes/libs/UDPTransport.php', 'UIDGenerator' => __DIR__ . '/includes/utils/UIDGenerator.php', 'UcdXmlReader' => __DIR__ . '/maintenance/language/generateCollationData.php', - 'UncategorizedCategoriesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedcategories.php', - 'UncategorizedImagesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedimages.php', - 'UncategorizedPagesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedpages.php', - 'UncategorizedTemplatesPage' => __DIR__ . '/includes/specials/SpecialUncategorizedtemplates.php', 'Undelete' => __DIR__ . '/maintenance/undelete.php', 'UnifiedDiffFormatter' => __DIR__ . '/includes/diff/UnifiedDiffFormatter.php', 'UnknownContent' => __DIR__ . '/includes/content/UnknownContent.php', @@ -1529,11 +1536,7 @@ $wgAutoloadLocalClasses = [ 'UnprotectAction' => __DIR__ . '/includes/actions/UnprotectAction.php', 'UnregisteredLocalFile' => __DIR__ . '/includes/filerepo/file/UnregisteredLocalFile.php', 'UnsupportedSlotDiffRenderer' => __DIR__ . '/includes/diff/UnsupportedSlotDiffRenderer.php', - 'UnusedCategoriesPage' => __DIR__ . '/includes/specials/SpecialUnusedcategories.php', - 'UnusedimagesPage' => __DIR__ . '/includes/specials/SpecialUnusedimages.php', - 'UnusedtemplatesPage' => __DIR__ . '/includes/specials/SpecialUnusedtemplates.php', 'UnwatchAction' => __DIR__ . '/includes/actions/UnwatchAction.php', - 'UnwatchedpagesPage' => __DIR__ . '/includes/specials/SpecialUnwatchedpages.php', 'UpdateArticleCount' => __DIR__ . '/maintenance/updateArticleCount.php', 'UpdateCollation' => __DIR__ . '/maintenance/updateCollation.php', 'UpdateDoubleWidthSearch' => __DIR__ . '/maintenance/updateDoubleWidthSearch.php', @@ -1597,11 +1600,9 @@ $wgAutoloadLocalClasses = [ 'WANCacheReapUpdate' => __DIR__ . '/includes/deferred/WANCacheReapUpdate.php', 'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/wancache/WANObjectCache.php', 'WANObjectCacheReaper' => __DIR__ . '/includes/libs/objectcache/wancache/WANObjectCacheReaper.php', - 'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php', 'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php', 'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php', 'WantedQueryPage' => __DIR__ . '/includes/specialpage/WantedQueryPage.php', - 'WantedTemplatesPage' => __DIR__ . '/includes/specials/SpecialWantedtemplates.php', 'WatchAction' => __DIR__ . '/includes/actions/WatchAction.php', 'WatchedItem' => __DIR__ . '/includes/watcheditem/WatchedItem.php', 'WatchedItemQueryService' => __DIR__ . '/includes/watcheditem/WatchedItemQueryService.php', @@ -1705,7 +1706,6 @@ $wgAutoloadLocalClasses = [ 'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php', 'WikitextLogFormatter' => __DIR__ . '/includes/logging/WikitextLogFormatter.php', 'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php', - 'WithoutInterwikiPage' => __DIR__ . '/includes/specials/SpecialWithoutinterwiki.php', 'WordLevelDiff' => __DIR__ . '/includes/diff/WordLevelDiff.php', 'WrapOldPasswords' => __DIR__ . '/maintenance/wrapOldPasswords.php', 'XCFHandler' => __DIR__ . '/includes/media/XCFHandler.php', diff --git a/docs/hooks.txt b/docs/hooks.txt index 43234c6590..43bfd8d4b4 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -1585,7 +1585,8 @@ entitled to be in. $user: user to promote. &$promote: groups that will be added. -'GetBlockedStatus': after loading blocking status of an user from the database +'GetBlockedStatus': DEPRECATED since 1.34 - use GetUserBlock instead. After +loading blocking status of a user from the database &$user: user (object) being checked 'GetCacheVaryCookies': Get cookies that should vary cache options. diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index f6ac3422de..c9bed29398 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4888,6 +4888,7 @@ $wgDefaultUserOptions = [ 'wllimit' => 250, 'useeditwarning' => 1, 'prefershttps' => 1, + 'requireemail' => 0, ]; /** @@ -4957,6 +4958,15 @@ $wgSessionProviders = [ ], ]; +/** + * Temporary feature flag that controls whether users will see a checkbox allowing them to + * require providing email during password resets. + * + * @deprecated This feature is under development, don't assume this flag's existence or function + * outside of MediaWiki. + */ +$wgAllowRequiringEmailForResets = false; + /** @} */ # end user accounts } /************************************************************************//** diff --git a/includes/MovePage.php b/includes/MovePage.php index 564c8f4d6c..634e7affe2 100644 --- a/includes/MovePage.php +++ b/includes/MovePage.php @@ -473,6 +473,7 @@ class MovePage { $mp = new MovePage( $oldSubpage, $newSubpage ); $method = $checkPermissions ? 'moveIfAllowed' : 'move'; + /** @var Status $status */ $status = $mp->$method( $user, $reason, $createRedirect, $changeTags ); if ( $status->isOK() ) { $status->setResult( true, $newSubpage->getPrefixedText() ); @@ -508,7 +509,7 @@ class MovePage { Hooks::run( 'TitleMoveStarting', [ $this->oldTitle, $this->newTitle, $user ] ); - $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE ); + $pageid = $this->oldTitle->getArticleID( Title::READ_LATEST ); $protected = $this->oldTitle->isProtected(); // Do the actual move; if this fails, it will throw an MWException(!) diff --git a/includes/Revision.php b/includes/Revision.php index 292d6ba574..828f647552 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -1005,7 +1005,7 @@ class Revision implements IDBAccessObject { $comment = CommentStoreComment::newUnsavedComment( $summary, null ); - $title = Title::newFromID( $pageId, Title::GAID_FOR_UPDATE ); + $title = Title::newFromID( $pageId, Title::READ_LATEST ); if ( $title === null ) { return null; } diff --git a/includes/Revision/RevisionStore.php b/includes/Revision/RevisionStore.php index 73f622a3db..735a2124d4 100644 --- a/includes/Revision/RevisionStore.php +++ b/includes/Revision/RevisionStore.php @@ -326,10 +326,10 @@ class RevisionStore $canUseTitleNewFromId = ( $pageId !== null && $pageId > 0 && $this->dbDomain === false ); list( $dbMode, $dbOptions ) = DBAccessObjectUtils::getDBOptions( $queryFlags ); - $titleFlags = ( $dbMode == DB_MASTER ? Title::GAID_FOR_UPDATE : 0 ); // Loading by ID is best, but Title::newFromID does not support that for foreign IDs. if ( $canUseTitleNewFromId ) { + $titleFlags = ( $dbMode == DB_MASTER ? Title::READ_LATEST : 0 ); // TODO: better foreign title handling (introduce TitleFactory) $title = Title::newFromID( $pageId, $titleFlags ); if ( $title ) { diff --git a/includes/Setup.php b/includes/Setup.php index 39f0c8116f..518531aacf 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -808,22 +808,17 @@ if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) { // Initialize the session try { $session = MediaWiki\Session\SessionManager::getGlobalSession(); - } catch ( OverflowException $ex ) { - if ( isset( $ex->sessionInfos ) && count( $ex->sessionInfos ) >= 2 ) { - // The exception is because the request had multiple possible - // sessions tied for top priority. Report this to the user. - $list = []; - foreach ( $ex->sessionInfos as $info ) { - $list[] = $info->getProvider()->describe( $wgContLang ); - } - $list = $wgContLang->listToText( $list ); - throw new HttpError( 400, - Message::newFromKey( 'sessionmanager-tie', $list )->inLanguage( $wgContLang )->plain() - ); + } catch ( MediaWiki\Session\SessionOverflowException $ex ) { + // The exception is because the request had multiple possible + // sessions tied for top priority. Report this to the user. + $list = []; + foreach ( $ex->getSessionInfos() as $info ) { + $list[] = $info->getProvider()->describe( $wgContLang ); } - - // Not the one we want, rethrow - throw $ex; + $list = $wgContLang->listToText( $list ); + throw new HttpError( 400, + Message::newFromKey( 'sessionmanager-tie', $list )->inLanguage( $wgContLang )->plain() + ); } if ( $session->isPersistent() ) { diff --git a/includes/api/ApiSetNotificationTimestamp.php b/includes/api/ApiSetNotificationTimestamp.php index d2bbe7bb88..7e9f56d09c 100644 --- a/includes/api/ApiSetNotificationTimestamp.php +++ b/includes/api/ApiSetNotificationTimestamp.php @@ -93,7 +93,7 @@ class ApiSetNotificationTimestamp extends ApiBase { $titles = $pageSet->getGoodTitles(); $title = reset( $titles ); if ( $title ) { - $revid = $title->getNextRevisionID( $params['newerthanrevid'], Title::GAID_FOR_UPDATE ); + $revid = $title->getNextRevisionID( $params['newerthanrevid'], Title::READ_LATEST ); if ( $revid ) { $timestamp = $dbw->timestamp( MediaWikiServices::getInstance()->getRevisionStore()->getTimestampFromId( $title, $revid ) diff --git a/includes/api/i18n/ar.json b/includes/api/i18n/ar.json index dc9c516d76..09d9c468a5 100644 --- a/includes/api/i18n/ar.json +++ b/includes/api/i18n/ar.json @@ -1607,6 +1607,8 @@ "apierror-cantoverwrite-sharedfile": "الملف الهدف موجود في مستودع مشترك وليست لديك صلاحية لتجاوزه.", "apierror-cantsend": "لم تقم بتسجيل الدخول أو ليس لديك عنوان بريد إلكتروني مؤكد أو غير مسموح لك بإرسال بريد إلكتروني إلى مستخدمين آخرين; لذلك لا يمكنك إرسال بريد إلكتروني.", "apierror-cantundelete": "تعذر الاسترجاع: قد لا تكون المراجعات المطلوبة موجودة، أو ربما تم الاسترجاع بالفعل.", + "apierror-cantview-deleted-description": "ليست لديك صلايبة لعرض أوصاف الملفات المحذوفة.", + "apierror-cantview-deleted-metadata": "ليست لديك صلايبة لعرض البيانات الوصفية للملفات المحذوفة.", "apierror-changeauth-norequest": "فشل في إنشاء طلب التغيير.", "apierror-chunk-too-small": "الحد الأدنى لحجم القطعة هو $1 {{PLURAL:$1|بايت}} للقطع غير النهائية.", "apierror-cidrtoobroad": "لا يُقبَل مدى $1 CIDR أكبر من /$2.", diff --git a/includes/api/i18n/fr.json b/includes/api/i18n/fr.json index 628edfafaa..7c5d2b5676 100644 --- a/includes/api/i18n/fr.json +++ b/includes/api/i18n/fr.json @@ -1630,6 +1630,8 @@ "apierror-cantoverwrite-sharedfile": "Le fichier cible existe dans un dépôt partagé et vous n’avez pas le droit de l’écraser.", "apierror-cantsend": "Vous n’êtes pas connecté, vous n’avez pas d’adresse de courriel confirmée, ou vous n’êtes pas autorisé à envoyer des courriels aux autres utilisateurs, donc vous ne pouvez envoyer de courriel.", "apierror-cantundelete": "Impossible d’annuler : les révisions demandées peuvent ne plus exister, ou avoir déjà été annulées.", + "apierror-cantview-deleted-description": "Vous n’avez pas le droit d’afficher les descriptions des fichiers supprimés.", + "apierror-cantview-deleted-metadata": "Vous n’avez pas le droit d’afficher les métadonnées des fichiers supprimés.", "apierror-changeauth-norequest": "Échec à la création de la requête de modification.", "apierror-chunk-too-small": "La taille minimale d’un segment est de $1 {{PLURAL:$1|octet|octets}} pour les segments hors le dernier.", "apierror-cidrtoobroad": "Les plages CIDR $1 plus large que /$2 ne sont pas acceptées.", diff --git a/includes/api/i18n/pl.json b/includes/api/i18n/pl.json index e71a9dc78e..d8ff5398c1 100644 --- a/includes/api/i18n/pl.json +++ b/includes/api/i18n/pl.json @@ -17,7 +17,8 @@ "InternerowyGołąb", "CiaPan", "Vlad5250", - "Railfail536" + "Railfail536", + "Rail" ] }, "apihelp-main-extended-description": "
\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentacja]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista dyskusyjna]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Ogłoszenia dotyczące API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Błędy i propozycje]\n
\nStan: Wszystkie funkcje opisane na tej stronie powinny działać, ale API nadal jest aktywnie rozwijane i mogą się zmienić w dowolnym czasie. Subskrybuj [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ listę dyskusyjną mediawiki-api-announce], aby móc na bieżąco dowiadywać się o aktualizacjach.\n\nBłędne żądania: Gdy zostanie wysłane błędne żądanie do API, zostanie wysłany w odpowiedzi nagłówek HTTP z kluczem \"MediaWiki-API-Error\" i zarówno jego wartość jak i wartość kodu błędu wysłanego w odpowiedzi będą miały taką samą wartość. Aby uzyskać więcej informacji, zobacz [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Błędy i ostrzeżenia]].\n\nTestowanie: Aby łatwo testować żądania API, zobacz [[Special:ApiSandbox]].", @@ -658,6 +659,8 @@ "apierror-cantimport": "Nie masz uprawnień do importowania stron.", "apierror-cantsend": "Nie jesteś zalogowany, nie masz potwierdzonego adresu e-mail, albo nie masz prawa wysyłać e-maili do innych użytkowników, więc nie możesz wysłać wiadomości e-mail.", "apierror-cantundelete": "Nie można przywrócić: dana wersja nie istnieje albo została już przywrócona.", + "apierror-cantview-deleted-description": "Nie masz uprawnień do podglądu opisów usuniętych plików.", + "apierror-cantview-deleted-metadata": "Nie masz uprawnień do podglądu metadanych usuniętych plików.", "apierror-exceptioncaught": "[$1] Stwierdzono wyjątek: $2", "apierror-filedoesnotexist": "Plik nie istnieje.", "apierror-import-unknownerror": "Nieznany błąd podczas importowania: $1.", diff --git a/includes/api/i18n/zh-hant.json b/includes/api/i18n/zh-hant.json index 14a7717171..7ee3751ae0 100644 --- a/includes/api/i18n/zh-hant.json +++ b/includes/api/i18n/zh-hant.json @@ -1613,6 +1613,8 @@ "apierror-cantoverwrite-sharedfile": "目標檔案存在於分享儲存庫上,因此您沒有權限來覆蓋掉。", "apierror-cantsend": "您尚未登入,您沒有已確認的電子郵件地址,或是您未被允許發送電子郵件給其他人,因此您不能發送電子郵件。", "apierror-cantundelete": "無法取消刪除:請求的修訂可能不存在,或是可能已被取消刪除。", + "apierror-cantview-deleted-description": "您沒有權限來檢視被刪除檔案的描述內容。", + "apierror-cantview-deleted-metadata": "您沒有權限來檢視被刪除檔案的詮釋資料。", "apierror-changeauth-norequest": "建立更改請求失敗。", "apierror-chunk-too-small": "對於非最終塊,最小塊的大小為 $1 {{PLURAL:$1|位元組|位元組}}。", "apierror-cidrtoobroad": "不能接受超出 /$2 的 $1 CIDR 範圍。", diff --git a/includes/cache/LinkCache.php b/includes/cache/LinkCache.php index 80181179e4..b2f24525a5 100644 --- a/includes/cache/LinkCache.php +++ b/includes/cache/LinkCache.php @@ -89,6 +89,7 @@ class LinkCache { * * @param bool|null $update * @return bool + * @deprecated Since 1.34 */ public function forUpdate( $update = null ) { return wfSetVar( $this->mForUpdate, $update ); diff --git a/includes/cache/localisation/LCStoreDB.php b/includes/cache/localisation/LCStoreDB.php index 88a70421fa..a79325aa61 100644 --- a/includes/cache/localisation/LCStoreDB.php +++ b/includes/cache/localisation/LCStoreDB.php @@ -27,18 +27,20 @@ use Wikimedia\Rdbms\DBQueryError; * This will work on any MediaWiki installation. */ class LCStoreDB implements LCStore { - /** @var string */ - private $currentLang; - /** @var bool */ - private $writesDone = false; + /** @var string Language code */ + private $code; + /** @var array Server configuration map */ + private $server; + + /** @var array Rows buffered for insertion */ + private $batch = []; + /** @var IDatabase|null */ private $dbw; - /** @var array */ - private $batch = []; - /** @var bool */ + /** @var bool Whether a batch of writes were recently written */ + private $writesDone = false; + /** @var bool Whether the DB is read-only or otherwise unavailable for writes */ private $readOnly = false; - /** @var array Server configuration map */ - private $server; public function __construct( $params ) { $this->server = $params['server'] ?? []; @@ -74,14 +76,14 @@ class LCStoreDB implements LCStore { $dbw = $this->getWriteConnection(); $this->readOnly = $dbw->isReadOnly(); - $this->currentLang = $code; + $this->code = $code; $this->batch = []; } public function finishWrite() { if ( $this->readOnly ) { return; - } elseif ( is_null( $this->currentLang ) ) { + } elseif ( is_null( $this->code ) ) { throw new MWException( __CLASS__ . ': must call startWrite() before finishWrite()' ); } @@ -91,7 +93,7 @@ class LCStoreDB implements LCStore { $dbw = $this->getWriteConnection(); $dbw->startAtomic( __METHOD__ ); try { - $dbw->delete( 'l10n_cache', [ 'lc_lang' => $this->currentLang ], __METHOD__ ); + $dbw->delete( 'l10n_cache', [ 'lc_lang' => $this->code ], __METHOD__ ); foreach ( array_chunk( $this->batch, 500 ) as $rows ) { $dbw->insert( 'l10n_cache', $rows, __METHOD__ ); } @@ -108,21 +110,21 @@ class LCStoreDB implements LCStore { $trxProfiler->setSilenced( $oldSilenced ); } - $this->currentLang = null; + $this->code = null; $this->batch = []; } public function set( $key, $value ) { if ( $this->readOnly ) { return; - } elseif ( is_null( $this->currentLang ) ) { + } elseif ( is_null( $this->code ) ) { throw new MWException( __CLASS__ . ': must call startWrite() before set()' ); } $dbw = $this->getWriteConnection(); $this->batch[] = [ - 'lc_lang' => $this->currentLang, + 'lc_lang' => $this->code, 'lc_key' => $key, 'lc_value' => $dbw->encodeBlob( serialize( $value ) ) ]; diff --git a/includes/deferred/LinksUpdate.php b/includes/deferred/LinksUpdate.php index 74e236fd4d..8345ee6575 100644 --- a/includes/deferred/LinksUpdate.php +++ b/includes/deferred/LinksUpdate.php @@ -125,7 +125,7 @@ class LinksUpdate extends DataUpdate { if ( !$this->mId ) { // NOTE: subclasses may initialize mId before calling this constructor! - $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE ); + $this->mId = $title->getArticleID( Title::READ_LATEST ); } if ( !$this->mId ) { diff --git a/includes/externalstore/ExternalStoreDB.php b/includes/externalstore/ExternalStoreDB.php index 4db351b25b..264eabcd44 100644 --- a/includes/externalstore/ExternalStoreDB.php +++ b/includes/externalstore/ExternalStoreDB.php @@ -26,6 +26,7 @@ use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\DBConnRef; use Wikimedia\Rdbms\MaintainableDBConnRef; use Wikimedia\Rdbms\DatabaseDomain; +use Wikimedia\Rdbms\DBUnexpectedError; /** * DB accessible external objects. @@ -113,7 +114,11 @@ class ExternalStoreDB extends ExternalStoreMedium { */ public function store( $location, $data ) { $dbw = $this->getMaster( $location ); - $dbw->insert( $this->getTable( $dbw ), [ 'blob_text' => $data ], __METHOD__ ); + $dbw->insert( + $this->getTable( $dbw, $location ), + [ 'blob_text' => $data ], + __METHOD__ + ); $id = $dbw->insertId(); if ( !$id ) { throw new MWException( __METHOD__ . ': no insert ID' ); @@ -149,10 +154,11 @@ class ExternalStoreDB extends ExternalStoreMedium { /** * Get a replica DB connection for the specified cluster * + * @since 1.34 * @param string $cluster Cluster name * @return DBConnRef */ - public function getSlave( $cluster ) { + public function getReplica( $cluster ) { $lb = $this->getLoadBalancer( $cluster ); return $lb->getConnectionRef( @@ -163,6 +169,17 @@ class ExternalStoreDB extends ExternalStoreMedium { ); } + /** + * Get a replica DB connection for the specified cluster + * + * @param string $cluster Cluster name + * @return DBConnRef + * @deprecated since 1.34 + */ + public function getSlave( $cluster ) { + return $this->getReplica( $cluster ); + } + /** * Get a master database connection for the specified cluster * @@ -211,15 +228,55 @@ class ExternalStoreDB extends ExternalStoreMedium { * Get the 'blobs' table name for this database * * @param IDatabase $db + * @param string|null $cluster Cluster name * @return string Table name ('blobs' by default) */ - public function getTable( $db ) { - $table = $db->getLBInfo( 'blobs table' ); - if ( is_null( $table ) ) { - $table = 'blobs'; + public function getTable( $db, $cluster = null ) { + if ( $cluster !== null ) { + $lb = $this->getLoadBalancer( $cluster ); + $info = $lb->getServerInfo( $lb->getWriterIndex() ); + if ( isset( $info['blobs table'] ) ) { + return $info['blobs table']; + } } - return $table; + return $db->getLBInfo( 'blobs table' ) ?? 'blobs'; // b/c + } + + /** + * Create the appropriate blobs table on this cluster + * + * @see getTable() + * @since 1.34 + * @param string $cluster + */ + public function initializeTable( $cluster ) { + global $IP; + + static $supportedTypes = [ 'mysql', 'sqlite' ]; + + $dbw = $this->getMaster( $cluster ); + if ( !in_array( $dbw->getType(), $supportedTypes, true ) ) { + throw new DBUnexpectedError( $dbw, "RDBMS type '{$dbw->getType()}' not supported." ); + } + + $sqlFilePath = "$IP/maintenance/storage/blobs.sql"; + $sql = file_get_contents( $sqlFilePath ); + if ( $sql === false ) { + throw new RuntimeException( "Failed to read '$sqlFilePath'." ); + } + + $rawTable = $this->getTable( $dbw, $cluster ); // e.g. "blobs_cluster23" + $encTable = $dbw->tableName( $rawTable ); + $dbw->query( + str_replace( + [ '/*$wgDBprefix*/blobs', '/*_*/blobs' ], + [ $encTable, $encTable ], + $sql + ), + __METHOD__, + $dbw::QUERY_IGNORE_DBO_TRX + ); } /** @@ -251,15 +308,23 @@ class ExternalStoreDB extends ExternalStoreMedium { $this->logger->debug( "ExternalStoreDB::fetchBlob cache miss on $cacheID" ); - $dbr = $this->getSlave( $cluster ); - $ret = $dbr->selectField( $this->getTable( $dbr ), - 'blob_text', [ 'blob_id' => $id ], __METHOD__ ); + $dbr = $this->getReplica( $cluster ); + $ret = $dbr->selectField( + $this->getTable( $dbr, $cluster ), + 'blob_text', + [ 'blob_id' => $id ], + __METHOD__ + ); if ( $ret === false ) { $this->logger->info( "ExternalStoreDB::fetchBlob master fallback on $cacheID" ); // Try the master $dbw = $this->getMaster( $cluster ); - $ret = $dbw->selectField( $this->getTable( $dbw ), - 'blob_text', [ 'blob_id' => $id ], __METHOD__ ); + $ret = $dbw->selectField( + $this->getTable( $dbw, $cluster ), + 'blob_text', + [ 'blob_id' => $id ], + __METHOD__ + ); if ( $ret === false ) { $this->logger->error( "ExternalStoreDB::fetchBlob master failed to find $cacheID" ); } @@ -283,9 +348,9 @@ class ExternalStoreDB extends ExternalStoreMedium { * Unlocated ids are not represented */ private function batchFetchBlobs( $cluster, array $ids ) { - $dbr = $this->getSlave( $cluster ); + $dbr = $this->getReplica( $cluster ); $res = $dbr->select( - $this->getTable( $dbr ), + $this->getTable( $dbr, $cluster ), [ 'blob_id', 'blob_text' ], [ 'blob_id' => array_keys( $ids ) ], __METHOD__ @@ -302,7 +367,8 @@ class ExternalStoreDB extends ExternalStoreMedium { ); // Try the master $dbw = $this->getMaster( $cluster ); - $res = $dbw->select( $this->getTable( $dbr ), + $res = $dbw->select( + $this->getTable( $dbr, $cluster ), [ 'blob_id', 'blob_text' ], [ 'blob_id' => array_keys( $ids ) ], __METHOD__ ); diff --git a/includes/installer/i18n/ia.json b/includes/installer/i18n/ia.json index eae79b0402..e5ebb42408 100644 --- a/includes/installer/i18n/ia.json +++ b/includes/installer/i18n/ia.json @@ -46,7 +46,11 @@ "config-restart": "Si, reinitia lo", "config-welcome": "=== Verificationes del ambiente ===\nVerificationes de base essera ora exequite pro determinar si iste ambiente es apte pro le installation de MediaWiki.\nNon oblida de includer iste information si tu cerca adjuta pro completar le installation.", "config-welcome-section-copyright": "=== Copyright and Terms ===\n\n$1\n\nIste programma es software libere; vos pote redistribuer lo e/o modificar lo sub le conditiones del Licentia Public General de GNU publicate per le Free Software Foundation; version 2 del Licentia, o (a vostre option) qualcunque version posterior.\n\nIste programma es distribuite in le sperantia que illo sia utile, ma '''sin garantia''', sin mesmo le implicite garantia de '''commercialisation''' o '''aptitude pro un proposito particular'''.\nVide le Licentia Public General de GNU pro plus detalios.\n\nVos deberea haber recipite [$2 un exemplar del Licentia Public General de GNU] con iste programma; si non, scribe al Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lege lo in linea].", - "config-sidebar": "* [https://www.mediawiki.org Pagina principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guida pro usatores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guida pro administratores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* Lege me\n* Notas de iste version\n* Conditiones de copia\n* Actualisation", + "config-sidebar": "* [https://www.mediawiki.org Pagina principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guida pro usatores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guida pro administratores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]", + "config-sidebar-readme": "Lege me", + "config-sidebar-relnotes": "Notas de iste version", + "config-sidebar-license": "Conditiones de copia", + "config-sidebar-upgrade": "Actualisation", "config-env-good": "Le ambiente ha essite verificate.\nTu pote installar MediaWiki.", "config-env-bad": "Le ambiente ha essite verificate.\nTu non pote installar MediaWiki.", "config-env-php": "PHP $1 es installate.", @@ -79,7 +83,7 @@ "config-uploads-not-safe": "'''Aviso:''' Le directorio predefinite pro files incargate $1 es vulnerabile al execution arbitrari de scripts.\nBen que MediaWiki verifica tote le files incargate contra le menacias de securitate, il es altemente recommendate [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security remediar iste vulnerabilitate de securitate] ante de activar le incargamento de files.", "config-no-cli-uploads-check": "'''Attention:''' Le directorio predefinite pro files incargate ($1) non es verificate contra le vulnerabilitate\nal execution arbitrari de scripts durante le installation de CLI.", "config-brokenlibxml": "Vostre systema ha un combination de versiones de PHP e libxml2 que es defectuose e pote causar corruption celate de datos in MediaWiki e altere applicationes web.\nActualisa a libxml2 2.7.3 o plus recente ([https://bugs.php.net/bug.php?id=45996 problema reportate presso PHP]).\nInstallation abortate.", - "config-suhosin-max-value-length": "Suhosin es installate e limita parametro length de GET a $1 bytes.\nLe componente ResourceLoader de MediaWiki va contornar iste limite, ma isto prejudicara le rendimento.\nSi possibile, tu deberea mitter suhosin.get.max_value_length a 1024 o superior in php.ini, e mitter $wgResourceLoaderMaxQueryLength al mesme valor in LocalSettings.php.", + "config-suhosin-max-value-length": "Suhosin es installate e limita parametro length de GET a $1 bytes.\nMediaWiki require que suhosin.get.max_value_length sia al minus $2. Disactiva iste parametro, o augmenta iste valor a $3 in php.ini.", "config-using-32bit": "Attention: tu systema pare operar con integres de 32 bits. Isto [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit non es recommendate].", "config-db-type": "Typo de base de datos:", "config-db-host": "Servitor de base de datos:", diff --git a/includes/installer/i18n/ja.json b/includes/installer/i18n/ja.json index 5da1914672..511b0dafa7 100644 --- a/includes/installer/i18n/ja.json +++ b/includes/installer/i18n/ja.json @@ -104,7 +104,7 @@ "config-uploads-not-safe": "警告: アップロードの既定ディレクトリ $1 に、任意のスクリプト実行に関する脆弱性があります。\nMediaWiki はアップロードされたファイルのセキュリティ上の脅威を確認しますが、アップロードを有効化する前に、[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security このセキュリティ上の脆弱性を解決する]ことを強く推奨します。", "config-no-cli-uploads-check": "警告: アップロード用のデフォルトディレクトリ ($1) が、CLIでのインストール中に任意のスクリプト実行の脆弱性チェックを受けていません。", "config-brokenlibxml": "このシステムで使われているPHPとlibxml2のバージョンのこの組み合わせにはバグがあります。具体的には、MediaWikiやその他のウェブアプリケーションでhiddenデータが破損する可能性があります。\nlibxml2を2.7.3以降のバージョンにアップグレードしてください([https://bugs.php.net/bug.php?id=45996 PHPでのバグ情報])。\nインストールを終了します。", - "config-suhosin-max-value-length": "Suhosin がインストールされており、GET パラメーターの length を $1 バイトに制限しています。\nMediaWiki の ResourceLoader コンポーネントはこの制限を回避しますが、パフォーマンスは低下します。\n可能な限り、php.ini で suhosin.get.max_value_length を 1024 以上に設定し、同じ値を LocalSettings.php 内で $wgResourceLoaderMaxQueryLength に設定してください。", + "config-suhosin-max-value-length": "Suhosin がインストールされており、GET パラメーターの length を $1 バイトに制限しています。(訳注:\nMediaWiki の ResourceLoader コンポーネントはこの制限を回避しますが、パフォーマンスは低下します。)\n可能な限り、 suhosin.get.max_value_length を $2 以上に設定します。これを無効に変更するか、php.ini で $3 に増加してください。", "config-using-32bit": "警告:システムが32ビットで動作しているようです。 これは[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit 非推奨]です。", "config-db-type": "データベースの種類:", "config-db-host": "データベースのホスト:", diff --git a/includes/installer/i18n/pl.json b/includes/installer/i18n/pl.json index fc66bdbe32..f3f211eb1d 100644 --- a/includes/installer/i18n/pl.json +++ b/includes/installer/i18n/pl.json @@ -24,7 +24,8 @@ "Sethakill", "Peter Bowman", "Ankam", - "Railfail536" + "Railfail536", + "Rail" ] }, "config-desc": "Instalator MediaWiki", diff --git a/includes/jobqueue/jobs/RefreshLinksJob.php b/includes/jobqueue/jobs/RefreshLinksJob.php index b4046a61bc..33b05b8571 100644 --- a/includes/jobqueue/jobs/RefreshLinksJob.php +++ b/includes/jobqueue/jobs/RefreshLinksJob.php @@ -276,8 +276,8 @@ class RefreshLinksJob extends Job { $title = $page->getTitle(); // Get the latest ID since acquirePageLock() in runForTitle() flushed the transaction. // This is used to detect edits/moves after loadPageData() but before the scope lock. - // The works around the chicken/egg problem of determining the scope lock key name. - $latest = $title->getLatestRevID( Title::GAID_FOR_UPDATE ); + // The works around the chicken/egg problem of determining the scope lock key name + $latest = $title->getLatestRevID( Title::READ_LATEST ); $triggeringRevisionId = $this->params['triggeringRevisionId'] ?? null; if ( $triggeringRevisionId && $triggeringRevisionId !== $latest ) { diff --git a/includes/libs/filebackend/FSFileBackend.php b/includes/libs/filebackend/FSFileBackend.php index 5534cbd710..9ed7ae3426 100644 --- a/includes/libs/filebackend/FSFileBackend.php +++ b/includes/libs/filebackend/FSFileBackend.php @@ -556,9 +556,7 @@ class FSFileBackend extends FileBackendStore { $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot; AtEase::suppressWarnings(); - if ( is_dir( $dir ) ) { - rmdir( $dir ); // remove directory if empty - } + rmdir( $dir ); // remove directory if empty AtEase::restoreWarnings(); return $status; diff --git a/includes/libs/filebackend/FileBackendStore.php b/includes/libs/filebackend/FileBackendStore.php index f2c07e8253..5caf2508d3 100644 --- a/includes/libs/filebackend/FileBackendStore.php +++ b/includes/libs/filebackend/FileBackendStore.php @@ -1587,7 +1587,7 @@ abstract class FileBackendStore extends FileBackend { // Validate and sanitize the relative path (backend-specific) $relPath = $this->resolveContainerPath( $shortCont, $relPath ); if ( $relPath !== null ) { - // Prepend any wiki ID prefix to the container name + // Prepend any domain ID prefix to the container name $container = $this->fullContainerName( $shortCont ); if ( self::isValidContainerName( $container ) ) { // Validate and sanitize the container name (backend-specific) @@ -1722,7 +1722,7 @@ abstract class FileBackendStore extends FileBackend { } /** - * Get the full container name, including the wiki ID prefix + * Get the full container name, including the domain ID prefix * * @param string $container * @return string diff --git a/includes/libs/filebackend/fileophandle/FileBackendStoreOpHandle.php b/includes/libs/filebackend/fileophandle/FileBackendStoreOpHandle.php index c366a0fff7..8697f9f0b6 100644 --- a/includes/libs/filebackend/fileophandle/FileBackendStoreOpHandle.php +++ b/includes/libs/filebackend/fileophandle/FileBackendStoreOpHandle.php @@ -34,8 +34,8 @@ abstract class FileBackendStoreOpHandle { public $backend; /** @var array */ public $resourcesToClose = []; - - public $call; // string; name that identifies the function called + /** @var callable name that identifies the function called */ + public $call; /** * Close all open file handles diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index c167f3a4cf..bad75da4fb 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -726,7 +726,7 @@ class WikiPage implements Page, IDBAccessObject { // Try using the replica DB first, then try the master $rev = $this->mTitle->getFirstRevision(); if ( !$rev ) { - $rev = $this->mTitle->getFirstRevision( Title::GAID_FOR_UPDATE ); + $rev = $this->mTitle->getFirstRevision( Title::READ_LATEST ); } return $rev; } diff --git a/includes/preferences/DefaultPreferencesFactory.php b/includes/preferences/DefaultPreferencesFactory.php index 8a82add61f..66c2bc33e5 100644 --- a/includes/preferences/DefaultPreferencesFactory.php +++ b/includes/preferences/DefaultPreferencesFactory.php @@ -84,6 +84,7 @@ class DefaultPreferencesFactory implements PreferencesFactory { * @since 1.34 */ public static $constructorOptions = [ + 'AllowRequiringEmailForResets', 'AllowUserCss', 'AllowUserCssPrefs', 'AllowUserJs', @@ -620,6 +621,16 @@ class DefaultPreferencesFactory implements PreferencesFactory { } } + if ( $this->options->get( 'AllowRequiringEmailForResets' ) ) { + $defaultPreferences['requireemail'] = [ + 'type' => 'toggle', + 'label-message' => 'tog-requireemail', + 'help-message' => 'prefs-help-requireemail', + 'section' => 'personal/email', + 'disabled' => $disableEmailPrefs, + ]; + } + if ( $this->options->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) { $defaultPreferences['disablemail'] = [ 'id' => 'wpAllowEmail', diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index 693afcf62a..4a23eae74f 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -1273,8 +1273,7 @@ MESSAGE; /** * Returns JS code which, when called, will register a given list of messages. * - * @param mixed $messages Either an associative array mapping message key to value, or a - * JSON-encoded message blob containing the same data, wrapped in an XmlJsCode object. + * @param mixed $messages Associative array mapping message key to value. * @return string JavaScript code */ public static function makeMessageSetScript( $messages ) { diff --git a/includes/session/SessionManager.php b/includes/session/SessionManager.php index 09cdf72f46..a3380ff82f 100644 --- a/includes/session/SessionManager.php +++ b/includes/session/SessionManager.php @@ -505,11 +505,10 @@ final class SessionManager implements SessionManagerInterface { } if ( count( $retInfos ) > 1 ) { - $ex = new \OverflowException( + throw new SessionOverflowException( + $retInfos, 'Multiple sessions for this request tied for top priority: ' . implode( ', ', $retInfos ) ); - $ex->sessionInfos = $retInfos; - throw $ex; } return $retInfos ? $retInfos[0] : null; diff --git a/includes/session/SessionOverflowException.php b/includes/session/SessionOverflowException.php new file mode 100644 index 0000000000..2a5ed2b80a --- /dev/null +++ b/includes/session/SessionOverflowException.php @@ -0,0 +1,34 @@ +sessionInfos = $sessionInfos; + } + + /** + * @return SessionInfo[] + */ + public function getSessionInfos() : array { + return $this->sessionInfos; + } +} diff --git a/includes/specialpage/LoginSignupSpecialPage.php b/includes/specialpage/LoginSignupSpecialPage.php index ce80c1a311..e5a28d9307 100644 --- a/includes/specialpage/LoginSignupSpecialPage.php +++ b/includes/specialpage/LoginSignupSpecialPage.php @@ -95,9 +95,8 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage { /** * Load basic request parameters for this Special page. - * @param string $subPage */ - private function loadRequestParameters( $subPage ) { + private function loadRequestParameters() { if ( $this->mLoadedRequest ) { return; } @@ -105,7 +104,6 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage { $request = $this->getRequest(); $this->mPosted = $request->wasPosted(); - $this->mIsReturn = $subPage === 'return'; $this->mAction = $request->getVal( 'action' ); $this->mFromHTTP = $request->getBool( 'fromhttp', false ) || $request->getBool( 'wpFromhttp', false ); @@ -124,7 +122,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage { protected function load( $subPage ) { global $wgSecureLogin; - $this->loadRequestParameters( $subPage ); + $this->loadRequestParameters(); if ( $this->mLoaded ) { return; } @@ -203,7 +201,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage { protected function beforeExecute( $subPage ) { // finish initializing the class before processing the request - T135924 - $this->loadRequestParameters( $subPage ); + $this->loadRequestParameters(); return parent::beforeExecute( $subPage ); } diff --git a/includes/specialpage/QueryPage.php b/includes/specialpage/QueryPage.php index 6cc6e4e94d..b7eb3c0764 100644 --- a/includes/specialpage/QueryPage.php +++ b/includes/specialpage/QueryPage.php @@ -77,40 +77,40 @@ abstract class QueryPage extends SpecialPage { if ( $qp === null ) { // QueryPage subclass, Special page name $qp = [ - [ AncientPagesPage::class, 'Ancientpages' ], - [ BrokenRedirectsPage::class, 'BrokenRedirects' ], - [ DeadendPagesPage::class, 'Deadendpages' ], - [ DoubleRedirectsPage::class, 'DoubleRedirects' ], - [ FileDuplicateSearchPage::class, 'FileDuplicateSearch' ], - [ ListDuplicatedFilesPage::class, 'ListDuplicatedFiles' ], - [ LinkSearchPage::class, 'LinkSearch' ], - [ ListredirectsPage::class, 'Listredirects' ], - [ LonelyPagesPage::class, 'Lonelypages' ], - [ LongPagesPage::class, 'Longpages' ], - [ MediaStatisticsPage::class, 'MediaStatistics' ], - [ MIMEsearchPage::class, 'MIMEsearch' ], - [ MostcategoriesPage::class, 'Mostcategories' ], + [ SpecialAncientPages::class, 'Ancientpages' ], + [ SpecialBrokenRedirects::class, 'BrokenRedirects' ], + [ SpecialDeadendPages::class, 'Deadendpages' ], + [ SpecialDoubleRedirects::class, 'DoubleRedirects' ], + [ SpecialFileDuplicateSearch::class, 'FileDuplicateSearch' ], + [ SpecialListDuplicatedFiles::class, 'ListDuplicatedFiles' ], + [ SpecialLinkSearch::class, 'LinkSearch' ], + [ SpecialListRedirects::class, 'Listredirects' ], + [ SpecialLonelyPages::class, 'Lonelypages' ], + [ SpecialLongPages::class, 'Longpages' ], + [ SpecialMediaStatistics::class, 'MediaStatistics' ], + [ SpecialMIMESearch::class, 'MIMEsearch' ], + [ SpecialMostCategories::class, 'Mostcategories' ], [ MostimagesPage::class, 'Mostimages' ], - [ MostinterwikisPage::class, 'Mostinterwikis' ], - [ MostlinkedCategoriesPage::class, 'Mostlinkedcategories' ], - [ MostlinkedTemplatesPage::class, 'Mostlinkedtemplates' ], - [ MostlinkedPage::class, 'Mostlinked' ], - [ MostrevisionsPage::class, 'Mostrevisions' ], - [ FewestrevisionsPage::class, 'Fewestrevisions' ], - [ ShortPagesPage::class, 'Shortpages' ], - [ UncategorizedCategoriesPage::class, 'Uncategorizedcategories' ], - [ UncategorizedPagesPage::class, 'Uncategorizedpages' ], - [ UncategorizedImagesPage::class, 'Uncategorizedimages' ], - [ UncategorizedTemplatesPage::class, 'Uncategorizedtemplates' ], - [ UnusedCategoriesPage::class, 'Unusedcategories' ], - [ UnusedimagesPage::class, 'Unusedimages' ], - [ WantedCategoriesPage::class, 'Wantedcategories' ], + [ SpecialMostInterwikis::class, 'Mostinterwikis' ], + [ SpecialMostLinkedCategories::class, 'Mostlinkedcategories' ], + [ SpecialMostLinkedTemplates::class, 'Mostlinkedtemplates' ], + [ SpecialMostLinked::class, 'Mostlinked' ], + [ SpecialMostRevisions::class, 'Mostrevisions' ], + [ SpecialFewestRevisions::class, 'Fewestrevisions' ], + [ SpecialShortPages::class, 'Shortpages' ], + [ SpecialUncategorizedCategories::class, 'Uncategorizedcategories' ], + [ SpecialUncategorizedPages::class, 'Uncategorizedpages' ], + [ SpecialUncategorizedImages::class, 'Uncategorizedimages' ], + [ SpecialUncategorizedTemplates::class, 'Uncategorizedtemplates' ], + [ SpecialUnusedCategories::class, 'Unusedcategories' ], + [ SpecialUnusedImages::class, 'Unusedimages' ], + [ SpecialWantedCategories::class, 'Wantedcategories' ], [ WantedFilesPage::class, 'Wantedfiles' ], [ WantedPagesPage::class, 'Wantedpages' ], - [ WantedTemplatesPage::class, 'Wantedtemplates' ], - [ UnwatchedpagesPage::class, 'Unwatchedpages' ], - [ UnusedtemplatesPage::class, 'Unusedtemplates' ], - [ WithoutInterwikiPage::class, 'Withoutinterwiki' ], + [ SpecialWantedTemplates::class, 'Wantedtemplates' ], + [ SpecialUnwatchedPages::class, 'Unwatchedpages' ], + [ SpecialUnusedTemplates::class, 'Unusedtemplates' ], + [ SpecialWithoutInterwiki::class, 'Withoutinterwiki' ], ]; Hooks::run( 'wgQueryPages', [ &$qp ] ); } diff --git a/includes/specialpage/SpecialPageFactory.php b/includes/specialpage/SpecialPageFactory.php index 40fcd74b15..5ac5f82c51 100644 --- a/includes/specialpage/SpecialPageFactory.php +++ b/includes/specialpage/SpecialPageFactory.php @@ -69,35 +69,35 @@ class SpecialPageFactory { */ private static $coreList = [ // Maintenance Reports - 'BrokenRedirects' => \BrokenRedirectsPage::class, - 'Deadendpages' => \DeadendPagesPage::class, - 'DoubleRedirects' => \DoubleRedirectsPage::class, - 'Longpages' => \LongPagesPage::class, - 'Ancientpages' => \AncientPagesPage::class, - 'Lonelypages' => \LonelyPagesPage::class, - 'Fewestrevisions' => \FewestrevisionsPage::class, - 'Withoutinterwiki' => \WithoutInterwikiPage::class, + 'BrokenRedirects' => \SpecialBrokenRedirects::class, + 'Deadendpages' => \SpecialDeadendPages::class, + 'DoubleRedirects' => \SpecialDoubleRedirects::class, + 'Longpages' => \SpecialLongPages::class, + 'Ancientpages' => \SpecialAncientPages::class, + 'Lonelypages' => \SpecialLonelyPages::class, + 'Fewestrevisions' => \SpecialFewestRevisions::class, + 'Withoutinterwiki' => \SpecialWithoutInterwiki::class, 'Protectedpages' => \SpecialProtectedpages::class, 'Protectedtitles' => \SpecialProtectedtitles::class, - 'Shortpages' => \ShortPagesPage::class, - 'Uncategorizedcategories' => \UncategorizedCategoriesPage::class, - 'Uncategorizedimages' => \UncategorizedImagesPage::class, - 'Uncategorizedpages' => \UncategorizedPagesPage::class, - 'Uncategorizedtemplates' => \UncategorizedTemplatesPage::class, - 'Unusedcategories' => \UnusedCategoriesPage::class, - 'Unusedimages' => \UnusedimagesPage::class, - 'Unusedtemplates' => \UnusedtemplatesPage::class, - 'Unwatchedpages' => \UnwatchedpagesPage::class, - 'Wantedcategories' => \WantedCategoriesPage::class, + 'Shortpages' => \SpecialShortPages::class, + 'Uncategorizedcategories' => \SpecialUncategorizedCategories::class, + 'Uncategorizedimages' => \SpecialUncategorizedImages::class, + 'Uncategorizedpages' => \SpecialUncategorizedPages::class, + 'Uncategorizedtemplates' => \SpecialUncategorizedTemplates::class, + 'Unusedcategories' => \SpecialUnusedCategories::class, + 'Unusedimages' => \SpecialUnusedImages::class, + 'Unusedtemplates' => \SpecialUnusedTemplates::class, + 'Unwatchedpages' => \SpecialUnwatchedPages::class, + 'Wantedcategories' => \SpecialWantedCategories::class, 'Wantedfiles' => \WantedFilesPage::class, 'Wantedpages' => \WantedPagesPage::class, - 'Wantedtemplates' => \WantedTemplatesPage::class, + 'Wantedtemplates' => \SpecialWantedTemplates::class, // List of pages 'Allpages' => \SpecialAllPages::class, 'Prefixindex' => \SpecialPrefixindex::class, 'Categories' => \SpecialCategories::class, - 'Listredirects' => \ListredirectsPage::class, + 'Listredirects' => \SpecialListRedirects::class, 'PagesWithProp' => \SpecialPagesWithProp::class, 'TrackingCategories' => \SpecialTrackingCategories::class, @@ -119,7 +119,7 @@ class SpecialPageFactory { 'ChangePassword' => \SpecialChangePassword::class, 'BotPasswords' => \SpecialBotPasswords::class, 'PasswordReset' => \SpecialPasswordReset::class, - 'DeletedContributions' => \DeletedContributionsPage::class, + 'DeletedContributions' => \SpecialDeletedContributions::class, 'Preferences' => \SpecialPreferences::class, 'ResetTokens' => \SpecialResetTokens::class, 'Contributions' => \SpecialContributions::class, @@ -144,12 +144,12 @@ class SpecialPageFactory { // Media reports and uploads 'Listfiles' => \SpecialListFiles::class, 'Filepath' => \SpecialFilepath::class, - 'MediaStatistics' => \MediaStatisticsPage::class, - 'MIMEsearch' => \MIMEsearchPage::class, - 'FileDuplicateSearch' => \FileDuplicateSearchPage::class, + 'MediaStatistics' => \SpecialMediaStatistics::class, + 'MIMEsearch' => \SpecialMIMESearch::class, + 'FileDuplicateSearch' => \SpecialFileDuplicateSearch::class, 'Upload' => \SpecialUpload::class, 'UploadStash' => \SpecialUploadStash::class, - 'ListDuplicatedFiles' => \ListDuplicatedFilesPage::class, + 'ListDuplicatedFiles' => \SpecialListDuplicatedFiles::class, // Data and tools 'ApiSandbox' => \SpecialApiSandbox::class, @@ -160,7 +160,7 @@ class SpecialPageFactory { 'Unlockdb' => \SpecialUnlockdb::class, // Redirecting special pages - 'LinkSearch' => \LinkSearchPage::class, + 'LinkSearch' => \SpecialLinkSearch::class, 'Randompage' => \RandomPage::class, 'RandomInCategory' => \SpecialRandomInCategory::class, 'Randomredirect' => \SpecialRandomredirect::class, @@ -168,13 +168,13 @@ class SpecialPageFactory { 'GoToInterwiki' => \SpecialGoToInterwiki::class, // High use pages - 'Mostlinkedcategories' => \MostlinkedCategoriesPage::class, + 'Mostlinkedcategories' => \SpecialMostLinkedCategories::class, 'Mostimages' => \MostimagesPage::class, - 'Mostinterwikis' => \MostinterwikisPage::class, - 'Mostlinked' => \MostlinkedPage::class, - 'Mostlinkedtemplates' => \MostlinkedTemplatesPage::class, - 'Mostcategories' => \MostcategoriesPage::class, - 'Mostrevisions' => \MostrevisionsPage::class, + 'Mostinterwikis' => \SpecialMostInterwikis::class, + 'Mostlinked' => \SpecialMostLinked::class, + 'Mostlinkedtemplates' => \SpecialMostLinkedTemplates::class, + 'Mostcategories' => \SpecialMostCategories::class, + 'Mostrevisions' => \SpecialMostRevisions::class, // Page tools 'ComparePages' => \SpecialComparePages::class, @@ -287,8 +287,8 @@ class SpecialPageFactory { } if ( $this->options->get( 'EmailAuthentication' ) ) { - $this->list['Confirmemail'] = \EmailConfirmation::class; - $this->list['Invalidateemail'] = \EmailInvalidation::class; + $this->list['Confirmemail'] = \SpecialConfirmEmail::class; + $this->list['Invalidateemail'] = \SpecialEmailInvalidate::class; } if ( $this->options->get( 'EnableEmail' ) ) { diff --git a/includes/specials/SpecialAncientPages.php b/includes/specials/SpecialAncientPages.php new file mode 100644 index 0000000000..a17c1219ec --- /dev/null +++ b/includes/specials/SpecialAncientPages.php @@ -0,0 +1,108 @@ + + MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces(), + 'page_is_redirect' => 0 + ]; + $joinConds = [ + 'revision' => [ + 'JOIN', [ + 'page_latest = rev_id' + ] + ], + ]; + + // Allow extensions to modify the query + Hooks::run( 'AncientPagesQuery', [ &$tables, &$conds, &$joinConds ] ); + + return [ + 'tables' => $tables, + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'rev_timestamp' + ], + 'conds' => $conds, + 'join_conds' => $joinConds + ]; + } + + public function usesTimestamps() { + return true; + } + + function sortDescending() { + return false; + } + + public function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $d = $this->getLanguage()->userTimeAndDate( $result->value, $this->getUser() ); + $title = Title::makeTitle( $result->namespace, $result->title ); + $linkRenderer = $this->getLinkRenderer(); + $link = $linkRenderer->makeKnownLink( + $title, + new HtmlArmor( MediaWikiServices::getInstance()->getContentLanguage()-> + convert( htmlspecialchars( $title->getPrefixedText() ) ) ) + ); + + return $this->getLanguage()->specialList( $link, htmlspecialchars( $d ) ); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialAncientpages.php b/includes/specials/SpecialAncientpages.php deleted file mode 100644 index a32393e56d..0000000000 --- a/includes/specials/SpecialAncientpages.php +++ /dev/null @@ -1,108 +0,0 @@ - - MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces(), - 'page_is_redirect' => 0 - ]; - $joinConds = [ - 'revision' => [ - 'JOIN', [ - 'page_latest = rev_id' - ] - ], - ]; - - // Allow extensions to modify the query - Hooks::run( 'AncientPagesQuery', [ &$tables, &$conds, &$joinConds ] ); - - return [ - 'tables' => $tables, - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'rev_timestamp' - ], - 'conds' => $conds, - 'join_conds' => $joinConds - ]; - } - - public function usesTimestamps() { - return true; - } - - function sortDescending() { - return false; - } - - public function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $d = $this->getLanguage()->userTimeAndDate( $result->value, $this->getUser() ); - $title = Title::makeTitle( $result->namespace, $result->title ); - $linkRenderer = $this->getLinkRenderer(); - $link = $linkRenderer->makeKnownLink( - $title, - new HtmlArmor( MediaWikiServices::getInstance()->getContentLanguage()-> - convert( htmlspecialchars( $title->getPrefixedText() ) ) ) - ); - - return $this->getLanguage()->specialList( $link, htmlspecialchars( $d ) ); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialBlockList.php b/includes/specials/SpecialBlockList.php index b3d2358193..638535940d 100644 --- a/includes/specials/SpecialBlockList.php +++ b/includes/specials/SpecialBlockList.php @@ -82,8 +82,9 @@ class SpecialBlockList extends SpecialPage { 'Options' => [ 'type' => 'multiselect', 'options-messages' => [ - 'blocklist-userblocks' => 'userblocks', 'blocklist-tempblocks' => 'tempblocks', + 'blocklist-indefblocks' => 'indefblocks', + 'blocklist-userblocks' => 'userblocks', 'blocklist-addressblocks' => 'addressblocks', 'blocklist-rangeblocks' => 'rangeblocks', ], @@ -136,6 +137,7 @@ class SpecialBlockList extends SpecialPage { */ protected function getBlockListPager() { $conds = []; + $db = $this->getDB(); # Is the user allowed to see hidden blocks? if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { $conds['ipb_deleted'] = 0; @@ -153,7 +155,7 @@ class SpecialBlockList extends SpecialPage { case DatabaseBlock::TYPE_IP: case DatabaseBlock::TYPE_RANGE: list( $start, $end ) = IP::parseRange( $target ); - $conds[] = wfGetDB( DB_REPLICA )->makeList( + $conds[] = $db->makeList( [ 'ipb_address' => $target, DatabaseBlock::getRangeCond( $start, $end ) @@ -174,9 +176,6 @@ class SpecialBlockList extends SpecialPage { if ( in_array( 'userblocks', $this->options ) ) { $conds['ipb_user'] = 0; } - if ( in_array( 'tempblocks', $this->options ) ) { - $conds['ipb_expiry'] = 'infinity'; - } if ( in_array( 'addressblocks', $this->options ) ) { $conds[] = "ipb_user != 0 OR ipb_range_end > ipb_range_start"; } @@ -184,10 +183,21 @@ class SpecialBlockList extends SpecialPage { $conds[] = "ipb_range_end = ipb_range_start"; } + $hideTemp = in_array( 'tempblocks', $this->options ); + $hideIndef = in_array( 'indefblocks', $this->options ); + if ( $hideTemp && $hideIndef ) { + // If both types are hidden, ensure query doesn't produce any results + $conds[] = '1=0'; + } elseif ( $hideTemp ) { + $conds['ipb_expiry'] = $db->getInfinity(); + } elseif ( $hideIndef ) { + $conds[] = "ipb_expiry != " . $db->addQuotes( $db->getInfinity() ); + } + if ( $this->blockType === 'sitewide' ) { - $conds[] = 'ipb_sitewide = 1'; + $conds['ipb_sitewide'] = 1; } elseif ( $this->blockType === 'partial' ) { - $conds[] = 'ipb_sitewide = 0'; + $conds['ipb_sitewide'] = 0; } return new BlockListPager( $this, $conds ); @@ -243,4 +253,13 @@ class SpecialBlockList extends SpecialPage { protected function getGroupName() { return 'users'; } + + /** + * Return a IDatabase object for reading + * + * @return IDatabase + */ + protected function getDB() { + return wfGetDB( DB_REPLICA ); + } } diff --git a/includes/specials/SpecialBrokenRedirects.php b/includes/specials/SpecialBrokenRedirects.php index 17f89f9c4a..9431cefbaf 100644 --- a/includes/specials/SpecialBrokenRedirects.php +++ b/includes/specials/SpecialBrokenRedirects.php @@ -30,7 +30,7 @@ use Wikimedia\Rdbms\IDatabase; * * @ingroup SpecialPage */ -class BrokenRedirectsPage extends QueryPage { +class SpecialBrokenRedirects extends QueryPage { function __construct( $name = 'BrokenRedirects' ) { parent::__construct( $name ); } diff --git a/includes/specials/SpecialConfirmEmail.php b/includes/specials/SpecialConfirmEmail.php new file mode 100644 index 0000000000..f86a133a13 --- /dev/null +++ b/includes/specials/SpecialConfirmEmail.php @@ -0,0 +1,175 @@ + + */ +class SpecialConfirmEmail extends UnlistedSpecialPage { + public function __construct() { + parent::__construct( 'Confirmemail', 'editmyprivateinfo' ); + } + + public function doesWrites() { + return true; + } + + /** + * Main execution point + * + * @param null|string $code Confirmation code passed to the page + * @throws PermissionsError + * @throws ReadOnlyError + * @throws UserNotLoggedIn + */ + function execute( $code ) { + // Ignore things like master queries/connections on GET requests. + // It's very convenient to just allow formless link usage. + $trxProfiler = Profiler::instance()->getTransactionProfiler(); + + $this->setHeaders(); + $this->checkReadOnly(); + $this->checkPermissions(); + + // This could also let someone check the current email address, so + // require both permissions. + if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) { + throw new PermissionsError( 'viewmyprivateinfo' ); + } + + if ( $code === null || $code === '' ) { + $this->requireLogin( 'confirmemail_needlogin' ); + if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) { + $this->showRequestForm(); + } else { + $this->getOutput()->addWikiMsg( 'confirmemail_noemail' ); + } + } else { + $old = $trxProfiler->setSilenced( true ); + $this->attemptConfirm( $code ); + $trxProfiler->setSilenced( $old ); + } + } + + /** + * Show a nice form for the user to request a confirmation mail + */ + function showRequestForm() { + $user = $this->getUser(); + $out = $this->getOutput(); + + if ( !$user->isEmailConfirmed() ) { + $descriptor = []; + if ( $user->isEmailConfirmationPending() ) { + $descriptor += [ + 'pending' => [ + 'type' => 'info', + 'raw' => true, + 'default' => "
\n" . + $this->msg( 'confirmemail_pending' )->escaped() . + "\n
", + ], + ]; + } + + $out->addWikiMsg( 'confirmemail_text' ); + $form = HTMLForm::factory( 'ooui', $descriptor, $this->getContext() ); + $form + ->setMethod( 'post' ) + ->setAction( $this->getPageTitle()->getLocalURL() ) + ->setSubmitTextMsg( 'confirmemail_send' ) + ->setSubmitCallback( [ $this, 'submitSend' ] ); + + $retval = $form->show(); + + if ( $retval === true ) { + // should never happen, but if so, don't let the user without any message + $out->addWikiMsg( 'confirmemail_sent' ); + } elseif ( $retval instanceof Status && $retval->isGood() ) { + $out->addWikiTextAsInterface( $retval->getValue() ); + } + } else { + // date and time are separate parameters to facilitate localisation. + // $time is kept for backward compat reasons. + // 'emailauthenticated' is also used in SpecialPreferences.php + $lang = $this->getLanguage(); + $emailAuthenticated = $user->getEmailAuthenticationTimestamp(); + $time = $lang->userTimeAndDate( $emailAuthenticated, $user ); + $d = $lang->userDate( $emailAuthenticated, $user ); + $t = $lang->userTime( $emailAuthenticated, $user ); + $out->addWikiMsg( 'emailauthenticated', $time, $d, $t ); + } + } + + /** + * Callback for HTMLForm send confirmation mail. + * + * @return Status Status object with the result + */ + public function submitSend() { + $status = $this->getUser()->sendConfirmationMail(); + if ( $status->isGood() ) { + return Status::newGood( $this->msg( 'confirmemail_sent' )->text() ); + } else { + return Status::newFatal( new RawMessage( + $status->getWikiText( 'confirmemail_sendfailed' ) + ) ); + } + } + + /** + * Attempt to confirm the user's email address and show success or failure + * as needed; if successful, take the user to log in + * + * @param string $code Confirmation code + */ + private function attemptConfirm( $code ) { + $user = User::newFromConfirmationCode( $code, User::READ_EXCLUSIVE ); + if ( !is_object( $user ) ) { + $this->getOutput()->addWikiMsg( 'confirmemail_invalid' ); + + return; + } + + // rate limit email confirmations + if ( $user->pingLimiter( 'confirmemail' ) ) { + $this->getOutput()->addWikiMsg( 'actionthrottledtext' ); + + return; + } + + $user->confirmEmail(); + $user->saveSettings(); + $message = $this->getUser()->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success'; + $this->getOutput()->addWikiMsg( $message ); + + if ( !$this->getUser()->isLoggedIn() ) { + $title = SpecialPage::getTitleFor( 'Userlogin' ); + $this->getOutput()->returnToMain( true, $title ); + } + } +} diff --git a/includes/specials/SpecialConfirmemail.php b/includes/specials/SpecialConfirmemail.php deleted file mode 100644 index 7f327194c5..0000000000 --- a/includes/specials/SpecialConfirmemail.php +++ /dev/null @@ -1,175 +0,0 @@ - - */ -class EmailConfirmation extends UnlistedSpecialPage { - public function __construct() { - parent::__construct( 'Confirmemail', 'editmyprivateinfo' ); - } - - public function doesWrites() { - return true; - } - - /** - * Main execution point - * - * @param null|string $code Confirmation code passed to the page - * @throws PermissionsError - * @throws ReadOnlyError - * @throws UserNotLoggedIn - */ - function execute( $code ) { - // Ignore things like master queries/connections on GET requests. - // It's very convenient to just allow formless link usage. - $trxProfiler = Profiler::instance()->getTransactionProfiler(); - - $this->setHeaders(); - $this->checkReadOnly(); - $this->checkPermissions(); - - // This could also let someone check the current email address, so - // require both permissions. - if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) { - throw new PermissionsError( 'viewmyprivateinfo' ); - } - - if ( $code === null || $code === '' ) { - $this->requireLogin( 'confirmemail_needlogin' ); - if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) { - $this->showRequestForm(); - } else { - $this->getOutput()->addWikiMsg( 'confirmemail_noemail' ); - } - } else { - $old = $trxProfiler->setSilenced( true ); - $this->attemptConfirm( $code ); - $trxProfiler->setSilenced( $old ); - } - } - - /** - * Show a nice form for the user to request a confirmation mail - */ - function showRequestForm() { - $user = $this->getUser(); - $out = $this->getOutput(); - - if ( !$user->isEmailConfirmed() ) { - $descriptor = []; - if ( $user->isEmailConfirmationPending() ) { - $descriptor += [ - 'pending' => [ - 'type' => 'info', - 'raw' => true, - 'default' => "
\n" . - $this->msg( 'confirmemail_pending' )->escaped() . - "\n
", - ], - ]; - } - - $out->addWikiMsg( 'confirmemail_text' ); - $form = HTMLForm::factory( 'ooui', $descriptor, $this->getContext() ); - $form - ->setMethod( 'post' ) - ->setAction( $this->getPageTitle()->getLocalURL() ) - ->setSubmitTextMsg( 'confirmemail_send' ) - ->setSubmitCallback( [ $this, 'submitSend' ] ); - - $retval = $form->show(); - - if ( $retval === true ) { - // should never happen, but if so, don't let the user without any message - $out->addWikiMsg( 'confirmemail_sent' ); - } elseif ( $retval instanceof Status && $retval->isGood() ) { - $out->addWikiTextAsInterface( $retval->getValue() ); - } - } else { - // date and time are separate parameters to facilitate localisation. - // $time is kept for backward compat reasons. - // 'emailauthenticated' is also used in SpecialPreferences.php - $lang = $this->getLanguage(); - $emailAuthenticated = $user->getEmailAuthenticationTimestamp(); - $time = $lang->userTimeAndDate( $emailAuthenticated, $user ); - $d = $lang->userDate( $emailAuthenticated, $user ); - $t = $lang->userTime( $emailAuthenticated, $user ); - $out->addWikiMsg( 'emailauthenticated', $time, $d, $t ); - } - } - - /** - * Callback for HTMLForm send confirmation mail. - * - * @return Status Status object with the result - */ - public function submitSend() { - $status = $this->getUser()->sendConfirmationMail(); - if ( $status->isGood() ) { - return Status::newGood( $this->msg( 'confirmemail_sent' )->text() ); - } else { - return Status::newFatal( new RawMessage( - $status->getWikiText( 'confirmemail_sendfailed' ) - ) ); - } - } - - /** - * Attempt to confirm the user's email address and show success or failure - * as needed; if successful, take the user to log in - * - * @param string $code Confirmation code - */ - private function attemptConfirm( $code ) { - $user = User::newFromConfirmationCode( $code, User::READ_EXCLUSIVE ); - if ( !is_object( $user ) ) { - $this->getOutput()->addWikiMsg( 'confirmemail_invalid' ); - - return; - } - - // rate limit email confirmations - if ( $user->pingLimiter( 'confirmemail' ) ) { - $this->getOutput()->addWikiMsg( 'actionthrottledtext' ); - - return; - } - - $user->confirmEmail(); - $user->saveSettings(); - $message = $this->getUser()->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success'; - $this->getOutput()->addWikiMsg( $message ); - - if ( !$this->getUser()->isLoggedIn() ) { - $title = SpecialPage::getTitleFor( 'Userlogin' ); - $this->getOutput()->returnToMain( true, $title ); - } - } -} diff --git a/includes/specials/SpecialDeadendPages.php b/includes/specials/SpecialDeadendPages.php new file mode 100644 index 0000000000..9159442be3 --- /dev/null +++ b/includes/specials/SpecialDeadendPages.php @@ -0,0 +1,99 @@ +msg( 'deadendpagestext' )->parseAsBlock(); + } + + /** + * LEFT JOIN is expensive + * + * @return bool + */ + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + /** + * @return bool + */ + function sortDescending() { + return false; + } + + function getQueryInfo() { + return [ + 'tables' => [ 'page', 'pagelinks' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + 'conds' => [ + 'pl_from IS NULL', + 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces(), + 'page_is_redirect' => 0 + ], + 'join_conds' => [ + 'pagelinks' => [ + 'LEFT JOIN', + [ 'page_id=pl_from' ] + ] + ] + ]; + } + + function getOrderFields() { + // For some crazy reason ordering by a constant + // causes a filesort + if ( count( MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces() ) > 1 + ) { + return [ 'page_namespace', 'page_title' ]; + } else { + return [ 'page_title' ]; + } + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialDeadendpages.php b/includes/specials/SpecialDeadendpages.php deleted file mode 100644 index 2a967c588f..0000000000 --- a/includes/specials/SpecialDeadendpages.php +++ /dev/null @@ -1,99 +0,0 @@ -msg( 'deadendpagestext' )->parseAsBlock(); - } - - /** - * LEFT JOIN is expensive - * - * @return bool - */ - function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - /** - * @return bool - */ - function sortDescending() { - return false; - } - - function getQueryInfo() { - return [ - 'tables' => [ 'page', 'pagelinks' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - 'conds' => [ - 'pl_from IS NULL', - 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces(), - 'page_is_redirect' => 0 - ], - 'join_conds' => [ - 'pagelinks' => [ - 'LEFT JOIN', - [ 'page_id=pl_from' ] - ] - ] - ]; - } - - function getOrderFields() { - // For some crazy reason ordering by a constant - // causes a filesort - if ( count( MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces() ) > 1 - ) { - return [ 'page_namespace', 'page_title' ]; - } else { - return [ 'page_title' ]; - } - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialDeletedContributions.php b/includes/specials/SpecialDeletedContributions.php index e9bf6a2372..b2cd8c0af7 100644 --- a/includes/specials/SpecialDeletedContributions.php +++ b/includes/specials/SpecialDeletedContributions.php @@ -28,7 +28,7 @@ use MediaWiki\MediaWikiServices; * Implements Special:DeletedContributions to display archived revisions * @ingroup SpecialPage */ -class DeletedContributionsPage extends SpecialPage { +class SpecialDeletedContributions extends SpecialPage { /** @var FormOptions */ protected $mOpts; diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php index fcf1bb2797..cccca5063a 100644 --- a/includes/specials/SpecialDoubleRedirects.php +++ b/includes/specials/SpecialDoubleRedirects.php @@ -30,7 +30,7 @@ use Wikimedia\Rdbms\IDatabase; * * @ingroup SpecialPage */ -class DoubleRedirectsPage extends QueryPage { +class SpecialDoubleRedirects extends QueryPage { function __construct( $name = 'DoubleRedirects' ) { parent::__construct( $name ); } diff --git a/includes/specials/SpecialEmailInvalidate.php b/includes/specials/SpecialEmailInvalidate.php index c54abadd54..7b9ee1a4fe 100644 --- a/includes/specials/SpecialEmailInvalidate.php +++ b/includes/specials/SpecialEmailInvalidate.php @@ -27,7 +27,7 @@ * * @ingroup SpecialPage */ -class EmailInvalidation extends UnlistedSpecialPage { +class SpecialEmailInvalidate extends UnlistedSpecialPage { public function __construct() { parent::__construct( 'Invalidateemail', 'editmyprivateinfo' ); } diff --git a/includes/specials/SpecialFewestRevisions.php b/includes/specials/SpecialFewestRevisions.php new file mode 100644 index 0000000000..204d73a625 --- /dev/null +++ b/includes/specials/SpecialFewestRevisions.php @@ -0,0 +1,108 @@ + [ 'revision', 'page' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'COUNT(*)', + ], + 'conds' => [ + 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces(), + 'page_id = rev_page', + 'page_is_redirect = 0', + ], + 'options' => [ + 'GROUP BY' => [ 'page_namespace', 'page_title' ] + ] + ]; + } + + function sortDescending() { + return false; + } + + /** + * @param Skin $skin + * @param object $result Database row + * @return string + */ + function formatResult( $skin, $result ) { + $nt = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$nt ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + $result->namespace, + $result->title + ) + ); + } + $linkRenderer = $this->getLinkRenderer(); + $text = MediaWikiServices::getInstance()->getContentLanguage()-> + convert( htmlspecialchars( $nt->getPrefixedText() ) ); + $plink = $linkRenderer->makeLink( $nt, new HtmlArmor( $text ) ); + + $nl = $this->msg( 'nrevisions' )->numParams( $result->value )->text(); + $redirect = isset( $result->redirect ) && $result->redirect ? + ' - ' . $this->msg( 'isredirect' )->escaped() : ''; + $nlink = $linkRenderer->makeKnownLink( + $nt, + $nl, + [], + [ 'action' => 'history' ] + ) . $redirect; + + return $this->getLanguage()->specialList( $plink, $nlink ); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialFewestrevisions.php b/includes/specials/SpecialFewestrevisions.php deleted file mode 100644 index cf9da49eff..0000000000 --- a/includes/specials/SpecialFewestrevisions.php +++ /dev/null @@ -1,108 +0,0 @@ - [ 'revision', 'page' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'COUNT(*)', - ], - 'conds' => [ - 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces(), - 'page_id = rev_page', - 'page_is_redirect = 0', - ], - 'options' => [ - 'GROUP BY' => [ 'page_namespace', 'page_title' ] - ] - ]; - } - - function sortDescending() { - return false; - } - - /** - * @param Skin $skin - * @param object $result Database row - * @return string - */ - function formatResult( $skin, $result ) { - $nt = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$nt ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - $result->namespace, - $result->title - ) - ); - } - $linkRenderer = $this->getLinkRenderer(); - $text = MediaWikiServices::getInstance()->getContentLanguage()-> - convert( htmlspecialchars( $nt->getPrefixedText() ) ); - $plink = $linkRenderer->makeLink( $nt, new HtmlArmor( $text ) ); - - $nl = $this->msg( 'nrevisions' )->numParams( $result->value )->text(); - $redirect = isset( $result->redirect ) && $result->redirect ? - ' - ' . $this->msg( 'isredirect' )->escaped() : ''; - $nlink = $linkRenderer->makeKnownLink( - $nt, - $nl, - [], - [ 'action' => 'history' ] - ) . $redirect; - - return $this->getLanguage()->specialList( $plink, $nlink ); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php index 5d8a415a00..f047e1f8bb 100644 --- a/includes/specials/SpecialFileDuplicateSearch.php +++ b/includes/specials/SpecialFileDuplicateSearch.php @@ -31,7 +31,7 @@ use MediaWiki\MediaWikiServices; * * @ingroup SpecialPage */ -class FileDuplicateSearchPage extends QueryPage { +class SpecialFileDuplicateSearch extends QueryPage { protected $hash = '', $filename = ''; /** diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index d08fe5cf4e..60aeddaf84 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -29,9 +29,15 @@ use Wikimedia\Rdbms\IDatabase; * Special:LinkSearch to search the external-links table. * @ingroup SpecialPage */ -class LinkSearchPage extends QueryPage { +class SpecialLinkSearch extends QueryPage { /** @var array|bool */ private $mungedQuery = false; + /** @var string|null */ + private $mQuery; + /** @var int|null */ + private $mNs; + /** @var string|null */ + private $mProt; function setParams( $params ) { $this->mQuery = $params['query']; diff --git a/includes/specials/SpecialListDuplicatedFiles.php b/includes/specials/SpecialListDuplicatedFiles.php index c7430cc498..d93c5701c6 100644 --- a/includes/specials/SpecialListDuplicatedFiles.php +++ b/includes/specials/SpecialListDuplicatedFiles.php @@ -32,7 +32,7 @@ use Wikimedia\Rdbms\IDatabase; * a duplicate of the current version of some other file. * @ingroup SpecialPage */ -class ListDuplicatedFilesPage extends QueryPage { +class SpecialListDuplicatedFiles extends QueryPage { function __construct( $name = 'ListDuplicatedFiles' ) { parent::__construct( $name ); } diff --git a/includes/specials/SpecialListRedirects.php b/includes/specials/SpecialListRedirects.php new file mode 100644 index 0000000000..e6862733f1 --- /dev/null +++ b/includes/specials/SpecialListRedirects.php @@ -0,0 +1,156 @@ + + */ + +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * Special:Listredirects - Lists all the redirects on the wiki. + * @ingroup SpecialPage + */ +class SpecialListRedirects extends QueryPage { + function __construct( $name = 'Listredirects' ) { + parent::__construct( $name ); + } + + public function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'p1' => 'page', 'redirect', 'p2' => 'page' ], + 'fields' => [ 'namespace' => 'p1.page_namespace', + 'title' => 'p1.page_title', + 'value' => 'p1.page_title', + 'rd_namespace', + 'rd_title', + 'rd_fragment', + 'rd_interwiki', + 'redirid' => 'p2.page_id' ], + 'conds' => [ 'p1.page_is_redirect' => 1 ], + 'join_conds' => [ 'redirect' => [ + 'LEFT JOIN', 'rd_from=p1.page_id' ], + 'p2' => [ 'LEFT JOIN', [ + 'p2.page_namespace=rd_namespace', + 'p2.page_title=rd_title' ] ] ] + ]; + } + + function getOrderFields() { + return [ 'p1.page_namespace', 'p1.page_title' ]; + } + + /** + * Cache page existence for performance + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + if ( !$res->numRows() ) { + return; + } + + $batch = new LinkBatch; + foreach ( $res as $row ) { + $batch->add( $row->namespace, $row->title ); + $redirTarget = $this->getRedirectTarget( $row ); + if ( $redirTarget ) { + $batch->addObj( $redirTarget ); + } + } + $batch->execute(); + + // Back to start for display + $res->seek( 0 ); + } + + /** + * @param stdClass $row + * @return Title|null + */ + protected function getRedirectTarget( $row ) { + if ( isset( $row->rd_title ) ) { + return Title::makeTitle( $row->rd_namespace, + $row->rd_title, $row->rd_fragment, + $row->rd_interwiki + ); + } else { + $title = Title::makeTitle( $row->namespace, $row->title ); + $article = WikiPage::factory( $title ); + + return $article->getRedirectTarget(); + } + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $linkRenderer = $this->getLinkRenderer(); + # Make a link to the redirect itself + $rd_title = Title::makeTitle( $result->namespace, $result->title ); + $rd_link = $linkRenderer->makeLink( + $rd_title, + null, + [], + [ 'redirect' => 'no' ] + ); + + # Find out where the redirect leads + $target = $this->getRedirectTarget( $result ); + if ( $target ) { + # Make a link to the destination page + $lang = $this->getLanguage(); + $arr = $lang->getArrow() . $lang->getDirMark(); + $targetLink = $linkRenderer->makeLink( $target, $target->getFullText() ); + + return "$rd_link $arr $targetLink"; + } else { + return "$rd_link"; + } + } + + public function execute( $par ) { + $this->addHelpLink( 'Help:Redirects' ); + parent::execute( $par ); + } + + protected function getGroupName() { + return 'pages'; + } +} diff --git a/includes/specials/SpecialListredirects.php b/includes/specials/SpecialListredirects.php deleted file mode 100644 index 3284c570ee..0000000000 --- a/includes/specials/SpecialListredirects.php +++ /dev/null @@ -1,156 +0,0 @@ - - */ - -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * Special:Listredirects - Lists all the redirects on the wiki. - * @ingroup SpecialPage - */ -class ListredirectsPage extends QueryPage { - function __construct( $name = 'Listredirects' ) { - parent::__construct( $name ); - } - - public function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function sortDescending() { - return false; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'p1' => 'page', 'redirect', 'p2' => 'page' ], - 'fields' => [ 'namespace' => 'p1.page_namespace', - 'title' => 'p1.page_title', - 'value' => 'p1.page_title', - 'rd_namespace', - 'rd_title', - 'rd_fragment', - 'rd_interwiki', - 'redirid' => 'p2.page_id' ], - 'conds' => [ 'p1.page_is_redirect' => 1 ], - 'join_conds' => [ 'redirect' => [ - 'LEFT JOIN', 'rd_from=p1.page_id' ], - 'p2' => [ 'LEFT JOIN', [ - 'p2.page_namespace=rd_namespace', - 'p2.page_title=rd_title' ] ] ] - ]; - } - - function getOrderFields() { - return [ 'p1.page_namespace', 'p1.page_title' ]; - } - - /** - * Cache page existence for performance - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - if ( !$res->numRows() ) { - return; - } - - $batch = new LinkBatch; - foreach ( $res as $row ) { - $batch->add( $row->namespace, $row->title ); - $redirTarget = $this->getRedirectTarget( $row ); - if ( $redirTarget ) { - $batch->addObj( $redirTarget ); - } - } - $batch->execute(); - - // Back to start for display - $res->seek( 0 ); - } - - /** - * @param stdClass $row - * @return Title|null - */ - protected function getRedirectTarget( $row ) { - if ( isset( $row->rd_title ) ) { - return Title::makeTitle( $row->rd_namespace, - $row->rd_title, $row->rd_fragment, - $row->rd_interwiki - ); - } else { - $title = Title::makeTitle( $row->namespace, $row->title ); - $article = WikiPage::factory( $title ); - - return $article->getRedirectTarget(); - } - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $linkRenderer = $this->getLinkRenderer(); - # Make a link to the redirect itself - $rd_title = Title::makeTitle( $result->namespace, $result->title ); - $rd_link = $linkRenderer->makeLink( - $rd_title, - null, - [], - [ 'redirect' => 'no' ] - ); - - # Find out where the redirect leads - $target = $this->getRedirectTarget( $result ); - if ( $target ) { - # Make a link to the destination page - $lang = $this->getLanguage(); - $arr = $lang->getArrow() . $lang->getDirMark(); - $targetLink = $linkRenderer->makeLink( $target, $target->getFullText() ); - - return "$rd_link $arr $targetLink"; - } else { - return "$rd_link"; - } - } - - public function execute( $par ) { - $this->addHelpLink( 'Help:Redirects' ); - parent::execute( $par ); - } - - protected function getGroupName() { - return 'pages'; - } -} diff --git a/includes/specials/SpecialLonelyPages.php b/includes/specials/SpecialLonelyPages.php new file mode 100644 index 0000000000..14f9435f3a --- /dev/null +++ b/includes/specials/SpecialLonelyPages.php @@ -0,0 +1,107 @@ +msg( 'lonelypagestext' )->parseAsBlock(); + } + + function sortDescending() { + return false; + } + + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function getQueryInfo() { + $tables = [ 'page', 'pagelinks', 'templatelinks' ]; + $conds = [ + 'pl_namespace IS NULL', + 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces(), + 'page_is_redirect' => 0, + 'tl_namespace IS NULL' + ]; + $joinConds = [ + 'pagelinks' => [ + 'LEFT JOIN', [ + 'pl_namespace = page_namespace', + 'pl_title = page_title' + ] + ], + 'templatelinks' => [ + 'LEFT JOIN', [ + 'tl_namespace = page_namespace', + 'tl_title = page_title' + ] + ] + ]; + + // Allow extensions to modify the query + Hooks::run( 'LonelyPagesQuery', [ &$tables, &$conds, &$joinConds ] ); + + return [ + 'tables' => $tables, + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + 'conds' => $conds, + 'join_conds' => $joinConds + ]; + } + + function getOrderFields() { + // For some crazy reason ordering by a constant + // causes a filesort in MySQL 5 + if ( count( MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces() ) > 1 + ) { + return [ 'page_namespace', 'page_title' ]; + } else { + return [ 'page_title' ]; + } + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialLonelypages.php b/includes/specials/SpecialLonelypages.php deleted file mode 100644 index ca3f4dae8c..0000000000 --- a/includes/specials/SpecialLonelypages.php +++ /dev/null @@ -1,107 +0,0 @@ -msg( 'lonelypagestext' )->parseAsBlock(); - } - - function sortDescending() { - return false; - } - - function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function getQueryInfo() { - $tables = [ 'page', 'pagelinks', 'templatelinks' ]; - $conds = [ - 'pl_namespace IS NULL', - 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces(), - 'page_is_redirect' => 0, - 'tl_namespace IS NULL' - ]; - $joinConds = [ - 'pagelinks' => [ - 'LEFT JOIN', [ - 'pl_namespace = page_namespace', - 'pl_title = page_title' - ] - ], - 'templatelinks' => [ - 'LEFT JOIN', [ - 'tl_namespace = page_namespace', - 'tl_title = page_title' - ] - ] - ]; - - // Allow extensions to modify the query - Hooks::run( 'LonelyPagesQuery', [ &$tables, &$conds, &$joinConds ] ); - - return [ - 'tables' => $tables, - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - 'conds' => $conds, - 'join_conds' => $joinConds - ]; - } - - function getOrderFields() { - // For some crazy reason ordering by a constant - // causes a filesort in MySQL 5 - if ( count( MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces() ) > 1 - ) { - return [ 'page_namespace', 'page_title' ]; - } else { - return [ 'page_title' ]; - } - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialLongPages.php b/includes/specials/SpecialLongPages.php new file mode 100644 index 0000000000..5227d4e7e1 --- /dev/null +++ b/includes/specials/SpecialLongPages.php @@ -0,0 +1,40 @@ + + */ + +use MediaWiki\MediaWikiServices; + +/** + * Searches the database for files of the requested MIME type, comparing this with the + * 'img_major_mime' and 'img_minor_mime' fields in the image table. + * @ingroup SpecialPage + */ +class SpecialMIMESearch extends QueryPage { + protected $major, $minor, $mime; + + function __construct( $name = 'MIMEsearch' ) { + parent::__construct( $name ); + } + + public function isExpensive() { + return false; + } + + function isSyndicated() { + return false; + } + + function isCacheable() { + return false; + } + + function linkParameters() { + return [ 'mime' => "{$this->major}/{$this->minor}" ]; + } + + public function getQueryInfo() { + $minorType = []; + if ( $this->minor !== '*' ) { + // Allow wildcard searching + $minorType['img_minor_mime'] = $this->minor; + } + $imgQuery = LocalFile::getQueryInfo(); + $qi = [ + 'tables' => $imgQuery['tables'], + 'fields' => [ + 'namespace' => NS_FILE, + 'title' => 'img_name', + // Still have a value field just in case, + // but it isn't actually used for sorting. + 'value' => 'img_name', + 'img_size', + 'img_width', + 'img_height', + 'img_user_text' => $imgQuery['fields']['img_user_text'], + 'img_timestamp' + ], + 'conds' => [ + 'img_major_mime' => $this->major, + // This is in order to trigger using + // the img_media_mime index in "range" mode. + // @todo how is order defined? use MimeAnalyzer::getMediaTypes? + 'img_media_type' => [ + MEDIATYPE_BITMAP, + MEDIATYPE_DRAWING, + MEDIATYPE_AUDIO, + MEDIATYPE_VIDEO, + MEDIATYPE_MULTIMEDIA, + MEDIATYPE_UNKNOWN, + MEDIATYPE_OFFICE, + MEDIATYPE_TEXT, + MEDIATYPE_EXECUTABLE, + MEDIATYPE_ARCHIVE, + MEDIATYPE_3D, + ], + ] + $minorType, + 'join_conds' => $imgQuery['joins'], + ]; + + return $qi; + } + + /** + * The index is on (img_media_type, img_major_mime, img_minor_mime) + * which unfortunately doesn't have img_name at the end for sorting. + * So tell db to sort it however it wishes (Its not super important + * that this report gives results in a logical order). As an aditional + * note, mysql seems to by default order things by img_name ASC, which + * is what we ideally want, so everything works out fine anyhow. + * @return array + */ + function getOrderFields() { + return []; + } + + /** + * Generate and output the form + */ + function getPageHeader() { + $formDescriptor = [ + 'mime' => [ + 'type' => 'combobox', + 'options' => $this->getSuggestionsForTypes(), + 'name' => 'mime', + 'label-message' => 'mimetype', + 'required' => true, + 'default' => $this->mime, + ], + ]; + + HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) + ->setSubmitTextMsg( 'ilsubmit' ) + ->setAction( $this->getPageTitle()->getLocalURL() ) + ->setMethod( 'get' ) + ->prepareForm() + ->displayForm( false ); + return ''; + } + + protected function getSuggestionsForTypes() { + $dbr = wfGetDB( DB_REPLICA ); + $lastMajor = null; + $suggestions = []; + $result = $dbr->select( + [ 'image' ], + // We ignore img_media_type, but using it in the query is needed for MySQL to choose a + // sensible execution plan + [ 'img_media_type', 'img_major_mime', 'img_minor_mime' ], + [], + __METHOD__, + [ 'GROUP BY' => [ 'img_media_type', 'img_major_mime', 'img_minor_mime' ] ] + ); + foreach ( $result as $row ) { + $major = $row->img_major_mime; + $minor = $row->img_minor_mime; + $suggestions[ "$major/$minor" ] = "$major/$minor"; + if ( $lastMajor === $major ) { + // If there are at least two with the same major mime type, also include the wildcard + $suggestions[ "$major/*" ] = "$major/*"; + } + $lastMajor = $major; + } + ksort( $suggestions ); + return $suggestions; + } + + public function execute( $par ) { + $this->addHelpLink( 'Help:Managing_files' ); + $this->mime = $par ?: $this->getRequest()->getText( 'mime' ); + $this->mime = trim( $this->mime ); + list( $this->major, $this->minor ) = File::splitMime( $this->mime ); + + if ( $this->major == '' || $this->minor == '' || $this->minor == 'unknown' || + !self::isValidType( $this->major ) + ) { + $this->setHeaders(); + $this->outputHeader(); + $this->getPageHeader(); + return; + } + + parent::execute( $par ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $linkRenderer = $this->getLinkRenderer(); + $nt = Title::makeTitle( $result->namespace, $result->title ); + $text = MediaWikiServices::getInstance()->getContentLanguage() + ->convert( htmlspecialchars( $nt->getText() ) ); + $plink = $linkRenderer->makeLink( + Title::newFromText( $nt->getPrefixedText() ), + new HtmlArmor( $text ) + ); + + $download = Linker::makeMediaLinkObj( $nt, $this->msg( 'download' )->escaped() ); + $download = $this->msg( 'parentheses' )->rawParams( $download )->escaped(); + $lang = $this->getLanguage(); + $bytes = htmlspecialchars( $lang->formatSize( $result->img_size ) ); + $dimensions = $this->msg( 'widthheight' )->numParams( $result->img_width, + $result->img_height )->escaped(); + $user = $linkRenderer->makeLink( + Title::makeTitle( NS_USER, $result->img_user_text ), + $result->img_user_text + ); + + $time = $lang->userTimeAndDate( $result->img_timestamp, $this->getUser() ); + $time = htmlspecialchars( $time ); + + return "$download $plink . . $dimensions . . $bytes . . $user . . $time"; + } + + /** + * @param string $type + * @return bool + */ + protected static function isValidType( $type ) { + // From maintenance/tables.sql => img_major_mime + $types = [ + 'unknown', + 'application', + 'audio', + 'image', + 'text', + 'video', + 'message', + 'model', + 'multipart', + 'chemical' + ]; + + return in_array( $type, $types ); + } + + public function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + protected function getGroupName() { + return 'media'; + } +} diff --git a/includes/specials/SpecialMIMEsearch.php b/includes/specials/SpecialMIMEsearch.php deleted file mode 100644 index d6ace1d0ce..0000000000 --- a/includes/specials/SpecialMIMEsearch.php +++ /dev/null @@ -1,244 +0,0 @@ - - */ - -use MediaWiki\MediaWikiServices; - -/** - * Searches the database for files of the requested MIME type, comparing this with the - * 'img_major_mime' and 'img_minor_mime' fields in the image table. - * @ingroup SpecialPage - */ -class MIMEsearchPage extends QueryPage { - protected $major, $minor, $mime; - - function __construct( $name = 'MIMEsearch' ) { - parent::__construct( $name ); - } - - public function isExpensive() { - return false; - } - - function isSyndicated() { - return false; - } - - function isCacheable() { - return false; - } - - function linkParameters() { - return [ 'mime' => "{$this->major}/{$this->minor}" ]; - } - - public function getQueryInfo() { - $minorType = []; - if ( $this->minor !== '*' ) { - // Allow wildcard searching - $minorType['img_minor_mime'] = $this->minor; - } - $imgQuery = LocalFile::getQueryInfo(); - $qi = [ - 'tables' => $imgQuery['tables'], - 'fields' => [ - 'namespace' => NS_FILE, - 'title' => 'img_name', - // Still have a value field just in case, - // but it isn't actually used for sorting. - 'value' => 'img_name', - 'img_size', - 'img_width', - 'img_height', - 'img_user_text' => $imgQuery['fields']['img_user_text'], - 'img_timestamp' - ], - 'conds' => [ - 'img_major_mime' => $this->major, - // This is in order to trigger using - // the img_media_mime index in "range" mode. - // @todo how is order defined? use MimeAnalyzer::getMediaTypes? - 'img_media_type' => [ - MEDIATYPE_BITMAP, - MEDIATYPE_DRAWING, - MEDIATYPE_AUDIO, - MEDIATYPE_VIDEO, - MEDIATYPE_MULTIMEDIA, - MEDIATYPE_UNKNOWN, - MEDIATYPE_OFFICE, - MEDIATYPE_TEXT, - MEDIATYPE_EXECUTABLE, - MEDIATYPE_ARCHIVE, - MEDIATYPE_3D, - ], - ] + $minorType, - 'join_conds' => $imgQuery['joins'], - ]; - - return $qi; - } - - /** - * The index is on (img_media_type, img_major_mime, img_minor_mime) - * which unfortunately doesn't have img_name at the end for sorting. - * So tell db to sort it however it wishes (Its not super important - * that this report gives results in a logical order). As an aditional - * note, mysql seems to by default order things by img_name ASC, which - * is what we ideally want, so everything works out fine anyhow. - * @return array - */ - function getOrderFields() { - return []; - } - - /** - * Generate and output the form - */ - function getPageHeader() { - $formDescriptor = [ - 'mime' => [ - 'type' => 'combobox', - 'options' => $this->getSuggestionsForTypes(), - 'name' => 'mime', - 'label-message' => 'mimetype', - 'required' => true, - 'default' => $this->mime, - ], - ]; - - HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) - ->setSubmitTextMsg( 'ilsubmit' ) - ->setAction( $this->getPageTitle()->getLocalURL() ) - ->setMethod( 'get' ) - ->prepareForm() - ->displayForm( false ); - return ''; - } - - protected function getSuggestionsForTypes() { - $dbr = wfGetDB( DB_REPLICA ); - $lastMajor = null; - $suggestions = []; - $result = $dbr->select( - [ 'image' ], - // We ignore img_media_type, but using it in the query is needed for MySQL to choose a - // sensible execution plan - [ 'img_media_type', 'img_major_mime', 'img_minor_mime' ], - [], - __METHOD__, - [ 'GROUP BY' => [ 'img_media_type', 'img_major_mime', 'img_minor_mime' ] ] - ); - foreach ( $result as $row ) { - $major = $row->img_major_mime; - $minor = $row->img_minor_mime; - $suggestions[ "$major/$minor" ] = "$major/$minor"; - if ( $lastMajor === $major ) { - // If there are at least two with the same major mime type, also include the wildcard - $suggestions[ "$major/*" ] = "$major/*"; - } - $lastMajor = $major; - } - ksort( $suggestions ); - return $suggestions; - } - - public function execute( $par ) { - $this->addHelpLink( 'Help:Managing_files' ); - $this->mime = $par ?: $this->getRequest()->getText( 'mime' ); - $this->mime = trim( $this->mime ); - list( $this->major, $this->minor ) = File::splitMime( $this->mime ); - - if ( $this->major == '' || $this->minor == '' || $this->minor == 'unknown' || - !self::isValidType( $this->major ) - ) { - $this->setHeaders(); - $this->outputHeader(); - $this->getPageHeader(); - return; - } - - parent::execute( $par ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $linkRenderer = $this->getLinkRenderer(); - $nt = Title::makeTitle( $result->namespace, $result->title ); - $text = MediaWikiServices::getInstance()->getContentLanguage() - ->convert( htmlspecialchars( $nt->getText() ) ); - $plink = $linkRenderer->makeLink( - Title::newFromText( $nt->getPrefixedText() ), - new HtmlArmor( $text ) - ); - - $download = Linker::makeMediaLinkObj( $nt, $this->msg( 'download' )->escaped() ); - $download = $this->msg( 'parentheses' )->rawParams( $download )->escaped(); - $lang = $this->getLanguage(); - $bytes = htmlspecialchars( $lang->formatSize( $result->img_size ) ); - $dimensions = $this->msg( 'widthheight' )->numParams( $result->img_width, - $result->img_height )->escaped(); - $user = $linkRenderer->makeLink( - Title::makeTitle( NS_USER, $result->img_user_text ), - $result->img_user_text - ); - - $time = $lang->userTimeAndDate( $result->img_timestamp, $this->getUser() ); - $time = htmlspecialchars( $time ); - - return "$download $plink . . $dimensions . . $bytes . . $user . . $time"; - } - - /** - * @param string $type - * @return bool - */ - protected static function isValidType( $type ) { - // From maintenance/tables.sql => img_major_mime - $types = [ - 'unknown', - 'application', - 'audio', - 'image', - 'text', - 'video', - 'message', - 'model', - 'multipart', - 'chemical' - ]; - - return in_array( $type, $types ); - } - - public function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - protected function getGroupName() { - return 'media'; - } -} diff --git a/includes/specials/SpecialMediaStatistics.php b/includes/specials/SpecialMediaStatistics.php index 45bd524223..333d9b3cca 100644 --- a/includes/specials/SpecialMediaStatistics.php +++ b/includes/specials/SpecialMediaStatistics.php @@ -28,7 +28,7 @@ use Wikimedia\Rdbms\IDatabase; /** * @ingroup SpecialPage */ -class MediaStatisticsPage extends QueryPage { +class SpecialMediaStatistics extends QueryPage { protected $totalCount = 0, $totalBytes = 0; /** diff --git a/includes/specials/SpecialMostCategories.php b/includes/specials/SpecialMostCategories.php new file mode 100644 index 0000000000..682a23ae94 --- /dev/null +++ b/includes/specials/SpecialMostCategories.php @@ -0,0 +1,114 @@ + + */ + +use MediaWiki\MediaWikiServices; +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * A special page that list pages that have highest category count + * + * @ingroup SpecialPage + */ +class SpecialMostCategories extends QueryPage { + function __construct( $name = 'Mostcategories' ) { + parent::__construct( $name ); + } + + public function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'categorylinks', 'page' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'COUNT(*)' + ], + 'conds' => [ 'page_namespace' => + MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces() ], + 'options' => [ + 'HAVING' => 'COUNT(*) > 1', + 'GROUP BY' => [ 'page_namespace', 'page_title' ] + ], + 'join_conds' => [ + 'page' => [ + 'LEFT JOIN', + 'page_id = cl_from' + ] + ] + ]; + } + + /** + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $title = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$title ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + $result->namespace, + $result->title + ) + ); + } + + $linkRenderer = $this->getLinkRenderer(); + if ( $this->isCached() ) { + $link = $linkRenderer->makeLink( $title ); + } else { + $link = $linkRenderer->makeKnownLink( $title ); + } + + $count = $this->msg( 'ncategories' )->numParams( $result->value )->escaped(); + + return $this->getLanguage()->specialList( $link, $count ); + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostInterwikis.php b/includes/specials/SpecialMostInterwikis.php new file mode 100644 index 0000000000..52777b0dfd --- /dev/null +++ b/includes/specials/SpecialMostInterwikis.php @@ -0,0 +1,117 @@ + [ + 'langlinks', + 'page' + ], 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'COUNT(*)' + ], 'conds' => [ + 'page_namespace' => + MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces() + ], 'options' => [ + 'HAVING' => 'COUNT(*) > 1', + 'GROUP BY' => [ + 'page_namespace', + 'page_title' + ] + ], 'join_conds' => [ + 'page' => [ + 'LEFT JOIN', + 'page_id = ll_from' + ] + ] + ]; + } + + /** + * Pre-fill the link cache + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * @param Skin $skin + * @param object $result + * @return string + */ + function formatResult( $skin, $result ) { + $title = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$title ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + $result->namespace, + $result->title + ) + ); + } + + $linkRenderer = $this->getLinkRenderer(); + if ( $this->isCached() ) { + $link = $linkRenderer->makeLink( $title ); + } else { + $link = $linkRenderer->makeKnownLink( $title ); + } + + $count = $this->msg( 'ninterwikis' )->numParams( $result->value )->escaped(); + + return $this->getLanguage()->specialList( $link, $count ); + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostLinked.php b/includes/specials/SpecialMostLinked.php new file mode 100644 index 0000000000..c0f1a2a06e --- /dev/null +++ b/includes/specials/SpecialMostLinked.php @@ -0,0 +1,135 @@ + + * @author Rob Church + */ + +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * A special page to show pages ordered by the number of pages linking to them. + * + * @ingroup SpecialPage + */ +class SpecialMostLinked extends QueryPage { + function __construct( $name = 'Mostlinked' ) { + parent::__construct( $name ); + } + + public function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'pagelinks', 'page' ], + 'fields' => [ + 'namespace' => 'pl_namespace', + 'title' => 'pl_title', + 'value' => 'COUNT(*)', + 'page_namespace' + ], + 'options' => [ + 'HAVING' => 'COUNT(*) > 1', + 'GROUP BY' => [ + 'pl_namespace', 'pl_title', + 'page_namespace' + ] + ], + 'join_conds' => [ + 'page' => [ + 'LEFT JOIN', + [ + 'page_namespace = pl_namespace', + 'page_title = pl_title' + ] + ] + ] + ]; + } + + /** + * Pre-fill the link cache + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * Make a link to "what links here" for the specified title + * + * @param Title $title Title being queried + * @param string $caption Text to display on the link + * @return string + */ + function makeWlhLink( $title, $caption ) { + $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedDBkey() ); + + $linkRenderer = $this->getLinkRenderer(); + return $linkRenderer->makeKnownLink( $wlh, $caption ); + } + + /** + * Make links to the page corresponding to the item, + * and the "what links here" page for it + * + * @param Skin $skin Skin to be used + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $title = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$title ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + $result->namespace, + $result->title ) + ); + } + + $linkRenderer = $this->getLinkRenderer(); + $link = $linkRenderer->makeLink( $title ); + $wlh = $this->makeWlhLink( + $title, + $this->msg( 'nlinks' )->numParams( $result->value )->text() + ); + + return $this->getLanguage()->specialList( $link, $wlh ); + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostLinkedCategories.php b/includes/specials/SpecialMostLinkedCategories.php new file mode 100644 index 0000000000..32157fa8a1 --- /dev/null +++ b/includes/specials/SpecialMostLinkedCategories.php @@ -0,0 +1,98 @@ + + */ + +use MediaWiki\MediaWikiServices; +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * A querypage to show categories ordered in descending order by the pages in them + * + * @ingroup SpecialPage + */ +class SpecialMostLinkedCategories extends QueryPage { + function __construct( $name = 'Mostlinkedcategories' ) { + parent::__construct( $name ); + } + + function isSyndicated() { + return false; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'category' ], + 'fields' => [ 'title' => 'cat_title', + 'namespace' => NS_CATEGORY, + 'value' => 'cat_pages' ], + 'conds' => [ 'cat_pages > 0' ], + ]; + } + + function sortDescending() { + return true; + } + + /** + * Fetch user page links and cache their existence + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $nt = Title::makeTitleSafe( NS_CATEGORY, $result->title ); + if ( !$nt ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + NS_CATEGORY, + $result->title ) + ); + } + + $text = MediaWikiServices::getInstance()->getContentLanguage() + ->convert( htmlspecialchars( $nt->getText() ) ); + $plink = $this->getLinkRenderer()->makeLink( $nt, new HtmlArmor( $text ) ); + $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); + + return $this->getLanguage()->specialList( $plink, $nlinks ); + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostLinkedTemplates.php b/includes/specials/SpecialMostLinkedTemplates.php new file mode 100644 index 0000000000..f7122c2859 --- /dev/null +++ b/includes/specials/SpecialMostLinkedTemplates.php @@ -0,0 +1,132 @@ + + */ + +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * Special page lists templates with a large number of + * transclusion links, i.e. "most used" templates + * + * @ingroup SpecialPage + */ +class SpecialMostLinkedTemplates extends QueryPage { + function __construct( $name = 'Mostlinkedtemplates' ) { + parent::__construct( $name ); + } + + /** + * Is this report expensive, i.e should it be cached? + * + * @return bool + */ + public function isExpensive() { + return true; + } + + /** + * Is there a feed available? + * + * @return bool + */ + public function isSyndicated() { + return false; + } + + /** + * Sort the results in descending order? + * + * @return bool + */ + public function sortDescending() { + return true; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'templatelinks' ], + 'fields' => [ + 'namespace' => 'tl_namespace', + 'title' => 'tl_title', + 'value' => 'COUNT(*)' + ], + 'options' => [ 'GROUP BY' => [ 'tl_namespace', 'tl_title' ] ] + ]; + } + + /** + * Pre-cache page existence to speed up link generation + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + public function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + /** + * Format a result row + * + * @param Skin $skin + * @param object $result Result row + * @return string + */ + public function formatResult( $skin, $result ) { + $title = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$title ) { + return Html::element( + 'span', + [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( + $this->getContext(), + $result->namespace, + $result->title + ) + ); + } + + return $this->getLanguage()->specialList( + $this->getLinkRenderer()->makeLink( $title ), + $this->makeWlhLink( $title, $result ) + ); + } + + /** + * Make a "what links here" link for a given title + * + * @param Title $title Title to make the link for + * @param object $result Result row + * @return string + */ + private function makeWlhLink( $title, $result ) { + $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() ); + $label = $this->msg( 'ntransclusions' )->numParams( $result->value )->text(); + + return $this->getLinkRenderer()->makeLink( $wlh, $label ); + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostRevisions.php b/includes/specials/SpecialMostRevisions.php new file mode 100644 index 0000000000..60e0fe7ec4 --- /dev/null +++ b/includes/specials/SpecialMostRevisions.php @@ -0,0 +1,39 @@ + + */ + +class SpecialMostRevisions extends SpecialFewestRevisions { + function __construct( $name = 'Mostrevisions' ) { + parent::__construct( $name ); + } + + function sortDescending() { + return true; + } + + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostcategories.php b/includes/specials/SpecialMostcategories.php deleted file mode 100644 index 0dd94372b6..0000000000 --- a/includes/specials/SpecialMostcategories.php +++ /dev/null @@ -1,114 +0,0 @@ - - */ - -use MediaWiki\MediaWikiServices; -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * A special page that list pages that have highest category count - * - * @ingroup SpecialPage - */ -class MostcategoriesPage extends QueryPage { - function __construct( $name = 'Mostcategories' ) { - parent::__construct( $name ); - } - - public function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'categorylinks', 'page' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'COUNT(*)' - ], - 'conds' => [ 'page_namespace' => - MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces() ], - 'options' => [ - 'HAVING' => 'COUNT(*) > 1', - 'GROUP BY' => [ 'page_namespace', 'page_title' ] - ], - 'join_conds' => [ - 'page' => [ - 'LEFT JOIN', - 'page_id = cl_from' - ] - ] - ]; - } - - /** - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $title = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$title ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - $result->namespace, - $result->title - ) - ); - } - - $linkRenderer = $this->getLinkRenderer(); - if ( $this->isCached() ) { - $link = $linkRenderer->makeLink( $title ); - } else { - $link = $linkRenderer->makeKnownLink( $title ); - } - - $count = $this->msg( 'ncategories' )->numParams( $result->value )->escaped(); - - return $this->getLanguage()->specialList( $link, $count ); - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialMostinterwikis.php b/includes/specials/SpecialMostinterwikis.php deleted file mode 100644 index 0fcf8420b9..0000000000 --- a/includes/specials/SpecialMostinterwikis.php +++ /dev/null @@ -1,117 +0,0 @@ - [ - 'langlinks', - 'page' - ], 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'COUNT(*)' - ], 'conds' => [ - 'page_namespace' => - MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces() - ], 'options' => [ - 'HAVING' => 'COUNT(*) > 1', - 'GROUP BY' => [ - 'page_namespace', - 'page_title' - ] - ], 'join_conds' => [ - 'page' => [ - 'LEFT JOIN', - 'page_id = ll_from' - ] - ] - ]; - } - - /** - * Pre-fill the link cache - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * @param Skin $skin - * @param object $result - * @return string - */ - function formatResult( $skin, $result ) { - $title = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$title ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - $result->namespace, - $result->title - ) - ); - } - - $linkRenderer = $this->getLinkRenderer(); - if ( $this->isCached() ) { - $link = $linkRenderer->makeLink( $title ); - } else { - $link = $linkRenderer->makeKnownLink( $title ); - } - - $count = $this->msg( 'ninterwikis' )->numParams( $result->value )->escaped(); - - return $this->getLanguage()->specialList( $link, $count ); - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialMostlinked.php b/includes/specials/SpecialMostlinked.php deleted file mode 100644 index c4553a4fad..0000000000 --- a/includes/specials/SpecialMostlinked.php +++ /dev/null @@ -1,135 +0,0 @@ - - * @author Rob Church - */ - -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * A special page to show pages ordered by the number of pages linking to them. - * - * @ingroup SpecialPage - */ -class MostlinkedPage extends QueryPage { - function __construct( $name = 'Mostlinked' ) { - parent::__construct( $name ); - } - - public function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'pagelinks', 'page' ], - 'fields' => [ - 'namespace' => 'pl_namespace', - 'title' => 'pl_title', - 'value' => 'COUNT(*)', - 'page_namespace' - ], - 'options' => [ - 'HAVING' => 'COUNT(*) > 1', - 'GROUP BY' => [ - 'pl_namespace', 'pl_title', - 'page_namespace' - ] - ], - 'join_conds' => [ - 'page' => [ - 'LEFT JOIN', - [ - 'page_namespace = pl_namespace', - 'page_title = pl_title' - ] - ] - ] - ]; - } - - /** - * Pre-fill the link cache - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * Make a link to "what links here" for the specified title - * - * @param Title $title Title being queried - * @param string $caption Text to display on the link - * @return string - */ - function makeWlhLink( $title, $caption ) { - $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedDBkey() ); - - $linkRenderer = $this->getLinkRenderer(); - return $linkRenderer->makeKnownLink( $wlh, $caption ); - } - - /** - * Make links to the page corresponding to the item, - * and the "what links here" page for it - * - * @param Skin $skin Skin to be used - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $title = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$title ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - $result->namespace, - $result->title ) - ); - } - - $linkRenderer = $this->getLinkRenderer(); - $link = $linkRenderer->makeLink( $title ); - $wlh = $this->makeWlhLink( - $title, - $this->msg( 'nlinks' )->numParams( $result->value )->text() - ); - - return $this->getLanguage()->specialList( $link, $wlh ); - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialMostlinkedcategories.php b/includes/specials/SpecialMostlinkedcategories.php deleted file mode 100644 index 56a701a304..0000000000 --- a/includes/specials/SpecialMostlinkedcategories.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ - -use MediaWiki\MediaWikiServices; -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * A querypage to show categories ordered in descending order by the pages in them - * - * @ingroup SpecialPage - */ -class MostlinkedCategoriesPage extends QueryPage { - function __construct( $name = 'Mostlinkedcategories' ) { - parent::__construct( $name ); - } - - function isSyndicated() { - return false; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'category' ], - 'fields' => [ 'title' => 'cat_title', - 'namespace' => NS_CATEGORY, - 'value' => 'cat_pages' ], - 'conds' => [ 'cat_pages > 0' ], - ]; - } - - function sortDescending() { - return true; - } - - /** - * Fetch user page links and cache their existence - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $nt = Title::makeTitleSafe( NS_CATEGORY, $result->title ); - if ( !$nt ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - NS_CATEGORY, - $result->title ) - ); - } - - $text = MediaWikiServices::getInstance()->getContentLanguage() - ->convert( htmlspecialchars( $nt->getText() ) ); - $plink = $this->getLinkRenderer()->makeLink( $nt, new HtmlArmor( $text ) ); - $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); - - return $this->getLanguage()->specialList( $plink, $nlinks ); - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialMostlinkedtemplates.php b/includes/specials/SpecialMostlinkedtemplates.php deleted file mode 100644 index 4544468d69..0000000000 --- a/includes/specials/SpecialMostlinkedtemplates.php +++ /dev/null @@ -1,132 +0,0 @@ - - */ - -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * Special page lists templates with a large number of - * transclusion links, i.e. "most used" templates - * - * @ingroup SpecialPage - */ -class MostlinkedTemplatesPage extends QueryPage { - function __construct( $name = 'Mostlinkedtemplates' ) { - parent::__construct( $name ); - } - - /** - * Is this report expensive, i.e should it be cached? - * - * @return bool - */ - public function isExpensive() { - return true; - } - - /** - * Is there a feed available? - * - * @return bool - */ - public function isSyndicated() { - return false; - } - - /** - * Sort the results in descending order? - * - * @return bool - */ - public function sortDescending() { - return true; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'templatelinks' ], - 'fields' => [ - 'namespace' => 'tl_namespace', - 'title' => 'tl_title', - 'value' => 'COUNT(*)' - ], - 'options' => [ 'GROUP BY' => [ 'tl_namespace', 'tl_title' ] ] - ]; - } - - /** - * Pre-cache page existence to speed up link generation - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - public function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - /** - * Format a result row - * - * @param Skin $skin - * @param object $result Result row - * @return string - */ - public function formatResult( $skin, $result ) { - $title = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$title ) { - return Html::element( - 'span', - [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( - $this->getContext(), - $result->namespace, - $result->title - ) - ); - } - - return $this->getLanguage()->specialList( - $this->getLinkRenderer()->makeLink( $title ), - $this->makeWlhLink( $title, $result ) - ); - } - - /** - * Make a "what links here" link for a given title - * - * @param Title $title Title to make the link for - * @param object $result Result row - * @return string - */ - private function makeWlhLink( $title, $result ) { - $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() ); - $label = $this->msg( 'ntransclusions' )->numParams( $result->value )->text(); - - return $this->getLinkRenderer()->makeLink( $wlh, $label ); - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialMostrevisions.php b/includes/specials/SpecialMostrevisions.php deleted file mode 100644 index 0471cafe2d..0000000000 --- a/includes/specials/SpecialMostrevisions.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ - -class MostrevisionsPage extends FewestrevisionsPage { - function __construct( $name = 'Mostrevisions' ) { - parent::__construct( $name ); - } - - function sortDescending() { - return true; - } - - protected function getGroupName() { - return 'highuse'; - } -} diff --git a/includes/specials/SpecialNewFiles.php b/includes/specials/SpecialNewFiles.php new file mode 100644 index 0000000000..29e7789e06 --- /dev/null +++ b/includes/specials/SpecialNewFiles.php @@ -0,0 +1,226 @@ +getContext() ); + + $this->setHeaders(); + $this->outputHeader(); + $mimeAnalyzer = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer(); + $this->mediaTypes = $mimeAnalyzer->getMediaTypes(); + + $out = $this->getOutput(); + $this->addHelpLink( 'Help:New images' ); + + $opts = new FormOptions(); + + $opts->add( 'like', '' ); + $opts->add( 'user', '' ); + $opts->add( 'showbots', false ); + $opts->add( 'hidepatrolled', false ); + $opts->add( 'mediatype', $this->mediaTypes ); + $opts->add( 'limit', 50 ); + $opts->add( 'offset', '' ); + $opts->add( 'start', '' ); + $opts->add( 'end', '' ); + + $opts->fetchValuesFromRequest( $this->getRequest() ); + + if ( $par !== null ) { + $opts->setValue( is_numeric( $par ) ? 'limit' : 'like', $par ); + } + + // If start date comes after end date chronologically, swap them. + // They are swapped in the interface by JS. + $start = $opts->getValue( 'start' ); + $end = $opts->getValue( 'end' ); + if ( $start !== '' && $end !== '' && $start > $end ) { + $temp = $end; + $end = $start; + $start = $temp; + + $opts->setValue( 'start', $start, true ); + $opts->setValue( 'end', $end, true ); + + // also swap values in request object, which is used by HTMLForm + // to pre-populate the fields with the previous input + $request = $context->getRequest(); + $context->setRequest( new DerivativeRequest( + $request, + [ 'start' => $start, 'end' => $end ] + $request->getValues(), + $request->wasPosted() + ) ); + } + + // if all media types have been selected, wipe out the array to prevent + // the pointless IN(...) query condition (which would have no effect + // because every possible type has been selected) + $missingMediaTypes = array_diff( $this->mediaTypes, $opts->getValue( 'mediatype' ) ); + if ( empty( $missingMediaTypes ) ) { + $opts->setValue( 'mediatype', [] ); + } + + $opts->validateIntBounds( 'limit', 0, 500 ); + + $this->opts = $opts; + + if ( !$this->including() ) { + $this->setTopText(); + $this->buildForm( $context ); + } + + $pager = new NewFilesPager( $context, $opts, $this->getLinkRenderer() ); + + $out->addHTML( $pager->getBody() ); + if ( !$this->including() ) { + $out->addHTML( $pager->getNavigationBar() ); + } + } + + protected function buildForm( IContextSource $context ) { + $mediaTypesText = array_map( function ( $type ) { + // mediastatistics-header-unknown, mediastatistics-header-bitmap, + // mediastatistics-header-drawing, mediastatistics-header-audio, + // mediastatistics-header-video, mediastatistics-header-multimedia, + // mediastatistics-header-office, mediastatistics-header-text, + // mediastatistics-header-executable, mediastatistics-header-archive, + // mediastatistics-header-3d, + return $this->msg( 'mediastatistics-header-' . strtolower( $type ) )->text(); + }, $this->mediaTypes ); + $mediaTypesOptions = array_combine( $mediaTypesText, $this->mediaTypes ); + ksort( $mediaTypesOptions ); + + $formDescriptor = [ + 'like' => [ + 'type' => 'text', + 'label-message' => 'newimages-label', + 'name' => 'like', + ], + + 'user' => [ + 'class' => 'HTMLUserTextField', + 'label-message' => 'newimages-user', + 'name' => 'user', + ], + + 'showbots' => [ + 'type' => 'check', + 'label-message' => 'newimages-showbots', + 'name' => 'showbots', + ], + + 'hidepatrolled' => [ + 'type' => 'check', + 'label-message' => 'newimages-hidepatrolled', + 'name' => 'hidepatrolled', + ], + + 'mediatype' => [ + 'type' => 'multiselect', + 'flatlist' => true, + 'name' => 'mediatype', + 'label-message' => 'newimages-mediatype', + 'options' => $mediaTypesOptions, + 'default' => $this->mediaTypes, + ], + + 'limit' => [ + 'type' => 'hidden', + 'default' => $this->opts->getValue( 'limit' ), + 'name' => 'limit', + ], + + 'offset' => [ + 'type' => 'hidden', + 'default' => $this->opts->getValue( 'offset' ), + 'name' => 'offset', + ], + + 'start' => [ + 'type' => 'date', + 'label-message' => 'date-range-from', + 'name' => 'start', + ], + + 'end' => [ + 'type' => 'date', + 'label-message' => 'date-range-to', + 'name' => 'end', + ], + ]; + + if ( $this->getConfig()->get( 'MiserMode' ) ) { + unset( $formDescriptor['like'] ); + } + + if ( !$this->getUser()->useFilePatrol() ) { + unset( $formDescriptor['hidepatrolled'] ); + } + + HTMLForm::factory( 'ooui', $formDescriptor, $context ) + // For the 'multiselect' field values to be preserved on submit + ->setFormIdentifier( 'specialnewimages' ) + ->setWrapperLegendMsg( 'newimages-legend' ) + ->setSubmitTextMsg( 'ilsubmit' ) + ->setMethod( 'get' ) + ->prepareForm() + ->displayForm( false ); + } + + protected function getGroupName() { + return 'changes'; + } + + /** + * Send the text to be displayed above the options + */ + function setTopText() { + $message = $this->msg( 'newimagestext' )->inContentLanguage(); + if ( !$message->isDisabled() ) { + $contLang = MediaWikiServices::getInstance()->getContentLanguage(); + $this->getOutput()->addWikiTextAsContent( + Html::rawElement( 'div', + [ + + 'lang' => $contLang->getHtmlCode(), + 'dir' => $contLang->getDir() + ], + "\n" . $message->plain() . "\n" + ) + ); + } + } +} diff --git a/includes/specials/SpecialNewimages.php b/includes/specials/SpecialNewimages.php deleted file mode 100644 index 29e7789e06..0000000000 --- a/includes/specials/SpecialNewimages.php +++ /dev/null @@ -1,226 +0,0 @@ -getContext() ); - - $this->setHeaders(); - $this->outputHeader(); - $mimeAnalyzer = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer(); - $this->mediaTypes = $mimeAnalyzer->getMediaTypes(); - - $out = $this->getOutput(); - $this->addHelpLink( 'Help:New images' ); - - $opts = new FormOptions(); - - $opts->add( 'like', '' ); - $opts->add( 'user', '' ); - $opts->add( 'showbots', false ); - $opts->add( 'hidepatrolled', false ); - $opts->add( 'mediatype', $this->mediaTypes ); - $opts->add( 'limit', 50 ); - $opts->add( 'offset', '' ); - $opts->add( 'start', '' ); - $opts->add( 'end', '' ); - - $opts->fetchValuesFromRequest( $this->getRequest() ); - - if ( $par !== null ) { - $opts->setValue( is_numeric( $par ) ? 'limit' : 'like', $par ); - } - - // If start date comes after end date chronologically, swap them. - // They are swapped in the interface by JS. - $start = $opts->getValue( 'start' ); - $end = $opts->getValue( 'end' ); - if ( $start !== '' && $end !== '' && $start > $end ) { - $temp = $end; - $end = $start; - $start = $temp; - - $opts->setValue( 'start', $start, true ); - $opts->setValue( 'end', $end, true ); - - // also swap values in request object, which is used by HTMLForm - // to pre-populate the fields with the previous input - $request = $context->getRequest(); - $context->setRequest( new DerivativeRequest( - $request, - [ 'start' => $start, 'end' => $end ] + $request->getValues(), - $request->wasPosted() - ) ); - } - - // if all media types have been selected, wipe out the array to prevent - // the pointless IN(...) query condition (which would have no effect - // because every possible type has been selected) - $missingMediaTypes = array_diff( $this->mediaTypes, $opts->getValue( 'mediatype' ) ); - if ( empty( $missingMediaTypes ) ) { - $opts->setValue( 'mediatype', [] ); - } - - $opts->validateIntBounds( 'limit', 0, 500 ); - - $this->opts = $opts; - - if ( !$this->including() ) { - $this->setTopText(); - $this->buildForm( $context ); - } - - $pager = new NewFilesPager( $context, $opts, $this->getLinkRenderer() ); - - $out->addHTML( $pager->getBody() ); - if ( !$this->including() ) { - $out->addHTML( $pager->getNavigationBar() ); - } - } - - protected function buildForm( IContextSource $context ) { - $mediaTypesText = array_map( function ( $type ) { - // mediastatistics-header-unknown, mediastatistics-header-bitmap, - // mediastatistics-header-drawing, mediastatistics-header-audio, - // mediastatistics-header-video, mediastatistics-header-multimedia, - // mediastatistics-header-office, mediastatistics-header-text, - // mediastatistics-header-executable, mediastatistics-header-archive, - // mediastatistics-header-3d, - return $this->msg( 'mediastatistics-header-' . strtolower( $type ) )->text(); - }, $this->mediaTypes ); - $mediaTypesOptions = array_combine( $mediaTypesText, $this->mediaTypes ); - ksort( $mediaTypesOptions ); - - $formDescriptor = [ - 'like' => [ - 'type' => 'text', - 'label-message' => 'newimages-label', - 'name' => 'like', - ], - - 'user' => [ - 'class' => 'HTMLUserTextField', - 'label-message' => 'newimages-user', - 'name' => 'user', - ], - - 'showbots' => [ - 'type' => 'check', - 'label-message' => 'newimages-showbots', - 'name' => 'showbots', - ], - - 'hidepatrolled' => [ - 'type' => 'check', - 'label-message' => 'newimages-hidepatrolled', - 'name' => 'hidepatrolled', - ], - - 'mediatype' => [ - 'type' => 'multiselect', - 'flatlist' => true, - 'name' => 'mediatype', - 'label-message' => 'newimages-mediatype', - 'options' => $mediaTypesOptions, - 'default' => $this->mediaTypes, - ], - - 'limit' => [ - 'type' => 'hidden', - 'default' => $this->opts->getValue( 'limit' ), - 'name' => 'limit', - ], - - 'offset' => [ - 'type' => 'hidden', - 'default' => $this->opts->getValue( 'offset' ), - 'name' => 'offset', - ], - - 'start' => [ - 'type' => 'date', - 'label-message' => 'date-range-from', - 'name' => 'start', - ], - - 'end' => [ - 'type' => 'date', - 'label-message' => 'date-range-to', - 'name' => 'end', - ], - ]; - - if ( $this->getConfig()->get( 'MiserMode' ) ) { - unset( $formDescriptor['like'] ); - } - - if ( !$this->getUser()->useFilePatrol() ) { - unset( $formDescriptor['hidepatrolled'] ); - } - - HTMLForm::factory( 'ooui', $formDescriptor, $context ) - // For the 'multiselect' field values to be preserved on submit - ->setFormIdentifier( 'specialnewimages' ) - ->setWrapperLegendMsg( 'newimages-legend' ) - ->setSubmitTextMsg( 'ilsubmit' ) - ->setMethod( 'get' ) - ->prepareForm() - ->displayForm( false ); - } - - protected function getGroupName() { - return 'changes'; - } - - /** - * Send the text to be displayed above the options - */ - function setTopText() { - $message = $this->msg( 'newimagestext' )->inContentLanguage(); - if ( !$message->isDisabled() ) { - $contLang = MediaWikiServices::getInstance()->getContentLanguage(); - $this->getOutput()->addWikiTextAsContent( - Html::rawElement( 'div', - [ - - 'lang' => $contLang->getHtmlCode(), - 'dir' => $contLang->getDir() - ], - "\n" . $message->plain() . "\n" - ) - ); - } - } -} diff --git a/includes/specials/SpecialRedirect.php b/includes/specials/SpecialRedirect.php index 50867dd879..82d8b73b1f 100644 --- a/includes/specials/SpecialRedirect.php +++ b/includes/specials/SpecialRedirect.php @@ -90,7 +90,9 @@ class SpecialRedirect extends FormSpecialPage { } $userpage = Title::makeTitle( NS_USER, $username ); - return Status::newGood( $userpage->getFullURL( '', false, PROTO_CURRENT ) ); + return Status::newGood( [ + $userpage->getFullURL( '', false, PROTO_CURRENT ), 302 + ] ); } /** diff --git a/includes/specials/SpecialShortPages.php b/includes/specials/SpecialShortPages.php new file mode 100644 index 0000000000..f1bd0656be --- /dev/null +++ b/includes/specials/SpecialShortPages.php @@ -0,0 +1,182 @@ +getConfig(); + $blacklist = $config->get( 'ShortPagesNamespaceBlacklist' ); + $tables = [ 'page' ]; + $conds = [ + 'page_namespace' => array_diff( + MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces(), + $blacklist + ), + 'page_is_redirect' => 0 + ]; + $joinConds = []; + $options = [ 'USE INDEX' => [ 'page' => 'page_redirect_namespace_len' ] ]; + + // Allow extensions to modify the query + Hooks::run( 'ShortPagesQuery', [ &$tables, &$conds, &$joinConds, &$options ] ); + + return [ + 'tables' => $tables, + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_len' + ], + 'conds' => $conds, + 'join_conds' => $joinConds, + 'options' => $options + ]; + } + + public function reallyDoQuery( $limit, $offset = false ) { + $fname = static::class . '::reallyDoQuery'; + $dbr = $this->getRecacheDB(); + $query = $this->getQueryInfo(); + $order = $this->getOrderFields(); + + if ( $this->sortDescending() ) { + foreach ( $order as &$field ) { + $field .= ' DESC'; + } + } + + $tables = isset( $query['tables'] ) ? (array)$query['tables'] : []; + $fields = isset( $query['fields'] ) ? (array)$query['fields'] : []; + $conds = isset( $query['conds'] ) ? (array)$query['conds'] : []; + $options = isset( $query['options'] ) ? (array)$query['options'] : []; + $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : []; + + if ( $limit !== false ) { + $options['LIMIT'] = intval( $limit ); + } + + if ( $offset !== false ) { + $options['OFFSET'] = intval( $offset ); + } + + $namespaces = $conds['page_namespace']; + if ( count( $namespaces ) === 1 ) { + $options['ORDER BY'] = $order; + $res = $dbr->select( $tables, $fields, $conds, $fname, + $options, $join_conds + ); + } else { + unset( $conds['page_namespace'] ); + $options['INNER ORDER BY'] = $order; + $options['ORDER BY'] = [ 'value' . ( $this->sortDescending() ? ' DESC' : '' ) ]; + $sql = $dbr->unionConditionPermutations( + $tables, + $fields, + [ 'page_namespace' => $namespaces ], + $conds, + $fname, + $options, + $join_conds + ); + $res = $dbr->query( $sql, $fname ); + } + + return $res; + } + + function getOrderFields() { + return [ 'page_len' ]; + } + + /** + * @param IDatabase $db + * @param IResultWrapper $res + */ + function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } + + function sortDescending() { + return false; + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $dm = $this->getLanguage()->getDirMark(); + + $title = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$title ) { + return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) ); + } + + $linkRenderer = $this->getLinkRenderer(); + $hlink = $linkRenderer->makeKnownLink( + $title, + $this->msg( 'hist' )->text(), + [], + [ 'action' => 'history' ] + ); + $hlinkInParentheses = $this->msg( 'parentheses' )->rawParams( $hlink )->escaped(); + + if ( $this->isCached() ) { + $plink = $linkRenderer->makeLink( $title ); + $exists = $title->exists(); + } else { + $plink = $linkRenderer->makeKnownLink( $title ); + $exists = true; + } + + $size = $this->msg( 'nbytes' )->numParams( $result->value )->escaped(); + + return $exists + ? "${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]" + : "${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]"; + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialShortpages.php b/includes/specials/SpecialShortpages.php deleted file mode 100644 index 94da25d172..0000000000 --- a/includes/specials/SpecialShortpages.php +++ /dev/null @@ -1,182 +0,0 @@ -getConfig(); - $blacklist = $config->get( 'ShortPagesNamespaceBlacklist' ); - $tables = [ 'page' ]; - $conds = [ - 'page_namespace' => array_diff( - MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces(), - $blacklist - ), - 'page_is_redirect' => 0 - ]; - $joinConds = []; - $options = [ 'USE INDEX' => [ 'page' => 'page_redirect_namespace_len' ] ]; - - // Allow extensions to modify the query - Hooks::run( 'ShortPagesQuery', [ &$tables, &$conds, &$joinConds, &$options ] ); - - return [ - 'tables' => $tables, - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_len' - ], - 'conds' => $conds, - 'join_conds' => $joinConds, - 'options' => $options - ]; - } - - public function reallyDoQuery( $limit, $offset = false ) { - $fname = static::class . '::reallyDoQuery'; - $dbr = $this->getRecacheDB(); - $query = $this->getQueryInfo(); - $order = $this->getOrderFields(); - - if ( $this->sortDescending() ) { - foreach ( $order as &$field ) { - $field .= ' DESC'; - } - } - - $tables = isset( $query['tables'] ) ? (array)$query['tables'] : []; - $fields = isset( $query['fields'] ) ? (array)$query['fields'] : []; - $conds = isset( $query['conds'] ) ? (array)$query['conds'] : []; - $options = isset( $query['options'] ) ? (array)$query['options'] : []; - $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : []; - - if ( $limit !== false ) { - $options['LIMIT'] = intval( $limit ); - } - - if ( $offset !== false ) { - $options['OFFSET'] = intval( $offset ); - } - - $namespaces = $conds['page_namespace']; - if ( count( $namespaces ) === 1 ) { - $options['ORDER BY'] = $order; - $res = $dbr->select( $tables, $fields, $conds, $fname, - $options, $join_conds - ); - } else { - unset( $conds['page_namespace'] ); - $options['INNER ORDER BY'] = $order; - $options['ORDER BY'] = [ 'value' . ( $this->sortDescending() ? ' DESC' : '' ) ]; - $sql = $dbr->unionConditionPermutations( - $tables, - $fields, - [ 'page_namespace' => $namespaces ], - $conds, - $fname, - $options, - $join_conds - ); - $res = $dbr->query( $sql, $fname ); - } - - return $res; - } - - function getOrderFields() { - return [ 'page_len' ]; - } - - /** - * @param IDatabase $db - * @param IResultWrapper $res - */ - function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } - - function sortDescending() { - return false; - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $dm = $this->getLanguage()->getDirMark(); - - $title = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$title ) { - return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) ); - } - - $linkRenderer = $this->getLinkRenderer(); - $hlink = $linkRenderer->makeKnownLink( - $title, - $this->msg( 'hist' )->text(), - [], - [ 'action' => 'history' ] - ); - $hlinkInParentheses = $this->msg( 'parentheses' )->rawParams( $hlink )->escaped(); - - if ( $this->isCached() ) { - $plink = $linkRenderer->makeLink( $title ); - $exists = $title->exists(); - } else { - $plink = $linkRenderer->makeKnownLink( $title ); - $exists = true; - } - - $size = $this->msg( 'nbytes' )->numParams( $result->value )->escaped(); - - return $exists - ? "${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]" - : "${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]"; - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialUncategorizedCategories.php b/includes/specials/SpecialUncategorizedCategories.php new file mode 100644 index 0000000000..7349d95e18 --- /dev/null +++ b/includes/specials/SpecialUncategorizedCategories.php @@ -0,0 +1,94 @@ +requestedNamespace = NS_CATEGORY; + } + + /** + * Returns an array of category titles (usually without the namespace), which + * shouldn't be listed on this page, even if they're uncategorized. + * + * @return array + */ + private function getExceptionList() { + if ( $this->exceptionList === null ) { + $this->exceptionList = []; + $exList = $this->msg( 'uncategorized-categories-exceptionlist' ) + ->inContentLanguage()->plain(); + $proposedTitles = explode( "\n", $exList ); + foreach ( $proposedTitles as $count => $titleStr ) { + if ( strpos( $titleStr, '*' ) !== 0 ) { + continue; + } + $titleStr = preg_replace( "/^\\*\\s*/", '', $titleStr ); + $title = Title::newFromText( $titleStr, NS_CATEGORY ); + if ( $title && $title->getNamespace() !== NS_CATEGORY ) { + $title = Title::makeTitleSafe( NS_CATEGORY, $titleStr ); + } + if ( $title ) { + $this->exceptionList[] = $title->getDBkey(); + } + } + } + return $this->exceptionList; + } + + public function getQueryInfo() { + $dbr = wfGetDB( DB_REPLICA ); + $query = parent::getQueryInfo(); + $exceptionList = $this->getExceptionList(); + if ( $exceptionList ) { + $query['conds'][] = 'page_title not in ( ' . $dbr->makeList( $exceptionList ) . ' )'; + } + + return $query; + } + + /** + * Formats the result + * @param Skin $skin The current skin + * @param object $result The query result + * @return string The category link + */ + function formatResult( $skin, $result ) { + $title = Title::makeTitle( NS_CATEGORY, $result->title ); + $text = $title->getText(); + + return $this->getLinkRenderer()->makeKnownLink( $title, $text ); + } +} diff --git a/includes/specials/SpecialUncategorizedImages.php b/includes/specials/SpecialUncategorizedImages.php new file mode 100644 index 0000000000..9dcd1bdd35 --- /dev/null +++ b/includes/specials/SpecialUncategorizedImages.php @@ -0,0 +1,66 @@ + + */ + +/** + * Special page lists images which haven't been categorised + * + * @ingroup SpecialPage + * @todo FIXME: Use an instance of UncategorizedPagesPage or something + */ +class SpecialUncategorizedImages extends ImageQueryPage { + function __construct( $name = 'Uncategorizedimages' ) { + parent::__construct( $name ); + $this->addHelpLink( 'Help:Categories' ); + } + + function sortDescending() { + return false; + } + + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function getQueryInfo() { + return [ + 'tables' => [ 'page', 'categorylinks' ], + 'fields' => [ 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' ], + 'conds' => [ 'cl_from IS NULL', + 'page_namespace' => NS_FILE, + 'page_is_redirect' => 0 ], + 'join_conds' => [ 'categorylinks' => [ + 'LEFT JOIN', 'cl_from=page_id' ] ] + ]; + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialUncategorizedPages.php b/includes/specials/SpecialUncategorizedPages.php new file mode 100644 index 0000000000..3610c011e7 --- /dev/null +++ b/includes/specials/SpecialUncategorizedPages.php @@ -0,0 +1,93 @@ +addHelpLink( 'Help:Categories' ); + } + + function sortDescending() { + return false; + } + + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function getQueryInfo() { + return [ + 'tables' => [ 'page', 'categorylinks' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + // default for page_namespace is all content namespaces (if requestedNamespace is false) + // otherwise, page_namespace is requestedNamespace + 'conds' => [ + 'cl_from IS NULL', + 'page_namespace' => $this->requestedNamespace !== false + ? $this->requestedNamespace + : MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces(), + 'page_is_redirect' => 0 + ], + 'join_conds' => [ + 'categorylinks' => [ 'LEFT JOIN', 'cl_from = page_id' ] + ] + ]; + } + + function getOrderFields() { + // For some crazy reason ordering by a constant + // causes a filesort + if ( $this->requestedNamespace === false && + count( MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces() ) > 1 + ) { + return [ 'page_namespace', 'page_title' ]; + } + + return [ 'page_title' ]; + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialUncategorizedTemplates.php b/includes/specials/SpecialUncategorizedTemplates.php new file mode 100644 index 0000000000..3d6d38317c --- /dev/null +++ b/includes/specials/SpecialUncategorizedTemplates.php @@ -0,0 +1,36 @@ + + */ + +/** + * Special page lists all uncategorised pages in the + * template namespace + * + * @ingroup SpecialPage + */ +class SpecialUncategorizedTemplates extends SpecialUncategorizedPages { + public function __construct( $name = 'Uncategorizedtemplates' ) { + parent::__construct( $name ); + $this->requestedNamespace = NS_TEMPLATE; + } +} diff --git a/includes/specials/SpecialUncategorizedcategories.php b/includes/specials/SpecialUncategorizedcategories.php deleted file mode 100644 index 60cbff12e3..0000000000 --- a/includes/specials/SpecialUncategorizedcategories.php +++ /dev/null @@ -1,94 +0,0 @@ -requestedNamespace = NS_CATEGORY; - } - - /** - * Returns an array of category titles (usually without the namespace), which - * shouldn't be listed on this page, even if they're uncategorized. - * - * @return array - */ - private function getExceptionList() { - if ( $this->exceptionList === null ) { - $this->exceptionList = []; - $exList = $this->msg( 'uncategorized-categories-exceptionlist' ) - ->inContentLanguage()->plain(); - $proposedTitles = explode( "\n", $exList ); - foreach ( $proposedTitles as $count => $titleStr ) { - if ( strpos( $titleStr, '*' ) !== 0 ) { - continue; - } - $titleStr = preg_replace( "/^\\*\\s*/", '', $titleStr ); - $title = Title::newFromText( $titleStr, NS_CATEGORY ); - if ( $title && $title->getNamespace() !== NS_CATEGORY ) { - $title = Title::makeTitleSafe( NS_CATEGORY, $titleStr ); - } - if ( $title ) { - $this->exceptionList[] = $title->getDBkey(); - } - } - } - return $this->exceptionList; - } - - public function getQueryInfo() { - $dbr = wfGetDB( DB_REPLICA ); - $query = parent::getQueryInfo(); - $exceptionList = $this->getExceptionList(); - if ( $exceptionList ) { - $query['conds'][] = 'page_title not in ( ' . $dbr->makeList( $exceptionList ) . ' )'; - } - - return $query; - } - - /** - * Formats the result - * @param Skin $skin The current skin - * @param object $result The query result - * @return string The category link - */ - function formatResult( $skin, $result ) { - $title = Title::makeTitle( NS_CATEGORY, $result->title ); - $text = $title->getText(); - - return $this->getLinkRenderer()->makeKnownLink( $title, $text ); - } -} diff --git a/includes/specials/SpecialUncategorizedimages.php b/includes/specials/SpecialUncategorizedimages.php deleted file mode 100644 index ed2d5cfbc8..0000000000 --- a/includes/specials/SpecialUncategorizedimages.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ - -/** - * Special page lists images which haven't been categorised - * - * @ingroup SpecialPage - * @todo FIXME: Use an instance of UncategorizedPagesPage or something - */ -class UncategorizedImagesPage extends ImageQueryPage { - function __construct( $name = 'Uncategorizedimages' ) { - parent::__construct( $name ); - $this->addHelpLink( 'Help:Categories' ); - } - - function sortDescending() { - return false; - } - - function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function getQueryInfo() { - return [ - 'tables' => [ 'page', 'categorylinks' ], - 'fields' => [ 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' ], - 'conds' => [ 'cl_from IS NULL', - 'page_namespace' => NS_FILE, - 'page_is_redirect' => 0 ], - 'join_conds' => [ 'categorylinks' => [ - 'LEFT JOIN', 'cl_from=page_id' ] ] - ]; - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialUncategorizedpages.php b/includes/specials/SpecialUncategorizedpages.php deleted file mode 100644 index 0b7da7bf8a..0000000000 --- a/includes/specials/SpecialUncategorizedpages.php +++ /dev/null @@ -1,93 +0,0 @@ -addHelpLink( 'Help:Categories' ); - } - - function sortDescending() { - return false; - } - - function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function getQueryInfo() { - return [ - 'tables' => [ 'page', 'categorylinks' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - // default for page_namespace is all content namespaces (if requestedNamespace is false) - // otherwise, page_namespace is requestedNamespace - 'conds' => [ - 'cl_from IS NULL', - 'page_namespace' => $this->requestedNamespace !== false - ? $this->requestedNamespace - : MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces(), - 'page_is_redirect' => 0 - ], - 'join_conds' => [ - 'categorylinks' => [ 'LEFT JOIN', 'cl_from = page_id' ] - ] - ]; - } - - function getOrderFields() { - // For some crazy reason ordering by a constant - // causes a filesort - if ( $this->requestedNamespace === false && - count( MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces() ) > 1 - ) { - return [ 'page_namespace', 'page_title' ]; - } - - return [ 'page_title' ]; - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialUncategorizedtemplates.php b/includes/specials/SpecialUncategorizedtemplates.php deleted file mode 100644 index af038fa88d..0000000000 --- a/includes/specials/SpecialUncategorizedtemplates.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ - -/** - * Special page lists all uncategorised pages in the - * template namespace - * - * @ingroup SpecialPage - */ -class UncategorizedTemplatesPage extends UncategorizedPagesPage { - public function __construct( $name = 'Uncategorizedtemplates' ) { - parent::__construct( $name ); - $this->requestedNamespace = NS_TEMPLATE; - } -} diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index 075b5df0c2..fe629dba8b 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -33,18 +33,28 @@ use Wikimedia\Rdbms\IResultWrapper; * @ingroup SpecialPage */ class SpecialUndelete extends SpecialPage { - private $mAction; - private $mTarget; - private $mTimestamp; - private $mRestore; - private $mRevdel; - private $mInvert; - private $mFilename; - private $mTargetTimestamp; - private $mAllowed; - private $mCanView; - private $mComment; - private $mToken; + private $mAction; + private $mTarget; + private $mTimestamp; + private $mRestore; + private $mRevdel; + private $mInvert; + private $mFilename; + private $mTargetTimestamp; + private $mAllowed; + private $mCanView; + private $mComment; + private $mToken; + /** @var bool|null */ + private $mPreview; + /** @var bool|null */ + private $mDiff; + /** @var bool|null */ + private $mDiffOnly; + /** @var bool|null */ + private $mUnsuppress; + /** @var int[]|null */ + private $mFileVersions; /** @var Title */ private $mTargetObj; diff --git a/includes/specials/SpecialUnusedCategories.php b/includes/specials/SpecialUnusedCategories.php new file mode 100644 index 0000000000..36367d2f27 --- /dev/null +++ b/includes/specials/SpecialUnusedCategories.php @@ -0,0 +1,90 @@ +msg( 'unusedcategoriestext' )->parseAsBlock(); + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'page', 'categorylinks', 'page_props' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + 'conds' => [ + 'cl_from IS NULL', + 'page_namespace' => NS_CATEGORY, + 'page_is_redirect' => 0, + 'pp_page IS NULL' + ], + 'join_conds' => [ + 'categorylinks' => [ 'LEFT JOIN', 'cl_to = page_title' ], + 'page_props' => [ 'LEFT JOIN', [ + 'page_id = pp_page', + 'pp_propname' => 'expectunusedcategory' + ] ] + ] + ]; + } + + /** + * A should come before Z (T32907) + * @return bool + */ + function sortDescending() { + return false; + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $title = Title::makeTitle( NS_CATEGORY, $result->title ); + + return $this->getLinkRenderer()->makeLink( $title, $title->getText() ); + } + + protected function getGroupName() { + return 'maintenance'; + } + + public function preprocessResults( $db, $res ) { + $this->executeLBFromResultWrapper( $res ); + } +} diff --git a/includes/specials/SpecialUnusedImages.php b/includes/specials/SpecialUnusedImages.php new file mode 100644 index 0000000000..cb215e3545 --- /dev/null +++ b/includes/specials/SpecialUnusedImages.php @@ -0,0 +1,90 @@ + [ 'image', 'imagelinks' ], + 'fields' => [ + 'namespace' => NS_FILE, + 'title' => 'img_name', + 'value' => 'img_timestamp', + ], + 'conds' => [ 'il_to IS NULL' ], + 'join_conds' => [ 'imagelinks' => [ 'LEFT JOIN', 'il_to = img_name' ] ] + ]; + + if ( $this->getConfig()->get( 'CountCategorizedImagesAsUsed' ) ) { + // Order is significant + $retval['tables'] = [ 'image', 'page', 'categorylinks', + 'imagelinks' ]; + $retval['conds']['page_namespace'] = NS_FILE; + $retval['conds'][] = 'cl_from IS NULL'; + $retval['conds'][] = 'img_name = page_title'; + $retval['join_conds']['categorylinks'] = [ + 'LEFT JOIN', 'cl_from = page_id' ]; + $retval['join_conds']['imagelinks'] = [ + 'LEFT JOIN', 'il_to = page_title' ]; + } + + return $retval; + } + + function usesTimestamps() { + return true; + } + + function getPageHeader() { + if ( $this->getConfig()->get( 'CountCategorizedImagesAsUsed' ) ) { + return $this->msg( + 'unusedimagestext-categorizedimgisused' + )->parseAsBlock(); + } + return $this->msg( 'unusedimagestext' )->parseAsBlock(); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialUnusedTemplates.php b/includes/specials/SpecialUnusedTemplates.php new file mode 100644 index 0000000000..119ef875a9 --- /dev/null +++ b/includes/specials/SpecialUnusedTemplates.php @@ -0,0 +1,97 @@ + + */ + +/** + * A special page that lists unused templates + * + * @ingroup SpecialPage + */ +class SpecialUnusedTemplates extends QueryPage { + function __construct( $name = 'Unusedtemplates' ) { + parent::__construct( $name ); + } + + public function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } + + public function getQueryInfo() { + return [ + 'tables' => [ 'page', 'templatelinks' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + 'conds' => [ + 'page_namespace' => NS_TEMPLATE, + 'tl_from IS NULL', + 'page_is_redirect' => 0 + ], + 'join_conds' => [ 'templatelinks' => [ + 'LEFT JOIN', [ 'tl_title = page_title', + 'tl_namespace = page_namespace' ] ] ] + ]; + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $linkRenderer = $this->getLinkRenderer(); + $title = Title::makeTitle( NS_TEMPLATE, $result->title ); + $pageLink = $linkRenderer->makeKnownLink( + $title, + null, + [], + [ 'redirect' => 'no' ] + ); + $wlhLink = $linkRenderer->makeKnownLink( + SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() ), + $this->msg( 'unusedtemplateswlh' )->text() + ); + + return $this->getLanguage()->specialList( $pageLink, $wlhLink ); + } + + function getPageHeader() { + return $this->msg( 'unusedtemplatestext' )->parseAsBlock(); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialUnusedcategories.php b/includes/specials/SpecialUnusedcategories.php deleted file mode 100644 index 2577a100cf..0000000000 --- a/includes/specials/SpecialUnusedcategories.php +++ /dev/null @@ -1,90 +0,0 @@ -msg( 'unusedcategoriestext' )->parseAsBlock(); - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'page', 'categorylinks', 'page_props' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - 'conds' => [ - 'cl_from IS NULL', - 'page_namespace' => NS_CATEGORY, - 'page_is_redirect' => 0, - 'pp_page IS NULL' - ], - 'join_conds' => [ - 'categorylinks' => [ 'LEFT JOIN', 'cl_to = page_title' ], - 'page_props' => [ 'LEFT JOIN', [ - 'page_id = pp_page', - 'pp_propname' => 'expectunusedcategory' - ] ] - ] - ]; - } - - /** - * A should come before Z (T32907) - * @return bool - */ - function sortDescending() { - return false; - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $title = Title::makeTitle( NS_CATEGORY, $result->title ); - - return $this->getLinkRenderer()->makeLink( $title, $title->getText() ); - } - - protected function getGroupName() { - return 'maintenance'; - } - - public function preprocessResults( $db, $res ) { - $this->executeLBFromResultWrapper( $res ); - } -} diff --git a/includes/specials/SpecialUnusedimages.php b/includes/specials/SpecialUnusedimages.php deleted file mode 100644 index bcb3b85e36..0000000000 --- a/includes/specials/SpecialUnusedimages.php +++ /dev/null @@ -1,90 +0,0 @@ - [ 'image', 'imagelinks' ], - 'fields' => [ - 'namespace' => NS_FILE, - 'title' => 'img_name', - 'value' => 'img_timestamp', - ], - 'conds' => [ 'il_to IS NULL' ], - 'join_conds' => [ 'imagelinks' => [ 'LEFT JOIN', 'il_to = img_name' ] ] - ]; - - if ( $this->getConfig()->get( 'CountCategorizedImagesAsUsed' ) ) { - // Order is significant - $retval['tables'] = [ 'image', 'page', 'categorylinks', - 'imagelinks' ]; - $retval['conds']['page_namespace'] = NS_FILE; - $retval['conds'][] = 'cl_from IS NULL'; - $retval['conds'][] = 'img_name = page_title'; - $retval['join_conds']['categorylinks'] = [ - 'LEFT JOIN', 'cl_from = page_id' ]; - $retval['join_conds']['imagelinks'] = [ - 'LEFT JOIN', 'il_to = page_title' ]; - } - - return $retval; - } - - function usesTimestamps() { - return true; - } - - function getPageHeader() { - if ( $this->getConfig()->get( 'CountCategorizedImagesAsUsed' ) ) { - return $this->msg( - 'unusedimagestext-categorizedimgisused' - )->parseAsBlock(); - } - return $this->msg( 'unusedimagestext' )->parseAsBlock(); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialUnusedtemplates.php b/includes/specials/SpecialUnusedtemplates.php deleted file mode 100644 index f73be43839..0000000000 --- a/includes/specials/SpecialUnusedtemplates.php +++ /dev/null @@ -1,97 +0,0 @@ - - */ - -/** - * A special page that lists unused templates - * - * @ingroup SpecialPage - */ -class UnusedtemplatesPage extends QueryPage { - function __construct( $name = 'Unusedtemplates' ) { - parent::__construct( $name ); - } - - public function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function sortDescending() { - return false; - } - - public function getQueryInfo() { - return [ - 'tables' => [ 'page', 'templatelinks' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - 'conds' => [ - 'page_namespace' => NS_TEMPLATE, - 'tl_from IS NULL', - 'page_is_redirect' => 0 - ], - 'join_conds' => [ 'templatelinks' => [ - 'LEFT JOIN', [ 'tl_title = page_title', - 'tl_namespace = page_namespace' ] ] ] - ]; - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $linkRenderer = $this->getLinkRenderer(); - $title = Title::makeTitle( NS_TEMPLATE, $result->title ); - $pageLink = $linkRenderer->makeKnownLink( - $title, - null, - [], - [ 'redirect' => 'no' ] - ); - $wlhLink = $linkRenderer->makeKnownLink( - SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedText() ), - $this->msg( 'unusedtemplateswlh' )->text() - ); - - return $this->getLanguage()->specialList( $pageLink, $wlhLink ); - } - - function getPageHeader() { - return $this->msg( 'unusedtemplatestext' )->parseAsBlock(); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialUnwatchedPages.php b/includes/specials/SpecialUnwatchedPages.php new file mode 100644 index 0000000000..42adaa0d42 --- /dev/null +++ b/includes/specials/SpecialUnwatchedPages.php @@ -0,0 +1,139 @@ + + */ + +use MediaWiki\MediaWikiServices; +use Wikimedia\Rdbms\IResultWrapper; +use Wikimedia\Rdbms\IDatabase; + +/** + * A special page that displays a list of pages that are not on anyones watchlist. + * + * @ingroup SpecialPage + */ +class SpecialUnwatchedPages extends QueryPage { + + function __construct( $name = 'Unwatchedpages' ) { + parent::__construct( $name, 'unwatchedpages' ); + } + + public function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + /** + * Pre-cache page existence to speed up link generation + * + * @param IDatabase $db + * @param IResultWrapper $res + */ + public function preprocessResults( $db, $res ) { + if ( !$res->numRows() ) { + return; + } + + $batch = new LinkBatch(); + foreach ( $res as $row ) { + $batch->add( $row->namespace, $row->title ); + } + $batch->execute(); + + $res->seek( 0 ); + } + + public function getQueryInfo() { + $dbr = wfGetDB( DB_REPLICA ); + return [ + 'tables' => [ 'page', 'watchlist' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_namespace' + ], + 'conds' => [ + 'wl_title IS NULL', + 'page_is_redirect' => 0, + 'page_namespace != ' . $dbr->addQuotes( NS_MEDIAWIKI ), + ], + 'join_conds' => [ 'watchlist' => [ + 'LEFT JOIN', [ 'wl_title = page_title', + 'wl_namespace = page_namespace' ] ] ] + ]; + } + + function sortDescending() { + return false; + } + + function getOrderFields() { + return [ 'page_namespace', 'page_title' ]; + } + + /** + * Add the JS + * @param string|null $par + */ + public function execute( $par ) { + parent::execute( $par ); + $this->getOutput()->addModules( 'mediawiki.special.unwatchedPages' ); + $this->addHelpLink( 'Help:Watchlist' ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $nt = Title::makeTitleSafe( $result->namespace, $result->title ); + if ( !$nt ) { + return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ], + Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) ); + } + + $text = MediaWikiServices::getInstance()->getContentLanguage()-> + convert( htmlspecialchars( $nt->getPrefixedText() ) ); + + $linkRenderer = $this->getLinkRenderer(); + + $plink = $linkRenderer->makeKnownLink( $nt, new HtmlArmor( $text ) ); + $wlink = $linkRenderer->makeKnownLink( + $nt, + $this->msg( 'watch' )->text(), + [ 'class' => 'mw-watch-link' ], + [ 'action' => 'watch' ] + ); + + return $this->getLanguage()->specialList( $plink, $wlink ); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialUnwatchedpages.php b/includes/specials/SpecialUnwatchedpages.php deleted file mode 100644 index 3c5de64baa..0000000000 --- a/includes/specials/SpecialUnwatchedpages.php +++ /dev/null @@ -1,139 +0,0 @@ - - */ - -use MediaWiki\MediaWikiServices; -use Wikimedia\Rdbms\IResultWrapper; -use Wikimedia\Rdbms\IDatabase; - -/** - * A special page that displays a list of pages that are not on anyones watchlist. - * - * @ingroup SpecialPage - */ -class UnwatchedpagesPage extends QueryPage { - - function __construct( $name = 'Unwatchedpages' ) { - parent::__construct( $name, 'unwatchedpages' ); - } - - public function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - /** - * Pre-cache page existence to speed up link generation - * - * @param IDatabase $db - * @param IResultWrapper $res - */ - public function preprocessResults( $db, $res ) { - if ( !$res->numRows() ) { - return; - } - - $batch = new LinkBatch(); - foreach ( $res as $row ) { - $batch->add( $row->namespace, $row->title ); - } - $batch->execute(); - - $res->seek( 0 ); - } - - public function getQueryInfo() { - $dbr = wfGetDB( DB_REPLICA ); - return [ - 'tables' => [ 'page', 'watchlist' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_namespace' - ], - 'conds' => [ - 'wl_title IS NULL', - 'page_is_redirect' => 0, - 'page_namespace != ' . $dbr->addQuotes( NS_MEDIAWIKI ), - ], - 'join_conds' => [ 'watchlist' => [ - 'LEFT JOIN', [ 'wl_title = page_title', - 'wl_namespace = page_namespace' ] ] ] - ]; - } - - function sortDescending() { - return false; - } - - function getOrderFields() { - return [ 'page_namespace', 'page_title' ]; - } - - /** - * Add the JS - * @param string|null $par - */ - public function execute( $par ) { - parent::execute( $par ); - $this->getOutput()->addModules( 'mediawiki.special.unwatchedPages' ); - $this->addHelpLink( 'Help:Watchlist' ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $nt = Title::makeTitleSafe( $result->namespace, $result->title ); - if ( !$nt ) { - return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ], - Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) ); - } - - $text = MediaWikiServices::getInstance()->getContentLanguage()-> - convert( htmlspecialchars( $nt->getPrefixedText() ) ); - - $linkRenderer = $this->getLinkRenderer(); - - $plink = $linkRenderer->makeKnownLink( $nt, new HtmlArmor( $text ) ); - $wlink = $linkRenderer->makeKnownLink( - $nt, - $this->msg( 'watch' )->text(), - [ 'class' => 'mw-watch-link' ], - [ 'action' => 'watch' ] - ); - - return $this->getLanguage()->specialList( $plink, $wlink ); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialWantedCategories.php b/includes/specials/SpecialWantedCategories.php new file mode 100644 index 0000000000..659650c6ab --- /dev/null +++ b/includes/specials/SpecialWantedCategories.php @@ -0,0 +1,130 @@ + [ 'categorylinks', 'page' ], + 'fields' => [ + 'namespace' => NS_CATEGORY, + 'title' => 'cl_to', + 'value' => 'COUNT(*)' + ], + 'conds' => [ 'page_title IS NULL' ], + 'options' => [ 'GROUP BY' => 'cl_to' ], + 'join_conds' => [ 'page' => [ 'LEFT JOIN', + [ 'page_title = cl_to', + 'page_namespace' => NS_CATEGORY ] ] ] + ]; + } + + function preprocessResults( $db, $res ) { + parent::preprocessResults( $db, $res ); + + $this->currentCategoryCounts = []; + + if ( !$res->numRows() || !$this->isCached() ) { + return; + } + + // Fetch (hopefully) up-to-date numbers of pages in each category. + // This should be fast enough as we limit the list to a reasonable length. + + $allCategories = []; + foreach ( $res as $row ) { + $allCategories[] = $row->title; + } + + $categoryRes = $db->select( + 'category', + [ 'cat_title', 'cat_pages' ], + [ 'cat_title' => $allCategories ], + __METHOD__ + ); + foreach ( $categoryRes as $row ) { + $this->currentCategoryCounts[$row->cat_title] = intval( $row->cat_pages ); + } + + // Back to start for display + $res->seek( 0 ); + } + + /** + * @param Skin $skin + * @param object $result Result row + * @return string + */ + function formatResult( $skin, $result ) { + $nt = Title::makeTitle( $result->namespace, $result->title ); + $text = new HtmlArmor( MediaWikiServices::getInstance()->getContentLanguage() + ->convert( htmlspecialchars( $nt->getText() ) ) ); + + if ( !$this->isCached() ) { + // We can assume the freshest data + $plink = $this->getLinkRenderer()->makeBrokenLink( + $nt, + $text + ); + $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); + } else { + $plink = $this->getLinkRenderer()->makeLink( $nt, $text ); + + $currentValue = $this->currentCategoryCounts[$result->title] ?? 0; + $cachedValue = intval( $result->value ); // T76910 + + // If the category has been created or emptied since the list was refreshed, strike it + if ( $nt->isKnown() || $currentValue === 0 ) { + $plink = "$plink"; + } + + // Show the current number of category entries if it changed + if ( $currentValue !== $cachedValue ) { + $nlinks = $this->msg( 'nmemberschanged' ) + ->numParams( $cachedValue, $currentValue )->escaped(); + } else { + $nlinks = $this->msg( 'nmembers' )->numParams( $cachedValue )->escaped(); + } + } + + return $this->getLanguage()->specialList( $plink, $nlinks ); + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialWantedTemplates.php b/includes/specials/SpecialWantedTemplates.php new file mode 100644 index 0000000000..453f7b8888 --- /dev/null +++ b/includes/specials/SpecialWantedTemplates.php @@ -0,0 +1,61 @@ + + * makeWlhLink() taken from SpecialMostlinkedtemplates by Rob Church + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup SpecialPage + * @author Danny B. + */ + +/** + * A querypage to list the most wanted templates + * + * @ingroup SpecialPage + */ +class SpecialWantedTemplates extends WantedQueryPage { + function __construct( $name = 'Wantedtemplates' ) { + parent::__construct( $name ); + } + + function getQueryInfo() { + return [ + 'tables' => [ 'templatelinks', 'page' ], + 'fields' => [ + 'namespace' => 'tl_namespace', + 'title' => 'tl_title', + 'value' => 'COUNT(*)' + ], + 'conds' => [ + 'page_title IS NULL', + 'tl_namespace' => NS_TEMPLATE + ], + 'options' => [ 'GROUP BY' => [ 'tl_namespace', 'tl_title' ] ], + 'join_conds' => [ 'page' => [ 'LEFT JOIN', + [ 'page_namespace = tl_namespace', + 'page_title = tl_title' ] ] ] + ]; + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialWantedcategories.php b/includes/specials/SpecialWantedcategories.php deleted file mode 100644 index 5c62298360..0000000000 --- a/includes/specials/SpecialWantedcategories.php +++ /dev/null @@ -1,130 +0,0 @@ - [ 'categorylinks', 'page' ], - 'fields' => [ - 'namespace' => NS_CATEGORY, - 'title' => 'cl_to', - 'value' => 'COUNT(*)' - ], - 'conds' => [ 'page_title IS NULL' ], - 'options' => [ 'GROUP BY' => 'cl_to' ], - 'join_conds' => [ 'page' => [ 'LEFT JOIN', - [ 'page_title = cl_to', - 'page_namespace' => NS_CATEGORY ] ] ] - ]; - } - - function preprocessResults( $db, $res ) { - parent::preprocessResults( $db, $res ); - - $this->currentCategoryCounts = []; - - if ( !$res->numRows() || !$this->isCached() ) { - return; - } - - // Fetch (hopefully) up-to-date numbers of pages in each category. - // This should be fast enough as we limit the list to a reasonable length. - - $allCategories = []; - foreach ( $res as $row ) { - $allCategories[] = $row->title; - } - - $categoryRes = $db->select( - 'category', - [ 'cat_title', 'cat_pages' ], - [ 'cat_title' => $allCategories ], - __METHOD__ - ); - foreach ( $categoryRes as $row ) { - $this->currentCategoryCounts[$row->cat_title] = intval( $row->cat_pages ); - } - - // Back to start for display - $res->seek( 0 ); - } - - /** - * @param Skin $skin - * @param object $result Result row - * @return string - */ - function formatResult( $skin, $result ) { - $nt = Title::makeTitle( $result->namespace, $result->title ); - $text = new HtmlArmor( MediaWikiServices::getInstance()->getContentLanguage() - ->convert( htmlspecialchars( $nt->getText() ) ) ); - - if ( !$this->isCached() ) { - // We can assume the freshest data - $plink = $this->getLinkRenderer()->makeBrokenLink( - $nt, - $text - ); - $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); - } else { - $plink = $this->getLinkRenderer()->makeLink( $nt, $text ); - - $currentValue = $this->currentCategoryCounts[$result->title] ?? 0; - $cachedValue = intval( $result->value ); // T76910 - - // If the category has been created or emptied since the list was refreshed, strike it - if ( $nt->isKnown() || $currentValue === 0 ) { - $plink = "$plink"; - } - - // Show the current number of category entries if it changed - if ( $currentValue !== $cachedValue ) { - $nlinks = $this->msg( 'nmemberschanged' ) - ->numParams( $cachedValue, $currentValue )->escaped(); - } else { - $nlinks = $this->msg( 'nmembers' )->numParams( $cachedValue )->escaped(); - } - } - - return $this->getLanguage()->specialList( $plink, $nlinks ); - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialWantedtemplates.php b/includes/specials/SpecialWantedtemplates.php deleted file mode 100644 index 66e681420f..0000000000 --- a/includes/specials/SpecialWantedtemplates.php +++ /dev/null @@ -1,61 +0,0 @@ - - * makeWlhLink() taken from SpecialMostlinkedtemplates by Rob Church - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup SpecialPage - * @author Danny B. - */ - -/** - * A querypage to list the most wanted templates - * - * @ingroup SpecialPage - */ -class WantedTemplatesPage extends WantedQueryPage { - function __construct( $name = 'Wantedtemplates' ) { - parent::__construct( $name ); - } - - function getQueryInfo() { - return [ - 'tables' => [ 'templatelinks', 'page' ], - 'fields' => [ - 'namespace' => 'tl_namespace', - 'title' => 'tl_title', - 'value' => 'COUNT(*)' - ], - 'conds' => [ - 'page_title IS NULL', - 'tl_namespace' => NS_TEMPLATE - ], - 'options' => [ 'GROUP BY' => [ 'tl_namespace', 'tl_title' ] ], - 'join_conds' => [ 'page' => [ 'LEFT JOIN', - [ 'page_namespace = tl_namespace', - 'page_title = tl_title' ] ] ] - ]; - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/SpecialWithoutInterwiki.php b/includes/specials/SpecialWithoutInterwiki.php new file mode 100644 index 0000000000..36aea75b96 --- /dev/null +++ b/includes/specials/SpecialWithoutInterwiki.php @@ -0,0 +1,113 @@ + + */ + +use MediaWiki\MediaWikiServices; + +/** + * Special page lists pages without language links + * + * @ingroup SpecialPage + */ +class SpecialWithoutInterwiki extends PageQueryPage { + private $prefix = ''; + + function __construct( $name = 'Withoutinterwiki' ) { + parent::__construct( $name ); + } + + function execute( $par ) { + $this->prefix = Title::capitalize( + $this->getRequest()->getVal( 'prefix', $par ), NS_MAIN ); + parent::execute( $par ); + } + + function getPageHeader() { + # Do not show useless input form if special page is cached + if ( $this->isCached() ) { + return ''; + } + + $formDescriptor = [ + 'prefix' => [ + 'label-message' => 'allpagesprefix', + 'name' => 'prefix', + 'id' => 'wiprefix', + 'type' => 'text', + 'size' => 20, + 'default' => $this->prefix + ] + ]; + + $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ); + $htmlForm->setWrapperLegend( '' ) + ->setSubmitTextMsg( 'withoutinterwiki-submit' ) + ->setMethod( 'get' ) + ->prepareForm() + ->displayForm( false ); + } + + function sortDescending() { + return false; + } + + function getOrderFields() { + return [ 'page_namespace', 'page_title' ]; + } + + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function getQueryInfo() { + $query = [ + 'tables' => [ 'page', 'langlinks' ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title' + ], + 'conds' => [ + 'll_title IS NULL', + 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> + getContentNamespaces(), + 'page_is_redirect' => 0 + ], + 'join_conds' => [ 'langlinks' => [ 'LEFT JOIN', 'll_from = page_id' ] ] + ]; + if ( $this->prefix ) { + $dbr = wfGetDB( DB_REPLICA ); + $query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() ); + } + + return $query; + } + + protected function getGroupName() { + return 'maintenance'; + } +} diff --git a/includes/specials/SpecialWithoutinterwiki.php b/includes/specials/SpecialWithoutinterwiki.php deleted file mode 100644 index 548e9212c3..0000000000 --- a/includes/specials/SpecialWithoutinterwiki.php +++ /dev/null @@ -1,113 +0,0 @@ - - */ - -use MediaWiki\MediaWikiServices; - -/** - * Special page lists pages without language links - * - * @ingroup SpecialPage - */ -class WithoutInterwikiPage extends PageQueryPage { - private $prefix = ''; - - function __construct( $name = 'Withoutinterwiki' ) { - parent::__construct( $name ); - } - - function execute( $par ) { - $this->prefix = Title::capitalize( - $this->getRequest()->getVal( 'prefix', $par ), NS_MAIN ); - parent::execute( $par ); - } - - function getPageHeader() { - # Do not show useless input form if special page is cached - if ( $this->isCached() ) { - return ''; - } - - $formDescriptor = [ - 'prefix' => [ - 'label-message' => 'allpagesprefix', - 'name' => 'prefix', - 'id' => 'wiprefix', - 'type' => 'text', - 'size' => 20, - 'default' => $this->prefix - ] - ]; - - $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ); - $htmlForm->setWrapperLegend( '' ) - ->setSubmitTextMsg( 'withoutinterwiki-submit' ) - ->setMethod( 'get' ) - ->prepareForm() - ->displayForm( false ); - } - - function sortDescending() { - return false; - } - - function getOrderFields() { - return [ 'page_namespace', 'page_title' ]; - } - - function isExpensive() { - return true; - } - - function isSyndicated() { - return false; - } - - function getQueryInfo() { - $query = [ - 'tables' => [ 'page', 'langlinks' ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title' - ], - 'conds' => [ - 'll_title IS NULL', - 'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()-> - getContentNamespaces(), - 'page_is_redirect' => 0 - ], - 'join_conds' => [ 'langlinks' => [ 'LEFT JOIN', 'll_from = page_id' ] ] - ]; - if ( $this->prefix ) { - $dbr = wfGetDB( DB_REPLICA ); - $query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() ); - } - - return $query; - } - - protected function getGroupName() { - return 'maintenance'; - } -} diff --git a/includes/specials/pagers/ActiveUsersPager.php b/includes/specials/pagers/ActiveUsersPager.php index 0f5355d383..0abe842e79 100644 --- a/includes/specials/pagers/ActiveUsersPager.php +++ b/includes/specials/pagers/ActiveUsersPager.php @@ -43,6 +43,12 @@ class ActiveUsersPager extends UsersPager { */ private $blockStatusByUid; + /** @var int */ + private $RCMaxAge; + + /** @var string[] */ + private $excludegroups; + /** * @param IContextSource|null $context * @param FormOptions $opts diff --git a/includes/specials/pagers/AllMessagesTablePager.php b/includes/specials/pagers/AllMessagesTablePager.php index c804b091a3..6b8b93b90a 100644 --- a/includes/specials/pagers/AllMessagesTablePager.php +++ b/includes/specials/pagers/AllMessagesTablePager.php @@ -46,6 +46,11 @@ class AllMessagesTablePager extends TablePager { */ protected $prefix; + /** + * @var string + */ + protected $suffix; + /** * @var Language */ diff --git a/includes/specials/pagers/MergeHistoryPager.php b/includes/specials/pagers/MergeHistoryPager.php index 9415cea12b..81c3ffb74c 100644 --- a/includes/specials/pagers/MergeHistoryPager.php +++ b/includes/specials/pagers/MergeHistoryPager.php @@ -30,10 +30,15 @@ class MergeHistoryPager extends ReverseChronologicalPager { /** @var array */ public $mConds; + /** @var int */ + private $articleID; + + /** @var int */ + private $maxTimestamp; + public function __construct( SpecialMergeHistory $form, $conds, Title $source, Title $dest ) { $this->mForm = $form; $this->mConds = $conds; - $this->title = $source; $this->articleID = $source->getArticleID(); $dbr = wfGetDB( DB_REPLICA ); diff --git a/includes/specials/pagers/ProtectedTitlesPager.php b/includes/specials/pagers/ProtectedTitlesPager.php index 296fe1112d..a00b37125c 100644 --- a/includes/specials/pagers/ProtectedTitlesPager.php +++ b/includes/specials/pagers/ProtectedTitlesPager.php @@ -34,6 +34,12 @@ class ProtectedTitlesPager extends AlphabeticPager { */ public $mConds; + /** @var string|null */ + private $level; + + /** @var int|null */ + private $namespace; + /** * @param SpecialProtectedtitles $form * @param array $conds @@ -50,7 +56,6 @@ class ProtectedTitlesPager extends AlphabeticPager { $this->mConds = $conds; $this->level = $level; $this->namespace = $namespace; - $this->size = intval( $size ); parent::__construct( $form->getContext() ); } @@ -90,7 +95,7 @@ class ProtectedTitlesPager extends AlphabeticPager { $conds['pt_create_perm'] = $this->level; } - if ( !is_null( $this->namespace ) ) { + if ( $this->namespace !== null ) { $conds[] = 'pt_namespace=' . $this->mDb->addQuotes( $this->namespace ); } diff --git a/includes/specials/pagers/UsersPager.php b/includes/specials/pagers/UsersPager.php index 57b575b8ec..ba078e9f3c 100644 --- a/includes/specials/pagers/UsersPager.php +++ b/includes/specials/pagers/UsersPager.php @@ -37,6 +37,24 @@ class UsersPager extends AlphabeticPager { */ protected $userGroupCache; + /** @var string */ + protected $requestedGroup; + + /** @var bool */ + protected $editsOnly; + + /** @var bool */ + protected $temporaryGroupsOnly; + + /** @var bool */ + protected $creationSort; + + /** @var bool|null */ + protected $including; + + /** @var string */ + protected $requestedUser; + /** * @param IContextSource|null $context * @param array|null $par (Default null) diff --git a/includes/user/User.php b/includes/user/User.php index 6893bf48fa..4445e1d3b1 100644 --- a/includes/user/User.php +++ b/includes/user/User.php @@ -1741,7 +1741,7 @@ class User implements IDBAccessObject, UserIdentity { // Avoid PHP 7.1 warning of passing $this by reference $thisUser = $this; // Extensions - Hooks::run( 'GetBlockedStatus', [ &$thisUser ] ); + Hooks::run( 'GetBlockedStatus', [ &$thisUser ], '1.34' ); } /** @@ -3717,7 +3717,7 @@ class User implements IDBAccessObject, UserIdentity { // If there is a new, unseen, revision, use its timestamp $nextid = $oldid - ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE ) + ? $title->getNextRevisionID( $oldid, Title::READ_LATEST ) : null; if ( $nextid ) { $this->setNewtalk( true, Revision::newFromId( $nextid ) ); diff --git a/includes/widget/CheckMatrixWidget.php b/includes/widget/CheckMatrixWidget.php index 06d8095ed4..3ae00ea6a9 100644 --- a/includes/widget/CheckMatrixWidget.php +++ b/includes/widget/CheckMatrixWidget.php @@ -9,14 +9,22 @@ namespace MediaWiki\Widget; * @license MIT */ class CheckMatrixWidget extends \OOUI\Widget { - - protected $name = ''; - protected $columns = []; - protected $rows = []; - protected $tooltips = []; - protected $values = []; - protected $forcedOn = []; - protected $forcedOff = []; + /** @var string|null */ + protected $name; + /** @var string|null */ + protected $id; + /** @var array */ + protected $columns; + /** @var array */ + protected $rows; + /** @var array */ + protected $tooltips; + /** @var array */ + protected $values; + /** @var array */ + protected $forcedOn; + /** @var array */ + protected $forcedOff; /** * Operates similarly to MultiSelectWidget, but instead of using an array of diff --git a/includes/widget/ComplexTitleInputWidget.php b/includes/widget/ComplexTitleInputWidget.php index 77370674f5..913816cc52 100644 --- a/includes/widget/ComplexTitleInputWidget.php +++ b/includes/widget/ComplexTitleInputWidget.php @@ -9,7 +9,8 @@ namespace MediaWiki\Widget; * @license MIT */ class ComplexTitleInputWidget extends \OOUI\Widget { - + /** @var array */ + protected $config; protected $namespace = null; protected $title = null; diff --git a/includes/widget/NamespaceInputWidget.php b/includes/widget/NamespaceInputWidget.php index 7802a2a017..a360fb8e91 100644 --- a/includes/widget/NamespaceInputWidget.php +++ b/includes/widget/NamespaceInputWidget.php @@ -9,8 +9,10 @@ namespace MediaWiki\Widget; * @license MIT */ class NamespaceInputWidget extends \OOUI\DropdownInputWidget { - - protected $includeAllValue = null; + /** @var string */ + protected $includeAllValue; + /** @var int[] */ + protected $exclude; /** * @param array $config Configuration options diff --git a/includes/widget/SelectWithInputWidget.php b/includes/widget/SelectWithInputWidget.php index a946653e6e..a792172b23 100644 --- a/includes/widget/SelectWithInputWidget.php +++ b/includes/widget/SelectWithInputWidget.php @@ -12,9 +12,12 @@ use OOUI\TextInputWidget; * @license MIT */ class SelectWithInputWidget extends \OOUI\Widget { - - protected $textinput = null; - protected $dropdowninput = null; + /** @var array */ + protected $config; + /** @var TextInputWidget */ + protected $textinput; + /** @var DropdownInputWidget */ + protected $dropdowninput; /** * A version of the SelectWithInputWidget, with `or` set to true. diff --git a/includes/widget/SizeFilterWidget.php b/includes/widget/SizeFilterWidget.php index 18c05bf6bc..26935b1214 100644 --- a/includes/widget/SizeFilterWidget.php +++ b/includes/widget/SizeFilterWidget.php @@ -13,9 +13,14 @@ use \OOUI\LabelWidget; * @license MIT */ class SizeFilterWidget extends \OOUI\Widget { - - protected $radioselectinput = null; - protected $textinput = null; + /** @var array */ + protected $config; + /** @var LabelWidget */ + protected $label; + /** @var RadioSelectInputWidget */ + protected $radioselectinput; + /** @var TextInputWidget */ + protected $textinput; /** * RadioSelectInputWidget and a TextInputWidget to set minimum or maximum byte size diff --git a/includes/widget/TagMultiselectWidget.php b/includes/widget/TagMultiselectWidget.php index 43e184cab2..e96160c715 100644 --- a/includes/widget/TagMultiselectWidget.php +++ b/includes/widget/TagMultiselectWidget.php @@ -12,41 +12,34 @@ use OOUI\MultilineTextInputWidget; * @license MIT */ abstract class TagMultiselectWidget extends \OOUI\Widget { - - protected $selectedArray = []; - protected $inputName = null; - protected $inputPlaceholder = null; - protected $tagLimit = null; + /** @var array */ + protected $selectedArray; + /** @var string|null */ + protected $inputName; + /** @var string|null */ + protected $inputPlaceholder; + /** @var array */ + protected $input; + /** @var int|null */ + protected $tagLimit; /** * @param array $config Configuration options * - array $config['default'] Array of items to use as preset data - * - array $config['name'] Name attribute (used in forms) - * - array $config['placeholder'] Placeholder message for input + * - string $config['name'] Name attribute (used in forms) + * - string $config['placeholder'] Placeholder message for input * - array $config['input'] Config options for the input widget - * - number $config['tagLimit'] Maximum number of selected items + * - int $config['tagLimit'] Maximum number of selected items */ public function __construct( array $config = [] ) { parent::__construct( $config ); // Properties - if ( isset( $config['default'] ) ) { - $this->selectedArray = $config['default']; - } - if ( isset( $config['name'] ) ) { - $this->inputName = $config['name']; - } - if ( isset( $config['placeholder'] ) ) { - $this->inputPlaceholder = $config['placeholder']; - } - if ( isset( $config['input'] ) ) { - $this->input = $config['input']; - } else { - $this->input = []; - } - if ( isset( $config['tagLimit'] ) ) { - $this->tagLimit = $config['tagLimit']; - } + $this->selectedArray = $config['default'] ?? []; + $this->inputName = $config['name'] ?? null; + $this->inputPlaceholder = $config['placeholder'] ?? null; + $this->input = $config['input'] ?? []; + $this->tagLimit = $config['tagLimit'] ?? null; $textarea = new MultilineTextInputWidget( array_merge( [ 'name' => $this->inputName, diff --git a/languages/i18n/cs.json b/languages/i18n/cs.json index 15f5ee1ab0..e8c22b492d 100644 --- a/languages/i18n/cs.json +++ b/languages/i18n/cs.json @@ -790,6 +790,9 @@ "content-model-css": "CSS", "content-json-empty-object": "Prázdný objekt", "content-json-empty-array": "Prázdné pole", + "unsupported-content-model": "Varování: Model obsahu $1 není na této wiki podporován.", + "unsupported-content-diff": "Rozdíly obsahů s modelem $1 nejsou podporovány.", + "unsupported-content-diff2": "Rozdíly mezi obsahy s modely $1 a $2 nejsou na této wiki podporovány.", "deprecated-self-close-category": "Stránky s neplatnými sebeuzavírajícími HTML značkami", "deprecated-self-close-category-desc": "Stránka obsahuje neplatné sebeuzavírající HTML značky, například <b/> nebo <span/>. Jejich chování se v zájmu konzistence se specifikací HTML5 brzy změní, proto je jejich použití ve wikitextu zastaralé.", "duplicate-args-warning": "Upozornění: Stránka [[:$1]] volá [[:$2]] s více než jednou hodnotou parametru „$3“. Použije se jen poslední uvedená hodnota.", @@ -825,6 +828,7 @@ "undo-norev": "Tuto editaci není možné vrátit, protože neexistuje nebo byla smazána.", "undo-nochange": "Zdá se, že editace již byla zrušena.", "undo-summary": "Zrušena verze $1 od uživatele [[Special:Contributions/$2|$2]] ([[User talk:$2|diskuse]])", + "undo-summary-anon": "Zrušena verze $1 od uživatele [[Special:Contributions/$2|$2]]", "undo-summary-username-hidden": "Zrušena verze $1 od skrytého uživatele", "cantcreateaccount-text": "Zakládání nových účtů z této IP adresy ($1) bylo zablokováno {{GENDER:$3|uživatelem|uživatelkou}} [[User:$3|$3]].\n\n$3 uvádí toto zdůvodnění: $2", "cantcreateaccount-range-text": "Zakládání nových účtů z IP adres v rozsahu $1, který obsahuje i vaši IP adresu ($4), bylo zablokováno {{GENDER:$3|uživatelem|uživatelkou}} [[User:$3|$3]].\n\n$3 uvádí toto zdůvodnění: $2", @@ -2333,6 +2337,7 @@ "alreadyrolled": "Nelze vrátit zpět poslední editaci [[:$1]] od uživatele [[User:$2|$2]] ([[User talk:$2|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), protože někdo jiný již stránku editoval nebo vrátil tuto změnu zpět.\n\nPoslední editaci této stránky {{GENDER:$3|provedl|provedla|provedl uživatel}} [[User:$3|$3]] ([[User talk:$3|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).", "editcomment": "Shrnutí editace bylo: $1.", "revertpage": "Editace uživatele „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|diskuse]]) vráceny do předchozího stavu, jehož autorem je „[[User:$1|$1]]“", + "revertpage-anon": "Editace uživatele „[[Special:Contributions/$2|$2]]“ vráceny do předchozího stavu, jehož autorem je „[[User:$1|$1]]“", "revertpage-nouser": "Editace skrytého uživatele vráceny do předchozího stavu, jehož {{GENDER:$1|autorem|autorkou}} je „[[User:$1|$1]]“", "rollback-success": "Editace {{GENDER:$3|uživatele|uživatelky}} $1 byly vráceny na poslední verzi od {{GENDER:$4|uživatele|uživatelky}} $2.", "sessionfailure-title": "Chyba relace", diff --git a/languages/i18n/diq.json b/languages/i18n/diq.json index 6ba40ff197..b64046bcf5 100644 --- a/languages/i18n/diq.json +++ b/languages/i18n/diq.json @@ -1923,7 +1923,7 @@ "checkbox-all": "Pêro", "checkbox-none": "Çıniyo", "checkbox-invert": "Dimlaşt ke", - "allpages": "Pêro peli", + "allpages": "Perri pêro", "nextpage": "Pela bahdoyêne ($1)", "prevpage": "Pela veri ($1)", "allpagesfrom": "Herfa kı pa liste bo:", diff --git a/languages/i18n/en.json b/languages/i18n/en.json index f6ea46c4a2..82826c513f 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -47,6 +47,7 @@ "tog-useeditwarning": "Warn me when I leave an edit page with unsaved changes", "tog-prefershttps": "Always use a secure connection while logged in", "tog-showrollbackconfirmation": "Show a confirmation prompt when clicking on a rollback link", + "tog-requireemail": "Require email for password resets", "underline-always": "Always", "underline-never": "Never", "underline-default": "Skin or browser default", @@ -1152,6 +1153,7 @@ "prefs-help-email": "Email address is optional, but is needed for password resets, should you forget your password.", "prefs-help-email-others": "You can also choose to let others contact you by email through a link on your user or talk page.\nYour email address is not revealed when other users contact you.", "prefs-help-email-required": "Email address is required.", + "prefs-help-requireemail": "If checked, will only send password reset emails if the resetting person has provided both username and email for this account.", "prefs-info": "Basic information", "prefs-i18n": "Internationalisation", "prefs-signature": "Signature", @@ -2694,6 +2696,7 @@ "ipblocklist-legend": "Find a blocked user", "blocklist-userblocks": "Hide account blocks", "blocklist-tempblocks": "Hide temporary blocks", + "blocklist-indefblocks": "Hide indefinite blocks", "blocklist-addressblocks": "Hide single IP blocks", "blocklist-type": "Type:", "blocklist-type-opt-all": "All", diff --git a/languages/i18n/exif/pl.json b/languages/i18n/exif/pl.json index f8709f329d..64725b43c0 100644 --- a/languages/i18n/exif/pl.json +++ b/languages/i18n/exif/pl.json @@ -6,7 +6,8 @@ "Matma Rex", "Sp5uhe", "Stlmch", - "Railfail536" + "Railfail536", + "Rail" ] }, "exif-imagewidth": "Szerokość", diff --git a/languages/i18n/fr.json b/languages/i18n/fr.json index 51327182de..41b229309a 100644 --- a/languages/i18n/fr.json +++ b/languages/i18n/fr.json @@ -812,8 +812,8 @@ "blockedtitle": "L’utilisateur est bloqué.", "blocked-email-user": "Votre nom d’utilisateur a été bloqué pour l’envoi de courriels. Vous pouvez toujours modifier d’autres pages sur ce wiki. Vous pouvez voir tous les détails du blocage sur [[Special:MyContributions|contributions du compte]].\n\nCe blocage a été fait par $1.\n\nLe motif fourni est $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage nº $5", "blockedtext-partial": "Votre nom d’utilisateur ou votre adresse IP a été bloqué pour effectuer des modifications sur cette page. Vous pouvez toujours modifier d’autres pages sur ce wiki. Vous pouvez voir tous les détails sur ce blocage sur [[Special:MyContributions|contributions du compte]].\n\nLe blocage a été effectué par $1.\n\nLe motif fourni est $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Cible souhaitée du blocage : $7\n* ID du blocage nº $5", - "blockedtext": "Votre compte utilisateur ou votre adresse IP a été bloqué.\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité ne vous a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", - "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité « {{int:emailuser}} » que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité ne vous a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", + "blockedtext": "Votre compte utilisateur ou votre adresse IP a été bloqué.\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité ne vous a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", + "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: $2.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité « {{int:emailuser}} » que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité ne vous a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", "systemblockedtext": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués automatiquement par MediaWiki.\nLa raison donnée est la suivante:\n\n: $2.\n\n* Le début du blocage: $8\n* Expiration du délai de blocage: $6\n* Elément concerné: $7\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.", "blockednoreason": "aucune raison donnée", "blockedtext-composite": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués.\n\nLa raison invoquée est :\n\n: $2.\n\n* Début du blocage : $8\n* Expiration du blocage le plus long : $6\n* $5\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chaque demande que vous ferez.", @@ -840,7 +840,7 @@ "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :", "clearyourcache": "Note : après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* Firefox / Safari : maintenez la touche Maj (Shift) en cliquant sur le bouton Actualiser ou pressez Ctrl-F5 ou Ctrl-R (⌘-R sur un Mac) \n* Google Chrome : appuyez sur Ctrl-Maj-R (⌘-Shift-R sur un Mac) \n* Internet Explorer : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5 \n* Opera : allez dans Menu → Settings (Opera → Préférences sur un Mac) et ensuite à Confidentialité & sécurité → Effacer les données d’exploration → Images et fichiers en cache.", "usercssyoucanpreview": "Astuce : utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l’enregistrer.", - "userjsonyoucanpreview": "Conseil : Utiliser le bouton « {{int:showpreview}} » pour tester votre nouveau JSON avant enregistrement.", + "userjsonyoucanpreview": "Conseil : utilisez le bouton « {{int:showpreview}} » pour tester votre nouveau JSON avant enregistrement.", "userjsyoucanpreview": "Astuce : utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l’enregistrer.", "usercsspreview": "Rappelez-vous que vous ne faites que prévisualiser votre propre feuille CSS. \nElle n’a pas encore été enregistrée !", "userjsonpreview": "Rappelez-vous que vous êtes seulement en train de tester/voir un aperçu de votre configuration utilisateur JSON.\nElle n’a pas encore été enregistrée !", @@ -848,7 +848,7 @@ "sitecsspreview": "Rappelez-vous que vous ne faites que prévisualiser cette feuille de style. \nElle n’a pas encore été enregistrée !", "sitejsonpreview": "Souvenez-vous que vous ne faites que regarder un aperçu de cette configuration JSON.\nElle n’a pas encore été enregistrée !", "sitejspreview": "Rappelez-vous que vous ne faites que prévisualiser ce code JavaScript. \nIl n’a pas encore été enregistré !", - "userinvalidconfigtitle": "Attention : il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css, .json et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.", + "userinvalidconfigtitle": "Attention : il n’existe pas d’habillage « $1 ».\nLes pages personnelles avec extensions .css, .json et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.", "updated": "(Mis à jour)", "note": "Note :", "previewnote": "Rappelez-vous que ce n’est qu’une prévisualisation.\nVos modifications n’ont pas encore été enregistrées !", @@ -1828,10 +1828,10 @@ "uploaded-event-handler-on-svg": "Fixer des attributs de gestionnaire d’événement $1=\"$2\" n’est pas autorisé dans les fichiers SVG.", "uploaded-href-attribute-svg": " les éléments ne peuvent être liés (href) qu’aux cibles de données : (fichier inclus), http:// ou https://, ou un fragment (#, même document). Pour les autres éléments, comme , seuls data: et fragment sont autorisés. Essayez les images incluses lors de l’export de votre SVG. <$1 $2=\"$3\"> obtenu.", "uploaded-href-unsafe-target-svg": "Un href vers des données non sûres a été trouvé dans le fichier SVG téléversé : URI cible <$1 $2=\"$3\">.", - "uploaded-animate-svg": "Balise « animate » trouvée, qui pourrait modifier le href en utilisant l’attribut « from » <$1 $2=\"$3\"> dans le fichier SVG téléversé.", + "uploaded-animate-svg": "Balise « animate » trouvée qui pourrait modifier le href en utilisant l’attribut « from » <$1 $2=\"$3\"> dans le fichier SVG téléversé.", "uploaded-setting-event-handler-svg": "Positionner les attributs du gestionnaire d’événements n'est pas possible, <$1 $2=\"$3\"> trouvé dans le fichier SVG téléversé.", "uploaded-setting-href-svg": "L’utilisation de la balise « set » pour ajouter un attribut « href » à l’élément parent est interdite.", - "uploaded-wrong-setting-svg": "L’utilisation de la balise « set » pour ajouter une cible distante, de données ou de type script, à un attribut quelconque, est interdite. <set to=\"$1\"> a été trouvé dans le fichier SVG téléversé.", + "uploaded-wrong-setting-svg": "L’utilisation de la balise « set » pour ajouter à un attribut quelconque une cible distante, de données ou de script est interdite. <set to=\"$1\"> a été trouvé dans le fichier SVG téléversé.", "uploaded-setting-handler-svg": "Les SVG qui positionnent l’attribut « handler » avec distant/données/script sont bloqués. $1=\"$2\" a été trouvé dans le fichier SVG téléversé.", "uploaded-remote-url-svg": "Les SVG qui positionnent un attribut de style avec une URL distante sont bloqués. $1=\"$2\" trouvé dans le fichier SVG téléversé.", "uploaded-image-filter-svg": "Filtre d’image avec URL trouvé : <$1 $2=\"$3\"> dans le fichier SVG téléversé.", @@ -1936,7 +1936,7 @@ "uploadstash-errclear": "La suppression des fichiers a échoué.", "uploadstash-refresh": "Actualiser la liste des fichiers", "uploadstash-thumbnail": "afficher la vignette", - "uploadstash-exception": "Impossible de stocker le téléversement dans la réserve ($1) : « $2 ».", + "uploadstash-exception": "Impossible de stocker le téléversement dans la réserve ($1) : « $2 ».", "uploadstash-bad-path": "Le chemin n’existe pas.", "uploadstash-bad-path-invalid": "Le chemin n’est pas valide.", "uploadstash-bad-path-unknown-type": "Type « $1 » inconnu.", diff --git a/languages/i18n/gom-deva.json b/languages/i18n/gom-deva.json index 9faad524e6..2a427a291b 100644 --- a/languages/i18n/gom-deva.json +++ b/languages/i18n/gom-deva.json @@ -106,6 +106,7 @@ "category-file-count": "{{PLURAL:$2|ह्या वर्गांत फकत सकयली फायल आसपावता.|ह्या वर्गांत सकयल दिल्लीं {{PLURAL:$1|फायल|$1 फायलीं}} आसता, वट्ट फायलीं $2}}", "listingcontinuesabbrev": "चालू.", "noindex-category": "बिननिर्देशांकी पानां", + "broken-file-category": "तुटलेल्या फायलींचो दुवे आसलेलीं पानां", "about": "विशीं", "article": "मजकूराचीं पानां", "newwindow": "(नव्या ज़ोणेलांत उकतें जाता)", @@ -226,6 +227,7 @@ "mainpage-nstab": "मुखेल पान", "nosuchaction": "असले तरेचे कार्य ना", "nosuchspecialpage": "असले कांयच विशेश पान ना", + "nospecialpagetext": "तुवें एक अवैद खेरीत पान मागलां.\n\nएक खेरीत पानाची वळेरी तुका हांगासर मेळूं येता [[Special:SpecialPages|{{int:specialpages}}]].", "error": "चूक", "databaseerror": "डॅटाबॅज त्रुटी", "databaseerror-textcl": "डॅटाबेज विरोध त्रुटी आयिल्ली आसा", @@ -236,6 +238,8 @@ "badtitle": "चुकीचो माथाळो", "badtitletext": "विनवणी केल्लें पानाचो माथाळो अवैध, रितो वा अयोग्य तरेन आंतरभाशी वा आंतर विकी माथाळ्या कडे जोडिल्लो आशिल्लो. तातूंत माथाळ्यांत वापरुं नजो अशी एक वा चड अक्षरां आसूं येतात.", "viewsource": "उगम पळेयात", + "viewsource-title": "$1‎ खातीर मूळ पळय", + "viewsourcetext": "तुज्यान ह्या पानाचें मूळ पळोवंक आनी नकल करुंक जाता.", "yourname": "वापरप्याचे नांव", "userlogin-yourname": "वापरप्याचे नांव", "userlogin-yourname-ph": "वापरप्याचे नांव घालात", @@ -369,7 +373,7 @@ "accmailtitle": "गुपीत उतर धाडलां", "newarticle": "(नवें)", "newarticletext": "जें पान अजून अस्तित्वांत ना अशा पानाचे दुवे फाटल्यान तुमी आसात. पान रचपाक सकयले चौकटींत टायप करपाक सुरु करात (चड म्हायती खातीर [$1 आदाराचें पान] पळेयात) जर ह्या पानार तुमी चुकून पावल्यात तर ब्रावजराचो बॅक (फटीं) हो बटन दामात", - "noarticletext": "सद्या ह्या पानाचेर कसलीच मजकूर ना. \nतुमी हेर पानांचेर [[Special:Search/{{PAGENAME}}|हो माथाळो]] सोदूं शकतात,\n[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} संबंदीत लॉग सोदूं शकतात],\nवा ह्या पानाक [{{fullurl:{{FULLPAGENAME}}|action=edit}} संपादीत] करूं शकतात।", + "noarticletext": "सध्याक हें पान रिंते आसा.\nतुज्यान दूसऱ्या पानानी [[Special:Search/{{PAGENAME}}| ह्या पानाचे नांव सोदूंक जाता]], [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAME}}}} संबंधी सत्रानी सोदूंक जाता], वा [{{fullurl:{{FULLPAGENAME}}|action=edit}} हें पान रचूंक जाता].", "noarticletext-nopermission": "तुर्ताक ह्या पानाचेर कसलोच मजकूर ना. तुमी हेर पानांचेर [[Special:Search/{{PAGENAME}}|ह्या माथाळ्याचो सोद]] घेवं शकतात,\nवा [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} संबंदीत लॉग सोदूं शकतात], पूण तुमकां हें पानाची रचणूक करपाची परवानगी ना।", "userpage-userdoesnotexist-view": "\"$1\" ह्या वापरप्याच्या खात्याची नोंदणी करूंक ना.", "previewnote": "'''ही फकत एक दाखवण हें मतींत दवरात.'''\nतुमचें बदल आडून राखून दवरूंक ना!", @@ -383,7 +387,7 @@ "hiddencategories": "हें पान {{PLURAL:$1|लिपिल्ले वर्गाचें}} आसा", "permissionserrorstext-withaction": "ह्या {{PLURAL:$1|कारण|कारणां}}: खातीर तुका $2 मान्यताय ना.", "recreate-moveddeleted-warn": "शिटकावणीः तुमी आदीं काडून उडयिल्लें पान परतून तयार करतात ह्या पानाचे फासून उडोवपी आनी दुसरे कडे व्हरपी लाग फकत सोपेपणा खातीर दिल्यात", - "moveddeleted-notice": "हें पान काडून उडयला.\nह्या पानाचो काडून उडोवपी आनी हालोवपी लॉग संदर्भा खातीर सकयल दिला.", + "moveddeleted-notice": "हें पान काडून उडयला.\nह्या पानाचें काडून उडोवपाचें, राखपाचें, आनी हालोवपाचें सत्र संदर्भा खातीर सकयल दिला.", "content-model-wikitext": "विकीमजकूर", "content-model-text": "सादोमजकूर", "post-expand-template-inclusion-warning": "शिटकावणीः सांचो धरून आकार अगडबंब जाता, कांय सांच्याचो आसपाव जावचो ना.", @@ -403,7 +407,7 @@ "page_first": "पयलें", "page_last": "निमणें", "histlegend": "फरकाची निवडणी : पुनर्नियाळांची तुळा करपा खातीर रेडियो चौकटीं चेर कुरु करात आनी ''एंटर'' ना तर तळाकडे आशिल्लो बुतांव दामात।
\nविवरण : ({{int:cur}}) = हालींची पुनर्नियाळा बरोबर फरक, ({{int:last}}) = आदली पुनर्नियाळा बरोबर फरक, {{int:minoreditletter}} = दाक्टें बदल।", - "history-fieldset-title": "चाळपाचो इतिहास", + "history-fieldset-title": "उजळण्यो चाळ", "history-show-deleted": "फकत काडून उडयिल्लें", "histfirst": "पोरणो", "histlast": "नवो ताल्ल", @@ -445,7 +449,7 @@ "searchprofile-advanced-tooltip": "खाशेल्या नांवथोळाणी सोदात", "search-result-size": "$1 ({{PLURAL:$2|1 उतर|$2 उतरां}})", "search-result-category-size": "{PLURAL:$1|1 सदस्य|$1 सदस्य}} ({{PLURAL:$2|1 उपगट|$2 उपगट}}, {{PLURAL:$3|1 फायल|$3 फायलीं}})", - "search-redirect": "(पुनर्निर्देशन $1)", + "search-redirect": "($1 सावन पुनर्निर्देशीत)", "search-section": "(विभाग $1)", "search-suggest": "तुमकां $1 अशें म्हणपाचें आसलें?", "search-rewritten": "$1 हाचो निकाल दाखयता.नाजाल्यार $2 हें सोदात.", @@ -472,6 +476,7 @@ "recentchanges": "हालींचे बदल", "recentchanges-legend": "हालींच जाल्ल्या बदलाचो विकल्प", "recentchanges-summary": "ह्या विकीचेर हालींच जाल्ल्या बदलांचो माग ह्या भरणांतल्यान दवरात", + "recentchanges-noresult": "दिलेल्या काळाचे बदल ह्या निकशाक जुळनांत.‎", "recentchanges-feed-description": "ह्या विकीचेर हालींच जाल्ल्या बदलांचो माग ह्या भरणांतल्यान दवरात.", "recentchanges-label-newpage": "ह्या संपादनांन नवें पान निर्माण केला.", "recentchanges-label-minor": "हें दाक्टे संपादन", @@ -480,7 +485,7 @@ "recentchanges-label-plusminus": "ह्या पानाचो आकार इतल्या बाइट्सन बदललो", "recentchanges-legend-heading": "कुंजी:", "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages| नव्या पानांची सुची]] पळयात)", - "rcnotefrom": "$2 पासून केल्ले बदल सकयल दिल्यात ($1 मेरेन दाखयल्यात)", + "rcnotefrom": "सकयल $3, $4 सावन {{PLURAL:$5 जालेले बदल दिल्यात}} ($1 मेरेन {{PLURAL:$5|दाखयलां|दाखयल्यांत}}).", "rclistfrom": "$3 $2 साकून नवें बदल दाखयात", "rcshowhideminor": "$1 दाकट्यो बदल", "rcshowhideminor-show": "दाखयात", @@ -489,6 +494,7 @@ "rcshowhidebots-show": "दाखयात", "rcshowhidebots-hide": "लिपयात", "rcshowhideliu": "$1 अधिकृत नोंदीचे वापरपी", + "rcshowhideliu-show": "दाखयात", "rcshowhideliu-hide": "लिपयात", "rcshowhideanons": "$1 निनांवी वापरपी", "rcshowhideanons-show": "दाखयात", @@ -498,7 +504,7 @@ "rcshowhidemine": "$1 म्हजें संपादन आंकडे", "rcshowhidemine-show": "दाखयात", "rcshowhidemine-hide": "लिपयात", - "rclinks": "फाटल्या $2 दिसांनी जाल्लो $1 बदल दाखयात", + "rclinks": "शेवटचे $2 दिसानीं जाल्ले $1 बदल दाखयात", "diff": "फरक", "hist": "इति", "hide": "लिपयात", @@ -541,8 +547,8 @@ "filehist-dimensions": "परिमाण", "filehist-comment": "शेरो", "imagelinks": "फायलिचो वापर", - "linkstoimage": "हे फायलीक सकयल दिल्ल्यो पानाच्यो जोडण्यो {{PLURAL:$1|आसात}}.", - "nolinkstoimage": "हे फायलीक दुवो आशिल्लीं आनीक पानां नात.", + "linkstoimage": "{{PLURAL:$1|हें पान|$1 हीं पानां}} ही फायल {{PLURAL:$1|वापरता|वापरतात}}:", + "nolinkstoimage": "ह्या फायलीक वापरतात तसलीं पानां नांत.", "sharedupload-desc-here": "ही फयल $1 हांगाची आनी ती हे प्रकल्पां खातीर वापरल्यार चलता. (तिच्या $2 ह्या फयलींतलें वर्णनाचे पान) तातूंतलें वर्णन सकयल दिलां.", "upload-disallowed-here": "तूं ह्या फायलीचेर अधिलेखीत करूंक शकना", "randompage": "खंयचेंय पान", @@ -609,6 +615,7 @@ "contributions-title": "$1 खातीर वापरप्याचीं योगदानां", "mycontris": "योगदान", "anoncontribs": "योगदान", + "contribsub2": "{{GENDER:$3|$1}} हाच्यो ($2)", "uctop": "हालीचें", "month": "ह्या म्हयन्या सावन (आनी आदलें):", "year": "ह्या वर्सा सावन (आनी आदलें):", @@ -619,6 +626,7 @@ "sp-contributions-search": "योगदानां सोदात", "sp-contributions-username": "आयपी नामो वा वापरप्याचें नांव", "sp-contributions-toponly": "फकत सगळ्यांत हालींचे पुनर्नियाळ आशिल्लीं संपादन दाखयात", + "sp-contributions-newonly": "फकत तसलेचच बदल दाखय, जांचे वर्वीं पान रचलां", "sp-contributions-submit": "सोद", "whatlinkshere": "हाका कितें जडता", "whatlinkshere-title": " \"$1\" हाका दुवे आशिल्लीं पानां", @@ -626,17 +634,17 @@ "linkshere": "मुखावेली पानां $2: हाका जडतात", "nolinkshere": "$2 हाका खंयच्याच पानाचो दुवो ना", "isredirect": "पुनर्निर्देशन पान", - "istemplate": "$1 दूसरात-समावेस", + "istemplate": "दुरास्थ-समावेस", "isimage": "फायलीचो दुवो", "whatlinkshere-prev": "{{PLURAL:$1|आदलें|आदलीं $1}}", "whatlinkshere-next": "{{PLURAL:$1|फुडलें|फुडलें $1}}", "whatlinkshere-links": "← दुवे", "whatlinkshere-hideredirs": "$1 पुनर्निर्देशन", - "whatlinkshere-hidetrans": "$1 दूस्रात-समावेश", + "whatlinkshere-hidetrans": "$1 दुरास्थ-समावेस", "whatlinkshere-hidelinks": "$1 दुवे", "whatlinkshere-hideimages": "$1 फायल दुवे", "whatlinkshere-filters": "गाळणे", - "ipboptions": "2 वरां: 2hours ,1 दीस:1 day,3 दीस:3 days,1 सुमान:1 week,2 सुमनां:2 weeks,1 म्हयनो:1 month,3 म्हयने:3 months,6 म्हयने:6 months,1 वर्स:1 year,अनिश्चीत:infinte", + "ipboptions": "2 वरां:2 hours,1 दीस:1 day,3 दीस:3 days,1 सुमान:1 week,2 सुमनां:2 weeks,1 म्हयनो:1 month,3 म्हयने:3 months,6 म्हयने:6 months,1 वर्स:1 year,शेवट ना:infinite", "ipblocklist": "आडायल्लें वापरपी", "blocklink": "आडावणी", "change-blocklink": "विभाग सुदारप", @@ -652,11 +660,11 @@ "allmessagesdefault": "पूर्वनिर्धारित संदेशाचो मजकूर", "thumbnail-more": "व्हड करात", "thumbnail_error": "$1ः लघुप्रतिमा करतांनाची चूक", - "tooltip-pt-userpage": "तुमचें वापरपाचें पान", - "tooltip-pt-mytalk": "तुमचें चर्चेचें पान", + "tooltip-pt-userpage": "{{GENDER:|तुमचें वापरप्याचें}} पान", + "tooltip-pt-mytalk": "{{GENDER:|तुमचें}} भासाभासाचें पान", "tooltip-pt-preferences": "{{GENDER:|तुमची}} पसंती", "tooltip-pt-watchlist": "तुमी बदल करपा खातीर देखरेख करतात त्या पानांची वळेरी", - "tooltip-pt-mycontris": "तुमच्या योगदानांची वळेरी", + "tooltip-pt-mycontris": "{{GENDER:|तुमच्या}} योगदानांची वळेरी", "tooltip-pt-login": "सत्रारंभ करप बरें, पूण तशी सक्ती ना.", "tooltip-pt-logout": "सत्र शेवट", "tooltip-pt-createaccount": "तुमी खातें उगडून सत्रारंभ करचें अशें सुचयतात, पूण तें सक्तीचें ना.", @@ -684,7 +692,7 @@ "tooltip-t-whatlinkshere": "हांगा दुवे आशिल्ल्या सगळ्या विकी पानांची वळेरी", "tooltip-t-recentchangeslinked": "ह्या पानावेल्यान दुवे दिल्ल्या पानांतले हालींचे बदल", "tooltip-feed-atom": "ह्या पाना खातीर ऍटम पूर्वण", - "tooltip-t-contributions": "ह्या वापरप्याची योगदानाची वळेरी", + "tooltip-t-contributions": "{{GENDER:$1|ह्या वापरप्याची}} योगदानाची वळेरी", "tooltip-t-emailuser": "{{GENDER:$1|ह्या उपेगकर्त्याक}} इ-मेल धाडात", "tooltip-t-upload": "फायली अपलोड करात", "tooltip-t-specialpages": "सगळ्या विशेश पानांची वळेरी", @@ -706,7 +714,7 @@ "tooltip-rollback": "निमाण्या योगदान करप्यान ह्या पानाचेर केल्लें संपादन रोलबॅक (फाटीं घेयात) एकाच क्लीकान मूळ पदार हाडटा", "tooltip-undo": "\"आदलें स्थितीर हाडचें\" ह्या बदलाक परत व्हरुन संपादन स्थितीन झलक रितीन दाखयतात.\nहाचेवरवीं सारांशान आदल्या स्थितीर हाडपाचें कारण बरोवं शकता.", "tooltip-summary": "आपरोसाची नोंदणी करात", - "simpleantispam-label": "एन्टी-स्पैम तपासप.\nहे भरीनकाय!", + "simpleantispam-label": "स्पमविरूध तपासणी.\nहें भर नाका!", "pageinfo-toolboxlink": "पानाची म्हायती", "pageinfo-contentpage-yes": "हय", "previousdiff": "← आदलें संपादन", diff --git a/languages/i18n/gom-latn.json b/languages/i18n/gom-latn.json index 1fe99ac4f9..9f1f7067f4 100644 --- a/languages/i18n/gom-latn.json +++ b/languages/i18n/gom-latn.json @@ -17,7 +17,7 @@ "tog-enotifwatchlistpages": "Mhojea sadurvollerintlem pan vo fayl bodol'li zalear mhaka email dhadd", "tog-shownumberswatching": "Nodor dovorpi vaporpeanche sonkhya dakhoi", "tog-oldsig": "Tujea sod'dheachi soy:", - "tog-uselivepreview": "Pan porot ugdinastana zolok dahkoi", + "tog-uselivepreview": "Pan porot ughoddnastana zholok dakhoi", "tog-watchlisthideown": "Sadurvollerint mhoje bodol lipoi", "tog-watchlisthidebots": "Sadurvollerint robotani kel'le bodol lipoi", "tog-watchlisthideminor": "Sadurvollerint dhaktem bodol lipoi", @@ -100,7 +100,7 @@ "listingcontinuesabbrev": "chalu", "index-category": "Suchi-potran zodlelim panam", "noindex-category": "Suchi-potran zoddunk-naslelim panam", - "broken-file-category": "Tuttlolea faylinchea duve aslelim panam‎", + "broken-file-category": "Tuttlolea faylinche duve aslelim panam‎", "about": "Hea vixoiavoir", "article": "Vixoi sombondhi pan", "newwindow": "(novea zonelant uktem zata)", @@ -191,7 +191,7 @@ "youhavenewmessages": "Tumkam $1 ($2) asat.", "youhavenewmessagesfromusers": "Tuka {{PLURAL:$3|ek vaporpi|$3 vaporpi}} koddlean $1 {{PLURAL:$4|asa|asat}} ($2).‎", "newmessageslinkplural": "{{PLURAL:$1|novo sondex|999=nove sondex}}‎", - "newmessagesdifflinkplural": "{{PLURAL:$1|nimanno bodol|999=nimanneo bodol}}", + "newmessagesdifflinkplural": "{{PLURAL:$1|nimanno bodol|999=nimanne bodol}}", "youhavenewmessagesmulti": "$1 cher tuka noveo sondex asat", "editsection": "bodol", "editold": "bodol", @@ -236,7 +236,7 @@ "databaseerror-textcl": "Totv-kox (database) sodtana chuk ghodli", "databaseerror-query": "Anurodh: $1", "databaseerror-error": "Chuk: $1", - "missing-article": "Totv-kox (Database) hantun mellunk zai aslem tem mozkur \"$1\" $2 mellunk-nam.\n\nHorxim, oxem ek pornem frk vo eka panachea itihasacho duvo kadun udoila, tedna zata.\n\nOxem nhoi zalear, tuka softwer-an chuk sampodlam zait.\nUpkar korun eka [[Special:ListUsers/sysop|karbhari]]chea nodrek hadd, Internet Zago Sodpi (URL) hachi nond gheun.", + "missing-article": "Totv-kox (Database) hantun mellunk zai aslem tem mozkur \"$1\" $2 mellunk-nam.\n\nHorxim, oxem ek pornem frk vo eka panachea itihasacho duvo kaddun uddoila, ten’na zata.\n\nOxem nhoi zalear, tuka software-ant chuk sampoddlea zait.\nUpkar korun eka [[Special:ListUsers/sysop|karbhari]]chea nodrek hadd, Ontorzalleant Zago Sodpi (URL) hachi nond ghevn.", "missingarticle-rev": "(uzollnni#: $1)", "missingarticle-diff": "(Frk: $1, $2)", "badtitle": "Chukichem nanv", @@ -289,7 +289,7 @@ "loginerror": "Sotrorombhachi truti", "createacct-error": "Khatem rochtanam truti", "createaccounterror": "Khatem rochunk zaunk na: $1", - "loginsuccesstitle": "Sotrorombh zalem", + "loginsuccesstitle": "Sotrarombh zalem", "nosuchusershort": "\"$1\" hea nanvan konn vapurpi na.\nNanv boroitana chuk zali gai?", "nouserspecified": "Vapurpeachem nanv diunk-uch zai.", "login-userblocked": "Hea vapurpeak addaila. Sotrorombh korunk zaina.", @@ -307,7 +307,7 @@ "emaildisabled": "Hi site mail dhadpak xokona.", "accountcreated": "Khatem rochlem.", "createaccount-title": "{{SITENAME}} -ak khatem rochlem", - "login-abort-generic": "Tujem sotrorombh opexi tharlam - Nixfolit", + "login-abort-generic": "Tujem sotrorombh opexi tharlam - Nixfollit", "login-migrated-generic": "Tujem khatem stholontrit zalam ani vapurpeachem nanv hea wikicher anink ostitvant na.", "loginlanguagelabel": "Bhas: $1", "pt-login": "Sotrorombh", @@ -332,8 +332,8 @@ "passwordreset-domain": "Domain:", "passwordreset-email": "Email potto:", "passwordreset-emailelement": "Vapurpeachem nanv: \n$1\n\nTatpurtem gupitutor: \n$2", - "passwordreset-emailsentemail": "Ho email pot'to tujea kontak zodlelem asa zalear, gupitutor portun tharaipacho email dhadlelem zatelem.", - "changeemail": "Email potto bodol vo kad", + "passwordreset-emailsentemail": "Ho email pot'to tujea hixobant zoddlolo asa zalear, gupitutor portun tharavpacho email dhaddlelem zatelem.", + "changeemail": "Email po’tto bodol vo kadd", "changeemail-oldemail": "Sodhyacho email potto:", "changeemail-newemail": "Novo email potto:", "changeemail-none": "(kai na)", @@ -375,7 +375,7 @@ "anoneditwarning": "Chotrai: Tuven sotrorombh korunk nai. Tu bodol korit zalear tuzo IP pot'to soglleank polleunk zatelem. Tu [$1 sotrorombh korit] vo [$2 kont rochit] zalear, tuje bodol tuzo vaporpeachem nanvak zoddteleo ani anik-ui faide asat.", "missingcommenttext": "Upkar korun tuzo xero boroi.", "blockedtitle": "Vapurpeak addaila", - "blockedtext": "Tujem vaporpeachem nanv vo IP pot'to addavpant aila.\n\nAddavop $1 hannem kelam.\nKaronn dilam tem $2.\n\n* Addavpachi survat: $8\n* Addavpachea somp’pacho vell: $6\n* Addavpak ievjila: $7\n\nTujean $1-ak vo dusrea [[{{MediaWiki:Grouppage-sysop}}|karbhariak]] addavnne bodol bhasabhas korunk sompork korunk zata. Tujean \"{{int:emailuser}}\" sobhavgunn vaprunk zaina kheriz ek void email pot'to tujea [[Special:Preferences|khatem posontint]] nischit kelea xivai ani tuka tem vaporpak addavnk na zalear. Tuzo chalont IP pot'to asa $3, ani addavnnecheo ank #$5 asa. Soglleo voileo bariksanno tum kortai tea vicharant somavex kor.", + "blockedtext": "Tujem vaporpeachem nanv vo IP pot'to addavpant aila.\n\nAddavop $1 hannem kelam.\nKaronn dilam tem $2.\n\n* Addavpachi survat: $8\n* Addavop sompovpacho vell: $6\n* Addavpak ievjila: $7\n\nTujean $1-ak vo dusrea [[{{MediaWiki:Grouppage-sysop}}|karbhariak]] addavnne bodol bhasabhas korunk sompork korunk zata. Tujean \"{{int:emailuser}}\" sobhavgunn vaprunk zaina kheriz ek void email pot'to tujea [[Special:Preferences|khatem posontint]] nischit kelea xivai ani tuka tem vaporpak addavnk na zalear. Tuzo chalont IP pot'to asa $3, ani addavnnecheo ank #$5 asa. Soglleo voileo bariksanno tum kortai tea vicharant somavex kor.", "blockednoreason": "Kainch karonn diunk na", "loginreqtitle": "Sotrorombh gorjechem", "loginreqlink": "sotrorombh kor", @@ -401,7 +401,7 @@ "permissionserrors": "Porvangechi chuk", "permissionserrorstext-withaction": "$2, hem korpak tuka porvangi na, {{PLURAL:$1|hea karnnak lagon|hea karnnank lagun}}:", "recreate-moveddeleted-warn": "Xittkavnni: Tum ek pan porot rochtai jem fattim kadun udoilelem.\n\nPanacho sompadon korop sarkem zalear dhean di.\nPan kadoupachem ani halovpachem sotr, sovloti khatir hangasor dilelem asa:", - "moveddeleted-notice": "Hem pan kadun udoilelem asa.\nPanachea kadun udounechi, rakhpachi, ani hallovnechi sotr sondorba khatir sokoil dilea.", + "moveddeleted-notice": "Hem pan kaddun udoilelem asa.\nPanachem kaddun uddovpachem, rakhpachem, ani halovpachem sotr sondhorba khatir sokoil dila.", "content-model-wikitext": "wikimozkur", "content-model-text": "Sado mozkur", "post-expand-template-inclusion-warning": "Chotrai: Sancho zoddpacho akar chod vhodlem asa.\nThodde sache zoddchenant.", @@ -423,7 +423,7 @@ "page_last": "akhirchem", "histlegend": "Frk nivoddni: Jeo uzollneo tuka comparar korunk zai, tenche fudle ''radio'' butao petoi ani ''Enter'' nazalear khalcho butao dab.
\nVivron: ({{int:cur}}) = halinchi uzollnie borobor forok, ({{int:last}}) = adli uzollnie borobor forok, {{int:minoreditletter}} = dhaktem bodol.", "history-fieldset-title": "Uzollnneo chall", - "history-show-deleted": "Fokot uzollnni kadun udoilelem", + "history-show-deleted": "Fokot uzollnni kadun uddoilolem", "histfirst": "sogleavon adhlem", "histlast": "sogleavon novem", "history-feed-title": "Uzollnniancho itihas", @@ -438,7 +438,7 @@ "revdel-restore": "Disnnem bodol", "pagehist": "Panacho itihas", "mergehistory-reason": "Karonn:", - "mergelog": "Vilin korpacho sotr", + "mergelog": "Vilin korpachem sotr", "revertmerge": "Doxim kor", "history-title": "\"$1\" hachea uzollnnecho itihas", "difference-title": "\"$1\"-chea avrutint ontor", @@ -493,7 +493,7 @@ "prefs-help-email": "Email potto sokticho na, pun tum gupitutor visroxi zalear gupitutor punorsthapon korunk email pottechi goroz podta.", "prefs-help-email-others": "Tujean dusreank tujea vapurpeacho panar vo bhasabhasache panar aslele eke email duve vorvim tuje xim sompork korunk diunk zata.\nDusre tuje xim sompork kortat tednam tuzo email potto tankam kollchenam.", "userrights-user-editname": "Ek vapurpeachem nanv ghal:", - "group-bot": "Robotam", + "group-bot": "Robottam", "group-sysop": "Karbhari", "group-all": "(soglle)", "grouppage-bot": "{{ns:project}}:Robotam", @@ -501,7 +501,7 @@ "right-move": "Panam haloi", "right-writeapi": "Borovpeache API-cho upeog", "newuserlogpage": "Vapurpi rochnnechem sotr", - "rightslog": "Vaporpeachea hokancho sotr", + "rightslog": "Vaporpeachea hokanchem sotr", "action-edit": "hem pan sudar", "action-createaccount": "hem vaporpeachem khatem roch", "nchanges": "$1 {{PLURAL:$1|bodol}}", @@ -639,7 +639,7 @@ "rc-change-size-new": "$1 {{PLURAL:$1|byte|byti}} bodol kel'lea uprant", "rc-enhanced-expand": "Bariksann dakhoi", "rc-enhanced-hide": "Bariksann lipoi", - "rc-old-title": "orombhant rochloli \"$1\" hea nanvan‎.", + "rc-old-title": "arombhant rochloli \"$1\" hea nanvan‎", "recentchangeslinked": "Sombondit bodol", "recentchangeslinked-feed": "Sombondit bodol", "recentchangeslinked-toolbox": "Sombondit bodol", @@ -699,7 +699,7 @@ "longpages": "Lamb panam", "protectedpages-filters": "Challnneo:", "listusers": "Vaporpeanchi volleri", - "usercreated": "$3 hannem $1 disa $2 vaztam rochlelem", + "usercreated": "$3 hannem $1 disa $2 vorancher rochlolem", "newpages": "Novim panam", "move": "Zago bodol", "pager-newer-n": "{{PLURAL:$1|novem 1|novim $1}}", @@ -710,8 +710,8 @@ "specialloguserlabel": "Korpi:", "speciallogtitlelabel": "Mokh (mathallo vo {{ns:user}}:vapurpeachem nanv):", "log": "Sotram", - "all-logs-page": "Soglle bhousache sotram", - "alllogstext": "{{SITENAME}} hacheo sogllea uplobdh sotranchi ektthaim dakhovnni.\nTujean tuzo dekhavo ornum ieta ek sotracho prokar vinchun, vaporpeachem nanv (vhodle and dhakte okxora modem forok podta), vo porinnam zalolem pan (hanga-ui vhodle and dhakte okxora modem forok podta).‎", + "all-logs-page": "Sogllim bhousachim sotram", + "alllogstext": "{{SITENAME}} hacheo sogllea uplobdh sotranchi ektthaim dakhovnni.\nTujean tuzo dekhavo ornum ieta ek sotracho prokar vinchun, vaporpeachem nanv (vhoddlea ani dhakttea okxora modem forok poddtta), vo porinnam zalolem pan (hangai vhoddle and dhaktte okxora modem forok poddtta).", "logempty": "Sotran zullpi nog nant.‎", "allpages": "Sogllim panam", "nextpage": "Fuddlem pan ($1)", @@ -719,7 +719,7 @@ "allpagesfrom": "Hanga thavn suru zatelea panank dakhoi:", "allarticles": "Sogllim panam", "allpagessubmit": "Voch", - "allpages-hide-redirects": "Punornirdexonam lipoi", + "allpages-hide-redirects": "Punornirdexona lipoi", "categories": "Vorg", "sp-deletedcontributions-contribs": "iogdan", "linksearch-ns": "Nanv-tholl:", @@ -737,9 +737,9 @@ "watch": "Nodor dovor", "watchthispage": "Hea panar dixtt dovor", "unwatch": "Nodor kadd", - "watchlist-details": "Tujea Sadurvollerint {{PLURAL:$1|$1 pan asa|$1 panam asat}} (te-bhair ulovpachim panam asat).", + "watchlist-details": "Tujea Sadurvollerint {{PLURAL:$1|$1 pan asa|$1 panam asat}} (tea-bhair ulovpachim panam asat).", "wlheader-showupdated": "Tujea fatle bhette san bodol'lean tim panam '''datt''' dakhoileant.", - "wlnote": "Sokoil {{PLURAL:$1|ho nimanno bodol|hem nimanneo $1 bodol}} nimannea {{PLURAL:$2|horan|$2 horanim}}, $3, $4 porian.‎", + "wlnote": "Sokoil {{PLURAL:$1|ho nimanno bodol|hem nimanneo $1 bodol}} nimannea {{PLURAL:$2|voran|$2 voramni}}, $3, $4 porian.‎", "watchlist-options": "Sadurvollericheo poryay", "watching": "Disht dovortanv...", "unwatching": "Disht kaddthanv...", @@ -794,13 +794,13 @@ "linkshere": "Sokoilim panam $2 ak zoddtat:", "nolinkshere": "Khoincheim pan $2 ak zoddna.", "isredirect": "punornirdexon pan", - "istemplate": "Durasth-somaves", + "istemplate": "durasth-somaves", "isimage": "faylicho duvo", "whatlinkshere-prev": "{{PLURAL:$1|adlem|adlem $1}}", "whatlinkshere-next": "{{PLURAL:$1|fuddlem|fuddlim $1}}", "whatlinkshere-links": "← duve", - "whatlinkshere-hideredirs": "$1 punornirdexonam", - "whatlinkshere-hidetrans": "$1 durasth-somaveso", + "whatlinkshere-hideredirs": "$1 punornirdexona", + "whatlinkshere-hidetrans": "$1 durasth-somavex", "whatlinkshere-hidelinks": "$1 duve", "whatlinkshere-hideimages": "$1 faylinche duve", "whatlinkshere-filters": "Challnio", @@ -813,7 +813,7 @@ "contribslink": "yogdan", "blocklogpage": "addavnnechem sotr", "blocklogentry": "[[$1]] addailelem $2 asun vellacho ont: $3", - "reblock-logentry": "addavpachem bosovp bodol’lam [[$1]] hache khatir sompacho vell dilam $2 $3‎", + "reblock-logentry": "addavpachem bosovp bodol’lam [[$1]] hache khatir sompovpacho vell dila $2 $3‎", "block-log-flags-nocreate": "Khatem rochop opatr kelam", "proxyblocker": "Protinidhi-sirvidor addavpi‎", "move-page": "$1 haloi", @@ -833,7 +833,7 @@ "allmessagesdefault": "Default sondex mozkur", "thumbnail-more": "Vhodlem kor", "thumbnail_error": "Lhan-imaz toiar kortana chuk zali. Karonn: $1", - "importlogpage": "Aiatacho sotr", + "importlogpage": "Aiatachem sotr", "tooltip-pt-userpage": "{{GENDER:|Tujem vaporpeachem}} pan", "tooltip-pt-mytalk": "{{GENDER:|Tumchem}} bhasabhasachem pan", "tooltip-pt-preferences": "{{GENDER:|Tumcheo}} avddi", @@ -877,7 +877,7 @@ "tooltip-ca-nstab-special": "Hem ek kherit pan, ani hem bodlunk zaina", "tooltip-ca-nstab-project": "Prokolpachem pan polloi", "tooltip-ca-nstab-image": "Faylichem pan polloi", - "tooltip-ca-nstab-mediawiki": "Iontronacho sondex polloi", + "tooltip-ca-nstab-mediawiki": "Iontronnacho sondex polloi", "tooltip-ca-nstab-template": "Sancho polloi", "tooltip-ca-nstab-category": "Vorgachem pan polloi", "tooltip-minoredit": "Haka ek kirkoll sudharop mhunn khunnay", @@ -890,7 +890,7 @@ "tooltip-rollback": "\"Kovllop\" hea panak nimannea yogdan korpean kello (kelle) bodol eka kollant portota.", "tooltip-undo": "\"Rodd' kor\" sudharop portita ani sudharopak Zholok ritin ukodta. Tem saran karon zoddunk dita.", "tooltip-summary": "Mottvo sar ghal", - "simpleantispam-label": "Spam-virudh topasni.\nHem bhori nakai!", + "simpleantispam-label": "Spam-virudh topasnni.\nHem bhor nakai!", "pageinfo-title": "\"$1\" ‎khatir mhaiti", "pageinfo-header-basic": "Mull mhaiti‎", "pageinfo-header-edits": "Bodolacho itihas", @@ -898,11 +898,11 @@ "pageinfo-header-properties": "Panache gunndhorm", "pageinfo-display-title": "Manddlolem mathallem", "pageinfo-default-sort": "Default arin manddunk chavi", - "pageinfo-length": "Panachi lambai (bayt-ant)‎", + "pageinfo-length": "Panachi lambai (baytt-ant)‎", "pageinfo-article-id": "Panacho ank", "pageinfo-language": "Panachea mozkurachi bhas", "pageinfo-content-model": "Panachea mozkuracho nomuno", - "pageinfo-robot-policy": "Robotam koddlean suchien ghalop", + "pageinfo-robot-policy": "Robotam koddlean suchent ghalop", "pageinfo-robot-index": "Porvangi asa", "pageinfo-robot-noindex": "Porvangi nam", "pageinfo-watchers": "Panacher dixtt dovortoleancho ankddo", @@ -924,7 +924,7 @@ "pageinfo-toolboxlink": "Panachi mahiti", "pageinfo-contentpage": "Ek mozkurachem pan koxem dhorpant ailam‎", "pageinfo-contentpage-yes": "Hoi", - "patrol-log-page": "Paro korpeacho sotr", + "patrol-log-page": "Paro korpachem sotr", "previousdiff": "← Adlo bodol", "nextdiff": "Fuddlem bodol →", "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|pan|panam}}", @@ -956,9 +956,9 @@ "redirect": "Fayl, vaporpi, pan, uzollnni vo sotr ank‎ vorvim punornirdexon kor", "redirect-summary": "Hem vixex pan punornirdexit korta eka faylik (faylichem nanv dilear), eke panak (uziollnecho ank vo panacho ank dilear), ek vaporpeachem panak (eke vaporpeache ank dilear), vo ek sotr nond (sotrachem ank dilear). Vapor: [[{{#Special:Redirect}}/file/Dekhik.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], vo [[{{#Special:Redirect}}/logid/186]].", "redirect-submit": "Voch‎", - "redirect-lookup": "Suchien polloi:", + "redirect-lookup": "Suchint polloi:", "redirect-value": "Mol:", - "redirect-user": "Vaporpeacho ank", + "redirect-user": "Vaporpeancho ank", "redirect-page": "Panacho ank", "redirect-revision": "Panachi uzollnni", "redirect-file": "Faylichem nanv", @@ -974,12 +974,12 @@ "htmlform-title-not-exists": "$1 ostitvant na.", "logentry-delete-delete": "$1, hannem {{GENDER:$2|kadun udoile}} pan $3", "logentry-delete-restore": "$1 hannem {{GENDER:$2|porot haddlam}} pan $3 ($4)‎", - "logentry-delete-revision": "$1 hannem {{PLURAL:$5|uzolliechem}} disnem $3, hea panar {{GENDER:$2|bodol’la}}: $4‎", + "logentry-delete-revision": "$1 hannem {{PLURAL:$5|uzollnnechem}} disnnem $3, hea panar {{GENDER:$2|bodol’la}}: $4‎", "revdelete-content-hid": "mozkur lipoila", "logentry-move-move": "$1, hannem $3 panak $4 {{GENDER:$2|haloilea}}", "logentry-move-move-noredirect": "$1, hannem pan $3 savn $4 {{GENDER:$2|haloilam}} punornirdexon dorinastanam‎", "logentry-move-move_redir": "$1 hannem pan $3 savn $4 {{GENDER:$2|haloilolo}} punornirdexonavoir", - "logentry-patrol-patrol-auto": "$1-an $3, hea panachem $4, hea uzollniecho paro kelam mhonn apoap {{GENDER:$2|khunnailam}}.", + "logentry-patrol-patrol-auto": "$1-an $3, hea panachem $4, hea uzollnnecho paro kelam mhonn apoap {{GENDER:$2|khunnailam}}.", "logentry-newusers-create": "Vapurpeacho kont $1 {{GENDER:$2|rochlam}}", "logentry-newusers-autocreate": "Vaporpeachem khatem $1 apoap {{GENDER:$2|rochun}} ailem", "logentry-upload-upload": "$1-an $3 {{GENDER:$2|upload kela}}", diff --git a/languages/i18n/hr.json b/languages/i18n/hr.json index 94945ffbe9..d04622afcc 100644 --- a/languages/i18n/hr.json +++ b/languages/i18n/hr.json @@ -931,6 +931,7 @@ "diff-multi-manyusers": "({{PLURAL:$1|Nije prikazana jedna međuinačica|Nisu prikazane $1 međuinačice|Nije prikazano $1 međuinačica}} više od {{PLURAL:$2|jednog|$2|$2}} suradnika)", "difference-missing-revision": "{{PLURAL:$2|Uređivanje|$2 uređivanja}} sljedeće šifre ($1) ne {{PLURAL:$2|postoji|postoje}}.\n\nOvo je obično uzrokovano kada kliknete na zastarjelu poveznicu na stranice koja je obrisana.\nViše informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} evidenciji brisanja].", "searchresults": "Rezultati pretrage", + "search-filter-title-prefix": "Pretraži samo stranice koje počinju prefiksom »$1«", "searchresults-title": "Rezultati pretrage za \"$1\"", "titlematches": "Pronađene stranice prema naslovu", "textmatches": "Pronađene stranice prema tekstu članka", diff --git a/languages/i18n/ja.json b/languages/i18n/ja.json index 8d7fdc17b6..8bd7bc052e 100644 --- a/languages/i18n/ja.json +++ b/languages/i18n/ja.json @@ -2419,9 +2419,9 @@ "sessionfailure": "ログインのセッションに問題が発生しました。\nセッション乗っ取りを防ぐため、操作を取り消しました。\nフォームを再送信してください。", "changecontentmodel": "ページのコンテンツ・モデルの変更", "changecontentmodel-legend": "コンテンツモデルを変更", - "changecontentmodel-title-label": "ページ名", + "changecontentmodel-title-label": "ページ名:", "changecontentmodel-current-label": "現在のコンテンツモデル", - "changecontentmodel-model-label": "新しい コンテンツ モデル", + "changecontentmodel-model-label": "新しい コンテンツ モデル:", "changecontentmodel-reason-label": "理由:", "changecontentmodel-submit": "変更", "changecontentmodel-success-title": "コンテンツ・モデルは変更されました", diff --git a/languages/i18n/mk.json b/languages/i18n/mk.json index e63cd39aee..410e3abded 100644 --- a/languages/i18n/mk.json +++ b/languages/i18n/mk.json @@ -816,6 +816,7 @@ "undo-norev": "Измената не можеше да биде вратена бидејќи не постои или била избришана.", "undo-nochange": "Се чини дека измената (уредувањето) е веќе вратена.", "undo-summary": "Откажано уредувањето $1 на уредникот [[Special:Contribs/$2|$2]] ([[User talk:$2|разговор]])", + "undo-summary-anon": "Отповикај ја преработката $1 на [[Special:Contributions/$2|$2]]", "undo-summary-username-hidden": "Поништи ја преработката $1 на скриен корисник", "cantcreateaccount-text": "Создавањето на корисничка сметка од оваа IP-адреса ($1) е блокирано од страна на [[User:$3|$3]].\n\nОбразложението дадено од страна на $3 е $2", "cantcreateaccount-range-text": "Создавањето на сметки од IP-адреси во опсегот $1 каде спаѓа вашата IP-адреса ($4) е блокирано од корисникот [[User:$3|$3]].\n\n$3 ја наведе следнава причина: $2", @@ -2332,7 +2333,8 @@ "cantrollback": "Уредувањето не може да се отповика.\nПоследниот уредник е воедно и единствениот автор на страницата.", "alreadyrolled": "Не може да се отповика последното уредување на страницата „[[:$1]]“ извршено од [[User:$2|$2]] ([[User talk:$2|разговор]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nнекој друг веќе ја изменил или отповикал страницата.\n\nПоследното уредување го изврши [[User:$3|$3]] ([[User talk:$3|разговор]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).", "editcomment": "Коментарот на уредувањето беше: $1.", - "revertpage": "Отстрането уредувањето на [[Special:Contributions/$2|$2]] ([[User talk:$2|разговор]]), вратено на последната верзија на [[User:$1|$1]]", + "revertpage": "Отповикани уредувањата на [[Special:Contributions/$2|$2]] ([[User talk:$2|разговор]]), враќајќи на последната преработка на [[User:$1|$1]]", + "revertpage-anon": "Отповикани уредувања на [[Special:Contributions/$2|$2]], враќајќи на последната преработка на [[User:$1|$1]]", "revertpage-nouser": "Вратени уредувања од скриен корисник на последната преработка на {{GENDER:$1|[[User:$1|$1]]}}", "rollback-success": "Откажани уредувањата на {{GENDER:$3|$1}};\nвратено на последната верзија на {{GENDER:$4|$2}}.", "sessionfailure-title": "Седницата не успеа", diff --git a/languages/i18n/pl.json b/languages/i18n/pl.json index 25d7bf1063..04fec53574 100644 --- a/languages/i18n/pl.json +++ b/languages/i18n/pl.json @@ -101,7 +101,8 @@ "Railfail536", "Vlad5250", "CiaPan", - "BadDog" + "BadDog", + "Rail" ] }, "tog-underline": "Podkreślenie linków:", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 478a52eb77..a33608f5fb 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -257,6 +257,7 @@ "tog-useeditwarning": "Used as label for the checkbox in [[Special:Preferences#mw-prefsection-editing|Special:Preferences]].", "tog-prefershttps": "Toggle option used in [[Special:Preferences]] that indicates if the user wants to use a secure connection when logged in", "tog-showrollbackconfirmation": "Toggle option used in [[Special:Preferences]] to enable/disable rollback confirmation prompt. Should be visible only to users with rollback rights.", + "tog-requireemail": "Toggle option used in [[Special:Preferences]]. Should be only visible to users who have confirmed their email address.\n\nSee also: {{msg-mw|prefs-help-requireemail}}", "underline-always": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"always underline links\", there are also options {{msg-mw|Underline-never}} and {{msg-mw|Underline-default}}.\n\n{{Gender}}\n{{Identical|Always}}", "underline-never": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"never underline links\", there are also options {{msg-mw|Underline-always}} and {{msg-mw|Underline-default}}.\n\n{{Gender}}\n{{Identical|Never}}", "underline-default": "Used in [[Special:Preferences#mw-prefsection-rendering|Preferences]].\n\nThis option means \"underline links as in your user skin or your browser\", there are also options {{msg-mw|Underline-never}} and {{msg-mw|Underline-always}}.\n\n{{Gender}}\n{{Identical|Browser default}}", @@ -1362,6 +1363,7 @@ "prefs-help-email": "Shown as explanation text on [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.\n\nSee also:\n* {{msg-mw|prefs-help-email-required|help}}\n* {{msg-mw|prefs-help-email-others|help}}\n* {{msg-mw|prefs-changeemail|link title}}\n* {{msg-mw|prefs-setemail|link title}}", "prefs-help-email-others": "This text is shown on account creation, below the description of the e-mail address field (which is optional).\n\nSee also:\n* {{msg-mw|prefs-help-email-required|help}}\n* {{msg-mw|prefs-help-email|help}}\n* {{msg-mw|prefs-changeemail|link title}}\n* {{msg-mw|prefs-setemail|link title}}", "prefs-help-email-required": "Shown as explanation text on [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.\n\nSee also:\n* {{msg-mw|prefs-help-email|help}}\n* {{msg-mw|prefs-help-email-others|help}}\n* {{msg-mw|prefs-changeemail|link title}}\n* {{msg-mw|prefs-setemail|link title}}", + "prefs-help-requireemail": "Shown as explanation text on [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.\n\nSee also: {{msg-mw|tog-requireemail}}", "prefs-info": "Header for the box giving basic information on the user account, displayed on the 'user profile' tab of the [[Special:Preferences|user preferences]] special page.\n{{Identical|Basic information}}", "prefs-i18n": "Field set legend for user preferences regarding the interface language", "prefs-signature": "{{Identical|Signature}}", @@ -2904,6 +2906,7 @@ "ipblocklist-legend": "Used as legend of the form in [[Special:BlockList]].\n\nSee also:\n* {{msg-mw|Ipblocklist-legend}}\n* {{msg-mw|Ipblocklist-submit}}", "blocklist-userblocks": "Used as the label for the multi-select checkbox in the form on [[Special:BlockList]].\n{{Related|Blocklist-blocks}}", "blocklist-tempblocks": "Used as the label for the multi-select checkbox in the form on [[Special:BlockList]].\n{{Related|Blocklist-blocks}}", + "blocklist-indefblocks": "Used as the label for the multi-select checkbox in the form on [[Special:BlockList]].\n{{Related|Blocklist-blocks}}", "blocklist-addressblocks": "Used as the label for the multi-select checkbox in the form on [[Special:BlockList]].\n{{Related|Blocklist-blocks}}", "blocklist-type": "Used as label for dropdown box in [[Special:BlockList]].", "blocklist-type-opt-all": "Used as option for dropdown box in [[Special:BlockList]]. This is the default option and indicates that \"all\" blocks will be listed\n{{Identical|All}}", diff --git a/languages/i18n/sv.json b/languages/i18n/sv.json index 5fe33ce23d..dcc686b910 100644 --- a/languages/i18n/sv.json +++ b/languages/i18n/sv.json @@ -869,6 +869,7 @@ "undo-norev": "Redigeringen kan inte göras ogjord eftersom den inte finns eller har raderats.", "undo-nochange": "Det verkar som att redigeringen redan har blivit ogjord.", "undo-summary": "Gör version $1 av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskussion]]) ogjord", + "undo-summary-anon": "Ångra sidversionen $1 av [[Special:Contributions/$2|$2]]", "undo-summary-username-hidden": "Gör version $1 av en dold användare ogjord", "cantcreateaccount-text": "[[User:$3|$3]] har blockerat den här IP-adressen ('''$1''') från att registrera konton.\n\nAnledningen till blockeringen var \"$2\".", "cantcreateaccount-range-text": "IP-adresserna i intervallet $1, som inkluderar din IP-adress ($4), har blockerats från att skapa konton av [[User:$3|$3]].\n\nAnledningen enligt $3 var $2", @@ -2383,6 +2384,7 @@ "alreadyrolled": "Det gick inte att rulla tillbaka den senaste redigeringen av [[User:$2|$2]] ([[User talk:$2|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) på sidan [[:$1|$1]]. Någon annan har redan rullat tillbaka eller redigerat sidan.\n\nSidan ändrades senast av [[User:$3|$3]] ([[User talk:$3|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]).", "editcomment": "Redigeringskommentaren var: $1.", "revertpage": "Återställde redigeringar av [[Special:Contributions/$2|$2]] ([[User talk:$2|användardiskussion]]) till senaste versionen av [[User:$1|$1]]", + "revertpage-anon": "Ångrade redigeringar av [[Special:Contributions/$2|$2]] till senaste sidversionen av [[User:$1|$1]]", "revertpage-nouser": "Återställde redigeringar av en dold användare till den senaste versionen av {{GENDER:$1|[[User:$1|$1]]}}", "rollback-success": "Återställde ändringar av {{GENDER:$3|$1}};\nändrade tillbaka till senaste versionen av {{GENDER:$4|$2}}.", "sessionfailure-title": "Sessionsfel", diff --git a/languages/i18n/szl.json b/languages/i18n/szl.json index cd35ab2e2e..8446ebd584 100644 --- a/languages/i18n/szl.json +++ b/languages/i18n/szl.json @@ -154,7 +154,7 @@ "morenotlisted": "Ńy je to kůmplytno lista", "mypage": "Zajta", "mytalk": "Dyskusyjŏ", - "anontalk": "Godka tygo IP", + "anontalk": "Dyskusyjŏ", "navigation": "Nawigacyjŏ", "and": " i", "faq": "FAQ", @@ -342,7 +342,7 @@ "filereadonlyerror": "Ńy idźe pomjyńać plika \"$1\" abo repozytorjum \"$2\" terozki je zawarte.\n\nAdministrator kery zawarł wćepał kůmyntorz: \"$3\".", "invalidtitle-knownnamespace": "Felerne mjano \"$3\" w przestrzeńy \"$2\".", "invalidtitle-unknownnamespace": "Felerne mjano ze ńyznomům nůmerům raumu mjan $1: \"$2\"", - "exception-nologin": "Ńy jest żeś zalogůwany", + "exception-nologin": "Niy je żeś wlogowany(ŏ)", "exception-nologin-text": "Prosza [[Special:Userlogin|zaloguj śe]] coby mjeć mogebność przejśćo do tyj zajty abo akcyji.", "virus-badscanner": "Felerno konfiguracyjo – ńyznany skaner antywirusowy ''$1''", "virus-scanfailed": "skanowańy ńyudone (feler $1)", @@ -456,7 +456,7 @@ "changepassword-success": "Twoje hasło zostoło půmyślńy půmjyńone!", "botpasswords": "Hasła bota", "resetpass_forbidden": "Ńy idźe sam půmjyńyć hasłůw.", - "resetpass-no-info": "Muśisz być zalogowany, coby uzyskoć bezpostrzedńi dostymp do tyj zajty.", + "resetpass-no-info": "Żeby mieć bezpostrzedni dostymp do tyj strōny, trzeba sie zalogować.", "resetpass-submit-loggedin": "Zmjyń hasło", "resetpass-submit-cancel": "Uodćepej", "resetpass-wrong-oldpass": "Felerne tymczasowe abo aktualne hasło.\nMożliwe co właśńy zmjyńiłżeś swoje hasło abo poprosiłżeś uo nowe tymczasowe hasło.", @@ -506,7 +506,7 @@ "showpreview": "Pokŏż podglōnd", "showdiff": "Pokŏż zmiany", "anoneditwarning": "Pozōr: Niy je żeś wlogowany(ŏ). Jak zrobisz jakeś zmiany, to Twoja adresa IP bydzie publicznie widać. Jeźli [$1 sie wlogujesz] abo [$2 stworzisz kōnto], to Twoje zmiany bydōm przipisane do kōnta społym ze inkszymi profitami.", - "anonpreviewwarning": "Ńy jeżeś zalogowany. Twój IP ausdruk uostańy spamjyntany, eli ty bydźesz sprowjać zajte.", + "anonpreviewwarning": "Niy je żeś wlogowany(ŏ). Spamiyntanie zmian zapisze twoja adresa IP we historyji edycyji tyj strōny.", "missingsummary": "'''Pozůr:''' Ńy wprowadźůł żeś uopisu pomjyńań. Kej go ńy chcesz wprowadzać, naćiś knefel Spamjyntej jeszcze roz.", "missingcommenttext": "Wkludź kōmyntŏrz niżyj.", "missingcommentheader": "'''Dej pozůr:''' Treść nagłůwka je blank - uzupełńij go! Jeli tego ńy zrobisz, Twůj kůmyntorz bydźe naszkryflony bez nagłůwka.", @@ -1014,7 +1014,7 @@ "upload": "Zaladuj zbiōr", "uploadbtn": "Prziślij zbiōr", "reuploaddesc": "Nazod do formulařa uod wćepywańo.", - "uploadnologin": "Ńy jest žeś zalogůwany", + "uploadnologin": "Niy je żeś wlogowany(ŏ)", "uploadnologintext": "Muśyš śe [[Special:UserLogin|zalůgować]] ńim wćepńeš pliki.", "upload_directory_missing": "Katalog lo wćepywanych plikůw ($1) ńy istńeje a serwer WWW ńy poradźi go utwořić.", "upload_directory_read_only": "Serwer ńy može škryflać do katalůgu ($1) kery je přeznačůny na wćepywane pliki.", @@ -1273,7 +1273,7 @@ "linksearch-text": "Idźe użyć symbola wjeloznacznygo „*”. Lů bajszpila „*.wikipedia.org” spowoduje sznupańy za wszyjstkimi linkůma kere prowadzům ku důmyńy „wikipedia.org” a jeij poddůmyn.
\nUobsůgiwane protokoły: $1", "linksearch-line": "$1 link na zajće $2", "linksearch-error": "Symbola wjeloznacznygo idźe użyć yno na anfangu mjana hosta.", - "listusersfrom": "Pokaž užytkowńikůw začynojůnc uod:", + "listusersfrom": "Pokŏż używŏczōw ôd:", "listusers-submit": "Uobejrzij", "listusers-noresult": "Ńy znejdźůno žodnygo užytkowńika.", "activeusers": "Lista aktywnych używŏczōw", @@ -1322,7 +1322,7 @@ "watchlistfor2": "{{GENDER:$1|Używŏcza|Używŏczki}} $1 $2", "nowatchlist": "Ńy ma žodnych pozycyji na liśće zajtůw, na kere dowoš pozůr.", "watchlistanontext": "$1 coby uobejřeć abo sprowjać elymynty listy zajtůw, na kere dowoš pozůr", - "watchnologin": "Ńy jest žeś zalůgowany", + "watchnologin": "Niy je żeś wlogowany(ŏ)", "addedwatchtext": "Zajta \"[[:$1]]\" zostoua dodano do Twojij [[Special:Watchlist|listy artiklůw, na kere dowoš pozůr]].\nNa tyi liśće bydźeš mjou rejer přišuych sprowjyń tyi zajty i jeji zajty godki, a mjano zajty bydźeš mjou škryflane '''tustym''' na [[Special:RecentChanges|liśće půmjyńanych na ůostatku]], cobyś mjou wygoda w jei pomjyńańa filować.", "removedwatchtext": "Artikel \"[[:$1]]\" zostou wyćepńjynty s [[Special:Watchlist|Twojij pozorlisty]].", "watch": "Ôbserwuj", @@ -1603,7 +1603,7 @@ "movepage-moved": "'''\"$1\" przećiśńjynto ku \"$2\"'''", "articleexists": "Artikel ze takym mjanym już je, abo mjano je złe.\nWybjer inksze mjano.", "cantmove-titleprotected": "Ńy możesz przećepnůńć zajty, beztuż co jeij nowe mjano je ńydozwolůne skuli zabezpjeczyńo przed utworzyńym", - "movetalk": "Przećiś godke, jak możno.", + "movetalk": "Przeniyś zwiōnzanõ ze strōnōm dyskusyjõ", "move-subpages": "Přećepńij podzajty", "move-talk-subpages": "Jeli je to możliwe przekludź wszyjstke zajty godki podzajtůw", "movepage-page-exists": "Zajta $1 już istńeje a ńy idźe jeij autůmatyczńy nadszkryflać.", diff --git a/languages/i18n/tt-cyrl.json b/languages/i18n/tt-cyrl.json index 16310ebd58..bda0a4f001 100644 --- a/languages/i18n/tt-cyrl.json +++ b/languages/i18n/tt-cyrl.json @@ -1008,9 +1008,9 @@ "grouppage-suppress": "{{ns:project}}:Назирләр", "right-read": "Битләрне карау", "right-edit": "Битләрне үзгәртү", - "right-createpage": "битләр ясау (бәхәс булмаганнарын)", - "right-createtalk": "бәхәс битен ясау", - "right-createaccount": "яңа кулланучы хисап язмасын ясау", + "right-createpage": "Битләрне төзү (бәхәс битләре булмаган)", + "right-createtalk": "Бәхәс битләрен төзү", + "right-createaccount": "Яңа кулланучы хисап язмасын төзү", "right-minoredit": "\"кече төзәтмә\" тамгасын кую", "right-move": "Битләрне күчерү", "right-move-subpages": "Битләрне асбитләр белән бергә күчерү", diff --git a/languages/i18n/zh-hant.json b/languages/i18n/zh-hant.json index 208449b812..c2cd05bedd 100644 --- a/languages/i18n/zh-hant.json +++ b/languages/i18n/zh-hant.json @@ -2405,7 +2405,7 @@ "editcomment": "編輯摘要為:$1。", "revertpage": "已還原[[Special:Contributions/$2|$2]]([[User talk:$2|討論]])的編輯至最後由[[User:$1|$1]]所修訂的版本", "revertpage-nouser": "已還原隱藏使用者的編輯為最後 {{GENDER:$1|[[User:$1|$1]]}} 修訂的版本", - "rollback-success": "已還原 {{GENDER:$3|$1}} 所做的編輯;\n變更回由 {{GENDER:$4|$2}} 修訂的最後一個版本。", + "rollback-success": "已還原{{GENDER:$3|$1}}所做的編輯;變更回由{{GENDER:$4|$2}}修訂的最後一個版本。", "sessionfailure-title": "連線階段失敗", "sessionfailure": "您的登入連線階段似乎有問題,為了預防連線階段受到劫持攻擊,此動作已經被取消。請重新提交表單。", "changecontentmodel": "變更頁面的內容模型", diff --git a/maintenance/cleanupTitles.php b/maintenance/cleanupTitles.php index cad612266b..5602e39646 100644 --- a/maintenance/cleanupTitles.php +++ b/maintenance/cleanupTitles.php @@ -133,7 +133,7 @@ class TitleCleanup extends TableCleanup { * @param Title $title */ protected function moveInconsistentPage( $row, Title $title ) { - if ( $title->exists( Title::GAID_FOR_UPDATE ) + if ( $title->exists( Title::READ_LATEST ) || $title->getInterwiki() || !$title->canExist() ) { diff --git a/maintenance/install.php b/maintenance/install.php index 28a1746d9e..16b8ccf837 100644 --- a/maintenance/install.php +++ b/maintenance/install.php @@ -133,6 +133,8 @@ class CommandLineInstaller extends Maintenance { if ( !$envChecksOnly ) { $status = $installer->execute(); if ( !$status->isGood() ) { + $installer->showStatusMessage( $status ); + return false; } $installer->writeConfigurationFile( $this->getOption( 'confpath', $IP ) ); diff --git a/maintenance/storage/blobs.sql b/maintenance/storage/blobs.sql index 979e68a94e..e3c6c02bc3 100644 --- a/maintenance/storage/blobs.sql +++ b/maintenance/storage/blobs.sql @@ -1,6 +1,6 @@ -- Blobs table for external storage -CREATE TABLE /*$wgDBprefix*/blobs ( +CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/blobs ( blob_id integer UNSIGNED NOT NULL AUTO_INCREMENT, blob_text longblob, PRIMARY KEY (blob_id) diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php index 060f32768d..37625cf2f0 100644 --- a/maintenance/storage/checkStorage.php +++ b/maintenance/storage/checkStorage.php @@ -232,7 +232,7 @@ class CheckStorage { } foreach ( $externalConcatBlobs as $cluster => $xBlobIds ) { $blobIds = array_keys( $xBlobIds ); - $extDb =& $this->dbStore->getSlave( $cluster ); + $extDb =& $this->dbStore->getReplica( $cluster ); $blobsTable = $this->dbStore->getTable( $extDb ); $res = $extDb->select( $blobsTable, [ 'blob_id' ], @@ -433,7 +433,7 @@ class CheckStorage { foreach ( $externalConcatBlobs as $cluster => $oldIds ) { $blobIds = array_keys( $oldIds ); - $extDb =& $this->dbStore->getSlave( $cluster ); + $extDb =& $this->dbStore->getReplica( $cluster ); $blobsTable = $this->dbStore->getTable( $extDb ); $headerLength = strlen( self::CONCAT_HEADER ); $res = $extDb->select( $blobsTable, diff --git a/maintenance/userOptions.php b/maintenance/userOptions.php index 98f1c24a86..2f8941f3da 100644 --- a/maintenance/userOptions.php +++ b/maintenance/userOptions.php @@ -110,11 +110,10 @@ The new option is NOT validated.' ); $ret[$option][$userValue] = ( $ret[$option][$userValue] ?? 0 ) + 1; } } else { - foreach ( $defaultOptions as $name => $defaultValue ) { $userValue = $user->getOption( $name ); if ( $userValue != $defaultValue ) { - $ret[$option][$userValue] = ( $ret[$option][$userValue] ?? 0 ) + 1; + $ret[$name][$userValue] = ( $ret[$name][$userValue] ?? 0 ) + 1; } } } diff --git a/tests/phpunit/includes/TitleTest.php b/tests/phpunit/includes/TitleTest.php index 18faeea3fc..82ccb9ae32 100644 --- a/tests/phpunit/includes/TitleTest.php +++ b/tests/phpunit/includes/TitleTest.php @@ -768,12 +768,12 @@ class TitleTest extends MediaWikiTestCase { $this->assertEquals( false, $title->exists(), - 'exists() should rely on link cache unless GAID_FOR_UPDATE is used' + 'exists() should rely on link cache unless READ_LATEST is used' ); $this->assertEquals( true, - $title->exists( Title::GAID_FOR_UPDATE ), - 'exists() should re-query database when GAID_FOR_UPDATE is used' + $title->exists( Title::READ_LATEST ), + 'exists() should re-query database when READ_LATEST is used' ); } diff --git a/tests/phpunit/includes/api/ApiDeleteTest.php b/tests/phpunit/includes/api/ApiDeleteTest.php index c68954c077..d2bbc1f837 100644 --- a/tests/phpunit/includes/api/ApiDeleteTest.php +++ b/tests/phpunit/includes/api/ApiDeleteTest.php @@ -67,7 +67,7 @@ class ApiDeleteTest extends ApiTestCase { $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null ); $jobs->execute(); - $this->assertFalse( Title::newFromText( $name )->exists( Title::GAID_FOR_UPDATE ) ); + $this->assertFalse( Title::newFromText( $name )->exists( Title::READ_LATEST ) ); } public function testDeleteNonexistent() { diff --git a/tests/phpunit/includes/api/ApiRevisionDeleteTest.php b/tests/phpunit/includes/api/ApiRevisionDeleteTest.php index a4ca8a10a1..5dcea65fa7 100644 --- a/tests/phpunit/includes/api/ApiRevisionDeleteTest.php +++ b/tests/phpunit/includes/api/ApiRevisionDeleteTest.php @@ -22,8 +22,7 @@ class ApiRevisionDeleteTest extends ApiTestCase { // Make a few edits for us to play with for ( $i = 1; $i <= 5; $i++ ) { self::editPage( self::$page, MWCryptRand::generateHex( 10 ), 'summary' ); - $this->revs[] = Title::newFromText( self::$page ) - ->getLatestRevID( Title::GAID_FOR_UPDATE ); + $this->revs[] = Title::newFromText( self::$page )->getLatestRevID( Title::READ_LATEST ); } } diff --git a/tests/phpunit/includes/session/SessionManagerTest.php b/tests/phpunit/includes/session/SessionManagerTest.php index cd0867d2ef..b2f525d741 100644 --- a/tests/phpunit/includes/session/SessionManagerTest.php +++ b/tests/phpunit/includes/session/SessionManagerTest.php @@ -259,14 +259,14 @@ class SessionManagerTest extends MediaWikiTestCase { try { $manager->getSessionForRequest( $request ); $this->fail( 'Expcected exception not thrown' ); - } catch ( \OverflowException $ex ) { + } catch ( SessionOverflowException $ex ) { $this->assertStringStartsWith( 'Multiple sessions for this request tied for top priority: ', $ex->getMessage() ); - $this->assertCount( 2, $ex->sessionInfos ); - $this->assertContains( $request->info1, $ex->sessionInfos ); - $this->assertContains( $request->info2, $ex->sessionInfos ); + $this->assertCount( 2, $ex->getSessionInfos() ); + $this->assertContains( $request->info1, $ex->getSessionInfos() ); + $this->assertContains( $request->info2, $ex->getSessionInfos() ); } $this->assertFalse( $request->unpersist1 ); $this->assertFalse( $request->unpersist2 ); diff --git a/tests/phpunit/includes/specials/QueryAllSpecialPagesTest.php b/tests/phpunit/includes/specials/QueryAllSpecialPagesTest.php index 58f83decba..a95d43c002 100644 --- a/tests/phpunit/includes/specials/QueryAllSpecialPagesTest.php +++ b/tests/phpunit/includes/specials/QueryAllSpecialPagesTest.php @@ -22,7 +22,7 @@ class QueryAllSpecialPagesTest extends MediaWikiTestCase { /** List query pages that can not be tested automatically */ protected $manualTest = [ - LinkSearchPage::class + SpecialLinkSearch::class ]; /** diff --git a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php index 4ecb813f8c..08225a697d 100644 --- a/tests/phpunit/includes/specials/SpecialMIMESearchTest.php +++ b/tests/phpunit/includes/specials/SpecialMIMESearchTest.php @@ -2,15 +2,15 @@ /** * @group Database - * @covers MIMEsearchPage + * @covers SpecialMIMESearch */ class SpecialMIMESearchTest extends MediaWikiTestCase { - /** @var MIMEsearchPage */ + /** @var SpecialMIMESearch */ private $page; function setUp() { - $this->page = new MIMEsearchPage; + $this->page = new SpecialMIMESearch; $context = new RequestContext(); $context->setTitle( Title::makeTitle( NS_SPECIAL, 'MIMESearch' ) ); $context->setRequest( new FauxRequest() ); diff --git a/tests/phpunit/includes/specials/SpecialShortPagesTest.php b/tests/phpunit/includes/specials/SpecialShortPagesTest.php new file mode 100644 index 0000000000..8891d0db8f --- /dev/null +++ b/tests/phpunit/includes/specials/SpecialShortPagesTest.php @@ -0,0 +1,43 @@ +setMwGlobals( [ + 'wgShortPagesNamespaceBlacklist' => $blacklistNS, + 'wgContentNamespaces' => $contentNS + ] ); + $this->setTemporaryHook( 'ShortPagesQuery', function () { + // empty hook handler + } ); + + $page = new SpecialShortPages(); + $queryInfo = $page->getQueryInfo(); + + $this->assertArrayHasKey( 'conds', $queryInfo ); + $this->assertArrayHasKey( 'page_namespace', $queryInfo[ 'conds' ] ); + $this->assertEquals( $expectedNS, $queryInfo[ 'conds' ][ 'page_namespace' ] ); + } + + public function provideGetQueryInfoRespectsContentNs() { + return [ + [ [ NS_MAIN, NS_FILE ], [], [ NS_MAIN, NS_FILE ] ], + [ [ NS_MAIN, NS_TALK ], [ NS_FILE ], [ NS_MAIN, NS_TALK ] ], + [ [ NS_MAIN, NS_FILE ], [ NS_FILE ], [ NS_MAIN ] ], + // NS_MAIN namespace is always forced + [ [], [ NS_FILE ], [ NS_MAIN ] ] + ]; + } + +} diff --git a/tests/phpunit/includes/specials/SpecialShortpagesTest.php b/tests/phpunit/includes/specials/SpecialShortpagesTest.php deleted file mode 100644 index 236c5c4e58..0000000000 --- a/tests/phpunit/includes/specials/SpecialShortpagesTest.php +++ /dev/null @@ -1,43 +0,0 @@ -setMwGlobals( [ - 'wgShortPagesNamespaceBlacklist' => $blacklistNS, - 'wgContentNamespaces' => $contentNS - ] ); - $this->setTemporaryHook( 'ShortPagesQuery', function () { - // empty hook handler - } ); - - $page = new ShortPagesPage(); - $queryInfo = $page->getQueryInfo(); - - $this->assertArrayHasKey( 'conds', $queryInfo ); - $this->assertArrayHasKey( 'page_namespace', $queryInfo[ 'conds' ] ); - $this->assertEquals( $expectedNS, $queryInfo[ 'conds' ][ 'page_namespace' ] ); - } - - public function provideGetQueryInfoRespectsContentNs() { - return [ - [ [ NS_MAIN, NS_FILE ], [], [ NS_MAIN, NS_FILE ] ], - [ [ NS_MAIN, NS_TALK ], [ NS_FILE ], [ NS_MAIN, NS_TALK ] ], - [ [ NS_MAIN, NS_FILE ], [ NS_FILE ], [ NS_MAIN ] ], - // NS_MAIN namespace is always forced - [ [], [ NS_FILE ], [ NS_MAIN ] ] - ]; - } - -} diff --git a/tests/phpunit/includes/specials/SpecialUncategorizedCategoriesTest.php b/tests/phpunit/includes/specials/SpecialUncategorizedCategoriesTest.php new file mode 100644 index 0000000000..daccd274a9 --- /dev/null +++ b/tests/phpunit/includes/specials/SpecialUncategorizedCategoriesTest.php @@ -0,0 +1,63 @@ +getMockBuilder( RequestContext::class )->getMock(); + $mockContext->method( 'msg' )->willReturn( $msg ); + $special = new SpecialUncategorizedCategories(); + $special->setContext( $mockContext ); + $this->assertEquals( [ + 'tables' => [ + 0 => 'page', + 1 => 'categorylinks', + ], + 'fields' => [ + 'namespace' => 'page_namespace', + 'title' => 'page_title', + 'value' => 'page_title', + ], + 'conds' => [ + 0 => 'cl_from IS NULL', + 'page_namespace' => 14, + 'page_is_redirect' => 0, + ] + $expected, + 'join_conds' => [ + 'categorylinks' => [ + 0 => 'LEFT JOIN', + 1 => 'cl_from = page_id', + ], + ], + ], $special->getQueryInfo() ); + } + + public function provideTestGetQueryInfoData() { + return [ + [ + "* Stubs\n* Test\n* *\n* * test123", + [ 1 => "page_title not in ( 'Stubs','Test','*','*_test123' )" ] + ], + [ + "Stubs\n* Test\n* *\n* * test123", + [ 1 => "page_title not in ( 'Test','*','*_test123' )" ] + ], + [ + "* StubsTest\n* *\n* * test123", + [ 1 => "page_title not in ( 'StubsTest','*','*_test123' )" ] + ], + [ "", [] ], + [ "\n\n\n", [] ], + [ "\n", [] ], + [ "Test\n*Test2", [ 1 => "page_title not in ( 'Test2' )" ] ], + [ "Test", [] ], + [ "*Test\nTest2", [ 1 => "page_title not in ( 'Test' )" ] ], + [ "Test\nTest2", [] ], + ]; + } +} diff --git a/tests/phpunit/includes/specials/UncategorizedCategoriesPageTest.php b/tests/phpunit/includes/specials/UncategorizedCategoriesPageTest.php deleted file mode 100644 index 80bd365f35..0000000000 --- a/tests/phpunit/includes/specials/UncategorizedCategoriesPageTest.php +++ /dev/null @@ -1,63 +0,0 @@ -getMockBuilder( RequestContext::class )->getMock(); - $mockContext->method( 'msg' )->willReturn( $msg ); - $special = new UncategorizedCategoriesPage(); - $special->setContext( $mockContext ); - $this->assertEquals( [ - 'tables' => [ - 0 => 'page', - 1 => 'categorylinks', - ], - 'fields' => [ - 'namespace' => 'page_namespace', - 'title' => 'page_title', - 'value' => 'page_title', - ], - 'conds' => [ - 0 => 'cl_from IS NULL', - 'page_namespace' => 14, - 'page_is_redirect' => 0, - ] + $expected, - 'join_conds' => [ - 'categorylinks' => [ - 0 => 'LEFT JOIN', - 1 => 'cl_from = page_id', - ], - ], - ], $special->getQueryInfo() ); - } - - public function provideTestGetQueryInfoData() { - return [ - [ - "* Stubs\n* Test\n* *\n* * test123", - [ 1 => "page_title not in ( 'Stubs','Test','*','*_test123' )" ] - ], - [ - "Stubs\n* Test\n* *\n* * test123", - [ 1 => "page_title not in ( 'Test','*','*_test123' )" ] - ], - [ - "* StubsTest\n* *\n* * test123", - [ 1 => "page_title not in ( 'StubsTest','*','*_test123' )" ] - ], - [ "", [] ], - [ "\n\n\n", [] ], - [ "\n", [] ], - [ "Test\n*Test2", [ 1 => "page_title not in ( 'Test2' )" ] ], - [ "Test", [] ], - [ "*Test\nTest2", [ 1 => "page_title not in ( 'Test' )" ] ], - [ "Test\nTest2", [] ], - ]; - } -}