From: Reedy Date: Sun, 14 Apr 2019 15:14:22 +0000 (+0100) Subject: Make most special pages class names match filename X-Git-Tag: 1.34.0-rc.0~296 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=dd71a77512db0b0e9a75724d34ee6878d806b8e9 Make most special pages class names match filename Change-Id: I3a9f932acb7d9cf44a984b5d97f9fbc6b8670f7d --- 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..2cebf99a48 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 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/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 2737e356a0..3f9c491576 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, @@ -282,8 +282,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/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..f2a3f80df1 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -29,7 +29,7 @@ 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; 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/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/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/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", [] ], - ]; - } -}