Whitelist existing violations, but enable the sniff to prevent
any new occurrences.
-->
- <exclude-pattern>*/includes/specials/SpecialMostinterwikis\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialAncientpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialBrokenRedirects\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialConfirmemail\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialDeadendpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialDeletedContributions\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialDoubleRedirects\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialEmailInvalidate\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialFewestrevisions\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialFileDuplicateSearch\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialLinkSearch\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListDuplicatedFiles\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListredirects\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialLonelypages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialLongpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMIMEsearch\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMediaStatistics\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMostcategories\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialMostimages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMostlinked\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMostlinkedcategories\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMostlinkedtemplates\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialMostrevisions\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialMovepage\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialNewimages\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialRandompage\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialShortpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUncategorizedcategories\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUncategorizedimages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUncategorizedpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUncategorizedtemplates\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUnusedcategories\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUnusedimages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUnusedtemplates\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialUnwatchedpages\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialUserrights\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialWantedcategories\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialWantedfiles\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialWantedpages\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialWantedtemplates\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialWithoutinterwiki\.php</exclude-pattern>
<exclude-pattern>*/maintenance/CodeCleanerGlobalsPass.inc</exclude-pattern>
<exclude-pattern>*/maintenance/archives/upgradeLogging\.php</exclude-pattern>
<exclude-pattern>*/maintenance/benchmarks/bench_HTTP_HTTPS\.php</exclude-pattern>
* $wgDBOracleDRCP - If you must use persistent connections, set DBO_PERSISTENT
in the 'flags' field for servers in $wgDBServers (or $wgLBFactoryConf).
* $wgMemCachedDebug - Set the cache "debug" field in $wgObjectCaches instead.
+* $wgActorTableSchemaMigrationStage has been removed. Extension code for
+ MediaWiki 1.31+ finding it unset should treat it as being SCHEMA_COMPAT_NEW.
=== New user-facing features in 1.34 ===
* Special:Mute has been added as a quick way for users to block unwanted emails
GetBlockedStatus.
* ObjectFactory is available as a service. When used as a service, the object
specs can now specify needed DI services.
+* (T222388) Special pages can now be specified as an ObjectFactory spec,
+ allowing the construction of special pages that require services to be
+ injected in their constructor.
+* (T222388) API modules can now be specified as an ObjectFactory spec,
+ allowing the construction of modules that require services to be injected
+ in their constructor.
=== External library changes in 1.34 ===
=== Action API changes in 1.34 ===
* The 'recenteditcount' response property from action=query list=allusers,
deprecated in 1.25, has been removed.
+* (T60993) action=query list=filearchive no longer requires the 'deletedhistory'
+ user right.
=== Action API internal changes in 1.34 ===
+* The exception thrown in ApiModuleManager::getModule has been changed
+ from an MWException to an UnexpectedValueException, thrown by ObjectFactory.
+ ApiModuleManager::getModule now also throws InvalidArgumentExceptions when
+ ObjectFactory is presented with an invalid spec or incorrectly constructed
+ objects.
* …
=== Languages updated in 1.34 ===
* 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
should only be used to unblock a blocked user.
* Parameters for index.php from PATH_INFO, such as the title, are no longer
written to $_GET.
-* …
+* The selectFields() methods on classes LocalFile, ArchivedFile, OldLocalFile,
+ DatabaseBlock, and RecentChange, deprecated in 1.31, have been removed. Use
+ the corresponding getQueryInfo() methods instead.
+* The following methods on Revision, deprecated since 1.31, have been removed.
+ Use RevisionStore::getQueryInfo() or RevisionStore::getArchiveQueryInfo()
+ instead.
+ * Revision::userJoinCond()
+ * Revision::pageJoinCond()
+ * Revision::selectFields()
+ * Revision::selectArchiveFields()
+ * Revision::selectTextFields()
+ * Revision::selectPageFields()
+ * Revision::selectUserFields()
=== Deprecations in 1.34 ===
* The MWNamespace class is deprecated. Use NamespaceInfo.
or extend RevisionSearchResult.
* Skin::getSkinNameMessages() is deprecated and no longer used.
* The mediawiki.RegExp module is deprecated; use mw.util.escapeRegExp() instead.
+* Specifying a SpecialPage object for the list of special pages (either through
+ the SpecialPage_initList hook or by adding to $wgSpecialPages) is now
+ deprecated.
+* Use of ActorMigration with 'ar_user', 'img_user', 'oi_user', 'fa_user',
+ 'rc_user', 'log_user', and 'ipb_by' is deprecated. Queries should be adjusted
+ to use the corresponding actor fields directly. Note that use with
+ 'rev_user' is *not* deprecated at this time.
+* Specifying both the class and factory parameters for
+ ApiModuleManager::addModule is now deprecated. The ObjectFactory spec should
+ be used instead.
+* The UserIsHidden hook is deprecated. Use GetUserBlock instead, and add a
+ system block that hides the user.
=== Other changes in 1.34 ===
* …
// So extensions (and other code) can check whether they're running in API mode
define( 'MW_API', true );
+define( 'MW_ENTRY_POINT', 'api' );
require __DIR__ . '/includes/WebStart.php';
'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',
'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',
'ComposerJson' => __DIR__ . '/includes/libs/composer/ComposerJson.php',
'ComposerLock' => __DIR__ . '/includes/libs/composer/ComposerLock.php',
'ComposerPackageModifier' => __DIR__ . '/includes/composer/ComposerPackageModifier.php',
+ 'ComposerPhpunitXmlCoverageEdit' => __DIR__ . '/includes/composer/ComposerPhpunitXmlCoverageEdit.php',
'ComposerVendorHtaccessCreator' => __DIR__ . '/includes/composer/ComposerVendorHtaccessCreator.php',
'ComposerVersionNormalizer' => __DIR__ . '/includes/composer/ComposerVersionNormalizer.php',
'CompressOld' => __DIR__ . '/maintenance/storage/compressOld.php',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
"autoload": {
"psr-0": {
"ComposerHookHandler": "includes/composer",
- "ComposerVendorHtaccessCreator": "includes/composer"
+ "ComposerVendorHtaccessCreator": "includes/composer",
+ "ComposerPhpunitXmlCoverageEdit":"includes/composer"
}
},
"autoload-dev": {
"phpunit": "phpunit",
"phpunit:unit": "phpunit --colors=always --testsuite=core:unit,extensions:unit,skins:unit",
"phpunit:integration": "phpunit --colors=always --testsuite=core:integration,extensions:integration,skins:integration",
- "phpunit:coverage": "phpunit --testsuite=core:unit --exclude-group Dump,Broken"
+ "phpunit:coverage": "phpunit --testsuite=core:unit --exclude-group Dump,Broken",
+ "phpunit:coverage-edit": "ComposerPhpunitXmlCoverageEdit::onEvent"
},
"config": {
"optimize-autoloader": true,
},
"SpecialPages": {
"type": "object",
- "description": "SpecialPages implemented in this extension (mapping of page name to class name)"
+ "description": "SpecialPages implemented in this extension (mapping of page name to class name or to ObjectFactory spec)"
},
"AutoloadNamespaces": {
"type": "object",
},
"SpecialPages": {
"type": "object",
- "description": "SpecialPages implemented in this extension (mapping of page name to class name)"
+ "description": "SpecialPages implemented in this extension (mapping of page name to class name or to ObjectFactory spec)"
},
"AutoloadNamespaces": {
"type": "object",
false if a UserGetRights hook might remove the named right.
$right: The user right being checked
-'UserIsHidden': Check if the user's name should be hidden. See User::isHidden().
+'UserIsHidden': DEPRECATED since 1.34 - use GetUserBlock instead, and add a
+system block that hides the user. Check if the user's name should be hidden.
+See User::isHidden().
$user: User in question.
&$hidden: Set true if the user's name should be hidden.
*/
define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
+define( 'MW_ENTRY_POINT', 'img_auth' );
require __DIR__ . '/includes/WebStart.php';
# Set action base paths so that WebRequest::getPathInfo()
* This class handles the logic for the actor table migration.
*
* This is not intended to be a long-term part of MediaWiki; it will be
- * deprecated and removed along with $wgActorTableSchemaMigrationStage.
+ * deprecated and removed once actor migration is complete.
*
* @since 1.31
+ * @since 1.34 Use with 'ar_user', 'img_user', 'oi_user', 'fa_user',
+ * 'rc_user', 'log_user', and 'ipb_by' is deprecated. Callers should
+ * reference the corresponding actor fields directly.
*/
class ActorMigration {
/**
* Constant for extensions to feature-test whether $wgActorTableSchemaMigrationStage
- * expects MIGRATION_* or SCHEMA_COMPAT_*
+ * (in MW <1.34) expects MIGRATION_* or SCHEMA_COMPAT_*
*/
const MIGRATION_STAGE_SCHEMA_COMPAT = 1;
*/
private static $formerTempTables = [];
+ /**
+ * Define fields that are deprecated for use with this class.
+ * @var (string|null)[] Keys are '$key', value is null for soft deprecation
+ * or a string naming the deprecated version for hard deprecation.
+ */
+ private static $deprecated = [
+ 'ar_user' => null, // 1.34
+ 'img_user' => null, // 1.34
+ 'oi_user' => null, // 1.34
+ 'fa_user' => null, // 1.34
+ 'rc_user' => null, // 1.34
+ 'log_user' => null, // 1.34
+ 'ipb_by' => null, // 1.34
+ ];
+
+ /**
+ * Define fields that are removed for use with this class.
+ * @var string[] Keys are '$key', value is the MediaWiki version in which
+ * use was removed.
+ */
+ private static $removed = [];
+
/**
* Define fields that use non-standard mapping
* @var array Keys are the user id column name, values are arrays with two
return MediaWikiServices::getInstance()->getActorMigration();
}
+ /**
+ * Issue deprecation warning/error as appropriate.
+ * @param string $key
+ */
+ private static function checkDeprecation( $key ) {
+ if ( isset( self::$removed[$key] ) ) {
+ throw new InvalidArgumentException(
+ "Use of " . static::class . " for '$key' was removed in MediaWiki " . self::$removed[$key]
+ );
+ }
+ if ( !empty( self::$deprecated[$key] ) ) {
+ wfDeprecated( static::class . " for '$key'", self::$deprecated[$key], false, 3 );
+ }
+ }
+
/**
* Return an SQL condition to test if a user field is anonymous
* @param string $field Field name or SQL fragment
* @phan-return array{tables:string[],fields:string[],joins:array}
*/
public function getJoin( $key ) {
+ self::checkDeprecation( $key );
+
if ( !isset( $this->joinCache[$key] ) ) {
$tables = [];
$fields = [];
* @return array to merge into `$values` to `IDatabase->update()` or `$a` to `IDatabase->insert()`
*/
public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
+ self::checkDeprecation( $key );
+
if ( isset( self::$tempTables[$key] ) ) {
throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
}
* and extra fields needed for the temp table.
*/
public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
+ self::checkDeprecation( $key );
+
if ( isset( self::$formerTempTables[$key] ) ) {
wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
} elseif ( !isset( self::$tempTables[$key] ) ) {
* All tables and joins are aliased, so `+` is safe to use.
*/
public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
+ self::checkDeprecation( $key );
+
$tables = [];
$conds = [];
$joins = [];
*/
$wgXmlDumpSchemaVersion = XML_DUMP_SCHEMA_VERSION_10;
-/**
- * Actor table schema migration stage.
- *
- * Use the SCHEMA_COMPAT_XXX flags. Supported values:
- * - SCHEMA_COMPAT_OLD
- * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
- * - SCHEMA_COMPAT_NEW
- *
- * Note that reading the old and new schema at the same time is not supported
- * in 1.32, but was (with significant query performance issues) in 1.31.
- *
- * @since 1.31
- * @since 1.32 changed allowed flags
- * @var int An appropriate combination of SCHEMA_COMPAT_XXX flags.
- */
-$wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_NEW;
-
/**
* Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
* or namespaces.
$mp = new MovePage( $oldSubpage, $newSubpage );
$method = $checkPermissions ? 'moveIfAllowed' : 'move';
+ /** @var Status $status */
$status = $mp->$method( $user, $reason, $createRedirect, $changeTags );
if ( $status->isOK() ) {
$status->setResult( true, $newSubpage->getPrefixedText() );
Hooks::run( 'TitleMoveStarting', [ $this->oldTitle, $this->newTitle, $user ] );
- $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE );
+ $pageid = $this->oldTitle->getArticleID( Title::READ_LATEST );
$protected = $this->oldTitle->isProtected();
// Do the actual move; if this fails, it will throw an MWException(!)
*
* @note This class uses setter methods instead of a constructor so that
* it can be compatible with PHP 4, PHP 5 and PHP 7 (without warnings).
- *
- * @class
*/
class PHPVersionCheck {
/* @var string The number of the MediaWiki version used. */
return $rec ? new Revision( $rec ) : null;
}
- /**
- * Return the value of a select() JOIN conds array for the user table.
- * This will get user table rows for logged-in users.
- * @since 1.19
- * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead.
- * @return array
- */
- public static function userJoinCond() {
- global $wgActorTableSchemaMigrationStage;
-
- wfDeprecated( __METHOD__, '1.31' );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's
- // no way the join it's trying to do can work once the old fields
- // aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
- }
-
- /**
- * Return the value of a select() page conds array for the page table.
- * This will assure that the revision(s) are not orphaned from live pages.
- * @since 1.19
- * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead.
- * @return array
- */
- public static function pageJoinCond() {
- wfDeprecated( __METHOD__, '1.31' );
- return [ 'JOIN', [ 'page_id = rev_page' ] ];
- }
-
- /**
- * Return the list of revision fields that should be selected to create
- * a new revision.
- * @deprecated since 1.31, use RevisionStore::getQueryInfo() instead.
- * @return array
- */
- public static function selectFields() {
- global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
- global $wgMultiContentRevisionSchemaMigrationStage;
-
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->rev_user or $row->rev_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- if ( !( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->rev_text_id or $row->rev_content_model and we can't give it
- // useful values here once those aren't being written anymore,
- // and may not exist at all.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__ . ' when $wgMultiContentRevisionSchemaMigrationStage '
- . 'does not have SCHEMA_COMPAT_WRITE_OLD set.'
- );
- }
-
- wfDeprecated( __METHOD__, '1.31' );
-
- $fields = [
- 'rev_id',
- 'rev_page',
- 'rev_text_id',
- 'rev_timestamp',
- 'rev_user_text',
- 'rev_user',
- 'rev_actor' => 'NULL',
- 'rev_minor_edit',
- 'rev_deleted',
- 'rev_len',
- 'rev_parent_id',
- 'rev_sha1',
- ];
-
- $fields += CommentStore::getStore()->getFields( 'rev_comment' );
-
- if ( $wgContentHandlerUseDB ) {
- $fields[] = 'rev_content_format';
- $fields[] = 'rev_content_model';
- }
-
- return $fields;
- }
-
- /**
- * Return the list of revision fields that should be selected to create
- * a new revision from an archive row.
- * @deprecated since 1.31, use RevisionStore::getArchiveQueryInfo() instead.
- * @return array
- */
- public static function selectArchiveFields() {
- global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
- global $wgMultiContentRevisionSchemaMigrationStage;
-
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->ar_user or $row->ar_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- if ( !( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->ar_text_id or $row->ar_content_model and we can't give it
- // useful values here once those aren't being written anymore,
- // and may not exist at all.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__ . ' when $wgMultiContentRevisionSchemaMigrationStage '
- . 'does not have SCHEMA_COMPAT_WRITE_OLD set.'
- );
- }
-
- wfDeprecated( __METHOD__, '1.31' );
-
- $fields = [
- 'ar_id',
- 'ar_page_id',
- 'ar_rev_id',
- 'ar_text_id',
- 'ar_timestamp',
- 'ar_user_text',
- 'ar_user',
- 'ar_actor' => 'NULL',
- 'ar_minor_edit',
- 'ar_deleted',
- 'ar_len',
- 'ar_parent_id',
- 'ar_sha1',
- ];
-
- $fields += CommentStore::getStore()->getFields( 'ar_comment' );
-
- if ( $wgContentHandlerUseDB ) {
- $fields[] = 'ar_content_format';
- $fields[] = 'ar_content_model';
- }
- return $fields;
- }
-
- /**
- * Return the list of text fields that should be selected to read the
- * revision text
- * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'text' ] ) instead.
- * @return array
- */
- public static function selectTextFields() {
- wfDeprecated( __METHOD__, '1.31' );
- return [
- 'old_text',
- 'old_flags'
- ];
- }
-
- /**
- * Return the list of page fields that should be selected from page table
- * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'page' ] ) instead.
- * @return array
- */
- public static function selectPageFields() {
- wfDeprecated( __METHOD__, '1.31' );
- return [
- 'page_namespace',
- 'page_title',
- 'page_id',
- 'page_latest',
- 'page_is_redirect',
- 'page_len',
- ];
- }
-
- /**
- * Return the list of user fields that should be selected from user table
- * @deprecated since 1.31, use RevisionStore::getQueryInfo( [ 'user' ] ) instead.
- * @return array
- */
- public static function selectUserFields() {
- wfDeprecated( __METHOD__, '1.31' );
- return [ 'user_name' ];
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new revision object.
$comment = CommentStoreComment::newUnsavedComment( $summary, null );
- $title = Title::newFromID( $pageId, Title::GAID_FOR_UPDATE );
+ $title = Title::newFromID( $pageId, Title::READ_LATEST );
if ( $title === null ) {
return null;
}
$canUseTitleNewFromId = ( $pageId !== null && $pageId > 0 && $this->dbDomain === false );
list( $dbMode, $dbOptions ) = DBAccessObjectUtils::getDBOptions( $queryFlags );
- $titleFlags = ( $dbMode == DB_MASTER ? Title::GAID_FOR_UPDATE : 0 );
// Loading by ID is best, but Title::newFromID does not support that for foreign IDs.
if ( $canUseTitleNewFromId ) {
+ $titleFlags = ( $dbMode == DB_MASTER ? Title::READ_LATEST : 0 );
// TODO: better foreign title handling (introduce TitleFactory)
$title = Title::newFromID( $pageId, $titleFlags );
if ( $title ) {
return [
'ActorMigration' => function ( MediaWikiServices $services ) : ActorMigration {
- return new ActorMigration(
- $services->getMainConfig()->get( 'ActorTableSchemaMigrationStage' )
- );
+ return new ActorMigration( SCHEMA_COMPAT_NEW );
},
'BadFileLookup' => function ( MediaWikiServices $services ) : BadFileLookup {
return new SpecialPageFactory(
new ServiceOptions(
SpecialPageFactory::$constructorOptions, $services->getMainConfig() ),
- $services->getContentLanguage()
+ $services->getContentLanguage(),
+ $services->getObjectFactory()
);
},
die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' );
}
+// Define MW_ENTRY_POINT if it's not already, so that config code can check the
+// value without using defined()
+if ( !defined( 'MW_ENTRY_POINT' ) ) {
+ /**
+ * The entry point, which may be either the script filename without the
+ * file extension, or "cli" for maintenance scripts, or "unknown" for any
+ * entry point that does not set the constant.
+ */
+ define( 'MW_ENTRY_POINT', 'unknown' );
+}
+
// Start the autoloader, so that extensions can derive classes from core files
require_once "$IP/includes/AutoLoader.php";
self::getCacheKey( $cache, $page->getTitle(), $page->getLatest() ),
WANObjectCache::TTL_WEEK,
function ( $oldValue, &$ttl, &$setOpts ) use ( $page, $config, $fname, $services ) {
- global $wgActorTableSchemaMigrationStage;
-
$title = $page->getTitle();
$id = $title->getArticleID();
$dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
$setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $tables = [ 'revision_actor_temp' ];
- $field = 'revactor_actor';
- $pageField = 'revactor_page';
- $tsField = 'revactor_timestamp';
- $joins = [];
- } else {
- $tables = [ 'revision' ];
- $field = 'rev_user_text';
- $pageField = 'rev_page';
- $tsField = 'rev_timestamp';
- $joins = [];
- }
+ $tables = [ 'revision_actor_temp' ];
+ $field = 'revactor_actor';
+ $pageField = 'revactor_page';
+ $tsField = 'revactor_timestamp';
+ $joins = [];
$watchedItemStore = $services->getWatchedItemStore();
}
$this->mResult->setErrorFormatter( $this->getErrorFormatter() );
- $this->mModuleMgr = new ApiModuleManager( $this );
+ $this->mModuleMgr = new ApiModuleManager(
+ $this,
+ MediaWikiServices::getInstance()->getObjectFactory()
+ );
$this->mModuleMgr->addModules( self::$Modules, 'action' );
$this->mModuleMgr->addModules( $config->get( 'APIModules' ), 'action' );
$this->mModuleMgr->addModules( self::$Formats, 'format' );
* @since 1.21
*/
+use MediaWiki\MediaWikiServices;
+use Wikimedia\ObjectFactory;
+
/**
* This class holds a list of modules and handles instantiation
*
* @var array[]
*/
private $mModules = [];
+ /**
+ * @var ObjectFactory
+ */
+ private $objectFactory;
/**
* Construct new module manager
+ *
* @param ApiBase $parentModule Parent module instance will be used during instantiation
+ * @param ObjectFactory|null $objectFactory Object factory to use when instantiating modules
*/
- public function __construct( ApiBase $parentModule ) {
+ public function __construct( ApiBase $parentModule, ObjectFactory $objectFactory = null ) {
$this->mParent = $parentModule;
+ $this->objectFactory = $objectFactory ?? MediaWikiServices::getInstance()->getObjectFactory();
}
/**
* Add a list of modules to the manager. Each module is described
- * by a module spec.
- *
- * Each module spec is an associative array containing at least
- * the 'class' key for the module's class, and optionally a
- * 'factory' key for the factory function to use for the module.
+ * by an ObjectFactory spec.
*
- * That factory function will be called with two parameters,
- * the parent module (an instance of ApiBase, usually ApiMain)
- * and the name the module was registered under. The return
- * value must be an instance of the class given in the 'class'
- * field.
+ * This simply calls `addModule()` for each module in `$modules`.
*
- * For backward compatibility, the module spec may also be a
- * simple string containing the module's class name. In that
- * case, the class' constructor will be called with the parent
- * module and module name as parameters, as described above.
- *
- * Examples for defining module specs:
- *
- * @code
- * $modules['foo'] = 'ApiFoo';
- * $modules['bar'] = [
- * 'class' => ApiBar::class,
- * 'factory' => function( $main, $name ) { ... }
- * ];
- * $modules['xyzzy'] = [
- * 'class' => ApiXyzzy::class,
- * 'factory' => [ XyzzyFactory::class, 'newApiModule' ]
- * ];
- * @endcode
- *
- * @param array $modules A map of ModuleName => ModuleSpec; The ModuleSpec
- * is either a string containing the module's class name, or an associative
- * array (see above for details).
+ * @see ApiModuleManager::addModule()
+ * @param array $modules A map of ModuleName => ModuleSpec
* @param string $group Which group modules belong to (action,format,...)
*/
public function addModules( array $modules, $group ) {
foreach ( $modules as $name => $moduleSpec ) {
- if ( is_array( $moduleSpec ) ) {
- $class = $moduleSpec['class'];
- $factory = ( $moduleSpec['factory'] ?? null );
- } else {
- $class = $moduleSpec;
- $factory = null;
- }
-
- $this->addModule( $name, $group, $class, $factory );
+ $this->addModule( $name, $group, $moduleSpec );
}
}
* classes who wish to add their own modules to their lexicon or override the
* behavior of inherent ones.
*
+ * ObjectFactory is used to instantiate the module when needed. The parent module
+ * (`$parentModule` from `__construct()`) and the `$name` are passed as extraArgs.
+ *
+ * @since 1.34, accepts an ObjectFactory spec as the third parameter. The old calling convention,
+ * passing a class name as parameter #3 and an optional factory callable as parameter #4, is
+ * deprecated.
* @param string $name The identifier for this module.
* @param string $group Name of the module group
- * @param string $class The class where this module is implemented.
- * @param callable|null $factory Callback for instantiating the module.
+ * @param string|array $spec The ObjectFactory spec for instantiating the module,
+ * or a class name to instantiate.
+ * @param callable|null $factory Callback for instantiating the module (deprecated).
*
* @throws InvalidArgumentException
*/
- public function addModule( $name, $group, $class, $factory = null ) {
+ public function addModule( $name, $group, $spec, $factory = null ) {
if ( !is_string( $name ) ) {
throw new InvalidArgumentException( '$name must be a string' );
}
throw new InvalidArgumentException( '$group must be a string' );
}
- if ( !is_string( $class ) ) {
- throw new InvalidArgumentException( '$class must be a string' );
- }
+ if ( is_string( $spec ) ) {
+ $spec = [
+ 'class' => $spec
+ ];
- if ( $factory !== null && !is_callable( $factory ) ) {
- throw new InvalidArgumentException( '$factory must be a callable (or null)' );
+ if ( is_callable( $factory ) ) {
+ wfDeprecated( __METHOD__ . ' with $class and $factory', '1.34' );
+ $spec['factory'] = $factory;
+ }
+ } elseif ( !is_array( $spec ) ) {
+ throw new InvalidArgumentException( '$spec must be a string or an array' );
+ } elseif ( !isset( $spec['class'] ) ) {
+ throw new InvalidArgumentException( '$spec must define a class name' );
}
$this->mGroups[$group] = null;
- $this->mModules[$name] = [ $group, $class, $factory ];
+ $this->mModules[$name] = [ $group, $spec ];
}
/**
return null;
}
- list( $moduleGroup, $moduleClass, $moduleFactory ) = $this->mModules[$moduleName];
+ list( $moduleGroup, $spec ) = $this->mModules[$moduleName];
if ( $group !== null && $moduleGroup !== $group ) {
return null;
return $this->mInstances[$moduleName];
} else {
// new instance
- $instance = $this->instantiateModule( $moduleName, $moduleClass, $moduleFactory );
+ $instance = $this->instantiateModule( $moduleName, $spec );
if ( !$ignoreCache ) {
// cache this instance in case it is needed later
* Instantiate the module using the given class or factory function.
*
* @param string $name The identifier for this module.
- * @param string $class The class where this module is implemented.
- * @param callable|null $factory Callback for instantiating the module.
+ * @param array $spec The ObjectFactory spec for instantiating the module.
*
- * @throws MWException
+ * @throws UnexpectedValueException
* @return ApiBase
*/
- private function instantiateModule( $name, $class, $factory = null ) {
- if ( $factory !== null ) {
- // create instance from factory
- $instance = call_user_func( $factory, $this->mParent, $name );
-
- if ( !$instance instanceof $class ) {
- throw new MWException(
- "The factory function for module $name did not return an instance of $class!"
- );
- }
- } else {
- // create instance from class name
- $instance = new $class( $this->mParent, $name );
- }
-
- return $instance;
+ private function instantiateModule( $name, $spec ) {
+ return $this->objectFactory->createObject(
+ $spec,
+ [
+ 'extraArgs' => [
+ $this->mParent,
+ $name
+ ],
+ 'assertClass' => $spec['class']
+ ]
+ );
}
/**
return array_keys( $this->mModules );
}
$result = [];
- foreach ( $this->mModules as $name => $grpCls ) {
- if ( $grpCls[0] === $group ) {
+ foreach ( $this->mModules as $name => $groupAndSpec ) {
+ if ( $groupAndSpec[0] === $group ) {
$result[] = $name;
}
}
*/
public function getNamesWithClasses( $group = null ) {
$result = [];
- foreach ( $this->mModules as $name => $grpCls ) {
- if ( $group === null || $grpCls[0] === $group ) {
- $result[$name] = $grpCls[1];
+ foreach ( $this->mModules as $name => $groupAndSpec ) {
+ if ( $group === null || $groupAndSpec[0] === $group ) {
+ $result[$name] = $groupAndSpec[1]['class'];
}
}
*/
public function getClassName( $module ) {
if ( isset( $this->mModules[$module] ) ) {
- return $this->mModules[$module][1];
+ return $this->mModules[$module][1]['class'];
}
return false;
$services = MediaWikiServices::getInstance();
$contLang = $services->getContentLanguage();
- foreach ( $titles as $title ) {
+ $titleObjects = [];
+ foreach ( $titles as $index => $title ) {
if ( is_string( $title ) ) {
try {
$titleObj = Title::newFromTextThrow( $title, $this->mDefaultNamespace );
} else {
$titleObj = $title;
}
+
+ $titleObjects[$index] = $titleObj;
+ }
+
+ // Get gender information
+ $genderCache = $services->getGenderCache();
+ $genderCache->doTitlesArray( $titleObjects, __METHOD__ );
+
+ foreach ( $titleObjects as $index => $titleObj ) {
+ $title = is_string( $titles[$index] ) ? $titles[$index] : false;
$unconvertedTitle = $titleObj->getPrefixedText();
$titleWasConverted = false;
if ( $titleObj->isExternal() ) {
) {
// Language::findVariantLink will modify titleText and titleObj into
// the canonical variant if possible
- $titleText = is_string( $title ) ? $title : $titleObj->getPrefixedText();
+ $titleText = $title !== false ? $title : $titleObj->getPrefixedText();
$contLang->findVariantLink( $titleText, $titleObj );
$titleWasConverted = $unconvertedTitle !== $titleObj->getPrefixedText();
}
if ( $titleWasConverted ) {
$this->mConvertedTitles[$unconvertedTitle] = $titleObj->getPrefixedText();
// In this case the page can't be Special.
- if ( is_string( $title ) && $title !== $unconvertedTitle ) {
+ if ( $title !== false && $title !== $unconvertedTitle ) {
$this->mNormalizedTitles[$title] = $unconvertedTitle;
}
- } elseif ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) {
+ } elseif ( $title !== false && $title !== $titleObj->getPrefixedText() ) {
$this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
}
-
- // Need gender information
- if (
- $services->getNamespaceInfo()->
- hasGenderDistinction( $titleObj->getNamespace() )
- ) {
- $usernames[] = $titleObj->getText();
- }
}
- // Get gender information
- $genderCache = $services->getGenderCache();
- $genderCache->doQuery( $usernames, __METHOD__ );
return $linkBatch;
}
* @file
*/
+use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\IDatabase;
/**
public function __construct( ApiMain $main, $action ) {
parent::__construct( $main, $action );
- $this->mModuleMgr = new ApiModuleManager( $this );
+ $this->mModuleMgr = new ApiModuleManager(
+ $this,
+ MediaWikiServices::getInstance()->getObjectFactory()
+ );
// Allow custom modules to be added in LocalSettings.php
$config = $this->getConfig();
* @return void
*/
protected function run( ApiPageSet $resultPageSet = null ) {
- global $wgActorTableSchemaMigrationStage;
-
$db = $this->getDB();
$params = $this->extractRequestParams( false );
$services = MediaWikiServices::getInstance();
$tsField = 'rev_timestamp';
$idField = 'rev_id';
$pageField = 'rev_page';
- if ( $params['user'] !== null &&
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ) {
+ if ( $params['user'] !== null ) {
// The query is probably best done using the actor_timestamp index on
// revision_actor_temp. Use the denormalized fields from that table.
$tsField = 'revactor_timestamp';
}
public function execute() {
- global $wgActorTableSchemaMigrationStage;
-
$params = $this->extractRequestParams();
$activeUserDays = $this->getConfig()->get( 'ActiveUserDays' );
] ] );
// Actually count the actions using a subquery (T66505 and T66507)
- $tables = [ 'recentchanges' ];
- $joins = [];
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_OLD ) {
- $userCond = 'rc_user_text = user_name';
- } else {
- $tables[] = 'actor';
- $joins['actor'] = [ 'JOIN', 'rc_actor = actor_id' ];
- $userCond = 'actor_user = user_id';
- }
+ $tables = [ 'recentchanges', 'actor' ];
+ $joins = [
+ 'actor' => [ 'JOIN', 'rc_actor = actor_id' ],
+ ];
$timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
$this->addFields( [
'recentactions' => '(' . $db->selectSQLText(
$tables,
'COUNT(*)',
[
- $userCond,
+ 'actor_user = user_id',
'rc_type != ' . $db->addQuotes( RC_EXTERNAL ), // no wikidata
'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ),
'rc_timestamp >= ' . $db->addQuotes( $timestamp ),
}
public function execute() {
- global $wgActorTableSchemaMigrationStage;
-
$db = $this->getDB();
$params = $this->extractRequestParams();
$this->requireMaxOneParameter( $params, 'group', 'excludegroup', 'rights', 'excluderights' );
$result = $this->getResult();
$revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
- // For SCHEMA_COMPAT_READ_NEW, target indexes on the
- // revision_actor_temp table, otherwise on the revision table.
- $pageField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ? 'revactor_page' : 'rev_page';
- $idField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ? 'revactor_actor' : $revQuery['fields']['rev_user'];
- $countField = ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ? 'revactor_actor' : $revQuery['fields']['rev_user_text'];
+ // Target indexes on the revision_actor_temp table.
+ $pageField = 'revactor_page';
+ $idField = 'revactor_actor';
+ $countField = 'revactor_actor';
// First, count anons
$this->addTables( $revQuery['tables'] );
}
public function execute() {
- // Before doing anything at all, let's check permissions
- $this->checkUserRightsAny( 'deletedhistory' );
-
$user = $this->getUser();
$db = $this->getDB();
$commentStore = CommentStore::getStore();
$fld_bitdepth = isset( $prop['bitdepth'] );
$fld_archivename = isset( $prop['archivename'] );
+ if ( $fld_description &&
+ !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' )
+ ) {
+ $this->dieWithError( 'apierror-cantview-deleted-description', 'permissiondenied' );
+ }
+ if ( $fld_metadata &&
+ !$this->getPermissionManager()->userHasAnyRight( $user, 'deletedtext', 'undelete' )
+ ) {
+ $this->dieWithError( 'apierror-cantview-deleted-metadata', 'permissiondenied' );
+ }
+
$fileQuery = ArchivedFile::getQueryInfo();
$this->addTables( $fileQuery['tables'] );
$this->addFields( $fileQuery['fields'] );
}
if ( $sha1 ) {
$this->addWhereFld( 'fa_sha1', $sha1 );
+ // Paranoia: avoid brute force searches (T19342)
+ if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedtext' ) ) {
+ $bitmask = File::DELETED_FILE;
+ } elseif ( !$this->getPermissionManager()
+ ->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
+ ) {
+ $bitmask = File::DELETED_FILE | File::DELETED_RESTRICTED;
+ } else {
+ $bitmask = 0;
+ }
+ if ( $bitmask ) {
+ $this->addWhere( $this->getDB()->bitAnd( 'fa_deleted', $bitmask ) . " != $bitmask" );
+ }
}
}
- // Exclude files this user can't view.
- if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedtext' ) ) {
- $bitmask = File::DELETED_FILE;
- } elseif ( !$this->getPermissionManager()
- ->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
- ) {
- $bitmask = File::DELETED_FILE | File::DELETED_RESTRICTED;
- } else {
- $bitmask = 0;
- }
- if ( $bitmask ) {
- $this->addWhere( $this->getDB()->bitAnd( 'fa_deleted', $bitmask ) . " != $bitmask" );
- }
-
$limit = $params['limit'];
$this->addOption( 'LIMIT', $limit + 1 );
$sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
break;
}
+ $canViewFile = RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_FILE, $user );
+
$file = [];
$file['id'] = (int)$row->fa_id;
$file['name'] = $row->fa_name;
$file['userid'] = (int)$row->fa_user;
$file['user'] = $row->fa_user_text;
}
- if ( $fld_sha1 ) {
+ if ( $fld_sha1 && $canViewFile ) {
$file['sha1'] = Wikimedia\base_convert( $row->fa_sha1, 36, 16, 40 );
}
if ( $fld_timestamp ) {
$file['timestamp'] = wfTimestamp( TS_ISO_8601, $row->fa_timestamp );
}
- if ( $fld_size || $fld_dimensions ) {
+ if ( ( $fld_size || $fld_dimensions ) && $canViewFile ) {
$file['size'] = $row->fa_size;
$pageCount = ArchivedFile::newFromRow( $row )->pageCount();
$file['height'] = $row->fa_height;
$file['width'] = $row->fa_width;
}
- if ( $fld_mediatype ) {
+ if ( $fld_mediatype && $canViewFile ) {
$file['mediatype'] = $row->fa_media_type;
}
- if ( $fld_metadata ) {
+ if ( $fld_metadata && $canViewFile ) {
$file['metadata'] = $row->fa_metadata
? ApiQueryImageInfo::processMetaData( unserialize( $row->fa_metadata ), $result )
: null;
}
- if ( $fld_bitdepth ) {
+ if ( $fld_bitdepth && $canViewFile ) {
$file['bitdepth'] = $row->fa_bits;
}
- if ( $fld_mime ) {
+ if ( $fld_mime && $canViewFile ) {
$file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
}
if ( $fld_archivename && !is_null( $row->fa_archive_name ) ) {
}
protected function run( ApiPageSet $resultPageSet = null ) {
- global $wgActorTableSchemaMigrationStage;
-
$params = $this->extractRequestParams( false );
$revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$idField = 'rev_id';
$tsField = 'rev_timestamp';
$pageField = 'rev_page';
- if ( $params['user'] !== null &&
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ) {
+ if ( $params['user'] !== null ) {
// We're going to want to use the page_actor_timestamp index (on revision_actor_temp)
// so use that table's denormalized fields.
$idField = 'revactor_rev';
$fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
public function execute() {
- global $wgActorTableSchemaMigrationStage;
-
// Parse some parameters
$this->params = $this->extractRequestParams();
// a wiki with users "Test00000001" to "Test99999999"), use a
// generator with batched lookup and continuation.
$userIter = call_user_func( function () use ( $dbSecondary, $sort, $op, $fname ) {
- global $wgActorTableSchemaMigrationStage;
-
$fromName = false;
if ( !is_null( $this->params['continue'] ) ) {
$continue = explode( '|', $this->params['continue'] );
do {
$from = $fromName ? "$op= " . $dbSecondary->addQuotes( $fromName ) : false;
-
- // For the new schema, pull from the actor table. For the
- // old, pull from rev_user.
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $res = $dbSecondary->select(
- 'actor',
- [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
- array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
- $fname,
- [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
- );
- } else {
- $res = $dbSecondary->select(
- 'revision',
- [ 'actor_id' => 'NULL', 'user_id' => 'rev_user', 'user_name' => 'rev_user_text' ],
- array_merge( [ "rev_user_text$like" ], $from ? [ "rev_user_text $from" ] : [] ),
- $fname,
- [ 'DISTINCT', 'ORDER BY' => [ "rev_user_text $sort" ], 'LIMIT' => $limit ]
- );
- }
+ $res = $dbSecondary->select(
+ 'actor',
+ [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
+ array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
+ $fname,
+ [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
+ );
$count = 0;
$fromName = false;
$from = "$op= $fromId";
}
- // For the new schema, just select from the actor table. For the
- // old, select from user.
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $res = $dbSecondary->select(
- 'actor',
- [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
- array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
- __METHOD__,
- [ 'ORDER BY' => "user_id $sort" ]
- );
- } else {
- $res = $dbSecondary->select(
- 'user',
- [ 'actor_id' => 'NULL', 'user_id' => 'user_id', 'user_name' => 'user_name' ],
- array_merge( [ 'user_id' => $ids ], $from ? [ "user_id $from" ] : [] ),
- __METHOD__,
- [ 'ORDER BY' => "user_id $sort" ]
- );
- }
+ $res = $dbSecondary->select(
+ 'actor',
+ [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+ array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
+ __METHOD__,
+ [ 'ORDER BY' => "user_id $sort" ]
+ );
$userIter = UserArray::newFromResult( $res );
$batchSize = count( $ids );
} else {
$from = "$op= " . $dbSecondary->addQuotes( $fromName );
}
- // For the new schema, just select from the actor table. For the
- // old, select from user then merge in any unknown users (IPs and imports).
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $res = $dbSecondary->select(
- 'actor',
- [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
- array_merge( [ 'actor_name' => array_keys( $names ) ], $from ? [ "actor_id $from" ] : [] ),
- __METHOD__,
- [ 'ORDER BY' => "actor_name $sort" ]
- );
- $userIter = UserArray::newFromResult( $res );
- } else {
- $res = $dbSecondary->select(
- 'user',
- [ 'actor_id' => 'NULL', 'user_id', 'user_name' ],
- array_merge( [ 'user_name' => array_keys( $names ) ], $from ? [ "user_name $from" ] : [] ),
- __METHOD__
- );
- foreach ( $res as $row ) {
- $names[$row->user_name] = $row;
- }
- if ( $this->params['dir'] == 'newer' ) {
- ksort( $names, SORT_STRING );
- } else {
- krsort( $names, SORT_STRING );
- }
- $neg = $op === '>' ? -1 : 1;
- $userIter = call_user_func( function () use ( $names, $fromName, $neg ) {
- foreach ( $names as $name => $row ) {
- if ( $fromName === false || $neg * strcmp( $name, $fromName ) <= 0 ) {
- $user = $row ? User::newFromRow( $row ) : User::newFromName( $name, false );
- yield $user;
- }
- }
- } );
- }
+ $res = $dbSecondary->select(
+ 'actor',
+ [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+ array_merge( [ 'actor_name' => array_keys( $names ) ], $from ? [ "actor_id $from" ] : [] ),
+ __METHOD__,
+ [ 'ORDER BY' => "actor_name $sort" ]
+ );
+ $userIter = UserArray::newFromResult( $res );
$batchSize = count( $names );
}
- // With the new schema, the DB query will order by actor so update $this->orderBy to match.
- if ( $batchSize > 1 && ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ) {
+ // The DB query will order by actor so update $this->orderBy to match.
+ if ( $batchSize > 1 ) {
$this->orderBy = 'actor';
}
- // Use the 'contributions' replica, but only if we're querying by user ID (T216656).
- if ( $this->orderBy === 'id' &&
- !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW )
- ) {
- $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
- }
-
$count = 0;
$limit = $this->params['limit'];
$userIter->rewind();
* @param int $limit
*/
private function prepareQuery( array $users, $limit ) {
- global $wgActorTableSchemaMigrationStage;
-
$this->resetQueryParams();
$db = $this->getDB();
$revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo( [ 'page' ] );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
- $orderUserField = 'rev_actor';
- $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
- $tsField = 'revactor_timestamp';
- $idField = 'revactor_rev';
-
- // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
- // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
- // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
- // because as generated by RevisionStore it'll have `revision` first rather than
- // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
- // helped in that case, and not when there's only one User because in that case it fetches
- // the one `actor` row as a constant and doesn't filesort.
- if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
- $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
- unset( $revQuery['joins']['temp_rev_user'] );
- $this->addOption( 'STRAIGHT_JOIN' );
- // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
- // when join conditions are given for all joins, but Gergő is wary of relying on that so pull
- // `revision_actor_temp` to the start.
- $revQuery['tables'] =
- [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
- }
- } else {
- // If we're dealing with user names (rather than IDs) in read-old mode,
- // pass false for ActorMigration::getWhere()'s $useId parameter so
- // $revWhere['conds'] isn't an OR.
- $revWhere = ActorMigration::newMigration()
- ->getWhere( $db, 'rev_user', $users, $this->orderBy === 'id' );
- $orderUserField = $this->orderBy === 'id' ? 'rev_user' : 'rev_user_text';
- $userField = $revQuery['fields'][$orderUserField];
- $tsField = 'rev_timestamp';
- $idField = 'rev_id';
+ $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
+ $orderUserField = 'rev_actor';
+ $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
+ $tsField = 'revactor_timestamp';
+ $idField = 'revactor_rev';
+
+ // T221511: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor`
+ // before `revision_actor_temp` and filesorting is somehow better than querying $limit+1 rows
+ // from `revision_actor_temp`. Tell it not to reorder the query (and also reorder it ourselves
+ // because as generated by RevisionStore it'll have `revision` first rather than
+ // `revision_actor_temp`). But not when uctag is used, as it seems as likely to be harmed as
+ // helped in that case, and not when there's only one User because in that case it fetches
+ // the one `actor` row as a constant and doesn't filesort.
+ if ( count( $users ) > 1 && !isset( $this->params['tag'] ) ) {
+ $revQuery['joins']['revision'] = $revQuery['joins']['temp_rev_user'];
+ unset( $revQuery['joins']['temp_rev_user'] );
+ $this->addOption( 'STRAIGHT_JOIN' );
+ // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
+ // when join conditions are given for all joins, but Gergő is wary of relying on that so pull
+ // `revision_actor_temp` to the start.
+ $revQuery['tables'] =
+ [ 'temp_rev_user' => $revQuery['tables']['temp_rev_user'] ] + $revQuery['tables'];
}
$this->addTables( $revQuery['tables'] );
* @return string|null ISO 8601 timestamp of current user's last contribution or null if none
*/
protected function getLatestContributionTime() {
- global $wgActorTableSchemaMigrationStage;
-
$user = $this->getUser();
$dbr = $this->getDB();
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- if ( $user->getActorId() === null ) {
- return null;
- }
- $res = $dbr->selectField( 'revision_actor_temp',
- 'MAX(revactor_timestamp)',
- [ 'revactor_actor' => $user->getActorId() ],
- __METHOD__
- );
- } else {
- if ( $user->isLoggedIn() ) {
- $conds = [ 'rev_user' => $user->getId() ];
- } else {
- $conds = [ 'rev_user_text' => $user->getName() ];
- }
- $res = $dbr->selectField( 'revision',
- 'MAX(rev_timestamp)',
- $conds,
- __METHOD__
- );
+ if ( $user->getActorId() === null ) {
+ return null;
}
+ $res = $dbr->selectField( 'revision_actor_temp',
+ 'MAX(revactor_timestamp)',
+ [ 'revactor_actor' => $user->getActorId() ],
+ __METHOD__
+ );
return $res ? wfTimestamp( TS_ISO_8601, $res ) : null;
}
$titles = $pageSet->getGoodTitles();
$title = reset( $titles );
if ( $title ) {
- $revid = $title->getNextRevisionID( $params['newerthanrevid'], Title::GAID_FOR_UPDATE );
+ $revid = $title->getNextRevisionID( $params['newerthanrevid'], Title::READ_LATEST );
if ( $revid ) {
$timestamp = $dbw->timestamp(
MediaWikiServices::getInstance()->getRevisionStore()->getTimestampFromId( $title, $revid )
"apierror-cantoverwrite-sharedfile": "The target file exists on a shared repository and you do not have permission to override it.",
"apierror-cantsend": "You are not logged in, you do not have a confirmed email address, or you are not allowed to send email to other users, so you cannot send email.",
"apierror-cantundelete": "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already.",
+ "apierror-cantview-deleted-description": "You don't have permission to view descriptions of deleted files.",
+ "apierror-cantview-deleted-metadata": "You don't have permission to view metadata of deleted files.",
"apierror-changeauth-norequest": "Failed to create change request.",
"apierror-chunk-too-small": "Minimum chunk size is $1 {{PLURAL:$1|byte|bytes}} for non-final chunks.",
"apierror-cidrtoobroad": "$1 CIDR ranges broader than /$2 are not accepted.",
"apierror-cantoverwrite-sharedfile": "{{doc-apierror}}",
"apierror-cantsend": "{{doc-apierror}}",
"apierror-cantundelete": "{{doc-apierror}}",
+ "apierror-cantview-deleted-description": "{{doc-apierror}}",
+ "apierror-cantview-deleted-metadata": "{{doc-apierror}}",
"apierror-changeauth-norequest": "{{doc-apierror}}",
"apierror-chunk-too-small": "{{doc-apierror}}\n\nParameters:\n* $1 - Minimum size in bytes.",
"apierror-cidrtoobroad": "{{doc-apierror}}\n\nParameters:\n* $1 - \"IPv4\" or \"IPv6\"\n* $2 - Minimum CIDR mask length.",
}
/**
+ * Select a request by class name.
+ *
* @codingStandardsIgnoreStart
- * @template T
+ * @phan-template T
* @codingStandardsIgnoreEnd
- * Select a request by class name.
* @param AuthenticationRequest[] $reqs
* @param string $class Class name
* @phan-param class-string<T> $class
use ActorMigration;
use AutoCommitUpdate;
-use BadMethodCallException;
use CommentStore;
use DeferredUpdates;
use Hooks;
}
}
- /**
- * Return the list of ipblocks fields that should be selected to create
- * a new block.
- * @deprecated since 1.31, use self::getQueryInfo() instead.
- * @return array
- */
- public static function selectFields() {
- global $wgActorTableSchemaMigrationStage;
-
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->ipb_by or $row->ipb_by_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- wfDeprecated( __METHOD__, '1.31' );
- return [
- 'ipb_id',
- 'ipb_address',
- 'ipb_by',
- 'ipb_by_text',
- 'ipb_by_actor' => 'NULL',
- 'ipb_timestamp',
- 'ipb_auto',
- 'ipb_anon_only',
- 'ipb_create_account',
- 'ipb_enable_autoblock',
- 'ipb_expiry',
- 'ipb_deleted',
- 'ipb_block_email',
- 'ipb_allow_usertalk',
- 'ipb_parent_block_id',
- 'ipb_sitewide',
- ] + CommentStore::getStore()->getFields( 'ipb_reason' );
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new block object.
*
* @param bool|null $update
* @return bool
+ * @deprecated Since 1.34
*/
public function forUpdate( $update = null ) {
return wfSetVar( $this->mForUpdate, $update );
* @param string $caller The calling method
*/
public function doQuery( array $userIds, $options = [], $caller = '' ) {
- global $wgActorTableSchemaMigrationStage;
-
$usersToCheck = [];
$usersToQuery = [];
// Lookup basic info for users not yet loaded...
if ( count( $usersToQuery ) ) {
$dbr = wfGetDB( DB_REPLICA );
- $tables = [ 'user' ];
+ $tables = [ 'user', 'actor' ];
$conds = [ 'user_id' => $usersToQuery ];
- $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
- $joinConds = [];
-
- // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
- $tables[] = 'actor';
- $fields[] = 'actor_id';
- $joinConds['actor'] = [
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ? 'JOIN' : 'LEFT JOIN',
- [ 'actor_user = user_id' ]
- ];
- }
+ $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id', 'actor_id' ];
+ $joinConds = [
+ 'actor' => [ 'JOIN', 'actor_user = user_id' ],
+ ];
$comment = __METHOD__;
if ( strval( $caller ) !== '' ) {
$this->cache[$userId]['name'] = $row->user_name;
$this->cache[$userId]['real_name'] = $row->user_real_name;
$this->cache[$userId]['registration'] = $row->user_registration;
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
- $this->cache[$userId]['actor'] = $row->actor_id;
- }
+ $this->cache[$userId]['actor'] = $row->actor_id;
$usersToCheck[$userId] = $row->user_name;
}
}
* This will work on any MediaWiki installation.
*/
class LCStoreDB implements LCStore {
- /** @var string */
- private $currentLang;
- /** @var bool */
- private $writesDone = false;
+ /** @var string Language code */
+ private $code;
+ /** @var array Server configuration map */
+ private $server;
+
+ /** @var array Rows buffered for insertion */
+ private $batch = [];
+
/** @var IDatabase|null */
private $dbw;
- /** @var array */
- private $batch = [];
- /** @var bool */
+ /** @var bool Whether a batch of writes were recently written */
+ private $writesDone = false;
+ /** @var bool Whether the DB is read-only or otherwise unavailable for writes */
private $readOnly = false;
- /** @var array Server configuration map */
- private $server;
public function __construct( $params ) {
$this->server = $params['server'] ?? [];
$dbw = $this->getWriteConnection();
$this->readOnly = $dbw->isReadOnly();
- $this->currentLang = $code;
+ $this->code = $code;
$this->batch = [];
}
public function finishWrite() {
if ( $this->readOnly ) {
return;
- } elseif ( is_null( $this->currentLang ) ) {
+ } elseif ( is_null( $this->code ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before finishWrite()' );
}
$dbw = $this->getWriteConnection();
$dbw->startAtomic( __METHOD__ );
try {
- $dbw->delete( 'l10n_cache', [ 'lc_lang' => $this->currentLang ], __METHOD__ );
+ $dbw->delete( 'l10n_cache', [ 'lc_lang' => $this->code ], __METHOD__ );
foreach ( array_chunk( $this->batch, 500 ) as $rows ) {
$dbw->insert( 'l10n_cache', $rows, __METHOD__ );
}
$trxProfiler->setSilenced( $oldSilenced );
}
- $this->currentLang = null;
+ $this->code = null;
$this->batch = [];
}
public function set( $key, $value ) {
if ( $this->readOnly ) {
return;
- } elseif ( is_null( $this->currentLang ) ) {
+ } elseif ( is_null( $this->code ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before set()' );
}
$dbw = $this->getWriteConnection();
$this->batch[] = [
- 'lc_lang' => $this->currentLang,
+ 'lc_lang' => $this->code,
'lc_key' => $key,
'lc_value' => $dbw->encodeBlob( serialize( $value ) )
];
}
}
- /**
- * Return the list of recentchanges fields that should be selected to create
- * a new recentchanges object.
- * @deprecated since 1.31, use self::getQueryInfo() instead.
- * @return array
- */
- public static function selectFields() {
- global $wgActorTableSchemaMigrationStage;
-
- wfDeprecated( __METHOD__, '1.31' );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->rc_user or $row->rc_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- return [
- 'rc_id',
- 'rc_timestamp',
- 'rc_user',
- 'rc_user_text',
- 'rc_actor' => 'NULL',
- 'rc_namespace',
- 'rc_title',
- 'rc_minor',
- 'rc_bot',
- 'rc_new',
- 'rc_cur_id',
- 'rc_this_oldid',
- 'rc_last_oldid',
- 'rc_type',
- 'rc_source',
- 'rc_patrolled',
- 'rc_ip',
- 'rc_old_len',
- 'rc_new_len',
- 'rc_deleted',
- 'rc_logid',
- 'rc_log_type',
- 'rc_log_action',
- 'rc_params',
- ] + CommentStore::getStore()->getFields( 'rc_comment' );
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new recentchanges object.
--- /dev/null
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * Edit phpunit.xml to speed up code coverage generation.
+ *
+ * Usage: composer phpunit:coverage-edit -- extensions/ExtensionName
+ *
+ * This class runs *outside* of the normal MediaWiki
+ * environment and cannot depend upon any MediaWiki
+ * code.
+ */
+class ComposerPhpunitXmlCoverageEdit {
+
+ public static function onEvent( $event ) {
+ $IP = dirname( dirname( __DIR__ ) );
+ // TODO: Support passing arbitrary directories for core (or extensions/skins).
+ $args = $event->getArguments();
+ if ( count( $args ) !== 1 ) {
+ throw new InvalidArgumentException( 'Pass extensions/$extensionName as an argument, ' .
+ 'e.g. "composer phpunit:coverage-edit -- extensions/BoilerPlate"' );
+ }
+ $project = current( $args );
+ $phpunitXml = \PHPUnit\Util\Xml::loadFile( $IP . '/phpunit.xml.dist' );
+ $whitelist = iterator_to_array( $phpunitXml->getElementsByTagName( 'whitelist' ) );
+ /** @var DOMNode $childNode */
+ foreach ( $whitelist as $childNode ) {
+ $childNode->parentNode->removeChild( $childNode );
+ }
+ $whitelistElement = $phpunitXml->createElement( 'whitelist' );
+ $whitelistElement->setAttribute( 'addUncoveredFilesFromWhitelist', 'false' );
+ // TODO: Use AutoloadClasses from extension.json to load the relevant directories
+ foreach ( [ 'includes', 'src', 'maintenance' ] as $dir ) {
+ $dirElement = $phpunitXml->createElement( 'directory', $project . '/' . $dir );
+ $dirElement->setAttribute( 'suffix', '.php' );
+ $whitelistElement->appendChild( $dirElement );
+
+ }
+ $phpunitXml->getElementsByTagName( 'filter' )->item( 0 )
+ ->appendChild( $whitelistElement );
+ $phpunitXml->formatOutput = true;
+ $phpunitXml->save( $IP . '/phpunit.xml' );
+ }
+}
* @author Daniel Kinzler
*/
abstract class DBAccessBase implements IDBAccessObject {
- /**
- * @var string|bool $wiki The target wiki's name. This must be an ID
- * that LBFactory can understand.
- */
- protected $wiki = false;
+ /** @var ILoadBalancer */
+ private $lb;
+
+ /** @var string|bool The target wiki's DB domain */
+ protected $dbDomain = false;
/**
- * @param string|bool $wiki The target wiki's name. This must be an ID
- * that LBFactory can understand.
+ * @param string|bool $dbDomain The target wiki's DB domain
*/
- public function __construct( $wiki = false ) {
- $this->wiki = $wiki;
+ public function __construct( $dbDomain = false ) {
+ $this->dbDomain = $dbDomain;
+ $this->lb = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()
+ ->getMainLB( $dbDomain );
}
/**
* Returns a database connection.
*
- * @see wfGetDB()
* @see LoadBalancer::getConnection()
*
* @since 1.21
* @return IDatabase
*/
protected function getConnection( $id, array $groups = [] ) {
- $loadBalancer = $this->getLoadBalancer();
-
- return $loadBalancer->getConnection( $id, $groups, $this->wiki );
+ return $this->getLoadBalancer()->getConnectionRef( $id, $groups, $this->dbDomain );
}
/**
* @since 1.21
*
* @param IDatabase $db The database connection to release.
+ * @deprecated Since 1.34
*/
protected function releaseConnection( IDatabase $db ) {
- if ( $this->wiki !== false ) {
- $loadBalancer = $this->getLoadBalancer();
- $loadBalancer->reuseConnection( $db );
- }
+ // no-op
}
/**
*
* @return ILoadBalancer The database load balancer object
*/
- public function getLoadBalancer() {
- $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
- return $lbFactory->getMainLB( $this->wiki );
+ protected function getLoadBalancer() {
+ return $this->lb;
}
}
if ( !$this->mId ) {
// NOTE: subclasses may initialize mId before calling this constructor!
- $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
+ $this->mId = $title->getArticleID( Title::READ_LATEST );
}
if ( !$this->mId ) {
if ( $samePage && $this->mNewPage && $permissionManager->quickUserCan(
'edit', $user, $this->mNewPage
) ) {
- if ( $this->mNewRev->isCurrent() && $permissionManager->userCan(
+ if ( $this->mNewRev->isCurrent() && $permissionManager->quickUserCan(
'rollback', $user, $this->mNewPage
) ) {
$rollbackLink = Linker::generateRollback( $this->mNewRev, $this->getContext(),
return $file;
}
- /**
- * Fields in the filearchive table
- * @deprecated since 1.31, use self::getQueryInfo() instead.
- * @return string[]
- */
- static function selectFields() {
- global $wgActorTableSchemaMigrationStage;
-
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->fa_user or $row->fa_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- wfDeprecated( __METHOD__, '1.31' );
- return [
- 'fa_id',
- 'fa_name',
- 'fa_archive_name',
- 'fa_storage_key',
- 'fa_storage_group',
- 'fa_size',
- 'fa_bits',
- 'fa_width',
- 'fa_height',
- 'fa_metadata',
- 'fa_media_type',
- 'fa_major_mime',
- 'fa_minor_mime',
- 'fa_user',
- 'fa_user_text',
- 'fa_actor' => 'NULL',
- 'fa_timestamp',
- 'fa_deleted',
- 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
- 'fa_sha1',
- ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'fa_description' );
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new archivedfile object.
}
}
- /**
- * Fields in the image table
- * @deprecated since 1.31, use self::getQueryInfo() instead.
- * @return string[]
- */
- static function selectFields() {
- global $wgActorTableSchemaMigrationStage;
-
- wfDeprecated( __METHOD__, '1.31' );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->img_user or $row->img_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- return [
- 'img_name',
- 'img_size',
- 'img_width',
- 'img_height',
- 'img_metadata',
- 'img_bits',
- 'img_media_type',
- 'img_major_mime',
- 'img_minor_mime',
- 'img_user',
- 'img_user_text',
- 'img_actor' => 'NULL',
- 'img_timestamp',
- 'img_sha1',
- ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'img_description' );
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new localfile object.
$oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null, $tags = [],
$createNullRevision = true, $revert = false
) {
- global $wgActorTableSchemaMigrationStage;
-
if ( is_null( $user ) ) {
global $wgUser;
$user = $wgUser;
'oi_major_mime' => 'img_major_mime',
'oi_minor_mime' => 'img_minor_mime',
'oi_sha1' => 'img_sha1',
+ 'oi_actor' => 'img_actor',
];
$joins = [];
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $fields['oi_user'] = 'img_user';
- $fields['oi_user_text'] = 'img_user_text';
- }
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $fields['oi_actor'] = 'img_actor';
- }
-
- if (
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_BOTH
- ) {
- // Upgrade any rows that are still old-style. Otherwise an upgrade
- // might be missed if a deletion happens while the migration script
- // is running.
- $res = $dbw->select(
- [ 'image' ],
- [ 'img_name', 'img_user', 'img_user_text' ],
- [ 'img_name' => $this->getName(), 'img_actor' => 0 ],
- __METHOD__
- );
- foreach ( $res as $row ) {
- $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
- $dbw->update(
- 'image',
- [ 'img_actor' => $actorId ],
- [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
- __METHOD__
- );
- }
- }
-
# (T36993) Note: $oldver can be empty here, if the previous
# version of the file was broken. Allow registration of the new
# version to continue anyway, because that's better than having
}
protected function doDBInserts() {
- global $wgActorTableSchemaMigrationStage;
-
$now = time();
$dbw = $this->file->repo->getMasterDB();
'fa_minor_mime' => 'img_minor_mime',
'fa_description_id' => 'img_description_id',
'fa_timestamp' => 'img_timestamp',
- 'fa_sha1' => 'img_sha1'
+ 'fa_sha1' => 'img_sha1',
+ 'fa_actor' => 'img_actor',
];
$joins = [];
$commentStore->insert( $dbw, 'fa_deleted_reason', $this->reason )
);
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $fields['fa_user'] = 'img_user';
- $fields['fa_user_text'] = 'img_user_text';
- }
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $fields['fa_actor'] = 'img_actor';
- }
-
- if (
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) === SCHEMA_COMPAT_WRITE_BOTH
- ) {
- // Upgrade any rows that are still old-style. Otherwise an upgrade
- // might be missed if a deletion happens while the migration script
- // is running.
- $res = $dbw->select(
- [ 'image' ],
- [ 'img_name', 'img_user', 'img_user_text' ],
- [ 'img_name' => $this->file->getName(), 'img_actor' => 0 ],
- __METHOD__
- );
- foreach ( $res as $row ) {
- $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
- $dbw->update(
- 'image',
- [ 'img_actor' => $actorId ],
- [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
- __METHOD__
- );
- }
- }
-
$dbw->insertSelect( 'filearchive', $tables, $fields,
[ 'img_name' => $this->file->getName() ], __METHOD__, [], [], $joins );
}
}
}
- /**
- * Fields in the oldimage table
- * @deprecated since 1.31, use self::getQueryInfo() instead.
- * @return string[]
- */
- static function selectFields() {
- global $wgActorTableSchemaMigrationStage;
-
- wfDeprecated( __METHOD__, '1.31' );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- // If code is using this instead of self::getQueryInfo(), there's a
- // decent chance it's going to try to directly access
- // $row->oi_user or $row->oi_user_text and we can't give it
- // useful values here once those aren't being used anymore.
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
- );
- }
-
- return [
- 'oi_name',
- 'oi_archive_name',
- 'oi_size',
- 'oi_width',
- 'oi_height',
- 'oi_metadata',
- 'oi_bits',
- 'oi_media_type',
- 'oi_major_mime',
- 'oi_minor_mime',
- 'oi_user',
- 'oi_user_text',
- 'oi_actor' => 'NULL',
- 'oi_timestamp',
- 'oi_deleted',
- 'oi_sha1',
- ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'oi_description' );
- }
-
/**
* Return the tables, fields, and join conditions to be selected to create
* a new oldlocalfile object.
}
$this->setVar( '_Extensions', $status->value );
} elseif ( isset( $options['with-extensions'] ) ) {
- $this->setVar( '_Extensions', array_keys( $this->findExtensions() ) );
+ $status = $this->findExtensions();
+ if ( !$status->isOK() ) {
+ throw new InstallException( $status );
+ }
+ $this->setVar( '_Extensions', array_keys( $status->value ) );
}
// Set up the default skins
}
$skins = $status->value;
} else {
- $skins = array_keys( $this->findExtensions( 'skins' ) );
+ $status = $this->findExtensions( 'skins' );
+ if ( !$status->isOK() ) {
+ throw new InstallException( $status );
+ }
+ $skins = array_keys( $status->value );
}
$this->setVar( '_Skins', $skins );
* @since 1.31
*/
protected function migrateActors() {
- global $wgActorTableSchemaMigrationStage;
- if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) &&
- !$this->updateRowExists( 'MigrateActors' )
- ) {
+ if ( !$this->updateRowExists( 'MigrateActors' ) ) {
$this->output(
"Migrating actors to the 'actor' table, printing progress markers. For large\n" .
"databases, you may want to hit Ctrl-C and do this manually with\n" .
}
}
}
+
+ /**
+ * Only run a function if the `actor` table does not exist
+ *
+ * The transition to the actor table is dropping several indexes (and a few
+ * fields) that old upgrades want to add. This function is used to prevent
+ * those from running to re-add things when the `actor` table exists, while
+ * still allowing them to run if this really is an upgrade from an old MW
+ * version.
+ *
+ * @since 1.34
+ * @param string|array|static $func Normally this is the string naming the method on $this to
+ * call. It may also be an array callable. If passed $this, it's assumed to be a call from
+ * runUpdates() with $passSelf = true: $params[0] is assumed to be the real $func and $this
+ * is prepended to the rest of $params.
+ * @param mixed ...$params Parameters for `$func`
+ * @return mixed Whatever $func returns, or null when skipped.
+ */
+ protected function ifNoActorTable( $func, ...$params ) {
+ if ( $this->tableExists( 'actor' ) ) {
+ return null;
+ }
+
+ // Handle $passSelf from runUpdates().
+ $passSelf = false;
+ if ( $func === $this ) {
+ $passSelf = true;
+ $func = array_shift( $params );
+ }
+
+ if ( !is_array( $func ) && method_exists( $this, $func ) ) {
+ $func = [ $this, $func ];
+ } elseif ( $passSelf ) {
+ array_unshift( $params, $this );
+ }
+
+ // @phan-suppress-next-line PhanUndeclaredInvokeInCallable Phan is confused
+ return $func( ...$params );
+ }
}
*
* @param string $directory Directory to search in, relative to $IP, must be either "extensions"
* or "skins"
- * @return array[][] [ $extName => [ 'screenshots' => [ '...' ] ]
+ * @return Status An object containing an error list. If there were no errors, an associative
+ * array of information about the extension can be found in $status->value.
*/
public function findExtensions( $directory = 'extensions' ) {
switch ( $directory ) {
*
* @param string $type Either "extension" or "skin"
* @param string $directory Directory to search in, relative to $IP
- * @return array [ $extName => [ 'screenshots' => [ '...' ] ]
+ * @return Status An object containing an error list. If there were no errors, an associative
+ * array of information about the extension can be found in $status->value.
*/
protected function findExtensionsByType( $type = 'extension', $directory = 'extensions' ) {
if ( $this->getVar( 'IP' ) === null ) {
- return [];
+ return Status::newGood( [] );
}
$extDir = $this->getVar( 'IP' ) . '/' . $directory;
if ( !is_readable( $extDir ) || !is_dir( $extDir ) ) {
- return [];
+ return Status::newGood( [] );
}
$dh = opendir( $extDir );
$exts = [];
+ $status = new Status;
while ( ( $file = readdir( $dh ) ) !== false ) {
- if ( !is_dir( "$extDir/$file" ) ) {
+ // skip non-dirs and hidden directories
+ if ( !is_dir( "$extDir/$file" ) || $file[0] === '.' ) {
continue;
}
- $status = $this->getExtensionInfo( $type, $directory, $file );
- if ( $status->isOK() ) {
- $exts[$file] = $status->value;
+ $extStatus = $this->getExtensionInfo( $type, $directory, $file );
+ if ( $extStatus->isOK() ) {
+ $exts[$file] = $extStatus->value;
+ } elseif ( $extStatus->hasMessage( 'config-extension-not-found' ) ) {
+ // (T225512) The directory is not actually an extension. Downgrade to warning.
+ $status->warning( 'config-extension-not-found', $file );
+ } else {
+ $status->merge( $extStatus );
}
}
closedir( $dh );
uksort( $exts, 'strnatcasecmp' );
- return $exts;
+ $status->value = $exts;
+
+ return $status;
}
/**
} elseif ( $e->missingExtensions || $e->missingSkins ) {
// There's an extension missing in the dependency tree,
// so add those to the dependency list and try again
- return $this->readExtension(
+ $status = $this->readExtension(
$fullJsonFile,
array_merge( $extDeps, $e->missingExtensions ),
array_merge( $skinDeps, $e->missingSkins )
);
+ if ( !$status->isOK() && !$status->hasMessage( 'config-extension-dependency' ) ) {
+ $status = Status::newFatal( 'config-extension-dependency',
+ basename( dirname( $fullJsonFile ) ), $status->getMessage() );
+ }
+ return $status;
}
// Some other kind of dependency error?
return Status::newFatal( 'config-extension-dependency',
[ 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ],
[ 'addTable', 'filearchive', 'patch-filearchive.sql' ],
[ 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ],
- [ 'addIndex', 'recentchanges', 'rc_ns_usertext', 'patch-recentchanges-utindex.sql' ],
- [ 'addIndex', 'recentchanges', 'rc_user_text', 'patch-rc_user_text-index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'recentchanges', 'rc_ns_usertext',
+ 'patch-recentchanges-utindex.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'recentchanges', 'rc_user_text',
+ 'patch-rc_user_text-index.sql' ],
// 1.9
[ 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ],
[ 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ],
[ 'doCategorylinksIndicesUpdate' ],
[ 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ],
- [ 'addIndex', 'archive', 'usertext_timestamp', 'patch-archive-user-index.sql' ],
- [ 'addIndex', 'image', 'img_usertext_timestamp', 'patch-image-user-index.sql' ],
- [ 'addIndex', 'oldimage', 'oi_usertext_timestamp', 'patch-oldimage-user-index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'archive', 'usertext_timestamp',
+ 'patch-archive-user-index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'image', 'img_usertext_timestamp',
+ 'patch-image-user-index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'oldimage', 'oi_usertext_timestamp',
+ 'patch-oldimage-user-index.sql' ],
[ 'addField', 'archive', 'ar_page_id', 'patch-archive-page_id.sql' ],
[ 'addField', 'image', 'img_sha1', 'patch-img_sha1.sql' ],
[ 'addTable', 'protected_titles', 'patch-protected_titles.sql' ],
// 1.13
- [ 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
+ [ 'ifNoActorTable', 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ],
[ 'addTable', 'page_props', 'patch-page_props.sql' ],
[ 'addTable', 'updatelog', 'patch-updatelog.sql' ],
[ 'addTable', 'category', 'patch-category.sql' ],
[ 'doPopulateParentId' ],
[ 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ],
[ 'doMaybeProfilingMemoryUpdate' ],
- [ 'doFilearchiveIndicesUpdate' ],
+ [ 'ifNoActorTable', 'doFilearchiveIndicesUpdate' ],
// 1.14
[ 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ],
// 1.16
[ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
[ 'addTable', 'log_search', 'patch-log_search.sql' ],
- [ 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
+ [ 'ifNoActorTable', 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
# listed separately from the previous update because 1.16 was released without this update
- [ 'doLogUsertextPopulation' ],
+ [ 'ifNoActorTable', 'doLogUsertextPopulation' ],
[ 'doLogSearchPopulation' ],
[ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
[ 'dropIndex', 'change_tag', 'ct_rc_id', 'patch-change_tag-indexes.sql' ],
// 1.23
[ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
- [ 'addIndex', 'logging', 'log_user_text_type_time',
+ [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_type_time',
'patch-logging_user_text_type_time_index.sql' ],
- [ 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_time',
+ 'patch-logging_user_text_time_index.sql' ],
[ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
[ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
[ 'doNonUniquePlTlIl' ],
[ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
[ 'modifyField', 'recentchanges', 'rc_ip', 'patch-rc_ip_modify.sql' ],
- [ 'addIndex', 'archive', 'usertext_timestamp', 'patch-rename-ar_usertext_timestamp.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'archive', 'usertext_timestamp',
+ 'patch-rename-ar_usertext_timestamp.sql' ],
// 1.29
[ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
[ 'dropIndex', 'user_groups', 'ug_user_group', 'patch-user_groups-primary-key.sql' ],
[ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
- [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
// 1.30
[ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
[ 'dropTable', 'tag_summary' ],
[ 'dropField', 'protected_titles', 'pt_reason', 'patch-drop-comment-fields.sql' ],
[ 'modifyTable', 'job', 'patch-job-params-mediumblob.sql' ],
+
+ // 1.34
+ [ 'dropField', 'logging', 'log_user', 'patch-drop-user-fields.sql' ],
];
}
[ 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ],
[ 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ],
[ 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ],
- [ 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ],
+ [ 'ifNoActorTable', 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ],
[ 'addPgField', 'ipblocks', 'ipb_block_email', 'SMALLINT NOT NULL DEFAULT 0' ],
[ 'addPgField', 'ipblocks', 'ipb_create_account', 'SMALLINT NOT NULL DEFAULT 1' ],
[ 'addPgField', 'ipblocks', 'ipb_deleted', 'SMALLINT NOT NULL DEFAULT 0' ],
[ 'addPgField', 'revision', 'rev_content_format', 'TEXT' ],
[ 'addPgField', 'site_stats', 'ss_active_users', "INTEGER DEFAULT '-1'" ],
[ 'addPgField', 'user_newtalk', 'user_last_timestamp', 'TIMESTAMPTZ' ],
- [ 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ],
+ [ 'ifNoActorTable', 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ],
[ 'addPgField', 'logging', 'log_page', 'INTEGER' ],
[ 'addPgField', 'interwiki', 'iw_api', "TEXT NOT NULL DEFAULT ''" ],
[ 'addPgField', 'interwiki', 'iw_wikiid', "TEXT NOT NULL DEFAULT ''" ],
[ 'checkOiDeleted' ],
# New indexes
- [ 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ],
+ [ 'ifNoActorTable', 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ],
[ 'addPgIndex', 'image', 'img_sha1', '(img_sha1)' ],
[ 'addPgIndex', 'ipblocks', 'ipb_parent_block_id', '(ipb_parent_block_id)' ],
[ 'addPgIndex', 'oldimage', 'oi_sha1', '(oi_sha1)' ],
[ 'addPgIndex', 'watchlist', 'wl_user', '(wl_user)' ],
[ 'addPgIndex', 'watchlist', 'wl_user_notificationtimestamp',
'(wl_user, wl_notificationtimestamp)' ],
- [ 'addPgIndex', 'logging', 'logging_user_type_time',
+ [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_type_time',
'(log_user, log_type, log_timestamp)' ],
[ 'addPgIndex', 'logging', 'logging_page_id_time', '(log_page,log_timestamp)' ],
[ 'addPgIndex', 'iwlinks', 'iwl_prefix_from_title', '(iwl_prefix, iwl_from, iwl_title)' ],
[ 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ],
[ 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ],
[ 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ],
- [ 'addPgIndex', 'logging', 'logging_user_text_type_time',
+ [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_text_type_time',
'(log_user_text, log_type, log_timestamp)' ],
- [ 'addPgIndex', 'logging', 'logging_user_text_time', '(log_user_text, log_timestamp)' ],
+ [ 'ifNoActorTable', 'addPgIndex', 'logging', 'logging_user_text_time',
+ '(log_user_text, log_timestamp)' ],
[ 'checkIndex', 'pagelink_unique', [
[ 'pl_from', 'int4_ops', 'btree', 0 ],
[ 'checkIwlPrefix' ],
# All FK columns should be deferred
- [ 'changeFkeyDeferrable', 'archive', 'ar_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'archive', 'ar_user',
+ 'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'categorylinks', 'cl_from', 'page(page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'externallinks', 'el_from', 'page(page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'filearchive', 'fa_deleted_user',
'mwuser(user_id) ON DELETE SET NULL' ],
- [ 'changeFkeyDeferrable', 'filearchive', 'fa_user', 'mwuser(user_id) ON DELETE SET NULL' ],
- [ 'changeFkeyDeferrable', 'image', 'img_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'filearchive', 'fa_user',
+ 'mwuser(user_id) ON DELETE SET NULL' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'image', 'img_user',
+ 'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'imagelinks', 'il_from', 'page(page_id) ON DELETE CASCADE' ],
- [ 'changeFkeyDeferrable', 'ipblocks', 'ipb_by', 'mwuser(user_id) ON DELETE CASCADE' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'ipblocks', 'ipb_by',
+ 'mwuser(user_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'ipblocks', 'ipb_user', 'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'ipblocks', 'ipb_parent_block_id',
'ipblocks(ipb_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'langlinks', 'll_from', 'page(page_id) ON DELETE CASCADE' ],
- [ 'changeFkeyDeferrable', 'logging', 'log_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'logging', 'log_user',
+ 'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'oldimage', 'oi_name',
'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ],
- [ 'changeFkeyDeferrable', 'oldimage', 'oi_user', 'mwuser(user_id) ON DELETE SET NULL' ],
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'oldimage', 'oi_user',
+ 'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'pagelinks', 'pl_from', 'page(page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'page_props', 'pp_page', 'page (page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',
'page(page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'protected_titles', 'pt_user',
'mwuser(user_id) ON DELETE SET NULL' ],
- [ 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
+ [ 'ifNoActorTable', 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
'mwuser(user_id) ON DELETE SET NULL' ],
[ 'changeFkeyDeferrable', 'redirect', 'rd_from', 'page(page_id) ON DELETE CASCADE' ],
[ 'changeFkeyDeferrable', 'revision', 'rev_page', 'page (page_id) ON DELETE CASCADE' ],
[ 'dropDefault', 'logging', 'log_comment_id' ],
[ 'dropPgField', 'protected_titles', 'pt_reason' ],
[ 'dropDefault', 'protected_titles', 'pt_reason_id' ],
+
+ // 1.34
+ [ 'dropPgIndex', 'archive', 'archive_user_text' ],
+ [ 'dropPgField', 'archive', 'ar_user' ],
+ [ 'dropPgField', 'archive', 'ar_user_text' ],
+ [ 'dropDefault', 'archive', 'ar_actor' ],
+ [ 'dropPgField', 'ipblocks', 'ipb_by' ],
+ [ 'dropPgField', 'ipblocks', 'ipb_by_text' ],
+ [ 'dropDefault', 'ipblocks', 'ipb_by_actor' ],
+ [ 'dropPgField', 'image', 'img_user' ],
+ [ 'dropPgField', 'image', 'img_user_text' ],
+ [ 'dropDefault', 'image', 'img_actor' ],
+ [ 'dropPgField', 'oldimage', 'oi_user' ],
+ [ 'dropPgField', 'oldimage', 'oi_user_text' ],
+ [ 'dropDefault', 'oldimage', 'oi_actor' ],
+ [ 'dropPgField', 'filearchive', 'fa_user' ],
+ [ 'dropPgField', 'filearchive', 'fa_user_text' ],
+ [ 'dropDefault', 'filearchive', 'fa_actor' ],
+ [ 'dropPgField', 'recentchanges', 'rc_user' ],
+ [ 'dropPgField', 'recentchanges', 'rc_user_text' ],
+ [ 'dropDefault', 'recentchanges', 'rc_actor' ],
+ [ 'dropPgIndex', 'logging', 'logging_user_time' ],
+ [ 'dropPgIndex', 'logging', 'logging_user_type_time' ],
+ [ 'dropPgIndex', 'logging', 'logging_user_text_type_time' ],
+ [ 'dropPgIndex', 'logging', 'logging_user_text_time' ],
+ [ 'dropPgField', 'logging', 'log_user' ],
+ [ 'dropPgField', 'logging', 'log_user_text' ],
+ [ 'dropDefault', 'logging', 'log_actor' ],
];
}
// 1.16
[ 'addTable', 'user_properties', 'patch-user_properties.sql' ],
[ 'addTable', 'log_search', 'patch-log_search.sql' ],
- [ 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
+ [ 'ifNoActorTable', 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ],
# listed separately from the previous update because 1.16 was released without this update
- [ 'doLogUsertextPopulation' ],
+ [ 'ifNoActorTable', 'doLogUsertextPopulation' ],
[ 'doLogSearchPopulation' ],
[ 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ],
[ 'dropIndex', 'change_tag', 'ct_rc_id', 'patch-change_tag-indexes.sql' ],
// 1.23
[ 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ],
- [ 'addIndex', 'logging', 'log_user_text_type_time',
+ [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_type_time',
'patch-logging_user_text_type_time_index.sql' ],
- [ 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'logging', 'log_user_text_time',
+ 'patch-logging_user_text_time_index.sql' ],
[ 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ],
[ 'addField', 'user', 'user_password_expires', 'patch-user_password_expire.sql' ],
// 1.29
[ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
[ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
- [ 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
+ [ 'ifNoActorTable', 'addIndex', 'image', 'img_user_timestamp', 'patch-image-user-index-2.sql' ],
// 1.30
[ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
[ 'dropField', 'recentchanges', 'rc_comment', 'patch-recentchanges-drop-rc_comment.sql' ],
[ 'dropField', 'logging', 'log_comment', 'patch-logging-drop-log_comment.sql' ],
[ 'dropField', 'protected_titles', 'pt_reason', 'patch-protected_titles-drop-pt_reason.sql' ],
+
+ // 1.34
+ [ 'dropField', 'archive', 'ar_user', 'patch-archive-drop-ar_user.sql' ],
+ [ 'dropField', 'ipblocks', 'ipb_by', 'patch-ipblocks-drop-ipb_by.sql' ],
+ [ 'dropField', 'image', 'img_user', 'patch-image-drop-img_user.sql' ],
+ [ 'dropField', 'oldimage', 'oi_user', 'patch-oldimage-drop-oi_user.sql' ],
+ [ 'dropField', 'filearchive', 'fa_user', 'patch-filearchive-drop-fa_user.sql' ],
+ [ 'dropField', 'recentchanges', 'rc_user', 'patch-recentchanges-drop-rc_user.sql' ],
+ [ 'dropField', 'logging', 'log_user', 'patch-logging-drop-log_user.sql' ],
];
}
$this->getFieldsetEnd()
);
- $skins = $this->parent->findExtensions( 'skins' );
+ $skins = $this->parent->findExtensions( 'skins' )->value;
+ '@phan-var array[] $skins';
$skinHtml = $this->getFieldsetStart( 'config-skins' );
$skinNames = array_map( 'strtolower', array_keys( $skins ) );
$this->getFieldsetEnd();
$this->addHTML( $skinHtml );
- $extensions = $this->parent->findExtensions();
+ $extensions = $this->parent->findExtensions()->value;
+ '@phan-var array[] $extensions';
$dependencyMap = [];
if ( $extensions ) {
return null;
}
+ /**
+ * @param string $name
+ * @param array $screenshots
+ */
private function makeScreenshotsLink( $name, $screenshots ) {
global $wgLang;
if ( count( $screenshots ) > 1 ) {
$links = [];
$counter = 1;
+
foreach ( $screenshots as $shot ) {
$links[] = Html::element(
'a',
* @return bool
*/
public function submitSkins() {
- $skins = array_keys( $this->parent->findExtensions( 'skins' ) );
+ $skins = array_keys( $this->parent->findExtensions( 'skins' )->value );
$this->parent->setVar( '_Skins', $skins );
if ( $skins ) {
$this->setVar( 'wgRightsIcon', '' );
}
- $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' ) );
+ $skinsAvailable = array_keys( $this->parent->findExtensions( 'skins' )->value );
$skinsToInstall = [];
foreach ( $skinsAvailable as $skin ) {
$this->parent->setVarsFromRequest( [ "skin-$skin" ] );
$retVal = false;
}
- $extsAvailable = array_keys( $this->parent->findExtensions() );
+ $extsAvailable = array_keys( $this->parent->findExtensions()->value );
$extsToInstall = [];
foreach ( $extsAvailable as $ext ) {
$this->parent->setVarsFromRequest( [ "ext-$ext" ] );
$title = $page->getTitle();
// Get the latest ID since acquirePageLock() in runForTitle() flushed the transaction.
// This is used to detect edits/moves after loadPageData() but before the scope lock.
- // The works around the chicken/egg problem of determining the scope lock key name.
- $latest = $title->getLatestRevID( Title::GAID_FOR_UPDATE );
+ // The works around the chicken/egg problem of determining the scope lock key name
+ $latest = $title->getLatestRevID( Title::READ_LATEST );
$triggeringRevisionId = $this->params['triggeringRevisionId'] ?? null;
if ( $triggeringRevisionId && $triggeringRevisionId !== $latest ) {
*/
use Wikimedia\AtEase\AtEase;
+use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Simulation of a backend storage in memory.
$this->files[$dst] = [
'data' => $params['content'],
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
$this->files[$dst] = [
'data' => $data,
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
$this->files[$dst] = [
'data' => $this->files[$src]['data'],
- 'mtime' => wfTimestamp( TS_MW, time() )
+ 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
];
return $status;
*/
use Wikimedia\AtEase\AtEase;
+use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Class representing a non-directory file on the file system
* @return int|bool
*/
public function getSize() {
- return filesize( $this->path );
+ AtEase::suppressWarnings();
+ $size = filesize( $this->path );
+ AtEase::restoreWarnings();
+
+ return $size;
}
/**
$timestamp = filemtime( $this->path );
AtEase::restoreWarnings();
if ( $timestamp !== false ) {
- $timestamp = wfTimestamp( TS_MW, $timestamp );
+ $timestamp = ConvertibleTimestamp::convert( TS_MW, $timestamp );
}
return $timestamp;
* @throws MWException
*/
public function insert( IDatabase $dbw = null ) {
- global $wgActorTableSchemaMigrationStage;
-
$dbw = $dbw ?: wfGetDB( DB_MASTER );
if ( $this->timestamp === null ) {
$params = $this->getParameters();
$relations = $this->relations;
- // Ensure actor relations are set
- if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) &&
- empty( $relations['target_author_actor'] )
- ) {
- $actorIds = [];
- if ( !empty( $relations['target_author_id'] ) ) {
- foreach ( $relations['target_author_id'] as $id ) {
- $actorIds[] = User::newFromId( $id )->getActorId( $dbw );
- }
- }
- if ( !empty( $relations['target_author_ip'] ) ) {
- foreach ( $relations['target_author_ip'] as $ip ) {
- $actorIds[] = User::newFromName( $ip, false )->getActorId( $dbw );
- }
- }
- if ( $actorIds ) {
- $relations['target_author_actor'] = $actorIds;
- $params['authorActors'] = $actorIds;
- }
- }
- if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
- unset( $relations['target_author_id'], $relations['target_author_ip'] );
- unset( $params['authorIds'], $params['authorIPs'] );
- }
-
// Additional fields for which there's no space in the database table schema
$revId = $this->getAssociatedRevId();
if ( $revId ) {
// Try using the replica DB first, then try the master
$rev = $this->mTitle->getFirstRevision();
if ( !$rev ) {
- $rev = $this->mTitle->getFirstRevision( Title::GAID_FOR_UPDATE );
+ $rev = $this->mTitle->getFirstRevision( Title::READ_LATEST );
}
return $rev;
}
*/
protected function archiveRevisions( $dbw, $id, $suppress ) {
global $wgContentHandlerUseDB, $wgMultiContentRevisionSchemaMigrationStage,
- $wgActorTableSchemaMigrationStage, $wgDeleteRevisionsBatchSize;
+ $wgDeleteRevisionsBatchSize;
// Given the lock above, we can be confident in the title and page ID values
$namespace = $this->getTitle()->getNamespace();
$dbw->delete( 'revision', [ 'rev_id' => $revids ], __METHOD__ );
$dbw->delete( 'revision_comment_temp', [ 'revcomment_rev' => $revids ], __METHOD__ );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $dbw->delete( 'revision_actor_temp', [ 'revactor_rev' => $revids ], __METHOD__ );
- }
+ $dbw->delete( 'revision_actor_temp', [ 'revactor_rev' => $revids ], __METHOD__ );
// Also delete records from ip_changes as applicable.
if ( count( $ipRevIds ) > 0 ) {
* @since 1.23 Added 'perItemStatus' param
*/
public function setVisibility( array $params ) {
- global $wgActorTableSchemaMigrationStage;
-
$status = Status::newGood();
$bitPars = $params['value'];
$missing = array_flip( $this->ids );
$this->clearFileOps();
$idsForLog = [];
- $authorIds = $authorIPs = $authorActors = [];
+ $authorActors = [];
if ( $perItemStatus ) {
$status->itemStatuses = [];
$virtualOldBits |= $removedBits;
$status->successCount++;
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- if ( $item->getAuthorId() > 0 ) {
- $authorIds[] = $item->getAuthorId();
- } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) {
- $authorIPs[] = $item->getAuthorName();
- }
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $actorId = $item->getAuthorActor();
- // During migration, the actor field might be empty. If so, populate
- // it here.
- if ( !$actorId ) {
- if ( $item->getAuthorId() > 0 ) {
- $user = User::newFromId( $item->getAuthorId() );
- } else {
- $user = User::newFromName( $item->getAuthorName(), false );
- }
- $actorId = $user->getActorId( $dbw );
- }
- $authorActors[] = $actorId;
- }
- } elseif ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $authorActors[] = $item->getAuthorActor();
- }
+ $authorActors[] = $item->getAuthorActor();
// Save the old and new bits in $visibilityChangeMap for
// later use.
// Log it
$authorFields = [];
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $authorFields['authorIds'] = $authorIds;
- $authorFields['authorIPs'] = $authorIPs;
- }
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $authorFields['authorActors'] = $authorActors;
- }
+ $authorFields['authorActors'] = $authorActors;
$this->updateLog(
$logType,
[
* title: The target title
* ids: The ID list
* comment: The log comment
- * authorIds: The array of the user IDs of the offenders
- * authorIPs: The array of the IP/anon user offenders
* authorActors: The array of the actor IDs of the offenders
* tags: The array of change tags to apply to the log entry
* @throws MWException
$relations = [
$field => $params['ids'],
];
- if ( isset( $params['authorIds'] ) ) {
- $relations += [
- 'target_author_id' => $params['authorIds'],
- 'target_author_ip' => $params['authorIPs'],
- ];
- }
if ( isset( $params['authorActors'] ) ) {
$relations += [
'target_author_actor' => $params['authorActors'],
* @return bool True on success, false on failure (e.g. invalid user ID)
*/
private static function setUsernameBitfields( $name, $userId, $op, IDatabase $dbw = null ) {
- global $wgActorTableSchemaMigrationStage;
-
if ( !$userId || ( $op !== '|' && $op !== '&' ) ) {
return false; // sanity check
}
$userTitle = Title::makeTitleSafe( NS_USER, $name );
$userDbKey = $userTitle->getDBkey();
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
+ $actorId = $dbw->selectField( 'actor', 'actor_id', [ 'actor_name' => $name ], __METHOD__ );
+ if ( $actorId ) {
# Hide name from live edits
- $dbw->update(
- 'revision',
- [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
- [ 'rev_user' => $userId ],
- __METHOD__ );
+ $ids = $dbw->selectFieldValues(
+ 'revision_actor_temp', 'revactor_rev', [ 'revactor_actor' => $actorId ], __METHOD__
+ );
+ if ( $ids ) {
+ $dbw->update(
+ 'revision',
+ [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
+ [ 'rev_id' => $ids ],
+ __METHOD__
+ );
+ }
# Hide name from deleted edits
$dbw->update(
'archive',
[ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
- [ 'ar_user_text' => $name ],
+ [ 'ar_actor' => $actorId ],
__METHOD__
);
$dbw->update(
'logging',
[ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
- [ 'log_user' => $userId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
+ [ 'log_actor' => $actorId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
__METHOD__
);
$dbw->update(
'recentchanges',
[ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
- [ 'rc_user_text' => $name ],
+ [ 'rc_actor' => $actorId ],
__METHOD__
);
$dbw->update(
'oldimage',
[ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
- [ 'oi_user_text' => $name ],
+ [ 'oi_actor' => $actorId ],
__METHOD__
);
$dbw->update(
'filearchive',
[ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
- [ 'fa_user_text' => $name ],
+ [ 'fa_actor' => $actorId ],
__METHOD__
);
}
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $actorId = $dbw->selectField( 'actor', 'actor_id', [ 'actor_name' => $name ], __METHOD__ );
- if ( $actorId ) {
- # Hide name from live edits
- $ids = $dbw->selectFieldValues(
- 'revision_actor_temp', 'revactor_rev', [ 'revactor_actor' => $actorId ], __METHOD__
- );
- if ( $ids ) {
- $dbw->update(
- 'revision',
- [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
- [ 'rev_id' => $ids ],
- __METHOD__
- );
- }
-
- # Hide name from deleted edits
- $dbw->update(
- 'archive',
- [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
- [ 'ar_actor' => $actorId ],
- __METHOD__
- );
-
- # Hide name from logs
- $dbw->update(
- 'logging',
- [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
- [ 'log_actor' => $actorId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
- __METHOD__
- );
-
- # Hide name from RC
- $dbw->update(
- 'recentchanges',
- [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
- [ 'rc_actor' => $actorId ],
- __METHOD__
- );
-
- # Hide name from live images
- $dbw->update(
- 'oldimage',
- [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
- [ 'oi_actor' => $actorId ],
- __METHOD__
- );
-
- # Hide name from deleted images
- $dbw->update(
- 'filearchive',
- [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
- [ 'fa_actor' => $actorId ],
- __METHOD__
- );
- }
- }
-
# Hide log entries pointing to the user page
$dbw->update(
'logging',
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 ] );
}
use SpecialPage;
use Title;
use User;
+use Wikimedia\ObjectFactory;
/**
* Factory for handling the special page list and generating SpecialPage objects.
*/
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,
'ChangePassword' => \SpecialChangePassword::class,
'BotPasswords' => \SpecialBotPasswords::class,
'PasswordReset' => \SpecialPasswordReset::class,
- 'DeletedContributions' => \DeletedContributionsPage::class,
+ 'DeletedContributions' => \SpecialDeletedContributions::class,
'Preferences' => \SpecialPreferences::class,
'ResetTokens' => \SpecialResetTokens::class,
'Contributions' => \SpecialContributions::class,
// 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,
'Unlockdb' => \SpecialUnlockdb::class,
// Redirecting special pages
- 'LinkSearch' => \LinkSearchPage::class,
+ 'LinkSearch' => \SpecialLinkSearch::class,
'Randompage' => \RandomPage::class,
'RandomInCategory' => \SpecialRandomInCategory::class,
'Randomredirect' => \SpecialRandomredirect::class,
'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,
/** @var Language */
private $contLang;
+ /** @var ObjectFactory */
+ private $objectFactory;
+
/**
* TODO Make this a const when HHVM support is dropped (T192166)
*
/**
* @param ServiceOptions $options
* @param Language $contLang
+ * @param ObjectFactory $objectFactory
*/
- public function __construct( ServiceOptions $options, Language $contLang ) {
+ public function __construct(
+ ServiceOptions $options,
+ Language $contLang,
+ ObjectFactory $objectFactory
+ ) {
$options->assertRequiredOptions( self::$constructorOptions );
$this->options = $options;
$this->contLang = $contLang;
+ $this->objectFactory = $objectFactory;
}
/**
}
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' ) ) {
if ( isset( $specialPageList[$realName] ) ) {
$rec = $specialPageList[$realName];
- if ( is_callable( $rec ) ) {
- // Use callback to instantiate the special page
- $page = $rec();
- } elseif ( is_string( $rec ) ) {
- $className = $rec;
- $page = new $className;
- } elseif ( $rec instanceof SpecialPage ) {
+ if ( $rec instanceof SpecialPage ) {
+ wfDeprecated(
+ "a SpecialPage instance (for $realName) in " .
+ '$wgSpecialPages or from the SpecialPage_initList hook',
+ '1.34'
+ );
+
$page = $rec; // XXX: we should deep clone here
+ } elseif ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
+ $page = $this->objectFactory->createObject(
+ $rec,
+ [
+ 'allowClassName' => true,
+ 'allowCallable' => true
+ ]
+ );
} else {
$page = null;
}
--- /dev/null
+<?php
+/**
+ * Implements Special:Ancientpages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Implements Special:Ancientpages
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialAncientPages extends QueryPage {
+
+ function __construct( $name = 'Ancientpages' ) {
+ parent::__construct( $name );
+ }
+
+ public function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ public function getQueryInfo() {
+ $tables = [ 'page', 'revision' ];
+ $conds = [
+ 'page_namespace' =>
+ 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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Ancientpages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * Implements Special:Ancientpages
- *
- * @ingroup SpecialPage
- */
-class AncientPagesPage extends QueryPage {
-
- function __construct( $name = 'Ancientpages' ) {
- parent::__construct( $name );
- }
-
- public function isExpensive() {
- return true;
- }
-
- function isSyndicated() {
- return false;
- }
-
- public function getQueryInfo() {
- $tables = [ 'page', 'revision' ];
- $conds = [
- 'page_namespace' =>
- 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';
- }
-}
*
* @ingroup SpecialPage
*/
-class BrokenRedirectsPage extends QueryPage {
+class SpecialBrokenRedirects extends QueryPage {
function __construct( $name = 'BrokenRedirects' ) {
parent::__construct( $name );
}
--- /dev/null
+<?php
+/**
+ * Implements Special:Confirmemail
+ *
+ * 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
+ */
+
+/**
+ * Special page allows users to request email confirmation message, and handles
+ * processing of the confirmation code when the link in the email is followed
+ *
+ * @ingroup SpecialPage
+ * @author Brion Vibber
+ * @author Rob Church <robchur@gmail.com>
+ */
+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' => "<div class=\"error mw-confirmemail-pending\">\n" .
+ $this->msg( 'confirmemail_pending' )->escaped() .
+ "\n</div>",
+ ],
+ ];
+ }
+
+ $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 );
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Confirmemail
- *
- * 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
- */
-
-/**
- * Special page allows users to request email confirmation message, and handles
- * processing of the confirmation code when the link in the email is followed
- *
- * @ingroup SpecialPage
- * @author Brion Vibber
- * @author Rob Church <robchur@gmail.com>
- */
-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' => "<div class=\"error mw-confirmemail-pending\">\n" .
- $this->msg( 'confirmemail_pending' )->escaped() .
- "\n</div>",
- ],
- ];
- }
-
- $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 );
- }
- }
-}
$filterSelection = Html::rawElement( 'div', [], '' );
}
- $labelUsername = Xml::radioLabel(
+ $labelUsername = Xml::label(
$this->msg( 'sp-contributions-username' )->text(),
- 'contribs',
- 'user',
- 'user',
- true,
- [ 'class' => 'mw-input' ]
+ 'mw-target-user-or-ip'
);
$input = Html::input(
'target',
--- /dev/null
+<?php
+/**
+ * Implements Special:Deadenpages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * A special page that list pages that contain no link to other pages
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialDeadendPages extends PageQueryPage {
+
+ function __construct( $name = 'Deadendpages' ) {
+ parent::__construct( $name );
+ }
+
+ function getPageHeader() {
+ return $this->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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Deadenpages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * A special page that list pages that contain no link to other pages
- *
- * @ingroup SpecialPage
- */
-class DeadendPagesPage extends PageQueryPage {
-
- function __construct( $name = 'Deadendpages' ) {
- parent::__construct( $name );
- }
-
- function getPageHeader() {
- return $this->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';
- }
-}
* Implements Special:DeletedContributions to display archived revisions
* @ingroup SpecialPage
*/
-class DeletedContributionsPage extends SpecialPage {
+class SpecialDeletedContributions extends SpecialPage {
/** @var FormOptions */
protected $mOpts;
*
* @ingroup SpecialPage
*/
-class DoubleRedirectsPage extends QueryPage {
+class SpecialDoubleRedirects extends QueryPage {
function __construct( $name = 'DoubleRedirects' ) {
parent::__construct( $name );
}
*
* @ingroup SpecialPage
*/
-class EmailInvalidation extends UnlistedSpecialPage {
+class SpecialEmailInvalidate extends UnlistedSpecialPage {
public function __construct() {
parent::__construct( 'Invalidateemail', 'editmyprivateinfo' );
}
--- /dev/null
+<?php
+/**
+ * Implements Special:Fewestrevisions
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Special page for listing the articles with the fewest revisions.
+ *
+ * @ingroup SpecialPage
+ * @author Martin Drashkov
+ */
+class SpecialFewestRevisions extends QueryPage {
+ function __construct( $name = 'Fewestrevisions' ) {
+ parent::__construct( $name );
+ }
+
+ public function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ public function getQueryInfo() {
+ return [
+ 'tables' => [ '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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Fewestrevisions
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * Special page for listing the articles with the fewest revisions.
- *
- * @ingroup SpecialPage
- * @author Martin Drashkov
- */
-class FewestrevisionsPage extends QueryPage {
- function __construct( $name = 'Fewestrevisions' ) {
- parent::__construct( $name );
- }
-
- public function isExpensive() {
- return true;
- }
-
- function isSyndicated() {
- return false;
- }
-
- public function getQueryInfo() {
- return [
- 'tables' => [ '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';
- }
-}
*
* @ingroup SpecialPage
*/
-class FileDuplicateSearchPage extends QueryPage {
+class SpecialFileDuplicateSearch extends QueryPage {
protected $hash = '', $filename = '';
/**
* Special:LinkSearch to search the external-links table.
* @ingroup SpecialPage
*/
-class LinkSearchPage extends QueryPage {
+class SpecialLinkSearch extends QueryPage {
/** @var array|bool */
private $mungedQuery = false;
* 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 );
}
--- /dev/null
+<?php
+/**
+ * Implements Special:Listredirects
+ *
+ * Copyright © 2006 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 Rob Church <robchur@gmail.com>
+ */
+
+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 "<del>$rd_link</del>";
+ }
+ }
+
+ public function execute( $par ) {
+ $this->addHelpLink( 'Help:Redirects' );
+ parent::execute( $par );
+ }
+
+ protected function getGroupName() {
+ return 'pages';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Listredirects
- *
- * Copyright © 2006 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 Rob Church <robchur@gmail.com>
- */
-
-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 "<del>$rd_link</del>";
- }
- }
-
- public function execute( $par ) {
- $this->addHelpLink( 'Help:Redirects' );
- parent::execute( $par );
- }
-
- protected function getGroupName() {
- return 'pages';
- }
-}
}
public function execute( $par ) {
- global $wgActorTableSchemaMigrationStage;
-
$this->setHeaders();
$this->outputHeader();
$out = $this->getOutput();
$offenderName = $opts->getValue( 'offender' );
$offender = empty( $offenderName ) ? null : User::newFromName( $offenderName, false );
if ( $offender ) {
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ];
- } elseif ( $offender->getId() > 0 ) {
- $qc = [ 'ls_field' => 'target_author_id', 'ls_value' => $offender->getId() ];
- } else {
- $qc = [ 'ls_field' => 'target_author_ip', 'ls_value' => $offender->getName() ];
- }
+ $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ];
}
} else {
// Allow extensions to add relations to their search types
--- /dev/null
+<?php
+/**
+ * Implements Special:Lonelypaages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * A special page looking for articles with no article linking to them,
+ * thus being lonely.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialLonelyPages extends PageQueryPage {
+ function __construct( $name = 'Lonelypages' ) {
+ parent::__construct( $name );
+ }
+
+ function getPageHeader() {
+ return $this->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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Lonelypaages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * A special page looking for articles with no article linking to them,
- * thus being lonely.
- *
- * @ingroup SpecialPage
- */
-class LonelyPagesPage extends PageQueryPage {
- function __construct( $name = 'Lonelypages' ) {
- parent::__construct( $name );
- }
-
- function getPageHeader() {
- return $this->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';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Longpages
+ *
+ * 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
+ */
+
+/**
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialLongPages extends SpecialShortPages {
+ function __construct( $name = 'Longpages' ) {
+ parent::__construct( $name );
+ }
+
+ function sortDescending() {
+ return true;
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Longpages
- *
- * 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
- */
-
-/**
- *
- * @ingroup SpecialPage
- */
-class LongPagesPage extends ShortPagesPage {
- function __construct( $name = 'Longpages' ) {
- parent::__construct( $name );
- }
-
- function sortDescending() {
- return true;
- }
-
- protected function getGroupName() {
- return 'maintenance';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:MIMESearch
+ *
+ * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:MIMESearch
- *
- * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-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';
- }
-}
/**
* @ingroup SpecialPage
*/
-class MediaStatisticsPage extends QueryPage {
+class SpecialMediaStatistics extends QueryPage {
protected $totalCount = 0, $totalBytes = 0;
/**
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostcategories
+ *
+ * Copyright © 2005 Ævar Arnfjörð Bjarmason
+ *
+ * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostinterwikis
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IResultWrapper;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * A special page that listed pages that have highest interwiki count
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialMostInterwikis extends QueryPage {
+ function __construct( $name = 'Mostinterwikis' ) {
+ parent::__construct( $name );
+ }
+
+ public function isExpensive() {
+ return true;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ public function getQueryInfo() {
+ return [
+ 'tables' => [
+ '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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostlinked
+ *
+ * Copyright © 2005 Ævar Arnfjörð Bjarmason, 2006 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ * @author Rob Church <robchur@gmail.com>
+ */
+
+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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostlinkedcategories
+ *
+ * Copyright © 2005, Ævar Arnfjörð Bjarmason
+ *
+ * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostlinkedtemplates
+ *
+ * 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 Rob Church <robchur@gmail.com>
+ */
+
+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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Mostrevisions
+ *
+ * Copyright © 2005 Ævar Arnfjörð Bjarmason
+ *
+ * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+class SpecialMostRevisions extends SpecialFewestRevisions {
+ function __construct( $name = 'Mostrevisions' ) {
+ parent::__construct( $name );
+ }
+
+ function sortDescending() {
+ return true;
+ }
+
+ protected function getGroupName() {
+ return 'highuse';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostcategories
- *
- * Copyright © 2005 Ævar Arnfjörð Bjarmason
- *
- * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostinterwikis
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-use Wikimedia\Rdbms\IResultWrapper;
-use Wikimedia\Rdbms\IDatabase;
-
-/**
- * A special page that listed pages that have highest interwiki count
- *
- * @ingroup SpecialPage
- */
-class MostinterwikisPage extends QueryPage {
- function __construct( $name = 'Mostinterwikis' ) {
- parent::__construct( $name );
- }
-
- public function isExpensive() {
- return true;
- }
-
- function isSyndicated() {
- return false;
- }
-
- public function getQueryInfo() {
- return [
- 'tables' => [
- '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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostlinked
- *
- * Copyright © 2005 Ævar Arnfjörð Bjarmason, 2006 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- * @author Rob Church <robchur@gmail.com>
- */
-
-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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostlinkedcategories
- *
- * Copyright © 2005, Ævar Arnfjörð Bjarmason
- *
- * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostlinkedtemplates
- *
- * 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 Rob Church <robchur@gmail.com>
- */
-
-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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Mostrevisions
- *
- * Copyright © 2005 Ævar Arnfjörð Bjarmason
- *
- * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-class MostrevisionsPage extends FewestrevisionsPage {
- function __construct( $name = 'Mostrevisions' ) {
- parent::__construct( $name );
- }
-
- function sortDescending() {
- return true;
- }
-
- protected function getGroupName() {
- return 'highuse';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Newimages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+class SpecialNewFiles extends IncludableSpecialPage {
+ /** @var FormOptions */
+ protected $opts;
+
+ /** @var string[] */
+ protected $mediaTypes;
+
+ public function __construct() {
+ parent::__construct( 'Newimages' );
+ }
+
+ public function execute( $par ) {
+ $context = new DerivativeContext( $this->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"
+ )
+ );
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Newimages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-class SpecialNewFiles extends IncludableSpecialPage {
- /** @var FormOptions */
- protected $opts;
-
- /** @var string[] */
- protected $mediaTypes;
-
- public function __construct() {
- parent::__construct( 'Newimages' );
- }
-
- public function execute( $par ) {
- $context = new DerivativeContext( $this->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"
- )
- );
- }
- }
-}
* The web server should generally be configured to make this accessible via a canonical URL/URI,
* such as <http://my.domain.org/data/main/Foo>.
*
- * @class
* @ingroup SpecialPage
*/
class SpecialPageData extends SpecialPage {
--- /dev/null
+<?php
+/**
+ * Implements Special:Shortpages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IResultWrapper;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * SpecialShortpages extends QueryPage. It is used to return the shortest
+ * pages in the database.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialShortPages extends QueryPage {
+
+ function __construct( $name = 'Shortpages' ) {
+ parent::__construct( $name );
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ public function getQueryInfo() {
+ $config = $this->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}]"
+ : "<del>${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]</del>";
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Shortpages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-use Wikimedia\Rdbms\IResultWrapper;
-use Wikimedia\Rdbms\IDatabase;
-
-/**
- * SpecialShortpages extends QueryPage. It is used to return the shortest
- * pages in the database.
- *
- * @ingroup SpecialPage
- */
-class ShortPagesPage extends QueryPage {
-
- function __construct( $name = 'Shortpages' ) {
- parent::__construct( $name );
- }
-
- function isSyndicated() {
- return false;
- }
-
- public function getQueryInfo() {
- $config = $this->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}]"
- : "<del>${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]</del>";
- }
-
- protected function getGroupName() {
- return 'maintenance';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Uncategorizedcategories
+ *
+ * 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
+ */
+
+/**
+ * A special page that lists uncategorized categories
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialUncategorizedCategories extends SpecialUncategorizedPages {
+ /**
+ * Holds a list of categories, which shouldn't be listed on this special page,
+ * even if it is uncategorized.
+ * @var array
+ */
+ private $exceptionList = null;
+
+ function __construct( $name = 'Uncategorizedcategories' ) {
+ parent::__construct( $name );
+ $this->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 );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Uncategorizedimages
+ *
+ * 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 Rob Church <robchur@gmail.com>
+ */
+
+/**
+ * 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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Uncategorizedpages
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * A special page looking for page without any category.
+ *
+ * @ingroup SpecialPage
+ * @todo FIXME: Make $requestedNamespace selectable, unify all subclasses into one
+ */
+class SpecialUncategorizedPages extends PageQueryPage {
+ /** @var int|false */
+ protected $requestedNamespace = false;
+
+ function __construct( $name = 'Uncategorizedpages' ) {
+ 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'
+ ],
+ // 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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Uncategorizedtemplates
+ *
+ * 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 Rob Church <robchur@gmail.com>
+ */
+
+/**
+ * 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;
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Uncategorizedcategories
- *
- * 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
- */
-
-/**
- * A special page that lists uncategorized categories
- *
- * @ingroup SpecialPage
- */
-class UncategorizedCategoriesPage extends UncategorizedPagesPage {
- /**
- * Holds a list of categories, which shouldn't be listed on this special page,
- * even if it is uncategorized.
- * @var array
- */
- private $exceptionList = null;
-
- function __construct( $name = 'Uncategorizedcategories' ) {
- parent::__construct( $name );
- $this->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 );
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Uncategorizedimages
- *
- * 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 Rob Church <robchur@gmail.com>
- */
-
-/**
- * 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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Uncategorizedpages
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * A special page looking for page without any category.
- *
- * @ingroup SpecialPage
- * @todo FIXME: Make $requestedNamespace selectable, unify all subclasses into one
- */
-class UncategorizedPagesPage extends PageQueryPage {
- /** @var int|false */
- protected $requestedNamespace = false;
-
- function __construct( $name = 'Uncategorizedpages' ) {
- 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'
- ],
- // 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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Uncategorizedtemplates
- *
- * 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 Rob Church <robchur@gmail.com>
- */
-
-/**
- * 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;
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Unusedcategories
+ *
+ * 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
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class SpecialUnusedCategories extends QueryPage {
+ function __construct( $name = 'Unusedcategories' ) {
+ parent::__construct( $name );
+ }
+
+ public function isExpensive() {
+ return true;
+ }
+
+ function getPageHeader() {
+ return $this->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 );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Unusedimages
+ *
+ * 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
+ */
+
+/**
+ * A special page that lists unused images
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialUnusedImages extends ImageQueryPage {
+ function __construct( $name = 'Unusedimages' ) {
+ parent::__construct( $name );
+ }
+
+ function isExpensive() {
+ return true;
+ }
+
+ function sortDescending() {
+ return false;
+ }
+
+ function isSyndicated() {
+ return false;
+ }
+
+ function getQueryInfo() {
+ $retval = [
+ 'tables' => [ '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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Unusedtemplates
+ *
+ * Copyright © 2006 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 Rob Church <robchur@gmail.com>
+ */
+
+/**
+ * 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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Unusedcategories
- *
- * 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
- */
-
-/**
- * @ingroup SpecialPage
- */
-class UnusedCategoriesPage extends QueryPage {
- function __construct( $name = 'Unusedcategories' ) {
- parent::__construct( $name );
- }
-
- public function isExpensive() {
- return true;
- }
-
- function getPageHeader() {
- return $this->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 );
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Unusedimages
- *
- * 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
- */
-
-/**
- * A special page that lists unused images
- *
- * @ingroup SpecialPage
- */
-class UnusedimagesPage extends ImageQueryPage {
- function __construct( $name = 'Unusedimages' ) {
- parent::__construct( $name );
- }
-
- function isExpensive() {
- return true;
- }
-
- function sortDescending() {
- return false;
- }
-
- function isSyndicated() {
- return false;
- }
-
- function getQueryInfo() {
- $retval = [
- 'tables' => [ '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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Unusedtemplates
- *
- * Copyright © 2006 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 Rob Church <robchur@gmail.com>
- */
-
-/**
- * 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';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Unwatchedpages
+ *
+ * Copyright © 2005 Ævar Arnfjörð Bjarmason
+ *
+ * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Unwatchedpages
- *
- * Copyright © 2005 Ævar Arnfjörð Bjarmason
- *
- * 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 Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-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';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Wantedcategories
+ *
+ * Copyright © 2005 Ævar Arnfjörð Bjarmason
+ *
+ * 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
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * A querypage to list the most wanted categories - implements Special:Wantedcategories
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialWantedCategories extends WantedQueryPage {
+ private $currentCategoryCounts;
+
+ function __construct( $name = 'Wantedcategories' ) {
+ parent::__construct( $name );
+ }
+
+ function getQueryInfo() {
+ return [
+ 'tables' => [ '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 = "<del>$plink</del>";
+ }
+
+ // 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';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Wantedtemplates
+ *
+ * Copyright © 2008, Danny B.
+ * Based on SpecialWantedcategories.php by Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ * makeWlhLink() taken from SpecialMostlinkedtemplates by Rob Church <robchur@gmail.com>
+ *
+ * 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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Wantedcategories
- *
- * Copyright © 2005 Ævar Arnfjörð Bjarmason
- *
- * 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
- */
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * A querypage to list the most wanted categories - implements Special:Wantedcategories
- *
- * @ingroup SpecialPage
- */
-class WantedCategoriesPage extends WantedQueryPage {
- private $currentCategoryCounts;
-
- function __construct( $name = 'Wantedcategories' ) {
- parent::__construct( $name );
- }
-
- function getQueryInfo() {
- return [
- 'tables' => [ '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 = "<del>$plink</del>";
- }
-
- // 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';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Wantedtemplates
- *
- * Copyright © 2008, Danny B.
- * Based on SpecialWantedcategories.php by Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- * makeWlhLink() taken from SpecialMostlinkedtemplates by Rob Church <robchur@gmail.com>
- *
- * 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';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Withoutinterwiki
+ *
+ * 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 Rob Church <robchur@gmail.com>
+ */
+
+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';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Withoutinterwiki
- *
- * 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 Rob Church <robchur@gmail.com>
- */
-
-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';
- }
-}
function getQueryInfo( $data = null ) {
$dbr = $this->getDatabase();
- $useActor = (bool)(
- $this->getConfig()->get( 'ActorTableSchemaMigrationStage' ) & SCHEMA_COMPAT_READ_NEW
- );
-
$activeUserSeconds = $this->getConfig()->get( 'ActiveUserDays' ) * 86400;
$timestamp = $dbr->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
$fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
// Inner subselect to pull the active users out of querycachetwo
- $tables = [ 'querycachetwo', 'user' ];
- $fields = [ 'qcc_title', 'user_id' ];
+ $tables = [ 'querycachetwo', 'user', 'actor' ];
+ $fields = [ 'qcc_title', 'user_id', 'actor_id' ];
$jconds = [
'user' => [ 'JOIN', 'user_name = qcc_title' ],
+ 'actor' => [ 'JOIN', 'actor_user = user_id' ],
];
$conds = [
'qcc_type' => 'activeusers',
'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ]
) . ')';
}
- if ( $useActor ) {
- $tables[] = 'actor';
- $jconds['actor'] = [
- 'JOIN',
- 'actor_user = user_id',
- ];
- $fields[] = 'actor_id';
- }
$subquery = $dbr->buildSelectSubquery( $tables, $fields, $conds, $fname, $options, $jconds );
// Outer query to select the recent edit counts for the selected active users
$tables = [ 'qcc_users' => $subquery, 'recentchanges' ];
$jconds = [ 'recentchanges' => [ 'LEFT JOIN', [
- $useActor ? 'rc_actor = actor_id' : 'rc_user_text = qcc_title',
+ 'rc_actor = actor_id',
'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Don't count wikidata.
'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ), // Don't count categorization changes.
'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ),
}
if ( $opts->getValue( 'hidepatrolled' ) ) {
- global $wgActorTableSchemaMigrationStage;
-
$tables[] = 'recentchanges';
$conds['rc_type'] = RC_LOG;
$conds['rc_log_type'] = 'upload';
$conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
$conds['rc_namespace'] = NS_FILE;
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
- $jcond = 'rc_actor = ' . $imgQuery['fields']['img_actor'];
- } else {
- $rcQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
- $tables += $rcQuery['tables'];
- $jconds += $rcQuery['joins'];
- $jcond = $rcQuery['fields']['rc_user'] . ' = ' . $imgQuery['fields']['img_user'];
- }
$jconds['recentchanges'] = [
'JOIN',
[
'rc_title = img_name',
- $jcond,
+ 'rc_actor = ' . $imgQuery['fields']['img_actor'],
'rc_timestamp = img_timestamp'
]
];
* Version number to tag cached versions of serialized User objects. Should be increased when
* {@link $mCacheVars} or one of it's members changes.
*/
- const VERSION = 13;
+ const VERSION = 14;
/**
* Exclude user options that are set to their default value.
case 'defaults':
$this->loadDefaults();
break;
- case 'name':
- // Make sure this thread sees its own changes
- $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
- if ( $lb->hasOrMadeRecentMasterChanges() ) {
- $flags |= self::READ_LATEST;
- $this->queryFlagsUsed = $flags;
- }
-
- $this->mId = self::idFromName( $this->mName, $flags );
- if ( !$this->mId ) {
- // Nonexistent user placeholder object
- $this->loadDefaults( $this->mName );
- } else {
- $this->loadFromId( $flags );
- }
- break;
case 'id':
// Make sure this thread sees its own changes, if the ID isn't 0
if ( $this->mId != 0 ) {
$this->loadFromId( $flags );
break;
case 'actor':
+ case 'name':
// Make sure this thread sees its own changes
$lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
if ( $lb->hasOrMadeRecentMasterChanges() ) {
list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
$row = wfGetDB( $index )->selectRow(
'actor',
- [ 'actor_user', 'actor_name' ],
- [ 'actor_id' => $this->mActorId ],
+ [ 'actor_id', 'actor_user', 'actor_name' ],
+ $this->mFrom === 'name' ? [ 'actor_name' => $this->mName ] : [ 'actor_id' => $this->mActorId ],
__METHOD__,
$options
);
if ( !$row ) {
// Ugh.
- $this->loadDefaults();
+ $this->loadDefaults( $this->mFrom === 'name' ? $this->mName : false );
} elseif ( $row->actor_user ) {
$this->mId = $row->actor_user;
$this->loadFromId( $flags );
} else {
- $this->loadDefaults( $row->actor_name );
+ $this->loadDefaults( $row->actor_name, $row->actor_id );
}
break;
case 'session':
* @return User The corresponding User object
*/
public static function newFromActorId( $id ) {
- global $wgActorTableSchemaMigrationStage;
-
- // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) ) {
- throw new BadMethodCallException(
- 'Cannot use ' . __METHOD__
- . ' when $wgActorTableSchemaMigrationStage lacks SCHEMA_COMPAT_NEW'
- );
- }
-
$u = new User;
$u->mActorId = $id;
$u->mFrom = 'actor';
* @return User
*/
public static function newFromAnyId( $userId, $userName, $actorId, $dbDomain = false ) {
- global $wgActorTableSchemaMigrationStage;
-
// Stop-gap solution for the problem described in T222212.
// Force the User ID and Actor ID to zero for users loaded from the database
// of another wiki, to prevent subtle data corruption and confusing failure modes.
$user = new User;
$user->mFrom = 'defaults';
- // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) && $actorId !== null ) {
+ if ( $actorId !== null ) {
$user->mActorId = (int)$actorId;
if ( $user->mActorId !== 0 ) {
$user->mFrom = 'actor';
* the constructor does that instead.
*
* @param string|bool $name
+ * @param int|null $actorId
*/
- public function loadDefaults( $name = false ) {
+ public function loadDefaults( $name = false, $actorId = null ) {
$this->mId = 0;
$this->mName = $name;
- $this->mActorId = null;
+ $this->mActorId = $actorId;
$this->mRealName = '';
$this->mEmail = '';
$this->mOptionOverrides = null;
* user_properties Array with properties out of the user_properties table
*/
protected function loadFromRow( $row, $data = null ) {
- global $wgActorTableSchemaMigrationStage;
-
if ( !is_object( $row ) ) {
throw new InvalidArgumentException( '$row must be an object' );
}
$this->mGroupMemberships = null; // deferred
- // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
- if ( isset( $row->actor_id ) ) {
- $this->mActorId = (int)$row->actor_id;
- if ( $this->mActorId !== 0 ) {
- $this->mFrom = 'actor';
- }
- $this->setItemLoaded( 'actor' );
- } else {
- $all = false;
+ if ( isset( $row->actor_id ) ) {
+ $this->mActorId = (int)$row->actor_id;
+ if ( $this->mActorId !== 0 ) {
+ $this->mFrom = 'actor';
}
+ $this->setItemLoaded( 'actor' );
+ } else {
+ $all = false;
}
if ( isset( $row->user_name ) && $row->user_name !== '' ) {
if ( !$this->mHideName ) {
// Reset for hook
$this->mHideName = false;
- Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
+ Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ], '1.34' );
}
return (bool)$this->mHideName;
}
* @return int The user's ID; 0 if the user is anonymous or nonexistent
*/
public function getId() {
- if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
+ if ( $this->mId === null && $this->mName !== null &&
+ ( self::isIP( $this->mName ) || ExternalUserNames::isExternal( $this->mName ) )
+ ) {
// Special case, we know the user is anonymous
return 0;
}
* @return int The actor's ID, or 0 if no actor ID exists and $dbw was null
*/
public function getActorId( IDatabase $dbw = null ) {
- global $wgActorTableSchemaMigrationStage;
-
- // Technically we should always return 0 without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( !( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) ) {
- return 0;
- }
-
if ( !$this->isItemLoaded( 'actor' ) ) {
$this->load();
}
- // Currently $this->mActorId might be null if $this was loaded from a
- // cache entry that was written when $wgActorTableSchemaMigrationStage
- // was SCHEMA_COMPAT_OLD. Once that is no longer a possibility (i.e. when
- // User::VERSION is incremented after $wgActorTableSchemaMigrationStage
- // has been removed), that condition may be removed.
- if ( $this->mActorId === null || !$this->mActorId && $dbw ) {
+ if ( !$this->mActorId && $dbw ) {
$q = [
'actor_user' => $this->getId() ?: null,
'actor_name' => (string)$this->getName(),
];
- if ( $dbw ) {
- if ( $q['actor_user'] === null && self::isUsableName( $q['actor_name'] ) ) {
+ if ( $q['actor_user'] === null && self::isUsableName( $q['actor_name'] ) ) {
+ throw new CannotCreateActorException(
+ 'Cannot create an actor for a usable name that is not an existing user'
+ );
+ }
+ if ( $q['actor_name'] === '' ) {
+ throw new CannotCreateActorException( 'Cannot create an actor for a user with no name' );
+ }
+ $dbw->insert( 'actor', $q, __METHOD__, [ 'IGNORE' ] );
+ if ( $dbw->affectedRows() ) {
+ $this->mActorId = (int)$dbw->insertId();
+ } else {
+ // Outdated cache?
+ // Use LOCK IN SHARE MODE to bypass any MySQL REPEATABLE-READ snapshot.
+ $this->mActorId = (int)$dbw->selectField(
+ 'actor',
+ 'actor_id',
+ $q,
+ __METHOD__,
+ [ 'LOCK IN SHARE MODE' ]
+ );
+ if ( !$this->mActorId ) {
throw new CannotCreateActorException(
- 'Cannot create an actor for a usable name that is not an existing user'
- );
- }
- if ( $q['actor_name'] === '' ) {
- throw new CannotCreateActorException( 'Cannot create an actor for a user with no name' );
- }
- $dbw->insert( 'actor', $q, __METHOD__, [ 'IGNORE' ] );
- if ( $dbw->affectedRows() ) {
- $this->mActorId = (int)$dbw->insertId();
- } else {
- // Outdated cache?
- // Use LOCK IN SHARE MODE to bypass any MySQL REPEATABLE-READ snapshot.
- $this->mActorId = (int)$dbw->selectField(
- 'actor',
- 'actor_id',
- $q,
- __METHOD__,
- [ 'LOCK IN SHARE MODE' ]
+ "Cannot create actor ID for user_id={$this->getId()} user_name={$this->getName()}"
);
- if ( !$this->mActorId ) {
- throw new CannotCreateActorException(
- "Cannot create actor ID for user_id={$this->getId()} user_name={$this->getName()}"
- );
- }
}
- $this->invalidateCache();
- } else {
- list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $this->queryFlagsUsed );
- $db = wfGetDB( $index );
- $this->mActorId = (int)$db->selectField( 'actor', 'actor_id', $q, __METHOD__, $options );
}
+ $this->invalidateCache();
$this->setItemLoaded( 'actor' );
}
// If there is a new, unseen, revision, use its timestamp
$nextid = $oldid
- ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
+ ? $title->getNextRevisionID( $oldid, Title::READ_LATEST )
: null;
if ( $nextid ) {
$this->setNewtalk( true, Revision::newFromId( $nextid ) );
$dbw = wfGetDB( DB_MASTER );
$dbw->doAtomicSection( __METHOD__, function ( IDatabase $dbw, $fname ) use ( $newTouched ) {
- global $wgActorTableSchemaMigrationStage;
-
$dbw->update( 'user',
[ /* SET */
'user_name' => $this->mName,
);
}
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $dbw->update(
- 'actor',
- [ 'actor_name' => $this->mName ],
- [ 'actor_user' => $this->mId ],
- $fname
- );
- }
+ $dbw->update(
+ 'actor',
+ [ 'actor_name' => $this->mName ],
+ [ 'actor_user' => $this->mId ],
+ $fname
+ );
} );
$this->mTouched = $newTouched;
* @param IDatabase $dbw Writable database handle
*/
private function updateActorId( IDatabase $dbw ) {
- global $wgActorTableSchemaMigrationStage;
-
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $dbw->insert(
- 'actor',
- [ 'actor_user' => $this->mId, 'actor_name' => $this->mName ],
- __METHOD__
- );
- $this->mActorId = (int)$dbw->insertId();
- }
+ $dbw->insert(
+ 'actor',
+ [ 'actor_user' => $this->mId, 'actor_name' => $this->mName ],
+ __METHOD__
+ );
+ $this->mActorId = (int)$dbw->insertId();
}
/**
* - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
*/
public static function getQueryInfo() {
- global $wgActorTableSchemaMigrationStage;
-
$ret = [
- 'tables' => [ 'user' ],
+ 'tables' => [ 'user', 'user_actor' => 'actor' ],
'fields' => [
'user_id',
'user_name',
'user_email_token_expires',
'user_registration',
'user_editcount',
+ 'user_actor.actor_id',
+ ],
+ 'joins' => [
+ 'user_actor' => [ 'JOIN', 'user_actor.actor_user = user_id' ],
],
- 'joins' => [],
];
- // Technically we shouldn't allow this without SCHEMA_COMPAT_READ_NEW,
- // but it does little harm and might be needed for write callers loading a User.
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_NEW ) {
- $ret['tables']['user_actor'] = 'actor';
- $ret['fields'][] = 'user_actor.actor_id';
- $ret['joins']['user_actor'] = [
- ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ? 'JOIN' : 'LEFT JOIN',
- [ 'user_actor.actor_user = user_id' ]
- ];
- }
-
return $ret;
}
* @file
*/
+define( 'MW_ENTRY_POINT', 'index' );
+
// Bail on old versions of PHP, or if composer has not been run yet to install
// dependencies. Using dirname( __FILE__ ) here because __DIR__ is PHP5.3+.
// phpcs:ignore MediaWiki.Usage.DirUsage.FunctionFound
// details of the session. Enforce this constraint with respect to session use.
define( 'MW_NO_SESSION', 1 );
+define( 'MW_ENTRY_POINT', 'load' );
+
require __DIR__ . '/includes/WebStart.php';
// URL safety checks
* @defgroup Maintenance Maintenance
*/
+define( 'MW_ENTRY_POINT', 'cli' );
+
// Bail on old versions of PHP, or if composer has not been run yet to install
// dependencies.
require_once __DIR__ . '/../includes/PHPVersionCheck.php';
--- /dev/null
+--
+-- patch-drop-user-fields.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+ALTER TABLE /*_*/archive
+ DROP INDEX /*i*/ar_usertext_timestamp,
+ DROP COLUMN ar_user,
+ DROP COLUMN ar_user_text,
+ ALTER COLUMN ar_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/ipblocks
+ DROP COLUMN ipb_by,
+ DROP COLUMN ipb_by_text,
+ ALTER COLUMN ipb_by_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/image
+ DROP INDEX /*i*/img_user_timestamp,
+ DROP INDEX /*i*/img_usertext_timestamp,
+ DROP COLUMN img_user,
+ DROP COLUMN img_user_text,
+ ALTER COLUMN img_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/oldimage
+ DROP INDEX /*i*/oi_usertext_timestamp,
+ DROP COLUMN oi_user,
+ DROP COLUMN oi_user_text,
+ ALTER COLUMN oi_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/filearchive
+ DROP INDEX /*i*/fa_user_timestamp,
+ DROP COLUMN fa_user,
+ DROP COLUMN fa_user_text,
+ ALTER COLUMN fa_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/recentchanges
+ DROP INDEX /*i*/rc_ns_usertext,
+ DROP INDEX /*i*/rc_user_text,
+ DROP COLUMN rc_user,
+ DROP COLUMN rc_user_text,
+ ALTER COLUMN rc_actor DROP DEFAULT;
+
+ALTER TABLE /*_*/logging
+ DROP INDEX /*i*/user_time,
+ DROP INDEX /*i*/log_user_type_time,
+ DROP INDEX /*i*/log_user_text_type_time,
+ DROP INDEX /*i*/log_user_text_time,
+ DROP COLUMN log_user,
+ DROP COLUMN log_user_text,
+ ALTER COLUMN log_actor DROP DEFAULT;
'callback' => 'processRow',
];
+ /** @var LocalRepo|null */
+ private $repo;
+
public function __construct() {
parent::__construct();
$this->addDescription( 'Script to clean up broken, unparseable upload filenames' );
* @return string
*/
private function filePath( $name ) {
- if ( !isset( $this->repo ) ) {
+ if ( $this->repo === null ) {
$this->repo = RepoGroup::singleton()->getLocalRepo();
}
* @param Title $title
*/
protected function moveInconsistentPage( $row, Title $title ) {
- if ( $title->exists( Title::GAID_FOR_UPDATE )
+ if ( $title->exists( Title::READ_LATEST )
|| $title->getInterwiki()
|| !$title->canExist()
) {
class CompareParsers extends DumpIterator {
private $count = 0;
+ /** @var bool */
+ private $saveFailed;
+ /** @var bool */
+ private $stripParametersEnabled;
+ /** @var bool */
+ private $showParsedOutput;
+ /** @var bool */
+ private $showDiff;
+ /** @var ParserOptions */
+ private $options;
+ /** @var int */
+ private $failed;
public function __construct() {
parent::__construct();
* @ingroup Maintenance
*/
abstract class DumpIterator extends Maintenance {
-
private $count = 0;
private $startTime;
+ /** @var string|bool|null */
+ private $from;
public function __construct() {
parent::__construct();
$revision->setTitle( Title::newFromText(
rawurldecode( basename( $this->getOption( 'file' ), '.txt' ) )
) );
+ $this->from = false;
$this->handleRevision( $revision );
return;
}
$this->count++;
- if ( isset( $this->from ) ) {
+ if ( $this->from !== false ) {
if ( $this->from != $title ) {
return;
}
}
public function execute() {
- if ( $this->getConfig()->get( 'AllDBsAreLocalhost' ) ) {
- $host = 'localhost';
- } elseif ( $this->hasOption( 'group' ) ) {
+ if ( $this->hasOption( 'group' ) ) {
$db = $this->getDB( DB_REPLICA, $this->getOption( 'group' ) );
$host = $db->getServer();
} else {
public $imageBasePath = false;
/** @var array|false */
public $nsFilter = false;
+ /** @var bool|resource */
+ public $stderr;
+ /** @var callable|null */
+ protected $importCallback;
+ /** @var callable|null */
+ protected $logItemCallback;
+ /** @var callable|null */
+ protected $uploadCallback;
+ /** @var int */
+ protected $startTime;
function __construct() {
parent::__construct();
public $dumpUploadFileContents = false;
public $orderRevs = false;
public $limitNamespaces = [];
+ /** @var bool|resource */
+ public $stderr;
protected $reportingInterval = 100;
protected $pageCount = 0;
protected $ID = 0;
+ /** @var int */
+ protected $startTime;
+ /** @var int */
+ protected $pageCountPart;
+ /** @var int */
+ protected $revCountPart;
+ /** @var int */
+ protected $maxCount;
+ /** @var int */
+ protected $timeOfCheckpoint;
+ /** @var ExportProgressFilter */
+ protected $egress;
+ /** @var string */
+ protected $buffer;
+ /** @var array|false */
+ protected $openElement;
+ /** @var bool */
+ protected $atStart;
+ /** @var string|null */
+ protected $thisRevModel;
+ /** @var string|null */
+ protected $thisRevFormat;
+ /** @var string */
+ protected $lastName;
+ /** @var string */
+ protected $state;
+
/**
* The dependency-injected database to use.
*
}
protected function doDBUpdates() {
- $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
- if ( !( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) ) {
- $this->output(
- "...cannot update while \$wgActorTableSchemaMigrationStage lacks SCHEMA_COMPAT_WRITE_NEW\n"
- );
- return false;
- }
-
$tables = $this->getOption( 'tables' );
if ( $tables !== null ) {
$this->tables = explode( ',', $tables );
return 0;
}
+ $dbw = $this->getDB( DB_MASTER );
+ if ( !$dbw->fieldExists( $table, $userField, __METHOD__ ) ) {
+ $this->output( "No need to migrate $table.$userField, field does not exist\n" );
+ return 0;
+ }
+
$complainedAboutUsers = [];
$primaryKey = (array)$primaryKey;
);
wfWaitForSlaves();
- $dbw = $this->getDB( DB_MASTER );
$actorIdSubquery = $this->makeActorIdSubquery( $dbw, $userField, $nameField );
$next = '1=1';
$countUpdated = 0;
* @param string $nameField User name field name
* @param string $newPrimaryKey Primary key of the new table.
* @param string $actorField Actor field name
+ * @return int Number of errors
*/
protected function migrateToTemp(
$table, $primaryKey, $extra, $userField, $nameField, $newPrimaryKey, $actorField
return 0;
}
+ $dbw = $this->getDB( DB_MASTER );
+ if ( !$dbw->fieldExists( $table, $userField, __METHOD__ ) ) {
+ $this->output( "No need to migrate $table.$userField, field does not exist\n" );
+ return 0;
+ }
+
$complainedAboutUsers = [];
$newTable = $table . '_actor_temp';
);
wfWaitForSlaves();
- $dbw = $this->getDB( DB_MASTER );
$actorIdSubquery = $this->makeActorIdSubquery( $dbw, $userField, $nameField );
$next = [];
$countUpdated = 0;
try {
$installer = InstallerOverrides::getCliInstaller( $siteName, $adminName, $this->mOptions );
} catch ( \MediaWiki\Installer\InstallException $e ) {
- $this->output( $e->getStatus()->getMessage()->parse() . "\n" );
+ $this->output( $e->getStatus()->getMessage()->text() . "\n" );
return false;
}
* @ingroup Maintenance
*/
class MWDocGen extends Maintenance {
+ /** @var string */
+ private $doxygen;
+ /** @var string */
+ private $mwVersion;
+ /** @var string */
+ private $output;
+ /** @var string */
+ private $input;
+ /** @var string */
+ private $inputFilter;
+ /** @var string */
+ private $template;
+ /** @var string[] */
+ private $excludes;
+ /** @var string[] */
+ private $excludePatterns;
+ /** @var bool */
+ private $doDot;
+ /** @var bool */
+ private $doMan;
/**
* Prepare Maintenance class
ar_parent_id INTEGER NULL,
ar_sha1 TEXT NOT NULL DEFAULT '',
ar_comment_id INTEGER NOT NULL,
- ar_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- ar_user_text TEXT NOT NULL DEFAULT '',
- ar_actor INTEGER NOT NULL DEFAULT 0,
+ ar_actor INTEGER NOT NULL,
ar_timestamp TIMESTAMPTZ NOT NULL,
ar_minor_edit SMALLINT NOT NULL DEFAULT 0,
ar_rev_id INTEGER NOT NULL,
);
ALTER SEQUENCE archive_ar_id_seq OWNED BY archive.ar_id;
CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
-CREATE INDEX archive_user_text ON archive (ar_user_text);
CREATE INDEX archive_actor ON archive (ar_actor);
CREATE UNIQUE INDEX ar_revid_uniq ON archive (ar_rev_id);
ipb_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('ipblocks_ipb_id_seq'),
ipb_address TEXT NULL,
ipb_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- ipb_by INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
- ipb_by_text TEXT NOT NULL DEFAULT '',
- ipb_by_actor INTEGER NOT NULL DEFAULT 0,
+ ipb_by_actor INTEGER NOT NULL,
ipb_reason_id INTEGER NOT NULL,
ipb_timestamp TIMESTAMPTZ NOT NULL,
ipb_auto SMALLINT NOT NULL DEFAULT 0,
img_major_mime TEXT DEFAULT 'unknown',
img_minor_mime TEXT DEFAULT 'unknown',
img_description_id INTEGER NOT NULL,
- img_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- img_user_text TEXT NOT NULL DEFAULT '',
- img_actor INTEGER NOT NULL DEFAULT 0,
+ img_actor INTEGER NOT NULL,
img_timestamp TIMESTAMPTZ,
img_sha1 TEXT NOT NULL DEFAULT ''
);
oi_height INTEGER NOT NULL,
oi_bits SMALLINT NULL,
oi_description_id INTEGER NOT NULL,
- oi_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- oi_user_text TEXT NOT NULL DEFAULT '',
- oi_actor INTEGER NOT NULL DEFAULT 0,
+ oi_actor INTEGER NOT NULL,
oi_timestamp TIMESTAMPTZ NULL,
oi_metadata BYTEA NOT NULL DEFAULT '',
oi_media_type TEXT NULL,
fa_major_mime TEXT DEFAULT 'unknown',
fa_minor_mime TEXT DEFAULT 'unknown',
fa_description_id INTEGER NOT NULL,
- fa_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- fa_user_text TEXT NOT NULL DEFAULT '',
- fa_actor INTEGER NOT NULL DEFAULT 0,
+ fa_actor INTEGER NOT NULL,
fa_timestamp TIMESTAMPTZ,
fa_deleted SMALLINT NOT NULL DEFAULT 0,
fa_sha1 TEXT NOT NULL DEFAULT ''
CREATE TABLE recentchanges (
rc_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('recentchanges_rc_id_seq'),
rc_timestamp TIMESTAMPTZ NOT NULL,
- rc_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- rc_user_text TEXT NOT NULL DEFAULT '',
- rc_actor INTEGER NOT NULL DEFAULT 0,
+ rc_actor INTEGER NOT NULL,
rc_namespace SMALLINT NOT NULL,
rc_title TEXT NOT NULL,
rc_comment_id INTEGER NOT NULL,
log_type TEXT NOT NULL,
log_action TEXT NOT NULL,
log_timestamp TIMESTAMPTZ NOT NULL,
- log_user INTEGER NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
- log_actor INTEGER NOT NULL DEFAULT 0,
+ log_actor INTEGER NOT NULL,
log_namespace SMALLINT NOT NULL,
log_title TEXT NOT NULL,
log_comment_id INTEGER NOT NULL,
log_params TEXT,
log_deleted SMALLINT NOT NULL DEFAULT 0,
- log_user_text TEXT NOT NULL DEFAULT '',
log_page INTEGER
);
ALTER SEQUENCE logging_log_id_seq OWNED BY logging.log_id;
CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
-CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
CREATE INDEX logging_actor_time_backwards ON logging (log_timestamp, log_actor);
CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);
CREATE INDEX logging_times ON logging (log_timestamp);
-CREATE INDEX logging_user_type_time ON logging (log_user, log_type, log_timestamp);
CREATE INDEX logging_actor_type_time ON logging (log_actor, log_type, log_timestamp);
CREATE INDEX logging_page_id_time ON logging (log_page, log_timestamp);
-CREATE INDEX logging_user_text_type_time ON logging (log_user_text, log_type, log_timestamp);
-CREATE INDEX logging_user_text_time ON logging (log_user_text, log_timestamp);
CREATE INDEX logging_actor_time ON logging (log_actor, log_timestamp);
CREATE INDEX logging_type_action ON logging (log_type, log_action, log_timestamp);
/* Variables for dressing up as a parser */
public $mTitle = 'PreprocessDump';
public $mPPNodeCount = 0;
+ /** @var Preprocessor */
+ public $mPreprocessor;
public function getStripList() {
$parser = MediaWikiServices::getInstance()->getParser();
class PPFuzzTest {
public $templates, $mainText, $title, $entryPoint, $output;
+ /** @var PPFuzzTester */
+ private $parent;
+ /** @var string */
+ public $nickname;
+ /** @var bool */
+ public $fancySig;
+
/**
* @param PPFuzzTester $tester
*/
* @license GPL-2.0-or-later
*/
-use Wikimedia\Rdbms\IDatabase;
-
require_once __DIR__ . '/Maintenance.php';
/**
* @return int Number of entries changed, or that would be changed
*/
private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
- $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
$dbw = $this->getDB( DB_MASTER );
$this->beginTransaction( $dbw, __METHOD__ );
if ( $total ) {
# Reassign edits
$this->output( "\nReassigning current edits..." );
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $dbw->update(
- 'revision',
- [
- 'rev_user' => $to->getId(),
- 'rev_user_text' => $to->getName(),
- ],
- $from->isLoggedIn()
- ? [ 'rev_user' => $from->getId() ] : [ 'rev_user_text' => $from->getName() ],
- __METHOD__
- );
- }
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $dbw->update(
- 'revision_actor_temp',
- [ 'revactor_actor' => $to->getActorId( $dbw ) ],
- [ 'revactor_actor' => $from->getActorId() ],
- __METHOD__
- );
- }
+ $dbw->update(
+ 'revision_actor_temp',
+ [ 'revactor_actor' => $to->getActorId( $dbw ) ],
+ [ 'revactor_actor' => $from->getActorId() ],
+ __METHOD__
+ );
$this->output( "done.\nReassigning deleted edits..." );
$dbw->update( 'archive',
- $this->userSpecification( $dbw, $to, 'ar_user', 'ar_user_text', 'ar_actor' ),
+ [ 'ar_actor' => $to->getActorId( $dbw ) ],
[ $arQueryInfo['conds'] ], __METHOD__ );
$this->output( "done.\n" );
# Update recent changes if required
if ( $rc ) {
$this->output( "Updating recent changes..." );
$dbw->update( 'recentchanges',
- $this->userSpecification( $dbw, $to, 'rc_user', 'rc_user_text', 'rc_actor' ),
+ [ 'rc_actor' => $to->getActorId( $dbw ) ],
[ $rcQueryInfo['conds'] ], __METHOD__ );
$this->output( "done.\n" );
}
return (int)$total;
}
- /**
- * Return user specifications for an UPDATE
- * i.e. user => id, user_text => text
- *
- * @param IDatabase $dbw Database handle
- * @param User $user User for the spec
- * @param string $idfield Field name containing the identifier
- * @param string $utfield Field name containing the user text
- * @param string $acfield Field name containing the actor ID
- * @return array
- */
- private function userSpecification( IDatabase $dbw, &$user, $idfield, $utfield, $acfield ) {
- $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
- $ret = [];
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $ret += [
- $idfield => $user->getId(),
- $utfield => $user->getName(),
- ];
- }
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $ret += [ $acfield => $user->getActorId( $dbw ) ];
- }
- return $ret;
- }
-
/**
* Initialise the user object
*
* @ingroup Maintenance
*/
class ImageBuilder extends Maintenance {
-
/**
* @var IMaintainableDatabase
*/
protected $dbw;
+ /** @var bool */
+ private $dryrun;
+
+ /** @var LocalRepo|null */
+ private $repo;
+
+ /** @var int */
+ private $updated;
+
+ /** @var int */
+ private $processed;
+
+ /** @var int */
+ private $count;
+
+ /** @var int */
+ private $startTime;
+
+ /** @var string */
+ private $table;
+
function __construct() {
parent::__construct();
* @return LocalRepo
*/
function getRepo() {
- if ( !isset( $this->repo ) ) {
+ if ( $this->repo === null ) {
$this->repo = RepoGroup::singleton()->getLocalRepo();
}
* @ingroup Maintenance
*/
class RecountCategories extends Maintenance {
+ /** @var string */
+ private $mode;
+
+ /** @var int */
+ private $minimumId;
+
public function __construct() {
parent::__construct();
$this->addDescription( <<<'TEXT'
}
public function execute() {
- $actorTableSchemaMigrationStage = $this->getConfig()->get( 'ActorTableSchemaMigrationStage' );
-
$this->output( "Remove unused accounts\n\n" );
# Do an initial scan for inactive accounts and report the result
$delUser = [];
$delActor = [];
$dbr = $this->getDB( DB_REPLICA );
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $res = $dbr->select(
- [ 'user', 'actor' ],
- [ 'user_id', 'user_name', 'user_touched', 'actor_id' ],
- '',
- __METHOD__,
- [],
- [ 'actor' => [ 'LEFT JOIN', 'user_id = actor_user' ] ]
- );
- } else {
- $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
- }
+ $res = $dbr->select(
+ [ 'user', 'actor' ],
+ [ 'user_id', 'user_name', 'user_touched', 'actor_id' ],
+ '',
+ __METHOD__,
+ [],
+ [ 'actor' => [ 'LEFT JOIN', 'user_id = actor_user' ] ]
+ );
if ( $this->hasOption( 'ignore-groups' ) ) {
$excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
} else {
$this->output( "\nDeleting unused accounts..." );
$dbw = $this->getDB( DB_MASTER );
$dbw->delete( 'user', [ 'user_id' => $delUser ], __METHOD__ );
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- # Keep actor rows referenced from ipblocks
- $keep = $dbw->selectFieldValues(
- 'ipblocks', 'ipb_by_actor', [ 'ipb_by_actor' => $delActor ], __METHOD__
- );
- $del = array_diff( $delActor, $keep );
- if ( $del ) {
- $dbw->delete( 'actor', [ 'actor_id' => $del ], __METHOD__ );
- }
- if ( $keep ) {
- $dbw->update( 'actor', [ 'actor_user' => 0 ], [ 'actor_id' => $keep ], __METHOD__ );
- }
+ # Keep actor rows referenced from ipblocks
+ $keep = $dbw->selectFieldValues(
+ 'ipblocks', 'ipb_by_actor', [ 'ipb_by_actor' => $delActor ], __METHOD__
+ );
+ $del = array_diff( $delActor, $keep );
+ if ( $del ) {
+ $dbw->delete( 'actor', [ 'actor_id' => $del ], __METHOD__ );
+ }
+ if ( $keep ) {
+ $dbw->update( 'actor', [ 'actor_user' => 0 ], [ 'actor_id' => $keep ], __METHOD__ );
}
$dbw->delete( 'user_groups', [ 'ug_user' => $delUser ], __METHOD__ );
$dbw->delete( 'user_former_groups', [ 'ufg_user' => $delUser ], __METHOD__ );
$dbw->delete( 'user_properties', [ 'up_user' => $delUser ], __METHOD__ );
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $dbw->delete( 'logging', [ 'log_actor' => $delActor ], __METHOD__ );
- $dbw->delete( 'recentchanges', [ 'rc_actor' => $delActor ], __METHOD__ );
- }
- if ( $actorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
- $dbw->delete( 'logging', [ 'log_user' => $delUser ], __METHOD__ );
- $dbw->delete( 'recentchanges', [ 'rc_user' => $delUser ], __METHOD__ );
- }
+ $dbw->delete( 'logging', [ 'log_actor' => $delActor ], __METHOD__ );
+ $dbw->delete( 'recentchanges', [ 'rc_actor' => $delActor ], __METHOD__ );
$this->output( "done.\n" );
# Update the site_stats.ss_users field
$users = $dbw->selectField( 'user', 'COUNT(*)', [], __METHOD__ );
private $count = 0;
private $outputDirectory, $startTime;
+ /** @var string */
+ private $prefix;
public function __construct() {
parent::__construct();
--- /dev/null
+--
+-- patch-archive-drop-ar_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+CREATE TABLE /*_*/archive_tmp (
+ ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ar_namespace int NOT NULL default 0,
+ ar_title varchar(255) binary NOT NULL default '',
+ ar_comment_id bigint unsigned NOT NULL,
+ ar_actor bigint unsigned NOT NULL,
+ ar_timestamp binary(14) NOT NULL default '',
+ ar_minor_edit tinyint NOT NULL default 0,
+ ar_rev_id int unsigned NOT NULL,
+ ar_text_id int unsigned NOT NULL DEFAULT 0,
+ ar_deleted tinyint unsigned NOT NULL default 0,
+ ar_len int unsigned,
+ ar_page_id int unsigned,
+ ar_parent_id int unsigned default NULL,
+ ar_sha1 varbinary(32) NOT NULL default '',
+ ar_content_model varbinary(32) DEFAULT NULL,
+ ar_content_format varbinary(64) DEFAULT NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+ ar_id, ar_namespace, ar_title, ar_comment_id, ar_actor,
+ ar_timestamp, ar_minor_edit, ar_rev_id, ar_text_id, ar_deleted,
+ ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model, ar_content_format
+ ) SELECT
+ ar_id, ar_namespace, ar_title, ar_comment_id, ar_actor,
+ ar_timestamp, ar_minor_edit, ar_rev_id, ar_text_id, ar_deleted,
+ ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model, ar_content_format
+ FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
+CREATE UNIQUE INDEX /*i*/ar_revid_uniq ON /*_*/archive (ar_rev_id);
+
+COMMIT;
--- /dev/null
+--
+-- patch-filearchive-drop-fa_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/filearchive_tmp;
+CREATE TABLE /*_*/filearchive_tmp (
+ fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ fa_name varchar(255) binary NOT NULL default '',
+ fa_archive_name varchar(255) binary default '',
+ fa_storage_group varbinary(16),
+ fa_storage_key varbinary(64) default '',
+ fa_deleted_user int,
+ fa_deleted_timestamp binary(14) default '',
+ fa_deleted_reason_id bigint unsigned NOT NULL,
+ fa_size int unsigned default 0,
+ fa_width int default 0,
+ fa_height int default 0,
+ fa_metadata mediumblob,
+ fa_bits int default 0,
+ fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+ fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
+ fa_minor_mime varbinary(100) default "unknown",
+ fa_description_id bigint unsigned NOT NULL,
+ fa_actor bigint unsigned NOT NULL DEFAULT 0,
+ fa_timestamp binary(14) default '',
+ fa_deleted tinyint unsigned NOT NULL default 0,
+ fa_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/filearchive_tmp (
+ fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+ fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason_id,
+ fa_size, fa_width, fa_height, fa_metadata, fa_bits,
+ fa_media_type, fa_major_mime, fa_minor_mime, fa_description_id,
+ fa_actor, fa_timestamp, fa_deleted, fa_sha1
+ ) SELECT
+ fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+ fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason_id,
+ fa_size, fa_width, fa_height, fa_metadata, fa_bits,
+ fa_media_type, fa_major_mime, fa_minor_mime, fa_description_id,
+ fa_actor, fa_timestamp, fa_deleted, fa_sha1
+ FROM /*_*/filearchive;
+
+DROP TABLE /*_*/filearchive;
+ALTER TABLE /*_*/filearchive_tmp RENAME TO /*_*/filearchive;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
+CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
+
+COMMIT;
--- /dev/null
+--
+-- patch-image-drop-img_description.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/image_tmp;
+CREATE TABLE /*_*/image_tmp (
+ img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+ img_size int unsigned NOT NULL default 0,
+ img_width int NOT NULL default 0,
+ img_height int NOT NULL default 0,
+ img_metadata mediumblob NOT NULL,
+ img_bits int NOT NULL default 0,
+ img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+ img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+ img_minor_mime varbinary(100) NOT NULL default "unknown",
+ img_description_id bigint unsigned NOT NULL,
+ img_actor bigint unsigned NOT NULL,
+ img_timestamp varbinary(14) NOT NULL default '',
+ img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/image_tmp (
+ img_name, img_size, img_width, img_height, img_metadata, img_bits,
+ img_media_type, img_major_mime, img_minor_mime, img_description_id,
+ img_actor, img_timestamp, img_sha1
+ ) SELECT
+ img_name, img_size, img_width, img_height, img_metadata, img_bits,
+ img_media_type, img_major_mime, img_minor_mime, img_description_id,
+ img_actor, img_timestamp, img_sha1
+ FROM /*_*/image;
+
+DROP TABLE /*_*/image;
+ALTER TABLE /*_*/image_tmp RENAME TO /*_*/image;
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1(10));
+CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);
+
+COMMIT;
--- /dev/null
+--
+-- patch-ipblocks-drop-ipb_by.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS ipblocks_tmp;
+CREATE TABLE /*_*/ipblocks_tmp (
+ ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ipb_address tinyblob NOT NULL,
+ ipb_user int unsigned NOT NULL default 0,
+ ipb_by_actor bigint unsigned NOT NULL,
+ ipb_reason_id bigint unsigned NOT NULL,
+ ipb_timestamp binary(14) NOT NULL default '',
+ ipb_auto bool NOT NULL default 0,
+ ipb_anon_only bool NOT NULL default 0,
+ ipb_create_account bool NOT NULL default 1,
+ ipb_enable_autoblock bool NOT NULL default '1',
+ ipb_expiry varbinary(14) NOT NULL default '',
+ ipb_range_start tinyblob NOT NULL,
+ ipb_range_end tinyblob NOT NULL,
+ ipb_deleted bool NOT NULL default 0,
+ ipb_block_email bool NOT NULL default 0,
+ ipb_allow_usertalk bool NOT NULL default 0,
+ ipb_parent_block_id int default NULL,
+ ipb_sitewide bool NOT NULL default 1
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/ipblocks_tmp (
+ ipb_id, ipb_address, ipb_user, ipb_by_actor, ipb_reason_id,
+ ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+ ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+ ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id, ipb_sitewide
+ ) SELECT
+ ipb_id, ipb_address, ipb_user, ipb_by_actor, ipb_reason_id,
+ ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+ ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+ ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id, ipb_sitewide
+ FROM /*_*/ipblocks;
+
+DROP TABLE /*_*/ipblocks;
+ALTER TABLE /*_*/ipblocks_tmp RENAME TO /*_*/ipblocks;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE INDEX /*i*/ipb_parent_block_id ON /*_*/ipblocks (ipb_parent_block_id);
+
+COMMIT;
--- /dev/null
+--
+-- patch-logging-drop-log_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/logging_tmp;
+CREATE TABLE /*_*/logging_tmp (
+ log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ log_type varbinary(32) NOT NULL default '',
+ log_action varbinary(32) NOT NULL default '',
+ log_timestamp binary(14) NOT NULL default '19700101000000',
+ log_actor bigint unsigned NOT NULL DEFAULT 0,
+ log_namespace int NOT NULL default 0,
+ log_title varchar(255) binary NOT NULL default '',
+ log_page int unsigned NULL,
+ log_comment_id bigint unsigned NOT NULL,
+ log_params blob NOT NULL,
+ log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/logging_tmp (
+ log_id, log_type, log_action, log_timestamp, log_actor,
+ log_namespace, log_title, log_page, log_comment_id, log_params, log_deleted
+ ) SELECT
+ log_id, log_type, log_action, log_timestamp, log_actor,
+ log_namespace, log_title, log_page, log_comment_id, log_params, log_deleted
+ FROM /*_*/logging;
+
+DROP TABLE /*_*/logging;
+ALTER TABLE /*_*/logging_tmp RENAME TO /*_*/logging;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
+CREATE INDEX /*i*/log_type_action ON /*_*/logging (log_type, log_action, log_timestamp);
+
+COMMIT;
--- /dev/null
+--
+-- patch-oldimage-drop-oi_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/oldimage_tmp;
+CREATE TABLE /*_*/oldimage_tmp (
+ oi_name varchar(255) binary NOT NULL default '',
+ oi_archive_name varchar(255) binary NOT NULL default '',
+ oi_size int unsigned NOT NULL default 0,
+ oi_width int NOT NULL default 0,
+ oi_height int NOT NULL default 0,
+ oi_bits int NOT NULL default 0,
+ oi_description_id bigint unsigned NOT NULL,
+ oi_actor bigint unsigned NOT NULL,
+ oi_timestamp binary(14) NOT NULL default '',
+ oi_metadata mediumblob NOT NULL,
+ oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE", "3D") default NULL,
+ oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+ oi_minor_mime varbinary(100) NOT NULL default "unknown",
+ oi_deleted tinyint unsigned NOT NULL default 0,
+ oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/oldimage_tmp (
+ oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+ oi_description_id, oi_actor, oi_timestamp, oi_metadata,
+ oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+ ) SELECT
+ oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+ oi_description_id, oi_actor, oi_timestamp, oi_metadata,
+ oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+ FROM /*_*/oldimage;
+
+DROP TABLE /*_*/oldimage;
+ALTER TABLE /*_*/oldimage_tmp RENAME TO /*_*/oldimage;
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1(10));
+
+COMMIT;
--- /dev/null
+--
+-- patch-recentchanges-drop-rc_user.sql
+--
+-- T188327. Drop old xx_user and xx_user_text fields, and defaults from xx_actor fields.
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/recentchanges_tmp;
+CREATE TABLE /*_*/recentchanges_tmp (
+ rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rc_timestamp varbinary(14) NOT NULL default '',
+ rc_actor bigint unsigned NOT NULL DEFAULT 0,
+ rc_namespace int NOT NULL default 0,
+ rc_title varchar(255) binary NOT NULL default '',
+ rc_comment_id bigint unsigned NOT NULL,
+ rc_minor tinyint unsigned NOT NULL default 0,
+ rc_bot tinyint unsigned NOT NULL default 0,
+ rc_new tinyint unsigned NOT NULL default 0,
+ rc_cur_id int unsigned NOT NULL default 0,
+ rc_this_oldid int unsigned NOT NULL default 0,
+ rc_last_oldid int unsigned NOT NULL default 0,
+ rc_type tinyint unsigned NOT NULL default 0,
+ rc_source varchar(16) binary not null default '',
+ rc_patrolled tinyint unsigned NOT NULL default 0,
+ rc_ip varbinary(40) NOT NULL default '',
+ rc_old_len int,
+ rc_new_len int,
+ rc_deleted tinyint unsigned NOT NULL default 0,
+ rc_logid int unsigned NOT NULL default 0,
+ rc_log_type varbinary(255) NULL default NULL,
+ rc_log_action varbinary(255) NULL default NULL,
+ rc_params blob NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/recentchanges_tmp (
+ rc_id, rc_timestamp, rc_actor, rc_namespace, rc_title,
+ rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id, rc_this_oldid, rc_last_oldid,
+ rc_type, rc_source, rc_patrolled, rc_ip, rc_old_len, rc_new_len, rc_deleted,
+ rc_logid, rc_log_type, rc_log_action, rc_params
+ ) SELECT
+ rc_id, rc_timestamp, rc_actor, rc_namespace, rc_title,
+ rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id, rc_this_oldid, rc_last_oldid,
+ rc_type, rc_source, rc_patrolled, rc_ip, rc_old_len, rc_new_len, rc_deleted,
+ rc_logid, rc_log_type, rc_log_action, rc_params
+ FROM /*_*/recentchanges;
+
+DROP TABLE /*_*/recentchanges;
+ALTER TABLE /*_*/recentchanges_tmp RENAME TO /*_*/recentchanges;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title_timestamp ON /*_*/recentchanges (rc_namespace, rc_title, rc_timestamp);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
+CREATE INDEX /*i*/rc_this_oldid ON /*_*/recentchanges (rc_this_oldid);
+
+COMMIT;
/** @var ConcatenatedGzipHistoryBlob|false */
public $cgz;
public $referrers;
+ /** @var array */
+ private $texts;
/**
* Create a transaction from a RecompressTracked object
-- Basic revision stuff...
ar_comment_id bigint unsigned NOT NULL,
- ar_user int unsigned NOT NULL default 0, -- Deprecated in favor of ar_actor
- ar_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of ar_actor
- ar_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ar_user/ar_user_text should be used)
+ ar_actor bigint unsigned NOT NULL,
ar_timestamp binary(14) NOT NULL default '',
ar_minor_edit tinyint NOT NULL default 0,
CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
-- Index for Special:DeletedContributions
-CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
-- Index for linking archive rows with tables that normally link with revision
-- Blocked user ID or 0 for IP blocks.
ipb_user int unsigned NOT NULL default 0,
- -- User ID who made the block.
- ipb_by int unsigned NOT NULL default 0, -- Deprecated in favor of ipb_by_actor
-
- -- User name of blocker
- ipb_by_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of ipb_by_actor
-
-- Actor who made the block.
- ipb_by_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ipb_by/ipb_by_text should be used)
+ ipb_by_actor bigint unsigned NOT NULL,
-- Key to comment_id. Text comment made by blocker.
ipb_reason_id bigint unsigned NOT NULL,
-- This is displayed in image upload history and logs.
img_description_id bigint unsigned NOT NULL,
- -- user_id and user_name of uploader.
- -- Deprecated in favor of img_actor.
- img_user int unsigned NOT NULL default 0,
- img_user_text varchar(255) binary NOT NULL DEFAULT '',
-
-- actor_id of the uploader.
- -- ("DEFAULT 0" is temporary, signaling that img_user/img_user_text should be used)
- img_actor bigint unsigned NOT NULL DEFAULT 0,
+ img_actor bigint unsigned NOT NULL,
-- Time of the upload.
img_timestamp varbinary(14) NOT NULL default '',
) /*$wgDBTableOptions*/;
-- Used by Special:Newimages and ApiQueryAllImages
-CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
-CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
-- Used by Special:ListFiles for sort-by-size
CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
oi_height int NOT NULL default 0,
oi_bits int NOT NULL default 0,
oi_description_id bigint unsigned NOT NULL,
- oi_user int unsigned NOT NULL default 0, -- Deprecated in favor of oi_actor
- oi_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of oi_actor
- oi_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that oi_user/oi_user_text should be used)
+ oi_actor bigint unsigned NOT NULL,
oi_timestamp binary(14) NOT NULL default '',
oi_metadata mediumblob NOT NULL,
oi_sha1 varbinary(32) NOT NULL default ''
) /*$wgDBTableOptions*/;
-CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
-- oi_archive_name truncated to 14 to avoid key length overflow
fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
fa_minor_mime varbinary(100) default "unknown",
fa_description_id bigint unsigned NOT NULL,
- fa_user int unsigned default 0, -- Deprecated in favor of fa_actor
- fa_user_text varchar(255) binary DEFAULT '', -- Deprecated in favor of fa_actor
- fa_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that fa_user/fa_user_text should be used)
+ fa_actor bigint unsigned NOT NULL,
fa_timestamp binary(14) default '',
-- Visibility of deleted revisions, bitfield
-- sort by deletion time
CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
-- sort by uploader
-CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
-- find file by sha1, 10 bytes will be enough for hashes to be indexed
CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
rc_timestamp varbinary(14) NOT NULL default '',
-- As in revision
- rc_user int unsigned NOT NULL default 0, -- Deprecated in favor of rc_actor
- rc_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of rc_actor
- rc_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that rc_user/rc_user_text should be used)
+ rc_actor bigint unsigned NOT NULL,
-- When pages are renamed, their RC entries do _not_ change.
rc_namespace int NOT NULL default 0,
CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
-- Probably intended for Special:NewPages namespace filter
-CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
-- SiteStats active user count, Special:ActiveUsers, Special:NewPages user filter
-CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
-- ApiQueryRecentChanges (T140108)
-- Timestamp. Duh.
log_timestamp binary(14) NOT NULL default '19700101000000',
- -- The user who performed this action; key to user_id
- log_user int unsigned NOT NULL default 0, -- Deprecated in favor of log_actor
-
- -- Name of the user who performed this action
- log_user_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of log_actor
-
-- The actor who performed this action
- log_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that log_user/log_user_text should be used)
+ log_actor bigint unsigned NOT NULL,
-- Key to the page affected. Where a user is the target,
-- this will point to the user page.
CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
-- Special:Log performer filter
-CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
-- Special:Log title filter, log extract
CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
-- Special:Log filter by performer and type
-CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
-- Apparently just used for a few maintenance pages (findMissingFiles.php, Flow).
-- Special:Log action filter
CREATE INDEX /*i*/log_type_action ON /*_*/logging (log_type, log_action, log_timestamp);
--- Special:Log filter by type and anonymous performer
-CREATE INDEX /*i*/log_user_text_type_time ON /*_*/logging (log_user_text, log_type, log_timestamp);
-
--- Special:Log filter by anonymous performer
-CREATE INDEX /*i*/log_user_text_time ON /*_*/logging (log_user_text, log_timestamp);
-
CREATE TABLE /*_*/log_search (
-- The type of ID (rev ID, log ID, rev timestamp, username)
// details of the session. Enforce this constraint with respect to session use.
define( 'MW_NO_SESSION', 1 );
+define( 'MW_ENTRY_POINT', 'opensearch_desc' );
+
require_once __DIR__ . '/includes/WebStart.php';
if ( $wgRequest->getVal( 'ctype' ) == 'application/xml' ) {
// details of the session. Enforce this constraint with respect to session use.
define( 'MW_NO_SESSION', 1 );
+define( 'MW_ENTRY_POINT', 'profileinfo' );
+
ini_set( 'zlib.output_compression', 'off' );
require __DIR__ . '/includes/WebStart.php';
],
'mediawiki.RegExp' => [
'deprecated' => 'Please use mw.util.escapeRegExp() instead.',
- 'scripts' => 'resources/src/mediawiki.RegExp.js',
'targets' => [ 'desktop', 'mobile' ],
'dependencies' => [
'mediawiki.util',
'mediawiki.util',
'mediawiki.Title',
'mediawiki.jqueryMsg',
- 'mediawiki.util',
],
'messages' => [
'watch',
+++ /dev/null
-( function () {
- mw.RegExp = {};
- // Backwards-compatible alias; @deprecated since 1.34
- mw.log.deprecate( mw.RegExp, 'escape', mw.util.escapeRegExp, 'Use mw.util.escapeRegExp() instead.', 'mw.RegExp.escape' );
-}() );
'[^' + mw.config.get( 'wgLegalTitleChars' ) + ']' +
// URL percent encoding sequences interfere with the ability
// to round-trip titles -- you can't link to them consistently.
- '|%[0-9A-Fa-f]{2}' +
+ '|%[\\dA-Fa-f]{2}' +
// XML/HTML character references produce similar issues.
- '|&[A-Za-z0-9\u0080-\uFFFF]+;' +
- '|&#[0-9]+;' +
- '|&#x[0-9A-Fa-f]+;'
+ '|&[\\dA-Za-z\u0080-\uFFFF]+;' +
+ '|&#\\d+;' +
+ '|&#x[\\dA-Fa-f]+;'
),
// From MediaWikiTitleCodec::splitTitleString() in PHP
rWhitespace = /[ _\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g,
// From MediaWikiTitleCodec::splitTitleString() in PHP
- rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]/g,
+ rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]+/g,
/**
* Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
},
// URL encoding (possibly)
{
- pattern: /%([0-9A-Fa-f]{2})/g,
+ pattern: /%([\dA-Fa-f]{2})/g,
replace: '% $1',
generalRule: true
},
// HTML-character-entities
{
- pattern: /&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g,
+ pattern: /&(([\dA-Za-z\x80-\xff]+|#\d+|#x[\dA-Fa-f]+);)/g,
replace: '& $1',
generalRule: true
},
* @return {Object|boolean}
*/
parse = function ( title, defaultNamespace ) {
- var namespace, m, id, i, fragment, ext;
+ var namespace, m, id, i, fragment;
namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
}
// Reject illegal characters
- if ( title.match( rInvalid ) ) {
+ if ( rInvalid.test( title ) ) {
return false;
}
return false;
}
- // For backwards-compatibility with old mw.Title, we separate the extension from the
- // rest of the title.
- i = title.lastIndexOf( '.' );
- if ( i === -1 || title.length <= i + 1 ) {
- // Extensions are the non-empty segment after the last dot
- ext = null;
- } else {
- ext = title.slice( i + 1 );
- title = title.slice( 0, i );
- }
-
return {
namespace: namespace,
title: title,
- ext: ext,
fragment: fragment
};
},
this.namespace = parsed.namespace;
this.title = parsed.title;
- this.ext = parsed.ext;
this.fragment = parsed.fragment;
}
t = Object.create( Title.prototype );
t.namespace = parsed.namespace;
t.title = parsed.title;
- t.ext = parsed.ext;
t.fragment = parsed.fragment;
return t;
* @return {mw.Title|null} A valid Title object or null if the title is invalid
*/
Title.newFromFileName = function ( uncleanName ) {
-
return Title.newFromUserInput( 'File:' + uncleanName, {
forUploading: true
} );
thumbPhpRegex = /thumb\.php/,
regexes = [
// Thumbnails
- /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
+ /\/[\da-f]\/[\da-f]{2}\/([^\s/]+)\/[^\s/]+-[^\s/]*$/,
// Full size images
- /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s/]+)$/,
+ /\/[\da-f]\/[\da-f]{2}\/([^\s/]+)$/,
// Thumbnails in non-hashed upload directories
/\/([^\s/]+)\/[^\s/]+-(?:\1|thumbnail)[^\s/]*$/,
src = img.jquery ? img[ 0 ].src : img.src;
- matches = src.match( thumbPhpRegex );
-
- if ( matches ) {
+ if ( thumbPhpRegex.test( src ) ) {
return mw.Title.newFromText( 'File:' + mw.util.getParamValue( 'f', src ) );
}
Title.normalizeExtension = function ( extension ) {
var
lower = extension.toLowerCase(),
- squish = {
+ normalizations = {
htm: 'html',
jpeg: 'jpg',
mpeg: 'mpg',
tiff: 'tif',
ogv: 'ogg'
};
- if ( Object.prototype.hasOwnProperty.call( squish, lower ) ) {
- return squish[ lower ];
- } else if ( /^[0-9a-z]+$/.test( lower ) ) {
+ if ( Object.hasOwnProperty.call( normalizations, lower ) ) {
+ return normalizations[ lower ];
+ } else if ( /^[\da-z]+$/.test( lower ) ) {
return lower;
} else {
return '';
* @return {string}
*/
getName: function () {
- if (
- mw.config.get( 'wgCaseSensitiveNamespaces' ).indexOf( this.namespace ) !== -1 ||
- !this.title.length
- ) {
- return this.title;
+ var ext = this.getExtension();
+ if ( ext === null ) {
+ return this.getMain();
}
- return mw.Title.phpCharToUpper( this.title[ 0 ] ) + this.title.slice( 1 );
+ return this.getMain().slice( 0, -ext.length - 1 );
},
/**
* @return {string|null} Name extension or null if there is none
*/
getExtension: function () {
- return this.ext;
+ var lastDot = this.title.lastIndexOf( '.' );
+ if ( lastDot === -1 ) {
+ return null;
+ }
+ return this.title.slice( lastDot + 1 ) || null;
},
/**
* @return {string}
*/
getDotExtension: function () {
- return this.ext === null ? '' : '.' + this.ext;
+ var ext = this.getExtension();
+ return ext === null ? '' : '.' + ext;
},
/**
* @return {string}
*/
getMain: function () {
- return this.getName() + this.getDotExtension();
+ if (
+ mw.config.get( 'wgCaseSensitiveNamespaces' ).indexOf( this.namespace ) !== -1 ||
+ !this.title.length
+ ) {
+ return this.title;
+ }
+ return mw.Title.phpCharToUpper( this.title[ 0 ] ) + this.title.slice( 1 );
},
/**
{
"ß": "ß",
"ʼn": "ʼn",
+ "ƀ": "ƀ",
+ "ƚ": "ƚ",
"Dž": "Dž",
"dž": "Dž",
"Lj": "Lj",
"ǰ": "ǰ",
"Dz": "Dz",
"dz": "Dz",
- "ʝ": "Ʝ",
+ "ȼ": "ȼ",
+ "ȿ": "ȿ",
+ "ɀ": "ɀ",
+ "ɂ": "ɂ",
+ "ɇ": "ɇ",
+ "ɉ": "ɉ",
+ "ɋ": "ɋ",
+ "ɍ": "ɍ",
+ "ɏ": "ɏ",
+ "ɐ": "ɐ",
+ "ɑ": "ɑ",
+ "ɒ": "ɒ",
+ "ɜ": "ɜ",
+ "ɡ": "ɡ",
+ "ɥ": "ɥ",
+ "ɦ": "ɦ",
+ "ɪ": "ɪ",
+ "ɫ": "ɫ",
+ "ɬ": "ɬ",
+ "ɱ": "ɱ",
+ "ɽ": "ɽ",
+ "ʂ": "ʂ",
+ "ʇ": "ʇ",
+ "ʉ": "ʉ",
+ "ʌ": "ʌ",
+ "ʝ": "ʝ",
+ "ʞ": "ʞ",
"ͅ": "ͅ",
+ "ͱ": "ͱ",
+ "ͳ": "ͳ",
+ "ͷ": "ͷ",
+ "ͻ": "ͻ",
+ "ͼ": "ͼ",
+ "ͽ": "ͽ",
"ΐ": "ΐ",
"ΰ": "ΰ",
+ "ϗ": "ϗ",
+ "ϲ": "Σ",
+ "ϳ": "ϳ",
+ "ϸ": "ϸ",
+ "ϻ": "ϻ",
+ "ӏ": "ӏ",
+ "ӷ": "ӷ",
+ "ӻ": "ӻ",
+ "ӽ": "ӽ",
+ "ӿ": "ӿ",
+ "ԑ": "ԑ",
+ "ԓ": "ԓ",
+ "ԕ": "ԕ",
+ "ԗ": "ԗ",
+ "ԙ": "ԙ",
+ "ԛ": "ԛ",
+ "ԝ": "ԝ",
+ "ԟ": "ԟ",
+ "ԡ": "ԡ",
+ "ԣ": "ԣ",
+ "ԥ": "ԥ",
+ "ԧ": "ԧ",
+ "ԩ": "ԩ",
+ "ԫ": "ԫ",
+ "ԭ": "ԭ",
+ "ԯ": "ԯ",
"և": "և",
- "ᏸ": "Ᏸ",
- "ᏹ": "Ᏹ",
- "ᏺ": "Ᏺ",
- "ᏻ": "Ᏻ",
- "ᏼ": "Ᏼ",
- "ᏽ": "Ᏽ",
+ "ა": "ა",
+ "ბ": "ბ",
+ "გ": "გ",
+ "დ": "დ",
+ "ე": "ე",
+ "ვ": "ვ",
+ "ზ": "ზ",
+ "თ": "თ",
+ "ი": "ი",
+ "კ": "კ",
+ "ლ": "ლ",
+ "მ": "მ",
+ "ნ": "ნ",
+ "ო": "ო",
+ "პ": "პ",
+ "ჟ": "ჟ",
+ "რ": "რ",
+ "ს": "ს",
+ "ტ": "ტ",
+ "უ": "უ",
+ "ფ": "ფ",
+ "ქ": "ქ",
+ "ღ": "ღ",
+ "ყ": "ყ",
+ "შ": "შ",
+ "ჩ": "ჩ",
+ "ც": "ც",
+ "ძ": "ძ",
+ "წ": "წ",
+ "ჭ": "ჭ",
+ "ხ": "ხ",
+ "ჯ": "ჯ",
+ "ჰ": "ჰ",
+ "ჱ": "ჱ",
+ "ჲ": "ჲ",
+ "ჳ": "ჳ",
+ "ჴ": "ჴ",
+ "ჵ": "ჵ",
+ "ჶ": "ჶ",
+ "ჷ": "ჷ",
+ "ჸ": "ჸ",
+ "ჹ": "ჹ",
+ "ჺ": "ჺ",
+ "ჽ": "ჽ",
+ "ჾ": "ჾ",
+ "ჿ": "ჿ",
+ "ᏸ": "ᏸ",
+ "ᏹ": "ᏹ",
+ "ᏺ": "ᏺ",
+ "ᏻ": "ᏻ",
+ "ᏼ": "ᏼ",
+ "ᏽ": "ᏽ",
+ "ᲀ": "ᲀ",
+ "ᲁ": "ᲁ",
+ "ᲂ": "ᲂ",
+ "ᲃ": "ᲃ",
+ "ᲄ": "ᲄ",
+ "ᲅ": "ᲅ",
+ "ᲆ": "ᲆ",
+ "ᲇ": "ᲇ",
+ "ᲈ": "ᲈ",
+ "ᵹ": "ᵹ",
+ "ᵽ": "ᵽ",
+ "ᶎ": "ᶎ",
"ẖ": "ẖ",
"ẗ": "ẗ",
"ẘ": "ẘ",
"ẙ": "ẙ",
"ẚ": "ẚ",
+ "ỻ": "ỻ",
+ "ỽ": "ỽ",
+ "ỿ": "ỿ",
"ὐ": "ὐ",
"ὒ": "ὒ",
"ὔ": "ὔ",
"ῶ": "ῶ",
"ῷ": "ῷ",
"ῼ": "ῼ",
+ "ⅎ": "ⅎ",
"ⅰ": "ⅰ",
"ⅱ": "ⅱ",
"ⅲ": "ⅲ",
"ⅽ": "ⅽ",
"ⅾ": "ⅾ",
"ⅿ": "ⅿ",
+ "ↄ": "ↄ",
"ⓐ": "ⓐ",
"ⓑ": "ⓑ",
"ⓒ": "ⓒ",
"ⓧ": "ⓧ",
"ⓨ": "ⓨ",
"ⓩ": "ⓩ",
- "ꞵ": "Ꞵ",
- "ꞷ": "Ꞷ",
- "ꭓ": "Ꭓ",
- "ꭰ": "Ꭰ",
- "ꭱ": "Ꭱ",
- "ꭲ": "Ꭲ",
- "ꭳ": "Ꭳ",
- "ꭴ": "Ꭴ",
- "ꭵ": "Ꭵ",
- "ꭶ": "Ꭶ",
- "ꭷ": "Ꭷ",
- "ꭸ": "Ꭸ",
- "ꭹ": "Ꭹ",
- "ꭺ": "Ꭺ",
- "ꭻ": "Ꭻ",
- "ꭼ": "Ꭼ",
- "ꭽ": "Ꭽ",
- "ꭾ": "Ꭾ",
- "ꭿ": "Ꭿ",
- "ꮀ": "Ꮀ",
- "ꮁ": "Ꮁ",
- "ꮂ": "Ꮂ",
- "ꮃ": "Ꮃ",
- "ꮄ": "Ꮄ",
- "ꮅ": "Ꮅ",
- "ꮆ": "Ꮆ",
- "ꮇ": "Ꮇ",
- "ꮈ": "Ꮈ",
- "ꮉ": "Ꮉ",
- "ꮊ": "Ꮊ",
- "ꮋ": "Ꮋ",
- "ꮌ": "Ꮌ",
- "ꮍ": "Ꮍ",
- "ꮎ": "Ꮎ",
- "ꮏ": "Ꮏ",
- "ꮐ": "Ꮐ",
- "ꮑ": "Ꮑ",
- "ꮒ": "Ꮒ",
- "ꮓ": "Ꮓ",
- "ꮔ": "Ꮔ",
- "ꮕ": "Ꮕ",
- "ꮖ": "Ꮖ",
- "ꮗ": "Ꮗ",
- "ꮘ": "Ꮘ",
- "ꮙ": "Ꮙ",
- "ꮚ": "Ꮚ",
- "ꮛ": "Ꮛ",
- "ꮜ": "Ꮜ",
- "ꮝ": "Ꮝ",
- "ꮞ": "Ꮞ",
- "ꮟ": "Ꮟ",
- "ꮠ": "Ꮠ",
- "ꮡ": "Ꮡ",
- "ꮢ": "Ꮢ",
- "ꮣ": "Ꮣ",
- "ꮤ": "Ꮤ",
- "ꮥ": "Ꮥ",
- "ꮦ": "Ꮦ",
- "ꮧ": "Ꮧ",
- "ꮨ": "Ꮨ",
- "ꮩ": "Ꮩ",
- "ꮪ": "Ꮪ",
- "ꮫ": "Ꮫ",
- "ꮬ": "Ꮬ",
- "ꮭ": "Ꮭ",
- "ꮮ": "Ꮮ",
- "ꮯ": "Ꮯ",
- "ꮰ": "Ꮰ",
- "ꮱ": "Ꮱ",
- "ꮲ": "Ꮲ",
- "ꮳ": "Ꮳ",
- "ꮴ": "Ꮴ",
- "ꮵ": "Ꮵ",
- "ꮶ": "Ꮶ",
- "ꮷ": "Ꮷ",
- "ꮸ": "Ꮸ",
- "ꮹ": "Ꮹ",
- "ꮺ": "Ꮺ",
- "ꮻ": "Ꮻ",
- "ꮼ": "Ꮼ",
- "ꮽ": "Ꮽ",
- "ꮾ": "Ꮾ",
- "ꮿ": "Ꮿ",
+ "ⰰ": "ⰰ",
+ "ⰱ": "ⰱ",
+ "ⰲ": "ⰲ",
+ "ⰳ": "ⰳ",
+ "ⰴ": "ⰴ",
+ "ⰵ": "ⰵ",
+ "ⰶ": "ⰶ",
+ "ⰷ": "ⰷ",
+ "ⰸ": "ⰸ",
+ "ⰹ": "ⰹ",
+ "ⰺ": "ⰺ",
+ "ⰻ": "ⰻ",
+ "ⰼ": "ⰼ",
+ "ⰽ": "ⰽ",
+ "ⰾ": "ⰾ",
+ "ⰿ": "ⰿ",
+ "ⱀ": "ⱀ",
+ "ⱁ": "ⱁ",
+ "ⱂ": "ⱂ",
+ "ⱃ": "ⱃ",
+ "ⱄ": "ⱄ",
+ "ⱅ": "ⱅ",
+ "ⱆ": "ⱆ",
+ "ⱇ": "ⱇ",
+ "ⱈ": "ⱈ",
+ "ⱉ": "ⱉ",
+ "ⱊ": "ⱊ",
+ "ⱋ": "ⱋ",
+ "ⱌ": "ⱌ",
+ "ⱍ": "ⱍ",
+ "ⱎ": "ⱎ",
+ "ⱏ": "ⱏ",
+ "ⱐ": "ⱐ",
+ "ⱑ": "ⱑ",
+ "ⱒ": "ⱒ",
+ "ⱓ": "ⱓ",
+ "ⱔ": "ⱔ",
+ "ⱕ": "ⱕ",
+ "ⱖ": "ⱖ",
+ "ⱗ": "ⱗ",
+ "ⱘ": "ⱘ",
+ "ⱙ": "ⱙ",
+ "ⱚ": "ⱚ",
+ "ⱛ": "ⱛ",
+ "ⱜ": "ⱜ",
+ "ⱝ": "ⱝ",
+ "ⱞ": "ⱞ",
+ "ⱡ": "ⱡ",
+ "ⱥ": "ⱥ",
+ "ⱦ": "ⱦ",
+ "ⱨ": "ⱨ",
+ "ⱪ": "ⱪ",
+ "ⱬ": "ⱬ",
+ "ⱳ": "ⱳ",
+ "ⱶ": "ⱶ",
+ "ⲁ": "ⲁ",
+ "ⲃ": "ⲃ",
+ "ⲅ": "ⲅ",
+ "ⲇ": "ⲇ",
+ "ⲉ": "ⲉ",
+ "ⲋ": "ⲋ",
+ "ⲍ": "ⲍ",
+ "ⲏ": "ⲏ",
+ "ⲑ": "ⲑ",
+ "ⲓ": "ⲓ",
+ "ⲕ": "ⲕ",
+ "ⲗ": "ⲗ",
+ "ⲙ": "ⲙ",
+ "ⲛ": "ⲛ",
+ "ⲝ": "ⲝ",
+ "ⲟ": "ⲟ",
+ "ⲡ": "ⲡ",
+ "ⲣ": "ⲣ",
+ "ⲥ": "ⲥ",
+ "ⲧ": "ⲧ",
+ "ⲩ": "ⲩ",
+ "ⲫ": "ⲫ",
+ "ⲭ": "ⲭ",
+ "ⲯ": "ⲯ",
+ "ⲱ": "ⲱ",
+ "ⲳ": "ⲳ",
+ "ⲵ": "ⲵ",
+ "ⲷ": "ⲷ",
+ "ⲹ": "ⲹ",
+ "ⲻ": "ⲻ",
+ "ⲽ": "ⲽ",
+ "ⲿ": "ⲿ",
+ "ⳁ": "ⳁ",
+ "ⳃ": "ⳃ",
+ "ⳅ": "ⳅ",
+ "ⳇ": "ⳇ",
+ "ⳉ": "ⳉ",
+ "ⳋ": "ⳋ",
+ "ⳍ": "ⳍ",
+ "ⳏ": "ⳏ",
+ "ⳑ": "ⳑ",
+ "ⳓ": "ⳓ",
+ "ⳕ": "ⳕ",
+ "ⳗ": "ⳗ",
+ "ⳙ": "ⳙ",
+ "ⳛ": "ⳛ",
+ "ⳝ": "ⳝ",
+ "ⳟ": "ⳟ",
+ "ⳡ": "ⳡ",
+ "ⳣ": "ⳣ",
+ "ⳬ": "ⳬ",
+ "ⳮ": "ⳮ",
+ "ⳳ": "ⳳ",
+ "ⴀ": "ⴀ",
+ "ⴁ": "ⴁ",
+ "ⴂ": "ⴂ",
+ "ⴃ": "ⴃ",
+ "ⴄ": "ⴄ",
+ "ⴅ": "ⴅ",
+ "ⴆ": "ⴆ",
+ "ⴇ": "ⴇ",
+ "ⴈ": "ⴈ",
+ "ⴉ": "ⴉ",
+ "ⴊ": "ⴊ",
+ "ⴋ": "ⴋ",
+ "ⴌ": "ⴌ",
+ "ⴍ": "ⴍ",
+ "ⴎ": "ⴎ",
+ "ⴏ": "ⴏ",
+ "ⴐ": "ⴐ",
+ "ⴑ": "ⴑ",
+ "ⴒ": "ⴒ",
+ "ⴓ": "ⴓ",
+ "ⴔ": "ⴔ",
+ "ⴕ": "ⴕ",
+ "ⴖ": "ⴖ",
+ "ⴗ": "ⴗ",
+ "ⴘ": "ⴘ",
+ "ⴙ": "ⴙ",
+ "ⴚ": "ⴚ",
+ "ⴛ": "ⴛ",
+ "ⴜ": "ⴜ",
+ "ⴝ": "ⴝ",
+ "ⴞ": "ⴞ",
+ "ⴟ": "ⴟ",
+ "ⴠ": "ⴠ",
+ "ⴡ": "ⴡ",
+ "ⴢ": "ⴢ",
+ "ⴣ": "ⴣ",
+ "ⴤ": "ⴤ",
+ "ⴥ": "ⴥ",
+ "ⴧ": "ⴧ",
+ "ⴭ": "ⴭ",
+ "ꙁ": "ꙁ",
+ "ꙃ": "ꙃ",
+ "ꙅ": "ꙅ",
+ "ꙇ": "ꙇ",
+ "ꙉ": "ꙉ",
+ "ꙋ": "ꙋ",
+ "ꙍ": "ꙍ",
+ "ꙏ": "ꙏ",
+ "ꙑ": "ꙑ",
+ "ꙓ": "ꙓ",
+ "ꙕ": "ꙕ",
+ "ꙗ": "ꙗ",
+ "ꙙ": "ꙙ",
+ "ꙛ": "ꙛ",
+ "ꙝ": "ꙝ",
+ "ꙟ": "ꙟ",
+ "ꙡ": "ꙡ",
+ "ꙣ": "ꙣ",
+ "ꙥ": "ꙥ",
+ "ꙧ": "ꙧ",
+ "ꙩ": "ꙩ",
+ "ꙫ": "ꙫ",
+ "ꙭ": "ꙭ",
+ "ꚁ": "ꚁ",
+ "ꚃ": "ꚃ",
+ "ꚅ": "ꚅ",
+ "ꚇ": "ꚇ",
+ "ꚉ": "ꚉ",
+ "ꚋ": "ꚋ",
+ "ꚍ": "ꚍ",
+ "ꚏ": "ꚏ",
+ "ꚑ": "ꚑ",
+ "ꚓ": "ꚓ",
+ "ꚕ": "ꚕ",
+ "ꚗ": "ꚗ",
+ "ꚙ": "ꚙ",
+ "ꚛ": "ꚛ",
+ "ꜣ": "ꜣ",
+ "ꜥ": "ꜥ",
+ "ꜧ": "ꜧ",
+ "ꜩ": "ꜩ",
+ "ꜫ": "ꜫ",
+ "ꜭ": "ꜭ",
+ "ꜯ": "ꜯ",
+ "ꜳ": "ꜳ",
+ "ꜵ": "ꜵ",
+ "ꜷ": "ꜷ",
+ "ꜹ": "ꜹ",
+ "ꜻ": "ꜻ",
+ "ꜽ": "ꜽ",
+ "ꜿ": "ꜿ",
+ "ꝁ": "ꝁ",
+ "ꝃ": "ꝃ",
+ "ꝅ": "ꝅ",
+ "ꝇ": "ꝇ",
+ "ꝉ": "ꝉ",
+ "ꝋ": "ꝋ",
+ "ꝍ": "ꝍ",
+ "ꝏ": "ꝏ",
+ "ꝑ": "ꝑ",
+ "ꝓ": "ꝓ",
+ "ꝕ": "ꝕ",
+ "ꝗ": "ꝗ",
+ "ꝙ": "ꝙ",
+ "ꝛ": "ꝛ",
+ "ꝝ": "ꝝ",
+ "ꝟ": "ꝟ",
+ "ꝡ": "ꝡ",
+ "ꝣ": "ꝣ",
+ "ꝥ": "ꝥ",
+ "ꝧ": "ꝧ",
+ "ꝩ": "ꝩ",
+ "ꝫ": "ꝫ",
+ "ꝭ": "ꝭ",
+ "ꝯ": "ꝯ",
+ "ꝺ": "ꝺ",
+ "ꝼ": "ꝼ",
+ "ꝿ": "ꝿ",
+ "ꞁ": "ꞁ",
+ "ꞃ": "ꞃ",
+ "ꞅ": "ꞅ",
+ "ꞇ": "ꞇ",
+ "ꞌ": "ꞌ",
+ "ꞑ": "ꞑ",
+ "ꞓ": "ꞓ",
+ "ꞔ": "ꞔ",
+ "ꞗ": "ꞗ",
+ "ꞙ": "ꞙ",
+ "ꞛ": "ꞛ",
+ "ꞝ": "ꞝ",
+ "ꞟ": "ꞟ",
+ "ꞡ": "ꞡ",
+ "ꞣ": "ꞣ",
+ "ꞥ": "ꞥ",
+ "ꞧ": "ꞧ",
+ "ꞩ": "ꞩ",
+ "ꞵ": "ꞵ",
+ "ꞷ": "ꞷ",
+ "ꞹ": "ꞹ",
+ "ꞻ": "ꞻ",
+ "ꞽ": "ꞽ",
+ "ꞿ": "ꞿ",
+ "ꟃ": "ꟃ",
+ "ꭓ": "ꭓ",
+ "ꭰ": "ꭰ",
+ "ꭱ": "ꭱ",
+ "ꭲ": "ꭲ",
+ "ꭳ": "ꭳ",
+ "ꭴ": "ꭴ",
+ "ꭵ": "ꭵ",
+ "ꭶ": "ꭶ",
+ "ꭷ": "ꭷ",
+ "ꭸ": "ꭸ",
+ "ꭹ": "ꭹ",
+ "ꭺ": "ꭺ",
+ "ꭻ": "ꭻ",
+ "ꭼ": "ꭼ",
+ "ꭽ": "ꭽ",
+ "ꭾ": "ꭾ",
+ "ꭿ": "ꭿ",
+ "ꮀ": "ꮀ",
+ "ꮁ": "ꮁ",
+ "ꮂ": "ꮂ",
+ "ꮃ": "ꮃ",
+ "ꮄ": "ꮄ",
+ "ꮅ": "ꮅ",
+ "ꮆ": "ꮆ",
+ "ꮇ": "ꮇ",
+ "ꮈ": "ꮈ",
+ "ꮉ": "ꮉ",
+ "ꮊ": "ꮊ",
+ "ꮋ": "ꮋ",
+ "ꮌ": "ꮌ",
+ "ꮍ": "ꮍ",
+ "ꮎ": "ꮎ",
+ "ꮏ": "ꮏ",
+ "ꮐ": "ꮐ",
+ "ꮑ": "ꮑ",
+ "ꮒ": "ꮒ",
+ "ꮓ": "ꮓ",
+ "ꮔ": "ꮔ",
+ "ꮕ": "ꮕ",
+ "ꮖ": "ꮖ",
+ "ꮗ": "ꮗ",
+ "ꮘ": "ꮘ",
+ "ꮙ": "ꮙ",
+ "ꮚ": "ꮚ",
+ "ꮛ": "ꮛ",
+ "ꮜ": "ꮜ",
+ "ꮝ": "ꮝ",
+ "ꮞ": "ꮞ",
+ "ꮟ": "ꮟ",
+ "ꮠ": "ꮠ",
+ "ꮡ": "ꮡ",
+ "ꮢ": "ꮢ",
+ "ꮣ": "ꮣ",
+ "ꮤ": "ꮤ",
+ "ꮥ": "ꮥ",
+ "ꮦ": "ꮦ",
+ "ꮧ": "ꮧ",
+ "ꮨ": "ꮨ",
+ "ꮩ": "ꮩ",
+ "ꮪ": "ꮪ",
+ "ꮫ": "ꮫ",
+ "ꮬ": "ꮬ",
+ "ꮭ": "ꮭ",
+ "ꮮ": "ꮮ",
+ "ꮯ": "ꮯ",
+ "ꮰ": "ꮰ",
+ "ꮱ": "ꮱ",
+ "ꮲ": "ꮲ",
+ "ꮳ": "ꮳ",
+ "ꮴ": "ꮴ",
+ "ꮵ": "ꮵ",
+ "ꮶ": "ꮶ",
+ "ꮷ": "ꮷ",
+ "ꮸ": "ꮸ",
+ "ꮹ": "ꮹ",
+ "ꮺ": "ꮺ",
+ "ꮻ": "ꮻ",
+ "ꮼ": "ꮼ",
+ "ꮽ": "ꮽ",
+ "ꮾ": "ꮾ",
+ "ꮿ": "ꮿ",
"ff": "ff",
"fi": "fi",
"fl": "fl",
"ﬔ": "ﬔ",
"ﬕ": "ﬕ",
"ﬖ": "ﬖ",
- "ﬗ": "ﬗ"
+ "ﬗ": "ﬗ",
+ "𐑎": "𐑎",
+ "𐑏": "𐑏",
+ "𐓘": "𐓘",
+ "𐓙": "𐓙",
+ "𐓚": "𐓚",
+ "𐓛": "𐓛",
+ "𐓜": "𐓜",
+ "𐓝": "𐓝",
+ "𐓞": "𐓞",
+ "𐓟": "𐓟",
+ "𐓠": "𐓠",
+ "𐓡": "𐓡",
+ "𐓢": "𐓢",
+ "𐓣": "𐓣",
+ "𐓤": "𐓤",
+ "𐓥": "𐓥",
+ "𐓦": "𐓦",
+ "𐓧": "𐓧",
+ "𐓨": "𐓨",
+ "𐓩": "𐓩",
+ "𐓪": "𐓪",
+ "𐓫": "𐓫",
+ "𐓬": "𐓬",
+ "𐓭": "𐓭",
+ "𐓮": "𐓮",
+ "𐓯": "𐓯",
+ "𐓰": "𐓰",
+ "𐓱": "𐓱",
+ "𐓲": "𐓲",
+ "𐓳": "𐓳",
+ "𐓴": "𐓴",
+ "𐓵": "𐓵",
+ "𐓶": "𐓶",
+ "𐓷": "𐓷",
+ "𐓸": "𐓸",
+ "𐓹": "𐓹",
+ "𐓺": "𐓺",
+ "𐓻": "𐓻",
+ "𐳀": "𐳀",
+ "𐳁": "𐳁",
+ "𐳂": "𐳂",
+ "𐳃": "𐳃",
+ "𐳄": "𐳄",
+ "𐳅": "𐳅",
+ "𐳆": "𐳆",
+ "𐳇": "𐳇",
+ "𐳈": "𐳈",
+ "𐳉": "𐳉",
+ "𐳊": "𐳊",
+ "𐳋": "𐳋",
+ "𐳌": "𐳌",
+ "𐳍": "𐳍",
+ "𐳎": "𐳎",
+ "𐳏": "𐳏",
+ "𐳐": "𐳐",
+ "𐳑": "𐳑",
+ "𐳒": "𐳒",
+ "𐳓": "𐳓",
+ "𐳔": "𐳔",
+ "𐳕": "𐳕",
+ "𐳖": "𐳖",
+ "𐳗": "𐳗",
+ "𐳘": "𐳘",
+ "𐳙": "𐳙",
+ "𐳚": "𐳚",
+ "𐳛": "𐳛",
+ "𐳜": "𐳜",
+ "𐳝": "𐳝",
+ "𐳞": "𐳞",
+ "𐳟": "𐳟",
+ "𐳠": "𐳠",
+ "𐳡": "𐳡",
+ "𐳢": "𐳢",
+ "𐳣": "𐳣",
+ "𐳤": "𐳤",
+ "𐳥": "𐳥",
+ "𐳦": "𐳦",
+ "𐳧": "𐳧",
+ "𐳨": "𐳨",
+ "𐳩": "𐳩",
+ "𐳪": "𐳪",
+ "𐳫": "𐳫",
+ "𐳬": "𐳬",
+ "𐳭": "𐳭",
+ "𐳮": "𐳮",
+ "𐳯": "𐳯",
+ "𐳰": "𐳰",
+ "𐳱": "𐳱",
+ "𐳲": "𐳲",
+ "𑣀": "𑣀",
+ "𑣁": "𑣁",
+ "𑣂": "𑣂",
+ "𑣃": "𑣃",
+ "𑣄": "𑣄",
+ "𑣅": "𑣅",
+ "𑣆": "𑣆",
+ "𑣇": "𑣇",
+ "𑣈": "𑣈",
+ "𑣉": "𑣉",
+ "𑣊": "𑣊",
+ "𑣋": "𑣋",
+ "𑣌": "𑣌",
+ "𑣍": "𑣍",
+ "𑣎": "𑣎",
+ "𑣏": "𑣏",
+ "𑣐": "𑣐",
+ "𑣑": "𑣑",
+ "𑣒": "𑣒",
+ "𑣓": "𑣓",
+ "𑣔": "𑣔",
+ "𑣕": "𑣕",
+ "𑣖": "𑣖",
+ "𑣗": "𑣗",
+ "𑣘": "𑣘",
+ "𑣙": "𑣙",
+ "𑣚": "𑣚",
+ "𑣛": "𑣛",
+ "𑣜": "𑣜",
+ "𑣝": "𑣝",
+ "𑣞": "𑣞",
+ "𑣟": "𑣟",
+ "𖹠": "𖹠",
+ "𖹡": "𖹡",
+ "𖹢": "𖹢",
+ "𖹣": "𖹣",
+ "𖹤": "𖹤",
+ "𖹥": "𖹥",
+ "𖹦": "𖹦",
+ "𖹧": "𖹧",
+ "𖹨": "𖹨",
+ "𖹩": "𖹩",
+ "𖹪": "𖹪",
+ "𖹫": "𖹫",
+ "𖹬": "𖹬",
+ "𖹭": "𖹭",
+ "𖹮": "𖹮",
+ "𖹯": "𖹯",
+ "𖹰": "𖹰",
+ "𖹱": "𖹱",
+ "𖹲": "𖹲",
+ "𖹳": "𖹳",
+ "𖹴": "𖹴",
+ "𖹵": "𖹵",
+ "𖹶": "𖹶",
+ "𖹷": "𖹷",
+ "𖹸": "𖹸",
+ "𖹹": "𖹹",
+ "𖹺": "𖹺",
+ "𖹻": "𖹻",
+ "𖹼": "𖹼",
+ "𖹽": "𖹽",
+ "𖹾": "𖹾",
+ "𖹿": "𖹿",
+ "𞤢": "𞤢",
+ "𞤣": "𞤣",
+ "𞤤": "𞤤",
+ "𞤥": "𞤥",
+ "𞤦": "𞤦",
+ "𞤧": "𞤧",
+ "𞤨": "𞤨",
+ "𞤩": "𞤩",
+ "𞤪": "𞤪",
+ "𞤫": "𞤫",
+ "𞤬": "𞤬",
+ "𞤭": "𞤭",
+ "𞤮": "𞤮",
+ "𞤯": "𞤯",
+ "𞤰": "𞤰",
+ "𞤱": "𞤱",
+ "𞤲": "𞤲",
+ "𞤳": "𞤳",
+ "𞤴": "𞤴",
+ "𞤵": "𞤵",
+ "𞤶": "𞤶",
+ "𞤷": "𞤷",
+ "𞤸": "𞤸",
+ "𞤹": "𞤹",
+ "𞤺": "𞤺",
+ "𞤻": "𞤻",
+ "𞤼": "𞤼",
+ "𞤽": "𞤽",
+ "𞤾": "𞤾",
+ "𞤿": "𞤿",
+ "𞥀": "𞥀",
+ "𞥁": "𞥁",
+ "𞥂": "𞥂",
+ "𞥃": "𞥃"
}
}
};
+// Backwards-compatible alias for mediawiki.RegExp module.
+// @deprecated since 1.34
+mw.RegExp = {};
+mw.log.deprecate( mw.RegExp, 'escape', util.escapeRegExp, 'Use mw.util.escapeRegExp() instead.', 'mw.RegExp.escape' );
+
// Not allowed outside unit tests
if ( window.QUnit ) {
util.setOptionsForTest = function ( opts ) {
use MediaWiki\Rest\EntryPoint;
+define( 'MW_REST_API', true );
+define( 'MW_ENTRY_POINT', 'rest' );
+
require __DIR__ . '/includes/WebStart.php';
EntryPoint::main();
* @return array
*/
private function listTables() {
- global $wgActorTableSchemaMigrationStage;
-
$tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
'protected_titles', 'revision', 'ip_changes', 'text', 'pagelinks', 'imagelinks',
'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
'archive', 'user_groups', 'page_props', 'category',
'slots', 'content', 'slot_roles', 'content_models',
- 'comment', 'revision_comment_temp',
+ 'comment', 'revision_comment_temp', 'actor', 'revision_actor_temp',
];
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- // The new tables for actors are in use
- $tables[] = 'actor';
- $tables[] = 'revision_actor_temp';
- }
-
if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite' ] ) ) {
array_push( $tables, 'searchindex' );
}
<?php
use MediaWiki\User\UserIdentity;
-use MediaWiki\MediaWikiServices;
+use Wikimedia\ScopedCallback;
use Wikimedia\TestingAccessWrapper;
/**
*/
class ActorMigrationTest extends MediaWikiLangTestCase {
+ protected $resetActorMigration = null;
+ protected static $amId = 0;
+
protected $tablesUsed = [
- 'revision',
- 'revision_actor_temp',
- 'ipblocks',
- 'recentchanges',
'actor',
];
+ protected function setUp() {
+ parent::setUp();
+
+ $w = TestingAccessWrapper::newFromClass( ActorMigration::class );
+ $data = [
+ 'tempTables' => $w->tempTables,
+ 'formerTempTables' => $w->formerTempTables,
+ 'deprecated' => $w->deprecated,
+ 'removed' => $w->removed,
+ 'specialFields' => $w->specialFields,
+ ];
+ $this->resetActorMigration = new ScopedCallback( function ( $w, $data ) {
+ foreach ( $data as $k => $v ) {
+ $w->$k = $v;
+ }
+ }, [ $w, $data ] );
+
+ $w->tempTables = [
+ 'am2_user' => [
+ 'table' => 'actormigration2_temp',
+ 'pk' => 'am2t_id',
+ 'field' => 'am2t_actor',
+ 'joinPK' => 'am2_id',
+ 'extra' => [],
+ ]
+ ];
+ $w->specialFields = [
+ 'am3_xxx' => [ 'am3_xxx_text', 'am3_xxx_actor' ],
+ ];
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ ScopedCallback::consume( $this->resetActorMigration );
+ }
+
+ protected function getSchemaOverrides( IMaintainableDatabase $db ) {
+ return [
+ 'scripts' => [
+ __DIR__ . '/ActorMigrationTest.sql',
+ ],
+ 'drop' => [],
+ 'create' => [ 'actormigration1', 'actormigration2', 'actormigration2_temp', 'actormigration3' ],
+ 'alter' => [],
+ ];
+ }
+
/**
* @dataProvider provideConstructor
* @param int $stage
public static function provideGetJoin() {
return [
'Simple table, old' => [
- SCHEMA_COMPAT_OLD, 'rc_user', [
+ SCHEMA_COMPAT_OLD, 'am1_user', [
'tables' => [],
'fields' => [
- 'rc_user' => 'rc_user',
- 'rc_user_text' => 'rc_user_text',
- 'rc_actor' => 'NULL',
+ 'am1_user' => 'am1_user',
+ 'am1_user_text' => 'am1_user_text',
+ 'am1_actor' => 'NULL',
],
'joins' => [],
],
],
'Simple table, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', [
'tables' => [],
'fields' => [
- 'rc_user' => 'rc_user',
- 'rc_user_text' => 'rc_user_text',
- 'rc_actor' => 'NULL',
+ 'am1_user' => 'am1_user',
+ 'am1_user_text' => 'am1_user_text',
+ 'am1_actor' => 'NULL',
],
'joins' => [],
],
],
'Simple table, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', [
- 'tables' => [ 'actor_rc_user' => 'actor' ],
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', [
+ 'tables' => [ 'actor_am1_user' => 'actor' ],
'fields' => [
- 'rc_user' => 'actor_rc_user.actor_user',
- 'rc_user_text' => 'actor_rc_user.actor_name',
- 'rc_actor' => 'rc_actor',
+ 'am1_user' => 'actor_am1_user.actor_user',
+ 'am1_user_text' => 'actor_am1_user.actor_name',
+ 'am1_actor' => 'am1_actor',
],
'joins' => [
- 'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+ 'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
],
],
],
'Simple table, new' => [
- SCHEMA_COMPAT_NEW, 'rc_user', [
- 'tables' => [ 'actor_rc_user' => 'actor' ],
+ SCHEMA_COMPAT_NEW, 'am1_user', [
+ 'tables' => [ 'actor_am1_user' => 'actor' ],
'fields' => [
- 'rc_user' => 'actor_rc_user.actor_user',
- 'rc_user_text' => 'actor_rc_user.actor_name',
- 'rc_actor' => 'rc_actor',
+ 'am1_user' => 'actor_am1_user.actor_user',
+ 'am1_user_text' => 'actor_am1_user.actor_name',
+ 'am1_actor' => 'am1_actor',
],
'joins' => [
- 'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+ 'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
],
],
],
- 'ipblocks, old' => [
- SCHEMA_COMPAT_OLD, 'ipb_by', [
+ 'Special name, old' => [
+ SCHEMA_COMPAT_OLD, 'am3_xxx', [
'tables' => [],
'fields' => [
- 'ipb_by' => 'ipb_by',
- 'ipb_by_text' => 'ipb_by_text',
- 'ipb_by_actor' => 'NULL',
+ 'am3_xxx' => 'am3_xxx',
+ 'am3_xxx_text' => 'am3_xxx_text',
+ 'am3_xxx_actor' => 'NULL',
],
'joins' => [],
],
],
- 'ipblocks, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', [
+ 'Special name, read-old' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', [
'tables' => [],
'fields' => [
- 'ipb_by' => 'ipb_by',
- 'ipb_by_text' => 'ipb_by_text',
- 'ipb_by_actor' => 'NULL',
+ 'am3_xxx' => 'am3_xxx',
+ 'am3_xxx_text' => 'am3_xxx_text',
+ 'am3_xxx_actor' => 'NULL',
],
'joins' => [],
],
],
- 'ipblocks, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', [
- 'tables' => [ 'actor_ipb_by' => 'actor' ],
+ 'Special name, read-new' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', [
+ 'tables' => [ 'actor_am3_xxx' => 'actor' ],
'fields' => [
- 'ipb_by' => 'actor_ipb_by.actor_user',
- 'ipb_by_text' => 'actor_ipb_by.actor_name',
- 'ipb_by_actor' => 'ipb_by_actor',
+ 'am3_xxx' => 'actor_am3_xxx.actor_user',
+ 'am3_xxx_text' => 'actor_am3_xxx.actor_name',
+ 'am3_xxx_actor' => 'am3_xxx_actor',
],
'joins' => [
- 'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+ 'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
],
],
],
- 'ipblocks, new' => [
- SCHEMA_COMPAT_NEW, 'ipb_by', [
- 'tables' => [ 'actor_ipb_by' => 'actor' ],
+ 'Special name, new' => [
+ SCHEMA_COMPAT_NEW, 'am3_xxx', [
+ 'tables' => [ 'actor_am3_xxx' => 'actor' ],
'fields' => [
- 'ipb_by' => 'actor_ipb_by.actor_user',
- 'ipb_by_text' => 'actor_ipb_by.actor_name',
- 'ipb_by_actor' => 'ipb_by_actor',
+ 'am3_xxx' => 'actor_am3_xxx.actor_user',
+ 'am3_xxx_text' => 'actor_am3_xxx.actor_name',
+ 'am3_xxx_actor' => 'am3_xxx_actor',
],
'joins' => [
- 'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+ 'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
],
],
],
- 'Revision, old' => [
- SCHEMA_COMPAT_OLD, 'rev_user', [
+ 'Temp table, old' => [
+ SCHEMA_COMPAT_OLD, 'am2_user', [
'tables' => [],
'fields' => [
- 'rev_user' => 'rev_user',
- 'rev_user_text' => 'rev_user_text',
- 'rev_actor' => 'NULL',
+ 'am2_user' => 'am2_user',
+ 'am2_user_text' => 'am2_user_text',
+ 'am2_actor' => 'NULL',
],
'joins' => [],
],
],
- 'Revision, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', [
+ 'Temp table, read-old' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', [
'tables' => [],
'fields' => [
- 'rev_user' => 'rev_user',
- 'rev_user_text' => 'rev_user_text',
- 'rev_actor' => 'NULL',
+ 'am2_user' => 'am2_user',
+ 'am2_user_text' => 'am2_user_text',
+ 'am2_actor' => 'NULL',
],
'joins' => [],
],
],
- 'Revision, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', [
+ 'Temp table, read-new' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', [
'tables' => [
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
+ 'temp_am2_user' => 'actormigration2_temp',
+ 'actor_am2_user' => 'actor',
],
'fields' => [
- 'rev_user' => 'actor_rev_user.actor_user',
- 'rev_user_text' => 'actor_rev_user.actor_name',
- 'rev_actor' => 'temp_rev_user.revactor_actor',
+ 'am2_user' => 'actor_am2_user.actor_user',
+ 'am2_user_text' => 'actor_am2_user.actor_name',
+ 'am2_actor' => 'temp_am2_user.am2t_actor',
],
'joins' => [
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+ 'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
+ 'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
],
],
],
- 'Revision, new' => [
- SCHEMA_COMPAT_NEW, 'rev_user', [
+ 'Temp table, new' => [
+ SCHEMA_COMPAT_NEW, 'am2_user', [
'tables' => [
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
+ 'temp_am2_user' => 'actormigration2_temp',
+ 'actor_am2_user' => 'actor',
],
'fields' => [
- 'rev_user' => 'actor_rev_user.actor_user',
- 'rev_user_text' => 'actor_rev_user.actor_name',
- 'rev_actor' => 'temp_rev_user.revactor_actor',
+ 'am2_user' => 'actor_am2_user.actor_user',
+ 'am2_user_text' => 'actor_am2_user.actor_name',
+ 'am2_actor' => 'temp_am2_user.am2t_actor',
],
'joins' => [
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+ 'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
+ 'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
],
],
],
return [
'Simple table, old' => [
- SCHEMA_COMPAT_OLD, 'rc_user', $genericUser, true, [
+ SCHEMA_COMPAT_OLD, 'am1_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "rc_user = '1'" ],
+ 'orconds' => [ 'userid' => "am1_user = '1'" ],
'joins' => [],
],
],
'Simple table, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $genericUser, true, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "rc_user = '1'" ],
+ 'orconds' => [ 'userid' => "am1_user = '1'" ],
'joins' => [],
],
],
'Simple table, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $genericUser, true, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor = '11'" ],
+ 'orconds' => [ 'actor' => "am1_actor = '11'" ],
'joins' => [],
],
],
'Simple table, new' => [
- SCHEMA_COMPAT_NEW, 'rc_user', $genericUser, true, [
+ SCHEMA_COMPAT_NEW, 'am1_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor = '11'" ],
+ 'orconds' => [ 'actor' => "am1_actor = '11'" ],
'joins' => [],
],
],
- 'ipblocks, old' => [
- SCHEMA_COMPAT_OLD, 'ipb_by', $genericUser, true, [
+ 'Special name, old' => [
+ SCHEMA_COMPAT_OLD, 'am3_xxx', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "ipb_by = '1'" ],
+ 'orconds' => [ 'userid' => "am3_xxx = '1'" ],
'joins' => [],
],
],
- 'ipblocks, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', $genericUser, true, [
+ 'Special name, read-old' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "ipb_by = '1'" ],
+ 'orconds' => [ 'userid' => "am3_xxx = '1'" ],
'joins' => [],
],
],
- 'ipblocks, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', $genericUser, true, [
+ 'Special name, read-new' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
+ 'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
'joins' => [],
],
],
- 'ipblocks, new' => [
- SCHEMA_COMPAT_NEW, 'ipb_by', $genericUser, true, [
+ 'Special name, new' => [
+ SCHEMA_COMPAT_NEW, 'am3_xxx', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
+ 'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
'joins' => [],
],
],
- 'Revision, old' => [
- SCHEMA_COMPAT_OLD, 'rev_user', $genericUser, true, [
+ 'Temp table, old' => [
+ SCHEMA_COMPAT_OLD, 'am2_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "rev_user = '1'" ],
+ 'orconds' => [ 'userid' => "am2_user = '1'" ],
'joins' => [],
],
],
- 'Revision, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', $genericUser, true, [
+ 'Temp table, read-old' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', $genericUser, true, [
'tables' => [],
- 'orconds' => [ 'userid' => "rev_user = '1'" ],
+ 'orconds' => [ 'userid' => "am2_user = '1'" ],
'joins' => [],
],
],
- 'Revision, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', $genericUser, true, [
+ 'Temp table, read-new' => [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', $genericUser, true, [
'tables' => [
- 'temp_rev_user' => 'revision_actor_temp',
+ 'temp_am2_user' => 'actormigration2_temp',
],
- 'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
+ 'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
'joins' => [
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
],
],
],
- 'Revision, new' => [
- SCHEMA_COMPAT_NEW, 'rev_user', $genericUser, true, [
+ 'Temp table, new' => [
+ SCHEMA_COMPAT_NEW, 'am2_user', $genericUser, true, [
'tables' => [
- 'temp_rev_user' => 'revision_actor_temp',
+ 'temp_am2_user' => 'actormigration2_temp',
],
- 'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
+ 'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
'joins' => [
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
],
],
],
'Multiple users, old' => [
- SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, true, [
+ SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, true, [
'tables' => [],
'orconds' => [
- 'userid' => "rc_user IN ('1','2','3') ",
- 'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
+ 'userid' => "am1_user IN ('1','2','3') ",
+ 'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
],
'joins' => [],
],
],
'Multiple users, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, true, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, true, [
'tables' => [],
'orconds' => [
- 'userid' => "rc_user IN ('1','2','3') ",
- 'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
+ 'userid' => "am1_user IN ('1','2','3') ",
+ 'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
],
'joins' => [],
],
],
'Multiple users, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, true, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+ 'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
'joins' => [],
],
],
'Multiple users, new' => [
- SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, true, [
+ SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, true, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+ 'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
'joins' => [],
],
],
'Multiple users, no use ID, old' => [
- SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, false, [
+ SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, false, [
'tables' => [],
'orconds' => [
- 'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+ 'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
],
'joins' => [],
],
],
'Multiple users, read-old' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, false, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, false, [
'tables' => [],
'orconds' => [
- 'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+ 'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
],
'joins' => [],
],
],
'Multiple users, read-new' => [
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, false, [
+ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, false, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+ 'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
'joins' => [],
],
],
'Multiple users, new' => [
- SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, false, [
+ SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, false, [
'tables' => [],
- 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+ 'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
'joins' => [],
],
],
* @param string $table
* @param string $key
* @param string $pk
- * @param array $extraFields
+ * @param bool $usesTemp
*/
- public function testInsertRoundTrip( $table, $key, $pk, $extraFields ) {
+ public function testInsertRoundTrip( $table, $key, $pk, $usesTemp ) {
$u = $this->getTestUser()->getUser();
$user = $this->getMock( UserIdentity::class );
$user->method( 'getId' )->willReturn( $u->getId() );
$user->method( 'getName' )->willReturn( $u->getName() );
- if ( $u->getActorId( $this->db ) ) {
- $user->method( 'getActorId' )->willReturn( $u->getActorId() );
- } else {
- $this->db->insert(
- 'actor',
- [ 'actor_user' => $u->getId(), 'actor_name' => $u->getName() ],
- __METHOD__
- );
- $user->method( 'getActorId' )->willReturn( $this->db->insertId() );
- }
+ $user->method( 'getActorId' )->willReturn( $u->getActorId( $this->db ) );
$stageNames = [
SCHEMA_COMPAT_OLD => 'old',
];
$nameKey = $key . '_text';
- $actorKey = $key === 'ipb_by' ? 'ipb_by_actor' : substr( $key, 0, -5 ) . '_actor';
+ $actorKey = ( $key === 'am3_xxx' ? $key : substr( $key, 0, -5 ) ) . '_actor';
foreach ( $stages as $writeStage => $possibleReadStages ) {
- if ( $key === 'ipb_by' ) {
- $extraFields['ipb_address'] = __CLASS__ . "#{$stageNames[$writeStage]}";
- }
-
$w = new ActorMigration( $writeStage );
- $usesTemp = $key === 'rev_user';
if ( $usesTemp ) {
list( $fields, $callback ) = $w->getInsertValuesWithTempTable( $this->db, $key, $user );
"new field, stage={$stageNames[$writeStage]}" );
}
- $this->db->insert( $table, $extraFields + $fields, __METHOD__ );
- $id = $this->db->insertId();
+ $id = ++self::$amId;
+ $this->db->insert( $table, [ $pk => $id ] + $fields, __METHOD__ );
if ( $usesTemp ) {
- $callback( $id, $extraFields );
+ $callback( $id, [] );
}
foreach ( $possibleReadStages as $readStage ) {
}
public static function provideInsertRoundTrip() {
- $db = wfGetDB( DB_REPLICA ); // for timestamps
-
- $comment = MediaWikiServices::getInstance()->getCommentStore()
- ->createComment( wfGetDB( DB_MASTER ), '' );
-
return [
- 'recentchanges' => [ 'recentchanges', 'rc_user', 'rc_id', [
- 'rc_timestamp' => $db->timestamp(),
- 'rc_namespace' => 0,
- 'rc_title' => 'Test',
- 'rc_this_oldid' => 42,
- 'rc_last_oldid' => 41,
- 'rc_source' => 'test',
- 'rc_comment_id' => $comment->id,
- ] ],
- 'ipblocks' => [ 'ipblocks', 'ipb_by', 'ipb_id', [
- 'ipb_range_start' => '',
- 'ipb_range_end' => '',
- 'ipb_timestamp' => $db->timestamp(),
- 'ipb_expiry' => $db->getInfinity(),
- 'ipb_reason_id' => $comment->id,
- ] ],
- 'revision' => [ 'revision', 'rev_user', 'rev_id', [
- 'rev_page' => 42,
- 'rev_len' => 0,
- 'rev_timestamp' => $db->timestamp(),
- ] ],
+ 'normal' => [ 'actormigration1', 'am1_user', 'am1_id', false ],
+ 'temp table' => [ 'actormigration2', 'am2_user', 'am2_id', true ],
+ 'special name' => [ 'actormigration3', 'am3_xxx', 'am3_id', false ],
];
}
* @dataProvider provideStages
* @param int $stage
* @expectedException InvalidArgumentException
- * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for rev_user
+ * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for am2_user
*/
public function testInsertWrong( $stage ) {
$m = new ActorMigration( $stage );
- $m->getInsertValues( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+ $m->getInsertValues( $this->db, 'am2_user', $this->getTestUser()->getUser() );
}
/**
* @dataProvider provideStages
* @param int $stage
* @expectedException InvalidArgumentException
- * @expectedExceptionMessage Must use getInsertValues() for rc_user
+ * @expectedExceptionMessage Must use getInsertValues() for am1_user
*/
public function testInsertWithTempTableWrong( $stage ) {
$m = new ActorMigration( $stage );
- $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+ $m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
}
/**
*/
public function testInsertWithTempTableDeprecated( $stage ) {
$wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
- $wrap->formerTempTables += [ 'rc_user' => '1.30' ];
+ $wrap->formerTempTables += [ 'am1_user' => '1.30' ];
- $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for rc_user' );
+ $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for am1_user' );
$m = new ActorMigration( $stage );
list( $fields, $callback )
- = $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+ = $m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
$this->assertTrue( is_callable( $callback ) );
}
* @dataProvider provideStages
* @param int $stage
* @expectedException InvalidArgumentException
- * @expectedExceptionMessage $extra[rev_timestamp] is not provided
+ * @expectedExceptionMessage $extra[foo_timestamp] is not provided
*/
public function testInsertWithTempTableCallbackMissingFields( $stage ) {
+ $w = TestingAccessWrapper::newFromClass( ActorMigration::class );
+ $w->tempTables = [
+ 'foo_user' => [
+ 'table' => 'foo_temp',
+ 'pk' => 'footmp_id',
+ 'field' => 'footmp_actor',
+ 'joinPK' => 'foo_id',
+ 'extra' => [ 'footmp_timestamp' => 'foo_timestamp' ],
+ ],
+ ];
+
$m = new ActorMigration( $stage );
list( $fields, $callback )
- = $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+ = $m->getInsertValuesWithTempTable( $this->db, 'foo_user', $this->getTestUser()->getUser() );
$callback( 1, [] );
}
* @param int $stage
*/
public function testInsertUserIdentity( $stage ) {
- $this->setMwGlobals( [
- // for User::getActorId()
- 'wgActorTableSchemaMigrationStage' => $stage
- ] );
-
$user = $this->getMutableTestUser()->getUser();
$userIdentity = $this->getMock( UserIdentity::class );
$userIdentity->method( 'getId' )->willReturn( $user->getId() );
$userIdentity->method( 'getName' )->willReturn( $user->getName() );
$userIdentity->method( 'getActorId' )->willReturn( 0 );
- list( $cFields, $cCallback ) = MediaWikiServices::getInstance()->getCommentStore()
- ->insertWithTempTable( $this->db, 'rev_comment', '' );
$m = new ActorMigration( $stage );
list( $fields, $callback ) =
- $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $userIdentity );
- $extraFields = [
- 'rev_page' => 42,
- 'rev_len' => 0,
- 'rev_timestamp' => $this->db->timestamp(),
- ] + $cFields;
- $this->db->insert( 'revision', $extraFields + $fields, __METHOD__ );
- $id = $this->db->insertId();
- $callback( $id, $extraFields );
- $cCallback( $id );
-
- $qi = $m->getJoin( 'rev_user' );
+ $m->getInsertValuesWithTempTable( $this->db, 'am2_user', $userIdentity );
+ $id = ++self::$amId;
+ $this->db->insert( 'actormigration2', [ 'am2_id' => $id ] + $fields, __METHOD__ );
+ $callback( $id, [] );
+
+ $qi = $m->getJoin( 'am2_user' );
$row = $this->db->selectRow(
- [ 'revision' ] + $qi['tables'], $qi['fields'], [ 'rev_id' => $id ], __METHOD__, [], $qi['joins']
+ [ 'actormigration2' ] + $qi['tables'],
+ $qi['fields'],
+ [ 'am2_id' => $id ],
+ __METHOD__,
+ [],
+ $qi['joins']
);
- $this->assertSame( $user->getId(), (int)$row->rev_user );
- $this->assertSame( $user->getName(), $row->rev_user_text );
+ $this->assertSame( $user->getId(), (int)$row->am2_user );
+ $this->assertSame( $user->getName(), $row->am2_user_text );
$this->assertSame(
( $stage & SCHEMA_COMPAT_READ_NEW ) ? $user->getActorId() : 0,
- (int)$row->rev_actor
+ (int)$row->am2_actor
);
$m = new ActorMigration( $stage );
];
}
+ public function testCheckDeprecation() {
+ $wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
+ $wrap->deprecated += [ 'soft' => null, 'hard' => '1.34' ];
+ $wrap->removed += [ 'gone' => '1.34' ];
+
+ $this->hideDeprecated( 'ActorMigration for \'hard\'' );
+
+ $wrap->checkDeprecation( 'valid' );
+ $wrap->checkDeprecation( 'soft' );
+ $wrap->checkDeprecation( 'hard' );
+ try {
+ $wrap->checkDeprecation( 'gone' );
+ } catch ( InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'Use of ActorMigration for \'gone\' was removed in MediaWiki 1.34',
+ $ex->getMessage()
+ );
+ }
+ }
+
}
--- /dev/null
+-- These are carefully crafted to work in all five supported databases
+
+CREATE TABLE /*_*/actormigration1 (
+ am1_id integer not null,
+ am1_user integer,
+ am1_user_text varchar(200),
+ am1_actor integer
+);
+
+CREATE TABLE /*_*/actormigration2 (
+ am2_id integer not null,
+ am2_user integer,
+ am2_user_text varchar(200)
+);
+
+CREATE TABLE /*_*/actormigration2_temp (
+ am2t_id integer not null,
+ am2t_actor integer
+);
+
+CREATE TABLE /*_*/actormigration3 (
+ am3_id integer not null,
+ am3_xxx integer,
+ am3_xxx_text varchar(200),
+ am3_xxx_actor integer
+);
];
}
- protected function getOldActorQueryFields( $prefix ) {
- return [
- "{$prefix}_user" => "{$prefix}_user",
- "{$prefix}_user_text" => "{$prefix}_user_text",
- "{$prefix}_actor" => 'NULL',
- ];
- }
-
protected function getNewActorQueryFields( $prefix, $tmp = false ) {
return [
"{$prefix}_user" => "actor_{$prefix}_user.actor_user",
"{$prefix}_user_text" => "actor_{$prefix}_user.actor_name",
- "{$prefix}_actor" => $tmp ?: "{$prefix}_actor",
- ];
- }
-
- protected function getNewActorJoins( $prefix ) {
- return [
- "temp_{$prefix}_user" => [
- "JOIN",
- "temp_{$prefix}_user.revactor_{$prefix} = {$prefix}_id",
- ],
- "actor_{$prefix}_user" => [
- "JOIN",
- "actor_{$prefix}_user.actor_id = temp_{$prefix}_user.revactor_actor",
- ],
+ "{$prefix}_actor" => $tmp ? "temp_{$prefix}_user.{$prefix}actor_actor" : "{$prefix}_actor",
];
}
yield 'MCR, comment, actor' => [
[
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
],
[
'tables' => [
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
],
[
'tables' => [
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
],
[
'tables' => [
'archive',
+ 'actor_ar_user' => 'actor',
'comment_ar_comment' => 'comment',
],
'fields' => array_merge(
$this->getArchiveQueryFields( true ),
$this->getContentHandlerQueryFields( 'ar' ),
- $this->getOldActorQueryFields( 'ar' ),
+ $this->getNewActorQueryFields( 'ar' ),
$this->getNewCommentQueryFields( 'ar' )
),
'joins' => [
'comment_ar_comment'
=> [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
+ 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
],
]
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[
'tables' => [
'archive',
+ 'actor_ar_user' => 'actor',
'comment_ar_comment' => 'comment',
],
'fields' => array_merge(
$this->getArchiveQueryFields( true ),
- $this->getOldActorQueryFields( 'ar' ),
+ $this->getNewActorQueryFields( 'ar' ),
$this->getNewCommentQueryFields( 'ar' )
),
'joins' => [
'comment_ar_comment'
=> [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
+ 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
],
]
];
[
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
],
[ 'page', 'user' ],
[
],
'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
]
];
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
],
[ 'page', 'user' ],
[
$this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
$this->getNewCommentQueryFields( 'rev' )
),
- 'joins' => array_merge(
- [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- 'user' => [
- 'LEFT JOIN',
- [
- 'actor_rev_user.actor_user != 0',
- 'user_id = actor_rev_user.actor_user',
- ]
- ],
- 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
- 'comment_rev_comment'
- => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'joins' => [
+ 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+ 'user' => [
+ 'LEFT JOIN',
+ [
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user',
+ ]
],
- $this->getNewActorJoins( 'rev' )
- ),
+ 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+ 'comment_rev_comment'
+ => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+ ],
]
];
yield 'MCR read-new' => [
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
],
[ 'page', 'user' ],
[
$this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
$this->getNewCommentQueryFields( 'rev' )
),
- 'joins' => array_merge(
- [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- 'user' => [
- 'LEFT JOIN',
- [
- 'actor_rev_user.actor_user != 0',
- 'user_id = actor_rev_user.actor_user'
- ]
- ],
- 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
- 'comment_rev_comment'
- => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'joins' => [
+ 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+ 'user' => [
+ 'LEFT JOIN',
+ [
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user'
+ ]
],
- $this->getNewActorJoins( 'rev' )
- ),
+ 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+ 'comment_rev_comment'
+ => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+ ],
]
];
yield 'MCR write-both/read-old' => [
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
],
[],
[
'revision',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getContentHandlerQueryFields( 'rev' ),
- $this->getOldActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
+ $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
]
];
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage'
=> SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
],
[ 'page', 'user' ],
[
'user',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getContentHandlerQueryFields( 'rev' ),
$this->getUserQueryFields(),
$this->getPageQueryFields(),
- $this->getOldActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
+ $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
$this->getNewCommentQueryFields( 'rev' )
),
- 'joins' => array_merge(
- [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- 'user' => [
- 'LEFT JOIN',
- [
- 'rev_user != 0',
- 'user_id = rev_user'
- ]
- ],
- 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
- 'comment_rev_comment'
- => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
- ]
- ),
+ 'joins' => [
+ 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
+ 'user' => [
+ 'LEFT JOIN',
+ [
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user',
+ ]
+ ],
+ 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
+ 'comment_rev_comment'
+ => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+ ],
]
];
yield 'pre-MCR' => [
[
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[],
[
'revision',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getContentHandlerQueryFields( 'rev' ),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
]
];
[
'wgContentHandlerUseDB' => true,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[ 'page', 'user' ],
[
'revision', 'page', 'user',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getContentHandlerQueryFields( 'rev' ),
$this->getPageQueryFields(),
$this->getUserQueryFields(),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
+ 'user' => [ 'LEFT JOIN', [
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user',
+ ] ],
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
]
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[],
[
'revision',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
],
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[ 'page' ],
[
'revision', 'page',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getPageQueryFields(),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
],
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[ 'user' ],
[
'revision', 'user',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getUserQueryFields(),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
- 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
+ 'user' => [ 'LEFT JOIN', [
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user',
+ ] ],
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
],
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[ 'text' ],
[
'revision', 'text',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getTextQueryFields(),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
],
];
[
'wgContentHandlerUseDB' => false,
'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
],
[ 'text', 'page', 'user' ],
[
'revision', 'page', 'user', 'text',
'temp_rev_comment' => 'revision_comment_temp',
'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp',
+ 'actor_rev_user' => 'actor',
],
'fields' => array_merge(
$this->getRevisionQueryFields( true ),
$this->getPageQueryFields(),
$this->getUserQueryFields(),
$this->getTextQueryFields(),
- $this->getOldActorQueryFields( 'rev' ),
+ $this->getNewActorQueryFields( 'rev', true ),
$this->getNewCommentQueryFields( 'rev' )
),
'joins' => [
'user' => [
'LEFT JOIN',
[
- 'rev_user != 0',
- 'user_id = rev_user',
+ 'actor_rev_user.actor_user != 0',
+ 'user_id = actor_rev_user.actor_user',
],
],
'text' => [
'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
'comment_rev_comment'
=> [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
+ 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+ 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
],
],
];
];
}
- public function provideSelectFields() {
- yield 'with model, comment, and actor' => [
- [
- 'wgContentHandlerUseDB' => true,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- ],
- 'fields' => array_merge(
- [
- 'rev_id',
- 'rev_page',
- 'rev_text_id',
- 'rev_timestamp',
- 'rev_user_text',
- 'rev_user',
- 'rev_actor' => 'NULL',
- 'rev_minor_edit',
- 'rev_deleted',
- 'rev_len',
- 'rev_parent_id',
- 'rev_sha1',
- ],
- $this->getContentHandlerQueryFields( 'rev' ),
- [
- 'rev_comment_pk' => 'rev_id',
- ]
- ),
- ];
- yield 'no mode, no comment, no actor' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- 'fields' => array_merge(
- [
- 'rev_id',
- 'rev_page',
- 'rev_text_id',
- 'rev_timestamp',
- 'rev_user_text',
- 'rev_user',
- 'rev_actor' => 'NULL',
- 'rev_minor_edit',
- 'rev_deleted',
- 'rev_len',
- 'rev_parent_id',
- 'rev_sha1',
- 'rev_comment_pk' => 'rev_id',
- ]
- ),
- ];
- }
-
- public function provideSelectArchiveFields() {
- yield 'with model, comment, and actor' => [
- [
- 'wgContentHandlerUseDB' => true,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- ],
- 'fields' => array_merge(
- [
- 'ar_id',
- 'ar_page_id',
- 'ar_rev_id',
- 'ar_text_id',
- 'ar_timestamp',
- 'ar_user_text',
- 'ar_user',
- 'ar_actor' => 'NULL',
- 'ar_minor_edit',
- 'ar_deleted',
- 'ar_len',
- 'ar_parent_id',
- 'ar_sha1',
- ],
- $this->getContentHandlerQueryFields( 'ar' ),
- [
- 'ar_comment_id' => 'ar_comment_id',
- ]
- ),
- ];
- yield 'no mode, no comment, no actor' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- 'fields' => array_merge(
- [
- 'ar_id',
- 'ar_page_id',
- 'ar_rev_id',
- 'ar_text_id',
- 'ar_timestamp',
- 'ar_user_text',
- 'ar_user',
- 'ar_actor' => 'NULL',
- 'ar_minor_edit',
- 'ar_deleted',
- 'ar_len',
- 'ar_parent_id',
- 'ar_sha1',
- 'ar_comment_id' => 'ar_comment_id',
- ]
- ),
- ];
- }
-
- /**
- * @dataProvider provideSelectFields
- * @covers Revision::selectFields
- */
- public function testRevisionSelectFields( $migrationStageSettings, $expected ) {
- $this->setMwGlobals( $migrationStageSettings );
-
- $this->hideDeprecated( 'Revision::selectFields' );
- $this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectFields() );
- }
-
- /**
- * @dataProvider provideSelectArchiveFields
- * @covers Revision::selectArchiveFields
- */
- public function testRevisionSelectArchiveFields( $migrationStageSettings, $expected ) {
- $this->setMwGlobals( $migrationStageSettings );
-
- $this->hideDeprecated( 'Revision::selectArchiveFields' );
- $this->assertArrayEqualsIgnoringIntKeyOrder( $expected, Revision::selectArchiveFields() );
- }
-
- /**
- * @covers Revision::userJoinCond
- */
- public function testRevisionUserJoinCond() {
- $this->hideDeprecated( 'Revision::userJoinCond' );
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_OLD );
- $this->assertEquals(
- [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
- Revision::userJoinCond()
- );
- }
-
- /**
- * @covers Revision::pageJoinCond
- */
- public function testRevisionPageJoinCond() {
- $this->hideDeprecated( 'Revision::pageJoinCond' );
- $this->assertEquals(
- [ 'JOIN', [ 'page_id = rev_page' ] ],
- Revision::pageJoinCond()
- );
- }
-
- /**
- * @covers Revision::selectTextFields
- */
- public function testRevisionSelectTextFields() {
- $this->hideDeprecated( 'Revision::selectTextFields' );
- $this->assertEquals(
- $this->getTextQueryFields(),
- Revision::selectTextFields()
- );
- }
-
- /**
- * @covers Revision::selectPageFields
- */
- public function testRevisionSelectPageFields() {
- $this->hideDeprecated( 'Revision::selectPageFields' );
- $this->assertEquals(
- $this->getPageQueryFields(),
- Revision::selectPageFields()
- );
- }
-
- /**
- * @covers Revision::selectUserFields
- */
- public function testRevisionSelectUserFields() {
- $this->hideDeprecated( 'Revision::selectUserFields' );
- $this->assertEquals(
- $this->getUserQueryFields(),
- Revision::selectUserFields()
- );
- }
-
/**
* @covers Revision::getArchiveQueryInfo
* @dataProvider provideArchiveQueryInfo
$this->setMwGlobals( [
'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
}
* @covers \MediaWiki\Revision\RevisionStore::getKnownCurrentRevision
*/
public function testGetKnownCurrentRevision_userNameChange() {
- global $wgActorTableSchemaMigrationStage;
-
$cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
$this->setService( 'MainWANObjectCache', $cache );
$this->db->update( 'user',
[ 'user_name' => $newUserName ],
[ 'user_id' => $rev->getUser()->getId() ] );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $this->db->update( 'actor',
- [ 'actor_name' => $newUserName ],
- [ 'actor_user' => $rev->getUser()->getId() ] );
- }
+ $this->db->update( 'actor',
+ [ 'actor_name' => $newUserName ],
+ [ 'actor_user' => $rev->getUser()->getId() ] );
// Reload the revision and regrab the user name.
$revAfter = $store->getKnownCurrentRevision( $page->getTitle(), $rev->getId() );
* @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
*/
public function testNewRevisionFromRow_userNameChange() {
- global $wgActorTableSchemaMigrationStage;
-
$page = $this->getTestPage();
$text = __METHOD__;
/** @var Revision $rev */
$this->db->update( 'user',
[ 'user_name' => $newUserName ],
[ 'user_id' => $record->getUser()->getId() ] );
- if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
- $this->db->update( 'actor',
- [ 'actor_name' => $newUserName ],
- [ 'actor_user' => $record->getUser()->getId() ] );
- }
+ $this->db->update( 'actor',
+ [ 'actor_name' => $newUserName ],
+ [ 'actor_user' => $record->getUser()->getId() ] );
// Reload the record, passing $fromCache as true to force fresh info from the db,
// and regrab the user name
$this->setMwGlobals( [
'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
if ( !$this->testPage ) {
* @covers Revision::loadFromTitle
*/
public function testLoadFromTitle() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
$title = $this->getMockTitle();
$conditions = [
$this->assertEquals(
false,
$title->exists(),
- 'exists() should rely on link cache unless GAID_FOR_UPDATE is used'
+ 'exists() should rely on link cache unless READ_LATEST is used'
);
$this->assertEquals(
true,
- $title->exists( Title::GAID_FOR_UPDATE ),
- 'exists() should re-query database when GAID_FOR_UPDATE is used'
+ $title->exists( Title::READ_LATEST ),
+ 'exists() should re-query database when READ_LATEST is used'
);
}
$jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
$jobs->execute();
- $this->assertFalse( Title::newFromText( $name )->exists( Title::GAID_FOR_UPDATE ) );
+ $this->assertFalse( Title::newFromText( $name )->exists( Title::READ_LATEST ) );
}
public function testDeleteNonexistent() {
$mock->method( 'needsToken' )->willReturn( true );
$api = new ApiMain( new FauxRequest( [ 'action' => 'testmodule' ] ) );
- $api->getModuleManager()->addModule( 'testmodule', 'action', get_class( $mock ),
- function () use ( $mock ) {
+ $api->getModuleManager()->addModule( 'testmodule', 'action', [
+ 'class' => get_class( $mock ),
+ 'factory' => function () use ( $mock ) {
return $mock;
}
- );
+ ] );
$api->execute();
}
$mock->method( 'mustBePosted' )->willReturn( false );
$api = new ApiMain( new FauxRequest( [ 'action' => 'testmodule' ] ) );
- $api->getModuleManager()->addModule( 'testmodule', 'action', get_class( $mock ),
- function () use ( $mock ) {
+ $api->getModuleManager()->addModule( 'testmodule', 'action', [
+ 'class' => get_class( $mock ),
+ 'factory' => function () use ( $mock ) {
return $mock;
}
- );
+ ] );
$api->execute();
}
$req->setRequestURL( "http://localhost" );
$api = new ApiMain( $req );
- $api->getModuleManager()->addModule( 'testmodule', 'action', get_class( $mock ),
- function () use ( $mock ) {
+ $api->getModuleManager()->addModule( 'testmodule', 'action', [
+ 'class' => get_class( $mock ),
+ 'factory' => function () use ( $mock ) {
return $mock;
}
- );
+ ] );
$wrapper = TestingAccessWrapper::newFromObject( $api );
$wrapper->mInternalMode = false;
<?php
+use MediaWiki\MediaWikiServices;
+use Wikimedia\ObjectFactory;
+
/**
* @covers ApiModuleManager
*
private function getModuleManager() {
$request = new FauxRequest();
$main = new ApiMain( $request );
- return new ApiModuleManager( $main );
+
+ return new ApiModuleManager( $main, MediaWikiServices::getInstance()->getObjectFactory() );
}
public function newApiLogin( $main, $action ) {
null,
],
- 'with factory' => [
+ 'with class and factory' => [
'login',
'action',
ApiLogin::class,
[ $this, 'newApiLogin' ],
],
- 'with closure' => [
- 'logout',
+ 'with spec (class only)' => [
+ 'login',
'action',
- ApiLogout::class,
- function ( ApiMain $main, $action ) {
- return new ApiLogout( $main, $action );
- },
+ [
+ 'class' => ApiLogin::class
+ ],
+ null,
+ ],
+
+ 'with spec' => [
+ 'login',
+ 'action',
+ [
+ 'class' => ApiLogin::class,
+ 'factory' => [ $this, 'newApiLogin' ],
+ ],
+ null,
],
+
+ 'with spec (using services)' => [
+ 'logout',
+ 'action',
+ [
+ 'class' => ApiLogout::class,
+ 'factory' => function ( ApiMain $main, $action, ObjectFactory $objectFactory ) {
+ return new ApiLogout( $main, $action );
+ },
+ 'services' => [
+ 'ObjectFactory'
+ ],
+ ],
+ null,
+ ]
];
}
/**
* @dataProvider addModuleProvider
*/
- public function testAddModule( $name, $group, $class, $factory = null ) {
+ public function testAddModule( $name, $group, $spec, $factory ) {
+ if ( $factory ) {
+ $this->hideDeprecated(
+ ApiModuleManager::class . '::addModule with $class and $factory'
+ );
+ }
+
$moduleManager = $this->getModuleManager();
- $moduleManager->addModule( $name, $group, $class, $factory );
+ $moduleManager->addModule( $name, $group, $spec, $factory );
$this->assertTrue( $moduleManager->isDefined( $name, $group ), 'isDefined' );
$this->assertNotNull( $moduleManager->getModule( $name, $group, true ), 'getModule' );
$moduleManager->getClassName( 'nonexistentmodule' )
);
}
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage $spec must define a class name
+ */
+ public function testAddModuleWithIncompleteSpec() {
+ $moduleManager = $this->getModuleManager();
+
+ $moduleManager->addModule(
+ 'logout',
+ 'action',
+ [
+ 'factory' => function ( ApiMain $main, $action ) {
+ return new ApiLogout( $main, $action );
+ },
+ ]
+ );
+ }
}
<?php
+use MediaWiki\MediaWikiServices;
+use Wikimedia\TestingAccessWrapper;
+
/**
* @group API
* @group medium
3 => [ "$userDbkey/subpage" => -3 ],
], $pageSet->getAllTitlesByNamespace() );
}
+
+ /**
+ * Test that ApiPageSet is calling GenderCache for provided user names to prefill the
+ * GenderCache and avoid a performance issue when loading each users' gender on it's own.
+ * The test is setting the "missLimit" to 0 on the GenderCache to trigger misses logic.
+ * When the "misses" property is no longer 0 at the end of the test,
+ * something was requested which is not part of the cache. Than the test is failing.
+ */
+ public function testGenderCaching() {
+ // Set up the user namespace to have gender aliases to trigger the gender cache
+ $this->setMwGlobals( [
+ 'wgExtraGenderNamespaces' => [ NS_USER => [ 'male' => 'Male', 'female' => 'Female' ] ]
+ ] );
+ $this->overrideMwServices();
+
+ // User names to test with - it is not needed that the user exists in the database
+ // to trigger gender cache
+ $userNames = [
+ 'Female',
+ 'Unknown',
+ 'Male',
+ ];
+
+ // Prepare the gender cache for testing - this is a fresh instance due to service override
+ $genderCache = TestingAccessWrapper::newFromObject(
+ MediaWikiServices::getInstance()->getGenderCache()
+ );
+ $genderCache->missLimit = 0;
+
+ // Do an api request to trigger ApiPageSet code
+ $this->doApiRequest( [
+ 'action' => 'query',
+ 'titles' => 'User:' . implode( '|User:', $userNames ),
+ ] );
+
+ $this->assertEquals( 0, $genderCache->misses,
+ 'ApiPageSet does not prefill the gender cache correctly' );
+ $this->assertEquals( $userNames, array_keys( $genderCache->cache ),
+ 'ApiPageSet does not prefill all users into the gender cache' );
+ }
}
$moduleManager->addModule(
'languageinfo',
'meta',
- ApiQueryLanguageinfo::class,
- function ( $parent, $name ) use ( $microtimeFunction ) {
- return new ApiQueryLanguageinfo(
- $parent,
- $name,
- $microtimeFunction
- );
- }
+ [
+ 'class' => ApiQueryLanguageinfo::class,
+ 'factory' => function ( $parent, $name ) use ( $microtimeFunction ) {
+ return new ApiQueryLanguageinfo(
+ $parent,
+ $name,
+ $microtimeFunction
+ );
+ }
+ ]
);
}
);
// Make a few edits for us to play with
for ( $i = 1; $i <= 5; $i++ ) {
self::editPage( self::$page, MWCryptRand::generateHex( 10 ), 'summary' );
- $this->revs[] = Title::newFromText( self::$page )
- ->getLatestRevID( Title::GAID_FOR_UPDATE );
+ $this->revs[] = Title::newFromText( self::$page )->getLatestRevID( Title::READ_LATEST );
}
}
$main = new ApiMain( $context );
$printer = $this->getMockFormatter( $main, 'mockfm' );
$mm = $printer->getMain()->getModuleManager();
- $mm->addModule( 'mockfm', 'format', ApiFormatBase::class, function () {
- return $mock;
- } );
- if ( $registerNonHtml ) {
- $mm->addModule( 'mock', 'format', ApiFormatBase::class, function () {
+ $mm->addModule( 'mockfm', 'format', [
+ 'class' => ApiFormatBase::class,
+ 'factory' => function () {
return $mock;
- } );
+ }
+ ] );
+ if ( $registerNonHtml ) {
+ $mm->addModule( 'mock', 'format', [
+ 'class' => ApiFormatBase::class,
+ 'factory' => function () {
+ return $mock;
+ }
+ ] );
}
$printer->initPrinter();
* - class: If set, register 'name' with this class (and 'factory', if that's set)
* - factory: Used with 'class' to register at runtime
* - returnPrinter: Return the printer object
- * @param callable|null $factory Factory to use instead of the normal one
* @return string|array The string if $options['returnPrinter'] isn't set, or an array if it is:
* - text: Output text string
* - printer: ApiFormatBase
$context->setRequest( new FauxRequest( $params, true ) );
$main = new ApiMain( $context );
if ( isset( $options['class'] ) ) {
- $factory = $options['factory'] ?? null;
- $main->getModuleManager()->addModule( $printerName, 'format', $options['class'], $factory );
+ $spec = [
+ 'class' => $options['class']
+ ];
+
+ if ( isset( $options['factory'] ) ) {
+ $spec['factory'] = $options['factory'];
+ }
+
+ $main->getModuleManager()->addModule( $printerName, 'format', $spec );
}
$result = $main->getResult();
$result->addArrayType( null, 'default' );
*/
class ApiQueryUserContribsTest extends ApiTestCase {
public function addDBDataOnce() {
- global $wgActorTableSchemaMigrationStage;
-
- $reset = new \Wikimedia\ScopedCallback( function ( $v ) {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $v );
- }, [ $wgActorTableSchemaMigrationStage ] );
- // Needs to WRITE_BOTH so READ_OLD tests below work. READ mode here doesn't really matter.
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage',
- SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW );
-
$users = [
User::newFromName( '192.168.2.2', false ),
User::newFromName( '192.168.2.1', false ),
/**
* @dataProvider provideSorting
- * @param int $stage SCHEMA_COMPAT contants for $wgActorTableSchemaMigrationStage
* @param array $params Extra parameters for the query
* @param bool $reverse Reverse order?
* @param int $revs Number of revisions to expect
*/
- public function testSorting( $stage, $params, $reverse, $revs ) {
+ public function testSorting( $params, $reverse, $revs ) {
// FIXME: fails under sqlite
$this->markTestSkippedIfDbType( 'sqlite' );
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
-
if ( isset( $params['ucuserids'] ) ) {
$params['ucuserids'] = implode( '|', array_map( 'User::idFromName', $params['ucuserids'] ) );
}
$users2 = [ __CLASS__ . ' A', __CLASS__ . ' B', __CLASS__ . ' D' ];
$ips = [ '192.168.2.1', '192.168.2.2', '192.168.2.3', '192.168.2.4' ];
- foreach (
- [
- 'old' => SCHEMA_COMPAT_OLD,
- 'read old' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- 'read new' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
- 'new' => SCHEMA_COMPAT_NEW,
- ] as $stageName => $stage
- ) {
- foreach ( [ false, true ] as $reverse ) {
- $name = $stageName . ( $reverse ? ', reverse' : '' );
- yield "Named users, $name" => [ $stage, [ 'ucuser' => $users ], $reverse, 9 ];
- yield "Named users including a no-edit user, $name" => [
- $stage, [ 'ucuser' => $users2 ], $reverse, 6
- ];
- yield "IP users, $name" => [ $stage, [ 'ucuser' => $ips ], $reverse, 9 ];
- yield "All users, $name" => [
- $stage, [ 'ucuser' => array_merge( $users, $ips ) ], $reverse, 18
- ];
- yield "User IDs, $name" => [ $stage, [ 'ucuserids' => $users ], $reverse, 9 ];
- yield "Users by prefix, $name" => [ $stage, [ 'ucuserprefix' => __CLASS__ ], $reverse, 9 ];
- yield "IPs by prefix, $name" => [ $stage, [ 'ucuserprefix' => '192.168.2.' ], $reverse, 9 ];
- }
+ foreach ( [ false, true ] as $reverse ) {
+ $name = ( $reverse ? ', reverse' : '' );
+ yield "Named users, $name" => [ [ 'ucuser' => $users ], $reverse, 9 ];
+ yield "Named users including a no-edit user, $name" => [
+ [ 'ucuser' => $users2 ], $reverse, 6
+ ];
+ yield "IP users, $name" => [ [ 'ucuser' => $ips ], $reverse, 9 ];
+ yield "All users, $name" => [
+ [ 'ucuser' => array_merge( $users, $ips ) ], $reverse, 18
+ ];
+ yield "User IDs, $name" => [ [ 'ucuserids' => $users ], $reverse, 9 ];
+ yield "Users by prefix, $name" => [ [ 'ucuserprefix' => __CLASS__ ], $reverse, 9 ];
+ yield "IPs by prefix, $name" => [ [ 'ucuserprefix' => '192.168.2.' ], $reverse, 9 ];
}
}
- /**
- * @dataProvider provideInterwikiUser
- * @param int $stage SCHEMA_COMPAT constants for $wgActorTableSchemaMigrationStage
- */
- public function testInterwikiUser( $stage ) {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
-
+ public function testInterwikiUser() {
$params = [
'action' => 'query',
'list' => 'usercontribs',
$this->assertSame( $sorted, $ids, "IDs are sorted" );
}
- public static function provideInterwikiUser() {
- return [
- 'old' => [ SCHEMA_COMPAT_OLD ],
- 'read old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD ],
- 'read new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW ],
- 'new' => [ SCHEMA_COMPAT_NEW ],
- ];
- }
-
}
* @param array $selectFields
* @param string[]|null $row
* @param string[]|null $expectedFields
- * @param int $actorMigration
*/
public function testNewFromId( $id,
array $selectFields,
array $row = null,
- array $expectedFields = null,
- $actorMigration
+ array $expectedFields = null
) {
- $this->setMwGlobals( [
- 'wgActorTableSchemaMigrationStage' => $actorMigration,
- ] );
-
$row = $row ? (object)$row : null;
$db = $this->getMock( IDatabase::class );
$db->expects( self::once() )
}
public function provideNewFromId() {
- $oldTables = [
- 'tables' => [
- 'logging', 'user',
- 'comment_log_comment' => 'comment',
- ],
- 'fields' => [
- 'log_id',
- 'log_type',
- 'log_action',
- 'log_timestamp',
- 'log_namespace',
- 'log_title',
- 'log_params',
- 'log_deleted',
- 'user_id',
- 'user_name',
- 'user_editcount',
- 'log_comment_text' => 'comment_log_comment.comment_text',
- 'log_comment_data' => 'comment_log_comment.comment_data',
- 'log_comment_cid' => 'comment_log_comment.comment_id',
- 'log_user' => 'log_user',
- 'log_user_text' => 'log_user_text',
- 'log_actor' => 'NULL',
- ],
- 'options' => [],
- 'join_conds' => [
- 'user' => [ 'LEFT JOIN', 'user_id=log_user' ],
- 'comment_log_comment' => [ 'JOIN', 'comment_log_comment.comment_id = log_comment_id' ],
- ],
- ];
$newTables = [
'tables' => [
'logging',
return [
[
0,
- $oldTables + [ 'conds' => [ 'log_id' => 0 ] ],
- null,
+ $newTables + [ 'conds' => [ 'log_id' => 0 ] ],
null,
- SCHEMA_COMPAT_OLD,
+ null
],
[
123,
- $oldTables + [ 'conds' => [ 'log_id' => 123 ] ],
+ $newTables + [ 'conds' => [ 'log_id' => 123 ] ],
[
'log_id' => 123,
'log_type' => 'foobarize',
'log_comment_text' => 'test!',
'log_comment_data' => null,
],
- [ 'type' => 'foobarize', 'comment' => 'test!' ],
- SCHEMA_COMPAT_OLD,
+ [ 'type' => 'foobarize', 'comment' => 'test!' ]
],
[
567,
'log_comment_text' => 'test!',
'log_comment_data' => null,
],
- [ 'type' => 'foobarize', 'comment' => 'test!' ],
- SCHEMA_COMPAT_NEW,
+ [ 'type' => 'foobarize', 'comment' => 'test!' ]
],
];
}
$this->tablesUsed += $this->getMcrTablesToReset();
$this->setMwGlobals( [
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
] );
}
public function testRcHidemyselfFilter() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
$this->assertConditions(
);
}
- public function testRcHidemyselfFilter_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $user = $this->getTestUser()->getUser();
- $user->getActorId( wfGetDB( DB_MASTER ) );
- $this->assertConditions(
- [ # expected
- "NOT((rc_user = '{$user->getId()}'))",
- ],
- [
- 'hidemyself' => 1,
- ],
- "rc conditions: hidemyself=1 (logged in)",
- $user
- );
-
- $user = User::newFromName( '10.11.12.13', false );
- $id = $user->getActorId( wfGetDB( DB_MASTER ) );
- $this->assertConditions(
- [ # expected
- "NOT((rc_user_text = '10.11.12.13'))",
- ],
- [
- 'hidemyself' => 1,
- ],
- "rc conditions: hidemyself=1 (anon)",
- $user
- );
- }
-
public function testRcHidebyothersFilter() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
$user = $this->getTestUser()->getUser();
$user->getActorId( wfGetDB( DB_MASTER ) );
$this->assertConditions(
);
}
- public function testRcHidebyothersFilter_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $user = $this->getTestUser()->getUser();
- $user->getActorId( wfGetDB( DB_MASTER ) );
- $this->assertConditions(
- [ # expected
- "(rc_user_text = '{$user->getName()}')",
- ],
- [
- 'hidebyothers' => 1,
- ],
- "rc conditions: hidebyothers=1 (logged in)",
- $user
- );
-
- $user = User::newFromName( '10.11.12.13', false );
- $id = $user->getActorId( wfGetDB( DB_MASTER ) );
- $this->assertConditions(
- [ # expected
- "(rc_user_text = '10.11.12.13')",
- ],
- [
- 'hidebyothers' => 1,
- ],
- "rc conditions: hidebyothers=1 (anon)",
- $user
- );
- }
-
public function testRcHidepageedits() {
$this->assertConditions(
[ # expected
}
public function testFilterUserExpLevelAllExperienceLevels() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
$this->assertConditions(
[
# expected
);
}
- public function testFilterUserExpLevelAllExperienceLevels_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $this->assertConditions(
- [
- # expected
- 'rc_user != 0',
- ],
- [
- 'userExpLevel' => 'newcomer;learner;experienced',
- ],
- "rc conditions: userExpLevel=newcomer;learner;experienced"
- );
- }
-
- public function testFilterUserExpLevelRegistrered() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+ public function testFilterUserExpLevelRegistered() {
$this->assertConditions(
[
# expected
);
}
- public function testFilterUserExpLevelRegistrered_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $this->assertConditions(
- [
- # expected
- 'rc_user != 0',
- ],
- [
- 'userExpLevel' => 'registered',
- ],
- "rc conditions: userExpLevel=registered"
- );
- }
-
- public function testFilterUserExpLevelUnregistrered() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+ public function testFilterUserExpLevelUnregistered() {
$this->assertConditions(
[
# expected
);
}
- public function testFilterUserExpLevelUnregistrered_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $this->assertConditions(
- [
- # expected
- 'rc_user = 0',
- ],
- [
- 'userExpLevel' => 'unregistered',
- ],
- "rc conditions: userExpLevel=unregistered"
- );
- }
-
- public function testFilterUserExpLevelRegistreredOrLearner() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+ public function testFilterUserExpLevelRegisteredOrLearner() {
$this->assertConditions(
[
# expected
);
}
- public function testFilterUserExpLevelRegistreredOrLearner_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $this->assertConditions(
- [
- # expected
- 'rc_user != 0',
- ],
- [
- 'userExpLevel' => 'registered;learner',
- ],
- "rc conditions: userExpLevel=registered;learner"
- );
- }
-
- public function testFilterUserExpLevelUnregistreredOrExperienced() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
-
+ public function testFilterUserExpLevelUnregisteredOrExperienced() {
$conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
$this->assertRegExp(
);
}
- public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
- $this->setMwGlobals(
- 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
- );
-
- $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
-
- $this->assertRegExp(
- '/\(rc_user = 0\) OR '
- . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
- reset( $conds ),
- "rc conditions: userExpLevel=unregistered;experienced"
- );
- }
-
public function testFilterUserExpLevel() {
$now = time();
$this->setMwGlobals( [
'callback array' => [
[ 'SpecialPageTestHelper', 'newSpecialAllPages' ],
false
+ ],
+ 'object factory spec' => [
+ [ 'class' => SpecialAllPages::class ],
+ false
]
];
}
$this->assertTrue( $called, 'Recursive call succeeded' );
}
+ /**
+ * @covers \MediaWiki\Special\SpecialPageFactory::getPage
+ */
+ public function testSpecialPageCreationThatRequiresService() {
+ $type = null;
+
+ $this->setMwGlobals( 'wgSpecialPages',
+ [ 'TestPage' => [
+ 'factory' => function ( $spf ) use ( &$type ) {
+ $type = get_class( $spf );
+
+ return new class() extends SpecialPage {
+
+ };
+ },
+ 'services' => [
+ 'SpecialPageFactory'
+ ]
+ ] ]
+ );
+
+ SpecialPageFactory::getPage( 'TestPage' );
+
+ $this->assertEquals( \MediaWiki\Special\SpecialPageFactory::class, $type );
+ }
}
/** List query pages that can not be tested automatically */
protected $manualTest = [
- LinkSearchPage::class
+ SpecialLinkSearch::class
];
/**
/**
* @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() );
--- /dev/null
+<?php
+
+/**
+ * Test class for SpecialShortPages class
+ *
+ * @since 1.30
+ *
+ * @license GPL-2.0-or-later
+ */
+class SpecialShortPagesTest extends MediaWikiTestCase {
+
+ /**
+ * @dataProvider provideGetQueryInfoRespectsContentNs
+ * @covers SpecialShortPages::getQueryInfo()
+ */
+ public function testGetQueryInfoRespectsContentNS( $contentNS, $blacklistNS, $expectedNS ) {
+ $this->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 ] ]
+ ];
+ }
+
+}
+++ /dev/null
-<?php
-
-/**
- * Test class for SpecialShortpages class
- *
- * @since 1.30
- *
- * @license GPL-2.0-or-later
- */
-class SpecialShortpagesTest extends MediaWikiTestCase {
-
- /**
- * @dataProvider provideGetQueryInfoRespectsContentNs
- * @covers ShortPagesPage::getQueryInfo()
- */
- public function testGetQueryInfoRespectsContentNS( $contentNS, $blacklistNS, $expectedNS ) {
- $this->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 ] ]
- ];
- }
-
-}
--- /dev/null
+<?php
+/**
+ * Tests for Special:UncategorizedCategories
+ */
+class SpecialUncategorizedCategoriesTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider provideTestGetQueryInfoData
+ * @covers SpecialUncategorizedCategories::getQueryInfo
+ */
+ public function testGetQueryInfo( $msgContent, $expected ) {
+ $msg = new RawMessage( $msgContent );
+ $mockContext = $this->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", [] ],
+ ];
+ }
+}
+++ /dev/null
-<?php
-/**
- * Tests for Special:Uncategorizedcategories
- */
-class UncategorizedCategoriesPageTest extends MediaWikiTestCase {
- /**
- * @dataProvider provideTestGetQueryInfoData
- * @covers UncategorizedCategoriesPage::getQueryInfo
- */
- public function testGetQueryInfo( $msgContent, $expected ) {
- $msg = new RawMessage( $msgContent );
- $mockContext = $this->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", [] ],
- ];
- }
-}
$this->setMwGlobals( [
'wgGroupPermissions' => [],
'wgRevokePermissions' => [],
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
$this->setUpPermissionGlobals();
'User::newFromActorId works for an anonymous user' );
}
- /**
- * Actor tests with SCHEMA_COMPAT_READ_OLD
- *
- * The only thing different from testActorId() is the behavior if the actor
- * row doesn't exist in the DB, since with SCHEMA_COMPAT_READ_NEW that
- * situation can't happen. But we copy all the other tests too just for good measure.
- *
- * @covers User::newFromActorId
- */
- public function testActorId_old() {
- $this->setMwGlobals( [
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
- ] );
-
- $domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
- $this->hideDeprecated( 'User::selectFields' );
-
- // Newly-created user has an actor ID
- $user = User::createNew( 'UserTestActorIdOld1' );
- $id = $user->getId();
- $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
-
- $user = User::newFromName( 'UserTestActorIdOld2' );
- $user->addToDatabase();
- $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
-
- $user = User::newFromName( 'UserTestActorIdOld1' );
- $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
-
- $user = User::newFromId( $id );
- $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
-
- $user2 = User::newFromActorId( $user->getActorId() );
- $this->assertEquals( $user->getId(), $user2->getId(),
- 'User::newFromActorId works for an existing user' );
-
- $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
- $user = User::newFromRow( $row );
- $this->assertTrue( $user->getActorId() > 0,
- 'Actor ID can be retrieved for user loaded with User::selectFields()' );
-
- $this->db->delete( 'actor', [ 'actor_user' => $id ], __METHOD__ );
- User::purge( $domain, $id );
- // Because WANObjectCache->delete() stupidly doesn't delete from the process cache.
-
- MediaWikiServices::getInstance()->getMainWANObjectCache()->clearProcessCache();
-
- $user = User::newFromId( $id );
- $this->assertFalse( $user->getActorId() > 0, 'No Actor ID by default if none in database' );
- $this->assertTrue( $user->getActorId( $this->db ) > 0, 'Actor ID can be created if none in db' );
-
- $user->setName( 'UserTestActorIdOld4-renamed' );
- $user->saveSettings();
- $this->assertEquals(
- $user->getName(),
- $this->db->selectField(
- 'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
- ),
- 'User::saveSettings updates actor table for name change'
- );
-
- // For sanity
- $ip = '192.168.12.34';
- $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
-
- $user = User::newFromName( $ip, false );
- $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
- $this->assertTrue( $user->getActorId( $this->db ) > 0,
- 'Actor ID can be created for an anonymous user' );
-
- $user = User::newFromName( $ip, false );
- $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
- $user2 = User::newFromActorId( $user->getActorId() );
- $this->assertEquals( $user->getName(), $user2->getName(),
- 'User::newFromActorId works for an anonymous user' );
- }
-
/**
* @covers User::newFromAnyId
*/
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7251,
+ 'log_actor' => 7251,
'log_params' => '',
'log_timestamp' => $dbw->timestamp( '20041223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => 7252,
+ 'log_actor' => 7252,
'log_params' => '',
'log_timestamp' => $dbw->timestamp( '20051223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'block',
'log_action' => 'block',
- 'log_user' => 7253,
+ 'log_actor' => 7253,
'log_params' => '',
'log_timestamp' => $dbw->timestamp( '20061223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7253,
+ 'log_actor' => 7253,
'log_params' => 'nanana',
'log_timestamp' => $dbw->timestamp( '20061223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => 7254,
+ 'log_actor' => 7254,
'log_params' => '',
'log_timestamp' => $dbw->timestamp( '20071223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7255,
+ 'log_actor' => 7255,
'log_params' => serialize( [ '6::auto' => true ] ),
'log_timestamp' => $dbw->timestamp( '20081223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7256,
+ 'log_actor' => 7256,
'log_params' => serialize( [ '6::auto' => false ] ),
'log_timestamp' => $dbw->timestamp( '20091223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7257,
+ 'log_actor' => 7257,
'log_params' => "9227851\n0\n1",
'log_timestamp' => $dbw->timestamp( '20081223210426' ),
'log_namespace' => NS_MAIN,
$logs[] = [
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => 7258,
+ 'log_actor' => 7258,
'log_params' => "9227851\n0\n0",
'log_timestamp' => $dbw->timestamp( '20091223210426' ),
'log_namespace' => NS_MAIN,
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7251',
+ 'log_actor' => '7251',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => '7252',
+ 'log_actor' => '7252',
],
(object)[
'log_type' => 'block',
'log_action' => 'block',
- 'log_user' => '7253',
+ 'log_actor' => '7253',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7253',
+ 'log_actor' => '7253',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => '7254',
+ 'log_actor' => '7254',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7255',
+ 'log_actor' => '7255',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7256',
+ 'log_actor' => '7256',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7257',
+ 'log_actor' => '7257',
],
(object)[
'log_type' => 'patrol',
'log_action' => 'patrol',
- 'log_user' => '7258',
+ 'log_actor' => '7258',
],
];
$remainingLogs = wfGetDB( DB_REPLICA )->select(
[ 'logging' ],
- [ 'log_type', 'log_action', 'log_user' ],
+ [ 'log_type', 'log_action', 'log_actor' ],
[],
__METHOD__,
[ 'ORDER BY' => 'log_id' ]
$remainingLogs = wfGetDB( DB_REPLICA )->select(
[ 'logging' ],
- [ 'log_type', 'log_action', 'log_user' ],
+ [ 'log_type', 'log_action', 'log_actor' ],
[],
__METHOD__,
[ 'ORDER BY' => 'log_id' ]
$deleted = [
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => '7254',
+ 'log_actor' => '7254',
];
$notDeleted = [
'log_type' => 'patrol',
'log_action' => 'autopatrol',
- 'log_user' => '7252',
+ 'log_actor' => '7252',
];
$remainingLogs = array_map(
use MediaWiki\MediaWikiServices;
define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
+define( 'MW_ENTRY_POINT', 'thumb' );
require __DIR__ . '/includes/WebStart.php';
// Don't use fancy MIME detection, just check the file extension for jpg/gif/png
*/
define( 'THUMB_HANDLER', true );
+define( 'MW_ENTRY_POINT', 'thumb_handler' );
// Execute thumb.php, having set THUMB_HANDLER so that
// it knows to extract params from a thumbnail file URL.