Whitelist existing violations, but enable the sniff to prevent
any new occurrences.
-->
- <exclude-pattern>*/includes/Feed\.php</exclude-pattern>
- <exclude-pattern>*/includes/installer/PhpBugTests\.php</exclude-pattern>
<exclude-pattern>*/includes/specials/SpecialMostinterwikis\.php</exclude-pattern>
- <exclude-pattern>*/includes/compat/XMPReader\.php</exclude-pattern>
- <exclude-pattern>*/includes/diff/DairikiDiff\.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>
Whitelist existing violations, but enable the sniff to prevent
any new occurrences.
-->
- <exclude-pattern>*/includes/specials/SpecialActiveusers\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialBooksources\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialEmailuser\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListfiles\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListgrants\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListgrouprights\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialListusers.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialRecentchanges\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialRecentchangeslinked\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialRevisiondelete\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/SpecialWhatlinkshere\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/alltrans\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/digit2html\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/langmemusage\.php</exclude-pattern>
any new occurrences.
-->
<exclude-pattern>*/includes/api/ApiErrorFormatter\.php</exclude-pattern>
- <exclude-pattern>*/includes/api/ApiImport\.php</exclude-pattern>
- <exclude-pattern>*/includes/api/ApiMessage\.php</exclude-pattern>
- <exclude-pattern>*/includes/api/ApiOpenSearch\.php</exclude-pattern>
- <exclude-pattern>*/includes/api/ApiRsd\.php</exclude-pattern>
- <exclude-pattern>*/includes/compat/XMPReader\.php</exclude-pattern>
- <exclude-pattern>*/includes/diff/DairikiDiff\.php</exclude-pattern>
- <exclude-pattern>*/includes/Feed\.php</exclude-pattern>
- <exclude-pattern>*/includes/filerepo/file/LocalFile\.php</exclude-pattern>
- <exclude-pattern>*/includes/htmlform/HTMLFormElement\.php</exclude-pattern>
- <exclude-pattern>*/includes/libs/filebackend/FileBackendStore\.php</exclude-pattern>
- <exclude-pattern>*/includes/libs/filebackend/FSFileBackend\.php</exclude-pattern>
- <exclude-pattern>*/includes/libs/filebackend/SwiftFileBackend\.php</exclude-pattern>
- <exclude-pattern>*/includes/logging/LogEntry\.php</exclude-pattern>
- <exclude-pattern>*/includes/logging/LogFormatter\.php</exclude-pattern>
- <exclude-pattern>*/includes/media/SVGMetadataExtractor\.php</exclude-pattern>
<exclude-pattern>*/includes/parser/Preprocessor_DOM\.php</exclude-pattern>
<exclude-pattern>*/includes/parser/Preprocessor_Hash\.php</exclude-pattern>
<exclude-pattern>*/includes/parser/Preprocessor\.php</exclude-pattern>
- <exclude-pattern>*/includes/PathRouter\.php</exclude-pattern>
- <exclude-pattern>*/includes/profiler/SectionProfiler\.php</exclude-pattern>
- <exclude-pattern>*/includes/specialpage/LoginSignupSpecialPage\.php</exclude-pattern>
- <exclude-pattern>*/includes/specials/forms/PreferencesFormLegacy\.php</exclude-pattern>
- <exclude-pattern>*/includes/StubObject\.php</exclude-pattern>
- <exclude-pattern>*/includes/upload/UploadStash\.php</exclude-pattern>
- <exclude-pattern>*/includes/utils/AutoloadGenerator\.php</exclude-pattern>
<exclude-pattern>*/maintenance/dumpIterator\.php</exclude-pattern>
<exclude-pattern>*/maintenance/Maintenance\.php</exclude-pattern>
<exclude-pattern>*/maintenance/findDeprecated\.php</exclude-pattern>
</rule>
<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
<exclude-pattern>*/includes/StubObject\.php</exclude-pattern>
+ <exclude-pattern>*/includes/StubUserLang\.php</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.AssignmentInReturn.AssignmentInReturn">
<exclude-pattern>*/tests/phpunit/*\.php</exclude-pattern>
{
"extends": "stylelint-config-wikimedia",
"rules": {
+ "selector-class-pattern": "^((mw|oo-ui)-|(wikitable|(toc(|toggle|hidden))|client-(no)?js)$)",
"no-descending-specificity": null,
-
"selector-max-id": null
}
}
configuration changes from after they were created, so you must reset
services after any configuration change. Even if your code works now, it is
likely to break in future versions as more code is moved to services.
-* …
+* The ill-defined "DatabaseOraclePostInit" hook has been removed.
+* PreferencesFormLegacy and PreferencesForm classes, deprecated in 1.32, have
+ been removed.
+* ObjectFactory class, deprecated in 1.31, has been removed.
+* HWLDFWordAccumudlator class, deprecated in 1.28, has been removed.
+* XMPInfo, XMPReader and XMPValidate, deprecated in 1.32, have been removed.
=== Deprecations in 1.34 ===
* The MWNamespace class is deprecated. Use MediaWikiServices::getNamespaceInfo.
'ApiFormatPhp' => __DIR__ . '/includes/api/ApiFormatPhp.php',
'ApiFormatRaw' => __DIR__ . '/includes/api/ApiFormatRaw.php',
'ApiFormatXml' => __DIR__ . '/includes/api/ApiFormatXml.php',
- 'ApiFormatXmlRsd' => __DIR__ . '/includes/api/ApiRsd.php',
+ 'ApiFormatXmlRsd' => __DIR__ . '/includes/api/ApiFormatXmlRsd.php',
'ApiHelp' => __DIR__ . '/includes/api/ApiHelp.php',
'ApiHelpParamValueMessage' => __DIR__ . '/includes/api/ApiHelpParamValueMessage.php',
'ApiImageRotate' => __DIR__ . '/includes/api/ApiImageRotate.php',
'ApiImport' => __DIR__ . '/includes/api/ApiImport.php',
- 'ApiImportReporter' => __DIR__ . '/includes/api/ApiImport.php',
+ 'ApiImportReporter' => __DIR__ . '/includes/api/ApiImportReporter.php',
'ApiLinkAccount' => __DIR__ . '/includes/api/ApiLinkAccount.php',
'ApiLogin' => __DIR__ . '/includes/api/ApiLogin.php',
'ApiLogout' => __DIR__ . '/includes/api/ApiLogout.php',
'ApiModuleManager' => __DIR__ . '/includes/api/ApiModuleManager.php',
'ApiMove' => __DIR__ . '/includes/api/ApiMove.php',
'ApiOpenSearch' => __DIR__ . '/includes/api/ApiOpenSearch.php',
- 'ApiOpenSearchFormatJson' => __DIR__ . '/includes/api/ApiOpenSearch.php',
+ 'ApiOpenSearchFormatJson' => __DIR__ . '/includes/api/ApiOpenSearchFormatJson.php',
'ApiOptions' => __DIR__ . '/includes/api/ApiOptions.php',
'ApiPageSet' => __DIR__ . '/includes/api/ApiPageSet.php',
'ApiParamInfo' => __DIR__ . '/includes/api/ApiParamInfo.php',
'ArrayUtils' => __DIR__ . '/includes/libs/ArrayUtils.php',
'Article' => __DIR__ . '/includes/page/Article.php',
'AssembleUploadChunksJob' => __DIR__ . '/includes/jobqueue/jobs/AssembleUploadChunksJob.php',
- 'AtomFeed' => __DIR__ . '/includes/Feed.php',
+ 'AtomFeed' => __DIR__ . '/includes/changes/AtomFeed.php',
'AtomicSectionUpdate' => __DIR__ . '/includes/deferred/AtomicSectionUpdate.php',
'AttachLatest' => __DIR__ . '/maintenance/attachLatest.php',
'AugmentPageProps' => __DIR__ . '/includes/search/AugmentPageProps.php',
'ChangesListSpecialPage' => __DIR__ . '/includes/specialpage/ChangesListSpecialPage.php',
'ChangesListStringOptionsFilter' => __DIR__ . '/includes/changes/ChangesListStringOptionsFilter.php',
'ChangesListStringOptionsFilterGroup' => __DIR__ . '/includes/changes/ChangesListStringOptionsFilterGroup.php',
- 'ChannelFeed' => __DIR__ . '/includes/Feed.php',
+ 'ChannelFeed' => __DIR__ . '/includes/changes/ChannelFeed.php',
'CheckBadRedirects' => __DIR__ . '/maintenance/checkBadRedirects.php',
'CheckComposerLockUpToDate' => __DIR__ . '/maintenance/checkComposerLockUpToDate.php',
'CheckExtensionsCLI' => __DIR__ . '/maintenance/language/checkLanguage.inc',
'CheckLess' => __DIR__ . '/maintenance/checkLess.php',
'CheckStorage' => __DIR__ . '/maintenance/storage/checkStorage.php',
'CheckUsernames' => __DIR__ . '/maintenance/checkUsernames.php',
- 'ClassCollector' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
+ 'ClassCollector' => __DIR__ . '/includes/utils/ClassCollector.php',
'CleanupAncientTables' => __DIR__ . '/maintenance/cleanupAncientTables.php',
'CleanupBlocks' => __DIR__ . '/maintenance/cleanupBlocks.php',
'CleanupCaps' => __DIR__ . '/maintenance/cleanupCaps.php',
'DatabaseBase' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
'DatabaseInstaller' => __DIR__ . '/includes/installer/DatabaseInstaller.php',
'DatabaseLag' => __DIR__ . '/maintenance/lag.php',
- 'DatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
+ 'DatabaseLogEntry' => __DIR__ . '/includes/logging/DatabaseLogEntry.php',
'DatabaseMssql' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMssql.php',
'DatabaseMysqlBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqlBase.php',
'DatabaseMysqli' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqli.php',
'DerivativeRequest' => __DIR__ . '/includes/DerivativeRequest.php',
'DerivativeResourceLoaderContext' => __DIR__ . '/includes/resourceloader/DerivativeResourceLoaderContext.php',
'DescribeFileOp' => __DIR__ . '/includes/libs/filebackend/fileop/DescribeFileOp.php',
- 'Diff' => __DIR__ . '/includes/diff/DairikiDiff.php',
+ 'Diff' => __DIR__ . '/includes/diff/Diff.php',
'DiffEngine' => __DIR__ . '/includes/diff/DiffEngine.php',
'DiffFormatter' => __DIR__ . '/includes/diff/DiffFormatter.php',
'DiffHistoryBlob' => __DIR__ . '/includes/historyblob/DiffHistoryBlob.php',
- 'DiffOp' => __DIR__ . '/includes/diff/DairikiDiff.php',
- 'DiffOpAdd' => __DIR__ . '/includes/diff/DairikiDiff.php',
- 'DiffOpChange' => __DIR__ . '/includes/diff/DairikiDiff.php',
- 'DiffOpCopy' => __DIR__ . '/includes/diff/DairikiDiff.php',
- 'DiffOpDelete' => __DIR__ . '/includes/diff/DairikiDiff.php',
+ 'DiffOp' => __DIR__ . '/includes/diff/DiffOp.php',
+ 'DiffOpAdd' => __DIR__ . '/includes/diff/DiffOpAdd.php',
+ 'DiffOpChange' => __DIR__ . '/includes/diff/DiffOpChange.php',
+ 'DiffOpCopy' => __DIR__ . '/includes/diff/DiffOpCopy.php',
+ 'DiffOpDelete' => __DIR__ . '/includes/diff/DiffOpDelete.php',
'DifferenceEngine' => __DIR__ . '/includes/diff/DifferenceEngine.php',
'DifferenceEngineSlotDiffRenderer' => __DIR__ . '/includes/diff/DifferenceEngineSlotDiffRenderer.php',
'Digit2Html' => __DIR__ . '/maintenance/language/digit2html.php',
'ExternalUserNames' => __DIR__ . '/includes/user/ExternalUserNames.php',
'FSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/FSFile.php',
'FSFileBackend' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
- 'FSFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
- 'FSFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
- 'FSFileBackendList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
- 'FSFileOpHandle' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php',
+ 'FSFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/fileiteration/FSFileBackendDirList.php',
+ 'FSFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/fileiteration/FSFileBackendFileList.php',
+ 'FSFileBackendList' => __DIR__ . '/includes/libs/filebackend/fileiteration/FSFileBackendList.php',
+ 'FSFileOpHandle' => __DIR__ . '/includes/libs/filebackend/fileophandle/FSFileOpHandle.php',
'FSLockManager' => __DIR__ . '/includes/libs/lockmanager/FSLockManager.php',
'FakeConverter' => __DIR__ . '/languages/FakeConverter.php',
'FakeMaintenance' => __DIR__ . '/maintenance/Maintenance.php',
'FatalError' => __DIR__ . '/includes/exception/FatalError.php',
'FauxRequest' => __DIR__ . '/includes/FauxRequest.php',
'FauxResponse' => __DIR__ . '/includes/FauxResponse.php',
- 'FeedItem' => __DIR__ . '/includes/Feed.php',
+ 'FeedItem' => __DIR__ . '/includes/changes/FeedItem.php',
'FeedUtils' => __DIR__ . '/includes/FeedUtils.php',
'FetchText' => __DIR__ . '/maintenance/fetchText.php',
'FewestrevisionsPage' => __DIR__ . '/includes/specials/SpecialFewestrevisions.php',
'FileAwareNodeVisitor' => __DIR__ . '/maintenance/findDeprecated.php',
'FileBackend' => __DIR__ . '/includes/libs/filebackend/FileBackend.php',
'FileBackendDBRepoWrapper' => __DIR__ . '/includes/filerepo/FileBackendDBRepoWrapper.php',
- 'FileBackendError' => __DIR__ . '/includes/libs/filebackend/FileBackendError.php',
+ 'FileBackendError' => __DIR__ . '/includes/libs/filebackend/exception/FileBackendError.php',
'FileBackendGroup' => __DIR__ . '/includes/filebackend/FileBackendGroup.php',
'FileBackendMultiWrite' => __DIR__ . '/includes/libs/filebackend/FileBackendMultiWrite.php',
'FileBackendStore' => __DIR__ . '/includes/libs/filebackend/FileBackendStore.php',
- 'FileBackendStoreOpHandle' => __DIR__ . '/includes/libs/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardDirIterator' => __DIR__ . '/includes/libs/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardFileIterator' => __DIR__ . '/includes/libs/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardListIterator' => __DIR__ . '/includes/libs/filebackend/FileBackendStore.php',
+ 'FileBackendStoreOpHandle' => __DIR__ . '/includes/libs/filebackend/fileophandle/FileBackendStoreOpHandle.php',
+ 'FileBackendStoreShardDirIterator' => __DIR__ . '/includes/libs/filebackend/fileiteration/FileBackendStoreShardDirIterator.php',
+ 'FileBackendStoreShardFileIterator' => __DIR__ . '/includes/libs/filebackend/fileiteration/FileBackendStoreShardFileIterator.php',
+ 'FileBackendStoreShardListIterator' => __DIR__ . '/includes/libs/filebackend/fileiteration/FileBackendStoreShardListIterator.php',
'FileBasedSiteLookup' => __DIR__ . '/includes/site/FileBasedSiteLookup.php',
'FileCacheBase' => __DIR__ . '/includes/cache/FileCacheBase.php',
'FileContentHandler' => __DIR__ . '/includes/content/FileContentHandler.php',
'HTMLFileCache' => __DIR__ . '/includes/cache/HTMLFileCache.php',
'HTMLFloatField' => __DIR__ . '/includes/htmlform/fields/HTMLFloatField.php',
'HTMLForm' => __DIR__ . '/includes/htmlform/HTMLForm.php',
- 'HTMLFormActionFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
+ 'HTMLFormActionFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormActionFieldLayout.php',
'HTMLFormElement' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
'HTMLFormField' => __DIR__ . '/includes/htmlform/HTMLFormField.php',
'HTMLFormFieldCloner' => __DIR__ . '/includes/htmlform/fields/HTMLFormFieldCloner.php',
- 'HTMLFormFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
+ 'HTMLFormFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormFieldLayout.php',
'HTMLFormFieldRequiredOptionsException' => __DIR__ . '/includes/htmlform/HTMLFormFieldRequiredOptionsException.php',
'HTMLFormFieldWithButton' => __DIR__ . '/includes/htmlform/fields/HTMLFormFieldWithButton.php',
'HTMLHiddenField' => __DIR__ . '/includes/htmlform/fields/HTMLHiddenField.php',
'HTMLUserTextField' => __DIR__ . '/includes/htmlform/fields/HTMLUserTextField.php',
'HTMLUsersMultiselectField' => __DIR__ . '/includes/htmlform/fields/HTMLUsersMultiselectField.php',
'HTTPFileStreamer' => __DIR__ . '/includes/libs/filebackend/HTTPFileStreamer.php',
- 'HWLDFWordAccumulator' => __DIR__ . '/includes/diff/DairikiDiff.php',
'HashBagOStuff' => __DIR__ . '/includes/libs/objectcache/HashBagOStuff.php',
'HashConfig' => __DIR__ . '/includes/config/HashConfig.php',
'HashRing' => __DIR__ . '/includes/libs/HashRing.php',
'LanguageZh_hans' => __DIR__ . '/languages/classes/LanguageZh_hans.php',
'Languages' => __DIR__ . '/maintenance/language/languages.inc',
'LayeredParameterizedPassword' => __DIR__ . '/includes/password/LayeredParameterizedPassword.php',
- 'LegacyLogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php',
+ 'LegacyLogFormatter' => __DIR__ . '/includes/logging/LegacyLogFormatter.php',
'License' => __DIR__ . '/includes/specials/helpers/License.php',
'Licenses' => __DIR__ . '/includes/specials/formfields/Licenses.php',
'LinkBatch' => __DIR__ . '/includes/cache/LinkBatch.php',
'LoadBalancer' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancer.php',
'LoadBalancerSingle' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php',
'LocalFile' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
- 'LocalFileDeleteBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
- 'LocalFileLockError' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
- 'LocalFileMoveBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
- 'LocalFileRestoreBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
+ 'LocalFileDeleteBatch' => __DIR__ . '/includes/filerepo/file/LocalFileDeleteBatch.php',
+ 'LocalFileLockError' => __DIR__ . '/includes/filerepo/file/LocalFileLockError.php',
+ 'LocalFileMoveBatch' => __DIR__ . '/includes/filerepo/file/LocalFileMoveBatch.php',
+ 'LocalFileRestoreBatch' => __DIR__ . '/includes/filerepo/file/LocalFileRestoreBatch.php',
'LocalIdLookup' => __DIR__ . '/includes/user/LocalIdLookup.php',
'LocalRepo' => __DIR__ . '/includes/filerepo/LocalRepo.php',
'LocalSettingsGenerator' => __DIR__ . '/includes/installer/LocalSettingsGenerator.php',
'LockManager' => __DIR__ . '/includes/libs/lockmanager/LockManager.php',
'LockManagerGroup' => __DIR__ . '/includes/filebackend/lockmanager/LockManagerGroup.php',
'LogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
- 'LogEntryBase' => __DIR__ . '/includes/logging/LogEntry.php',
+ 'LogEntryBase' => __DIR__ . '/includes/logging/LogEntryBase.php',
'LogEventsList' => __DIR__ . '/includes/logging/LogEventsList.php',
'LogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php',
'LogPage' => __DIR__ . '/includes/logging/LogPage.php',
'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php',
'ManageForeignResources' => __DIR__ . '/maintenance/manageForeignResources.php',
'ManageJobs' => __DIR__ . '/maintenance/manageJobs.php',
- 'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
+ 'ManualLogEntry' => __DIR__ . '/includes/logging/ManualLogEntry.php',
'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php',
'MappedIterator' => __DIR__ . '/includes/libs/MappedIterator.php',
'MarkpatrolledAction' => __DIR__ . '/includes/actions/MarkpatrolledAction.php',
'ORAField' => __DIR__ . '/includes/db/ORAField.php',
'ORAResult' => __DIR__ . '/includes/db/ORAResult.php',
'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
- 'ObjectFactory' => __DIR__ . '/includes/compat/ObjectFactory.php',
'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
'OldLocalFile' => __DIR__ . '/includes/filerepo/file/OldLocalFile.php',
'OldRevisionImporter' => __DIR__ . '/includes/import/OldRevisionImporter.php',
'PerRowAugmentor' => __DIR__ . '/includes/search/PerRowAugmentor.php',
'PermissionsError' => __DIR__ . '/includes/exception/PermissionsError.php',
'PhpHttpRequest' => __DIR__ . '/includes/http/PhpHttpRequest.php',
- 'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php',
+ 'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpXmlBugTester.php',
'Pingback' => __DIR__ . '/includes/Pingback.php',
'PoolCounter' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
'PoolCounterNull' => __DIR__ . '/includes/poolcounter/PoolCounterNull.php',
'PostgreSqlLockManager' => __DIR__ . '/includes/libs/lockmanager/PostgreSqlLockManager.php',
'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php',
'PostgresUpdater' => __DIR__ . '/includes/installer/PostgresUpdater.php',
- 'PreferencesForm' => __DIR__ . '/includes/specials/forms/PreferencesFormLegacy.php',
- 'PreferencesFormLegacy' => __DIR__ . '/includes/specials/forms/PreferencesFormLegacy.php',
'PreferencesFormOOUI' => __DIR__ . '/includes/specials/forms/PreferencesFormOOUI.php',
'PrefixSearch' => __DIR__ . '/includes/search/PrefixSearch.php',
'PrefixingStatsdDataFactoryProxy' => __DIR__ . '/includes/libs/stats/PrefixingStatsdDataFactoryProxy.php',
'QuorumLockManager' => __DIR__ . '/includes/libs/lockmanager/QuorumLockManager.php',
'RCCacheEntry' => __DIR__ . '/includes/changes/RCCacheEntry.php',
'RCCacheEntryFactory' => __DIR__ . '/includes/changes/RCCacheEntryFactory.php',
- 'RCDatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
+ 'RCDatabaseLogEntry' => __DIR__ . '/includes/logging/RCDatabaseLogEntry.php',
'RCFeed' => __DIR__ . '/includes/rcfeed/RCFeed.php',
'RCFeedEngine' => __DIR__ . '/includes/rcfeed/RCFeedEngine.php',
'RCFeedFormatter' => __DIR__ . '/includes/rcfeed/RCFeedFormatter.php',
'RESTBagOStuff' => __DIR__ . '/includes/libs/objectcache/RESTBagOStuff.php',
- 'RSSFeed' => __DIR__ . '/includes/Feed.php',
+ 'RSSFeed' => __DIR__ . '/includes/changes/RSSFeed.php',
'RandomPage' => __DIR__ . '/includes/specials/SpecialRandompage.php',
'RangeChronologicalPager' => __DIR__ . '/includes/pager/RangeChronologicalPager.php',
'RangeDifference' => __DIR__ . '/includes/diff/RangeDifference.php',
'RunJobs' => __DIR__ . '/maintenance/runJobs.php',
'RunnableJob' => __DIR__ . '/includes/jobqueue/RunnableJob.php',
'SVGMetadataExtractor' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
- 'SVGReader' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
+ 'SVGReader' => __DIR__ . '/includes/media/SVGReader.php',
'SamplingStatsdClient' => __DIR__ . '/includes/libs/stats/SamplingStatsdClient.php',
'Sanitizer' => __DIR__ . '/includes/parser/Sanitizer.php',
'ScopedLock' => __DIR__ . '/includes/libs/lockmanager/ScopedLock.php',
'SearchSuggestion' => __DIR__ . '/includes/search/SearchSuggestion.php',
'SearchSuggestionSet' => __DIR__ . '/includes/search/SearchSuggestionSet.php',
'SearchUpdate' => __DIR__ . '/includes/deferred/SearchUpdate.php',
- 'SectionProfileCallback' => __DIR__ . '/includes/profiler/SectionProfiler.php',
+ 'SectionProfileCallback' => __DIR__ . '/includes/profiler/SectionProfileCallback.php',
'SectionProfiler' => __DIR__ . '/includes/profiler/SectionProfiler.php',
'SevenZipStream' => __DIR__ . '/maintenance/7zip.inc',
'ShiConverter' => __DIR__ . '/languages/classes/LanguageShi.php',
'SkinTemplate' => __DIR__ . '/includes/skins/SkinTemplate.php',
'SlideshowImageGallery' => __DIR__ . '/includes/gallery/SlideshowImageGallery.php',
'SlotDiffRenderer' => __DIR__ . '/includes/diff/SlotDiffRenderer.php',
- 'SpecialActiveUsers' => __DIR__ . '/includes/specials/SpecialActiveusers.php',
+ 'SpecialActiveUsers' => __DIR__ . '/includes/specials/SpecialActiveUsers.php',
'SpecialAllMessages' => __DIR__ . '/includes/specials/SpecialAllMessages.php',
'SpecialAllMyUploads' => __DIR__ . '/includes/specials/redirects/SpecialAllMyUploads.php',
'SpecialAllPages' => __DIR__ . '/includes/specials/SpecialAllPages.php',
'SpecialBlankpage' => __DIR__ . '/includes/specials/SpecialBlankpage.php',
'SpecialBlock' => __DIR__ . '/includes/specials/SpecialBlock.php',
'SpecialBlockList' => __DIR__ . '/includes/specials/SpecialBlockList.php',
- 'SpecialBookSources' => __DIR__ . '/includes/specials/SpecialBooksources.php',
+ 'SpecialBookSources' => __DIR__ . '/includes/specials/SpecialBookSources.php',
'SpecialBotPasswords' => __DIR__ . '/includes/specials/SpecialBotPasswords.php',
'SpecialCachedPage' => __DIR__ . '/includes/specials/SpecialCachedPage.php',
'SpecialCategories' => __DIR__ . '/includes/specials/SpecialCategories.php',
'SpecialDiff' => __DIR__ . '/includes/specials/SpecialDiff.php',
'SpecialEditTags' => __DIR__ . '/includes/specials/SpecialEditTags.php',
'SpecialEditWatchlist' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
- 'SpecialEmailUser' => __DIR__ . '/includes/specials/SpecialEmailuser.php',
+ 'SpecialEmailUser' => __DIR__ . '/includes/specials/SpecialEmailUser.php',
'SpecialExpandTemplates' => __DIR__ . '/includes/specials/SpecialExpandTemplates.php',
'SpecialExport' => __DIR__ . '/includes/specials/SpecialExport.php',
'SpecialFilepath' => __DIR__ . '/includes/specials/SpecialFilepath.php',
'SpecialLinkAccounts' => __DIR__ . '/includes/specials/SpecialLinkAccounts.php',
'SpecialListAdmins' => __DIR__ . '/includes/specials/redirects/SpecialListAdmins.php',
'SpecialListBots' => __DIR__ . '/includes/specials/redirects/SpecialListBots.php',
- 'SpecialListFiles' => __DIR__ . '/includes/specials/SpecialListfiles.php',
- 'SpecialListGrants' => __DIR__ . '/includes/specials/SpecialListgrants.php',
- 'SpecialListGroupRights' => __DIR__ . '/includes/specials/SpecialListgrouprights.php',
- 'SpecialListUsers' => __DIR__ . '/includes/specials/SpecialListusers.php',
+ 'SpecialListFiles' => __DIR__ . '/includes/specials/SpecialListFiles.php',
+ 'SpecialListGrants' => __DIR__ . '/includes/specials/SpecialListGrants.php',
+ 'SpecialListGroupRights' => __DIR__ . '/includes/specials/SpecialListGroupRights.php',
+ 'SpecialListUsers' => __DIR__ . '/includes/specials/SpecialListUsers.php',
'SpecialLockdb' => __DIR__ . '/includes/specials/SpecialLockdb.php',
'SpecialLog' => __DIR__ . '/includes/specials/SpecialLog.php',
'SpecialMergeHistory' => __DIR__ . '/includes/specials/SpecialMergeHistory.php',
'SpecialRandomInCategory' => __DIR__ . '/includes/specials/SpecialRandomInCategory.php',
'SpecialRandomredirect' => __DIR__ . '/includes/specials/SpecialRandomredirect.php',
'SpecialRandomrootpage' => __DIR__ . '/includes/specials/SpecialRandomrootpage.php',
- 'SpecialRecentChanges' => __DIR__ . '/includes/specials/SpecialRecentchanges.php',
- 'SpecialRecentChangesLinked' => __DIR__ . '/includes/specials/SpecialRecentchangeslinked.php',
+ 'SpecialRecentChanges' => __DIR__ . '/includes/specials/SpecialRecentChanges.php',
+ 'SpecialRecentChangesLinked' => __DIR__ . '/includes/specials/SpecialRecentChangesLinked.php',
'SpecialRedirect' => __DIR__ . '/includes/specials/SpecialRedirect.php',
'SpecialRedirectToSpecial' => __DIR__ . '/includes/specialpage/SpecialRedirectToSpecial.php',
'SpecialRemoveCredentials' => __DIR__ . '/includes/specials/SpecialRemoveCredentials.php',
'SpecialResetTokens' => __DIR__ . '/includes/specials/SpecialResetTokens.php',
- 'SpecialRevisionDelete' => __DIR__ . '/includes/specials/SpecialRevisiondelete.php',
+ 'SpecialRevisionDelete' => __DIR__ . '/includes/specials/SpecialRevisionDelete.php',
'SpecialRunJobs' => __DIR__ . '/includes/specials/SpecialRunJobs.php',
'SpecialSearch' => __DIR__ . '/includes/specials/SpecialSearch.php',
'SpecialSpecialpages' => __DIR__ . '/includes/specials/SpecialSpecialpages.php',
'SpecialUserLogout' => __DIR__ . '/includes/specials/SpecialUserLogout.php',
'SpecialVersion' => __DIR__ . '/includes/specials/SpecialVersion.php',
'SpecialWatchlist' => __DIR__ . '/includes/specials/SpecialWatchlist.php',
- 'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatlinkshere.php',
+ 'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatLinksHere.php',
'SqlBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php',
'SqlSearchResultSet' => __DIR__ . '/includes/search/SqlSearchResultSet.php',
'Sqlite' => __DIR__ . '/maintenance/sqlite.inc',
'StringUtils' => __DIR__ . '/includes/libs/StringUtils.php',
'StripState' => __DIR__ . '/includes/parser/StripState.php',
'StubObject' => __DIR__ . '/includes/StubObject.php',
- 'StubUserLang' => __DIR__ . '/includes/StubObject.php',
+ 'StubUserLang' => __DIR__ . '/includes/StubUserLang.php',
'SubmitAction' => __DIR__ . '/includes/actions/SubmitAction.php',
'SubpageImportTitleFactory' => __DIR__ . '/includes/title/SubpageImportTitleFactory.php',
'SvgHandler' => __DIR__ . '/includes/media/SvgHandler.php',
'SwiftFileBackend' => __DIR__ . '/includes/libs/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendList' => __DIR__ . '/includes/libs/filebackend/SwiftFileBackend.php',
- 'SwiftFileOpHandle' => __DIR__ . '/includes/libs/filebackend/SwiftFileBackend.php',
+ 'SwiftFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/fileiteration/SwiftFileBackendDirList.php',
+ 'SwiftFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/fileiteration/SwiftFileBackendFileList.php',
+ 'SwiftFileBackendList' => __DIR__ . '/includes/libs/filebackend/fileiteration/SwiftFileBackendList.php',
+ 'SwiftFileOpHandle' => __DIR__ . '/includes/libs/filebackend/fileophandle/SwiftFileOpHandle.php',
'SwiftVirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/SwiftVirtualRESTService.php',
'SyncFileBackend' => __DIR__ . '/maintenance/syncFileBackend.php',
'TableCleanup' => __DIR__ . '/maintenance/cleanupTable.inc',
'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
'UploadStashBadPathException' => __DIR__ . '/includes/upload/exception/UploadStashBadPathException.php',
'UploadStashException' => __DIR__ . '/includes/upload/exception/UploadStashException.php',
- 'UploadStashFile' => __DIR__ . '/includes/upload/UploadStash.php',
+ 'UploadStashFile' => __DIR__ . '/includes/upload/UploadStashFile.php',
'UploadStashFileException' => __DIR__ . '/includes/upload/exception/UploadStashFileException.php',
'UploadStashFileNotFoundException' => __DIR__ . '/includes/upload/exception/UploadStashFileNotFoundException.php',
'UploadStashNoSuchKeyException' => __DIR__ . '/includes/upload/exception/UploadStashNoSuchKeyException.php',
'WrapOldPasswords' => __DIR__ . '/maintenance/wrapOldPasswords.php',
'XCFHandler' => __DIR__ . '/includes/media/XCFHandler.php',
'XMLRCFeedFormatter' => __DIR__ . '/includes/rcfeed/XMLRCFeedFormatter.php',
- 'XMPInfo' => __DIR__ . '/includes/compat/XMPReader.php',
- 'XMPReader' => __DIR__ . '/includes/compat/XMPReader.php',
- 'XMPValidate' => __DIR__ . '/includes/compat/XMPReader.php',
'Xhprof' => __DIR__ . '/includes/libs/Xhprof.php',
'XhprofData' => __DIR__ . '/includes/libs/XhprofData.php',
'Xml' => __DIR__ . '/includes/Xml.php',
"php": {
"type": "string",
"description": "Version constraint string against PHP."
+ },
+ "ability-shell": {
+ "type": "boolean",
+ "default": false,
+ "description": "Whether this extension requires shell access."
}
},
"patternProperties": {
"php": {
"type": "string",
"description": "Version constraint string against PHP."
+ },
+ "ability-shell": {
+ "type": "boolean",
+ "default": false,
+ "description": "Whether this extension requires shell access."
}
},
"patternProperties": {
$article: Article being edited
$user: User performing the edit
-'DatabaseOraclePostInit': Called after initialising an Oracle database
-$db: the DatabaseOracle object
-
'DeletedContribsPager::reallyDoQuery': Called before really executing the query
for Special:DeletedContributions
Similar to ContribsPager::reallyDoQuery
}
// Run hook for extension authorization plugins
- /** @var $result array */
+ /** @var array $result */
$result = null;
if ( !Hooks::run( 'ImgAuthBeforeStream', [ &$title, &$path, &$name, &$result ] ) ) {
wfForbidden( $result[0], $result[1], array_slice( $result, 2 ) );
* subsequent arguments to $msg2 will be passed as parameters only for replacing in $msg2
* @param string $msg1
* @param string $msg2
+ * @param mixed ...$args To pass as params to wfMessage() with $msg2. Either variadic, or a single
+ * array argument.
*/
-function wfForbidden( $msg1, $msg2 ) {
+function wfForbidden( $msg1, $msg2, ...$args ) {
global $wgImgAuthDetails;
- $args = func_get_args();
- array_shift( $args );
- array_shift( $args );
$args = ( isset( $args[0] ) && is_array( $args[0] ) ) ? $args[0] : $args;
$msgHdr = wfMessage( $msg1 )->escaped();
// For back-compat, it is supported that mResponseCode be a string like " 200 OK"
// (with leading space and the status message after). Cast response code to an integer
// to take advantage of PHP's conversion rules which will turn " 200 OK" into 200.
- // https://secure.php.net/manual/en/language.types.string.php#language.types.string.conversion
+ // https://www.php.net/manual/en/language.types.string.php#language.types.string.conversion
$n = intval( trim( $this->mResponseCode ) );
HttpStatus::header( $n );
}
* Category objects are immutable, strictly speaking. If you call methods that change the database,
* like to refresh link counts, the objects will be appropriately reinitialized.
* Member variables are lazy-initialized.
- *
- * @todo Move some stuff from CategoryPage.php to here, and use that.
*/
class Category {
/** Name of the category, normalized to DB-key form */
/**
* Show Exif data, on by default if available.
- * Requires PHP's Exif extension: https://secure.php.net/manual/en/ref.exif.php
+ * Requires PHP's Exif extension: https://www.php.net/manual/en/ref.exif.php
*
* @note FOR WINDOWS USERS:
* To enable Exif functions, add the following line to the "Windows
* Defaults to the wiki timezone ($wgLocaltimezone).
*
* A list of usable timezones can found at:
- * https://secure.php.net/manual/en/timezones.php
+ * https://www.php.net/manual/en/timezones.php
*
* @par Examples:
* @code
* timezone-nameinlowercase like timezone-utc.
*
* A list of usable timezones can found at:
- * https://secure.php.net/manual/en/timezones.php
+ * https://www.php.net/manual/en/timezones.php
*
* @par Examples:
* @code
* all languages in a mediocre way. However, it is better than "uppercase".
*
* To use the uca-default collation, you must have PHP's intl extension
- * installed. See https://secure.php.net/manual/en/intl.setup.php . The details of the
+ * installed. See https://www.php.net/manual/en/intl.setup.php . The details of the
* resulting collation will depend on the version of ICU installed on the
* server.
*
/**
* Origin Trials tokens.
*
- * @since 1.34
+ * @since 1.33
* @var array
*/
$wgOriginTrials = [];
*
* @warning EXPERIMENTAL!
*
- * @since 1.34
+ * @since 1.33
* @var bool
*/
$wgPriorityHints = false;
*
* @warning EXPERIMENTAL!
*
- * @since 1.34
+ * @since 1.33
* @var bool
*/
$wgElementTiming = false;
+/**
+ * Expiry of the endpoint definition for the Reporting API.
+ *
+ * @warning EXPERIMENTAL!
+ *
+ * @since 1.34
+ * @var int
+ */
+$wgReportToExpiry = 86400;
+
+/**
+ * List of endpoints for the Reporting API.
+ *
+ * @warning EXPERIMENTAL!
+ *
+ * @since 1.34
+ * @var array
+ */
+$wgReportToEndpoints = [];
+
+/**
+ * List of Feature Policy Reporting types to enable.
+ * Each entry is turned into a Feature-Policy-Report-Only header.
+ *
+ * @warning EXPERIMENTAL!
+ *
+ * @since 1.34
+ * @var array
+ */
+$wgFeaturePolicyReportOnly = [];
+
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
* Debugging: MediaWiki
*/
global $wgDevelopmentWarnings, $wgShowExceptionDetails, $wgShowHostnames,
- $wgDebugRawPage, $wgDebugComments, $wgDebugDumpSql, $wgDebugTimestamps,
- $wgCommandLineMode, $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups;
+ $wgDebugRawPage, $wgSQLMode, $wgCommandLineMode, $wgDebugLogFile,
+ $wgDBerrorLog, $wgDebugLogGroups;
// Use of wfWarn() should cause tests to fail
$wgDevelopmentWarnings = true;
+++ /dev/null
-<?php
-/**
- * Basic support for outputting syndication feeds in RSS, other formats.
- *
- * Contain a feed class as well as classes to build rss / atom ... feeds
- * Available feeds are defined in Defines.php
- *
- * Copyright © 2004 Brion Vibber <brion@pobox.com>
- * https://www.mediawiki.org/
- *
- * 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
- */
-
-/**
- * @defgroup Feed Feed
- */
-
-/**
- * A base class for basic support for outputting syndication feeds in RSS and other formats.
- *
- * @ingroup Feed
- */
-class FeedItem {
- /** @var Title */
- public $title;
-
- public $description;
-
- public $url;
-
- public $date;
-
- public $author;
-
- public $uniqueId;
-
- public $comments;
-
- public $rssIsPermalink = false;
-
- /**
- * @param string|Title $title Item's title
- * @param string $description
- * @param string $url URL uniquely designating the item.
- * @param string $date Item's date
- * @param string $author Author's user name
- * @param string $comments
- */
- function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) {
- $this->title = $title;
- $this->description = $description;
- $this->url = $url;
- $this->uniqueId = $url;
- $this->date = $date;
- $this->author = $author;
- $this->comments = $comments;
- }
-
- /**
- * Encode $string so that it can be safely embedded in a XML document
- *
- * @param string $string String to encode
- * @return string
- */
- public function xmlEncode( $string ) {
- $string = str_replace( "\r\n", "\n", $string );
- $string = preg_replace( '/[\x00-\x08\x0b\x0c\x0e-\x1f]/', '', $string );
- return htmlspecialchars( $string );
- }
-
- /**
- * Get the unique id of this item; already xml-encoded
- * @return string
- */
- public function getUniqueID() {
- $id = $this->getUniqueIdUnescaped();
- if ( $id ) {
- return $this->xmlEncode( $id );
- }
- }
-
- /**
- * Get the unique id of this item, without any escaping
- * @return string
- */
- public function getUniqueIdUnescaped() {
- if ( $this->uniqueId ) {
- return wfExpandUrl( $this->uniqueId, PROTO_CURRENT );
- }
- }
-
- /**
- * Set the unique id of an item
- *
- * @param string $uniqueId Unique id for the item
- * @param bool $rssIsPermalink Set to true if the guid (unique id) is a permalink (RSS feeds only)
- */
- public function setUniqueId( $uniqueId, $rssIsPermalink = false ) {
- $this->uniqueId = $uniqueId;
- $this->rssIsPermalink = $rssIsPermalink;
- }
-
- /**
- * Get the title of this item; already xml-encoded
- *
- * @return string
- */
- public function getTitle() {
- return $this->xmlEncode( $this->title );
- }
-
- /**
- * Get the URL of this item; already xml-encoded
- *
- * @return string
- */
- public function getUrl() {
- return $this->xmlEncode( $this->url );
- }
-
- /** Get the URL of this item without any escaping
- *
- * @return string
- */
- public function getUrlUnescaped() {
- return $this->url;
- }
-
- /**
- * Get the description of this item; already xml-encoded
- *
- * @return string
- */
- public function getDescription() {
- return $this->xmlEncode( $this->description );
- }
-
- /**
- * Get the description of this item without any escaping
- *
- * @return string
- */
- public function getDescriptionUnescaped() {
- return $this->description;
- }
-
- /**
- * Get the language of this item
- *
- * @return string
- */
- public function getLanguage() {
- global $wgLanguageCode;
- return LanguageCode::bcp47( $wgLanguageCode );
- }
-
- /**
- * Get the date of this item
- *
- * @return string
- */
- public function getDate() {
- return $this->date;
- }
-
- /**
- * Get the author of this item; already xml-encoded
- *
- * @return string
- */
- public function getAuthor() {
- return $this->xmlEncode( $this->author );
- }
-
- /**
- * Get the author of this item without any escaping
- *
- * @return string
- */
- public function getAuthorUnescaped() {
- return $this->author;
- }
-
- /**
- * Get the comment of this item; already xml-encoded
- *
- * @return string
- */
- public function getComments() {
- return $this->xmlEncode( $this->comments );
- }
-
- /**
- * Get the comment of this item without any escaping
- *
- * @return string
- */
- public function getCommentsUnescaped() {
- return $this->comments;
- }
-
- /**
- * Quickie hack... strip out wikilinks to more legible form from the comment.
- *
- * @param string $text Wikitext
- * @return string
- */
- public static function stripComment( $text ) {
- return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
- }
- /**#@-*/
-}
-
-/**
- * Class to support the outputting of syndication feeds in Atom and RSS format.
- *
- * @ingroup Feed
- */
-abstract class ChannelFeed extends FeedItem {
-
- /** @var TemplateParser */
- protected $templateParser;
-
- /**
- * @param string|Title $title Feed's title
- * @param string $description
- * @param string $url URL uniquely designating the feed.
- * @param string $date Feed's date
- * @param string $author Author's user name
- * @param string $comments
- */
- function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) {
- parent::__construct( $title, $description, $url, $date, $author, $comments );
- $this->templateParser = new TemplateParser();
- }
-
- /**
- * Generate Header of the feed
- * @par Example:
- * @code
- * print "<feed>";
- * @endcode
- */
- abstract public function outHeader();
-
- /**
- * Generate an item
- * @par Example:
- * @code
- * print "<item>...</item>";
- * @endcode
- * @param FeedItem $item
- */
- abstract public function outItem( $item );
-
- /**
- * Generate Footer of the feed
- * @par Example:
- * @code
- * print "</feed>";
- * @endcode
- */
- abstract public function outFooter();
-
- /**
- * Setup and send HTTP headers. Don't send any content;
- * content might end up being cached and re-sent with
- * these same headers later.
- *
- * This should be called from the outHeader() method,
- * but can also be called separately.
- */
- public function httpHeaders() {
- global $wgOut, $wgVaryOnXFP;
-
- # We take over from $wgOut, excepting its cache header info
- $wgOut->disable();
- $mimetype = $this->contentType();
- header( "Content-type: $mimetype; charset=UTF-8" );
-
- // Set a sane filename
- $exts = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer()
- ->getExtensionsForType( $mimetype );
- $ext = $exts ? strtok( $exts, ' ' ) : 'xml';
- header( "Content-Disposition: inline; filename=\"feed.{$ext}\"" );
-
- if ( $wgVaryOnXFP ) {
- $wgOut->addVaryHeader( 'X-Forwarded-Proto' );
- }
- $wgOut->sendCacheControl();
- }
-
- /**
- * Return an internet media type to be sent in the headers.
- *
- * @return string
- */
- private function contentType() {
- global $wgRequest;
-
- $ctype = $wgRequest->getVal( 'ctype', 'application/xml' );
- $allowedctypes = [
- 'application/xml',
- 'text/xml',
- 'application/rss+xml',
- 'application/atom+xml'
- ];
-
- return ( in_array( $ctype, $allowedctypes ) ? $ctype : 'application/xml' );
- }
-
- /**
- * Output the initial XML headers.
- */
- protected function outXmlHeader() {
- $this->httpHeaders();
- echo '<?xml version="1.0"?>' . "\n";
- }
-}
-
-/**
- * Generate a RSS feed
- *
- * @ingroup Feed
- */
-class RSSFeed extends ChannelFeed {
-
- /**
- * Format a date given a timestamp. If a timestamp is not given, nothing is returned
- *
- * @param int|null $ts Timestamp
- * @return string|null Date string
- */
- function formatTime( $ts ) {
- if ( $ts ) {
- return gmdate( 'D, d M Y H:i:s \G\M\T', wfTimestamp( TS_UNIX, $ts ) );
- }
- }
-
- /**
- * Output an RSS 2.0 header
- */
- function outHeader() {
- global $wgVersion;
-
- $this->outXmlHeader();
- // Manually escaping rather than letting Mustache do it because Mustache
- // uses htmlentities, which does not work with XML
- $templateParams = [
- 'title' => $this->getTitle(),
- 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ),
- 'description' => $this->getDescription(),
- 'language' => $this->xmlEncode( $this->getLanguage() ),
- 'version' => $this->xmlEncode( $wgVersion ),
- 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) )
- ];
- print $this->templateParser->processTemplate( 'RSSHeader', $templateParams );
- }
-
- /**
- * Output an RSS 2.0 item
- * @param FeedItem $item Item to be output
- */
- function outItem( $item ) {
- // Manually escaping rather than letting Mustache do it because Mustache
- // uses htmlentities, which does not work with XML
- $templateParams = [
- "title" => $item->getTitle(),
- "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ),
- "permalink" => $item->rssIsPermalink,
- "uniqueID" => $item->getUniqueID(),
- "description" => $item->getDescription(),
- "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ),
- "author" => $item->getAuthor()
- ];
- $comments = $item->getCommentsUnescaped();
- if ( $comments ) {
- $commentsEscaped = $this->xmlEncode( wfExpandUrl( $comments, PROTO_CURRENT ) );
- $templateParams["comments"] = $commentsEscaped;
- }
- print $this->templateParser->processTemplate( 'RSSItem', $templateParams );
- }
-
- /**
- * Output an RSS 2.0 footer
- */
- function outFooter() {
- print "</channel></rss>";
- }
-}
-
-/**
- * Generate an Atom feed
- *
- * @ingroup Feed
- */
-class AtomFeed extends ChannelFeed {
- /**
- * Format a date given timestamp, if one is given.
- *
- * @param string|int|null $timestamp
- * @return string|null
- */
- function formatTime( $timestamp ) {
- if ( $timestamp ) {
- // need to use RFC 822 time format at least for rss2.0
- return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $timestamp ) );
- }
- }
-
- /**
- * Outputs a basic header for Atom 1.0 feeds.
- */
- function outHeader() {
- global $wgVersion;
- $this->outXmlHeader();
- // Manually escaping rather than letting Mustache do it because Mustache
- // uses htmlentities, which does not work with XML
- $templateParams = [
- 'language' => $this->xmlEncode( $this->getLanguage() ),
- 'feedID' => $this->getFeedId(),
- 'title' => $this->getTitle(),
- 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ),
- 'selfUrl' => $this->getSelfUrl(),
- 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) ),
- 'description' => $this->getDescription(),
- 'version' => $this->xmlEncode( $wgVersion ),
- ];
- print $this->templateParser->processTemplate( 'AtomHeader', $templateParams );
- }
-
- /**
- * Atom 1.0 requires a unique, opaque IRI as a unique identifier
- * for every feed we create. For now just use the URL, but who
- * can tell if that's right? If we put options on the feed, do we
- * have to change the id? Maybe? Maybe not.
- *
- * @return string
- */
- private function getFeedId() {
- return $this->getSelfUrl();
- }
-
- /**
- * Atom 1.0 requests a self-reference to the feed.
- * @return string
- */
- private function getSelfUrl() {
- global $wgRequest;
- return htmlspecialchars( $wgRequest->getFullRequestURL() );
- }
-
- /**
- * Output a given item.
- * @param FeedItem $item
- */
- function outItem( $item ) {
- global $wgMimeType;
- // Manually escaping rather than letting Mustache do it because Mustache
- // uses htmlentities, which does not work with XML
- $templateParams = [
- "uniqueID" => $item->getUniqueID(),
- "title" => $item->getTitle(),
- "mimeType" => $this->xmlEncode( $wgMimeType ),
- "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ),
- "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ),
- "description" => $item->getDescription(),
- "author" => $item->getAuthor()
- ];
- print $this->templateParser->processTemplate( 'AtomItem', $templateParams );
- }
-
- /**
- * Outputs the footer for Atom 1.0 feed (basically '\</feed\>').
- */
- function outFooter() {
- print "</feed>";
- }
-}
/** @name ArrayAccess functions
* These functions implement the ArrayAccess PHP interface.
- * @see https://secure.php.net/manual/en/class.arrayaccess.php
+ * @see https://www.php.net/manual/en/class.arrayaccess.php
*/
/* @{ */
/**
*/
function wfRecursiveRemoveDir( $dir ) {
wfDebug( __FUNCTION__ . "( $dir )\n" );
- // taken from https://secure.php.net/manual/en/function.rmdir.php#98622
+ // taken from https://www.php.net/manual/en/function.rmdir.php#98622
if ( is_dir( $dir ) ) {
$objects = scandir( $dir );
foreach ( $objects as $object ) {
*/
class MergeHistory {
- /** @const int Maximum number of revisions that can be merged at once */
+ /** Maximum number of revisions that can be merged at once */
const REVISION_LIMIT = 5000;
/** @var Title Page from which history will be merged */
return $config->get( 'OriginTrials' );
}
+ private function getReportTo() {
+ $config = $this->getConfig();
+
+ $expiry = $config->get( 'ReportToExpiry' );
+
+ if ( !$expiry ) {
+ return false;
+ }
+
+ $endpoints = $config->get( 'ReportToEndpoints' );
+
+ if ( !$endpoints ) {
+ return false;
+ }
+
+ $output = [ 'max_age' => $expiry, 'endpoints' => [] ];
+
+ foreach ( $endpoints as $endpoint ) {
+ $output['endpoints'][] = [ 'url' => $endpoint ];
+ }
+
+ return json_encode( $output, JSON_UNESCAPED_SLASHES );
+ }
+
+ private function getFeaturePolicyReportOnly() {
+ $config = $this->getConfig();
+
+ $features = $config->get( 'FeaturePolicyReportOnly' );
+ return implode( ';', $features );
+ }
+
/**
* Send cache control HTTP headers
*/
$response->header( "Origin-Trial: $originTrial", false );
}
+ $reportTo = $this->getReportTo();
+ if ( $reportTo ) {
+ $response->header( "Report-To: $reportTo" );
+ }
+
+ $featurePolicyReportOnly = $this->getFeaturePolicyReportOnly();
+ if ( $featurePolicyReportOnly ) {
+ $response->header( "Feature-Policy-Report-Only: $featurePolicyReportOnly" );
+ }
+
ContentSecurityPolicy::sendHeaders( $this );
if ( $this->mArticleBodyOnly ) {
'vendor' => 'the PHP Group',
'upstreamSupported' => '5.6.0',
'minSupported' => '7.0.13',
- 'upgradeURL' => 'https://secure.php.net/downloads.php',
+ 'upgradeURL' => 'https://www.php.net/downloads.php',
);
}
$missingExtText = '';
$missingExtHtml = '';
- $baseUrl = 'https://secure.php.net';
+ $baseUrl = 'https://www.php.net';
foreach ( $missingExtensions as $ext ) {
$missingExtText .= " * $ext <$baseUrl/$ext>\n";
$missingExtHtml .= "<li><b>$ext</b> "
$method = __METHOD__;
if ( $out->getFlag( 'vary-revision' ) ) {
- // XXX: Would be just keep the output if the speculative revision ID was correct,
- // but that can go wrong for some edge cases, like {{PAGEID}} during page creation.
- // For that specific case, it would perhaps nice to have a vary-page flag.
+ // If {{PAGEID}} resolved to 0 or {{REVISIONTIMESTAMP}} used the current
+ // timestamp rather than that of an actual revision, then those words need
+ // to resolve to the actual page ID or revision timestamp, respectively.
$this->saveParseLogger->info(
"$method: Prepared output has vary-revision...\n"
);
"$method: Prepared output has vary-revision-id with wrong ID...\n"
);
return true;
+ } elseif ( $out->getFlag( 'vary-revision-exists' ) ) {
+ // If {{REVISIONID}} resolved to '', it now needs to resolve to '-'.
+ // Note that edit stashing always uses '-', which can be used for both
+ // edit filter checks and canonical parser cache.
+ $this->saveParseLogger->info(
+ "$method: Prepared output has vary-revision-exists...\n"
+ );
+ return true;
} else {
// NOTE: In the original fix for T135261, the output was discarded if 'vary-user' was
// set for a null-edit. The reason was that the original rendering in that case was
* which is not fun
*
* @param array $array1
+ * @param array ...$arrays
*
* @return array
*/
- static function arrayMerge( $array1/* ... */ ) {
+ static function arrayMerge( array $array1, ...$arrays ) {
$out = $array1;
- $argsCount = func_num_args();
- for ( $i = 1; $i < $argsCount; $i++ ) {
- foreach ( func_get_arg( $i ) as $key => $value ) {
+ foreach ( $arrays as $array ) {
+ foreach ( $array as $key => $value ) {
if ( isset( $out[$key] ) && is_array( $out[$key] ) && is_array( $value ) ) {
$out[$key] = self::arrayMerge( $out[$key], $value );
} elseif ( !isset( $out[$key] ) || !$out[$key] && !is_numeric( $key ) ) {
$parentRevision = $this->grabCurrentRevision();
- $this->slotsOutput = [];
- $this->canonicalParserOutput = null;
-
// The edit may have already been prepared via api.php?action=stashedit
$stashedEdit = false;
$preparedEdit = new PreparedEdit();
$preparedEdit->popts = $this->getCanonicalParserOptions();
- $preparedEdit->output = $this->getCanonicalParserOutput();
+ $preparedEdit->parserOutputCallback = [ $this, 'getCanonicalParserOutput' ];
$preparedEdit->pstContent = $this->revision->getContent( SlotRecord::MAIN );
$preparedEdit->newContent =
$slotsUpdate->isModifiedSlot( SlotRecord::MAIN )
$legacyUser = User::newFromIdentity( $this->user );
$legacyRevision = new Revision( $this->revision );
- $this->doParserCacheUpdate();
+ $userParserOptions = ParserOptions::newFromUser( $legacyUser );
+ // Decide whether to save the final canonical parser ouput based on the fact that
+ // users are typically redirected to viewing pages right after they edit those pages.
+ // Due to vary-revision-id, getting/saving that output here might require a reparse.
+ if ( $userParserOptions->matchesForCacheKey( $this->getCanonicalParserOptions() ) ) {
+ // Whether getting the final output requires a reparse or not, the user will
+ // need canonical output anyway, since that is what their parser options use.
+ // A reparse now at least has the benefit of various warm process caches.
+ $this->doParserCacheUpdate();
+ } else {
+ // If the user does not have canonical parse options, then don't risk another parse
+ // to make output they cannot use on the page refresh that typically occurs after
+ // editing. Doing the parser output save post-send will still benefit *other* users.
+ DeferredUpdates::addCallableUpdate( function () {
+ $this->doParserCacheUpdate();
+ } );
+ }
- $this->doSecondaryDataUpdates( [
- // T52785 do not update any other pages on a null edit
- 'recursive' => $this->options['changed'],
- 'defer' => DeferredUpdates::POSTSEND,
- ] );
+ // Defer the getCannonicalParserOutput() call triggered by getSecondaryDataUpdates()
+ DeferredUpdates::addCallableUpdate( function () {
+ $this->doSecondaryDataUpdates( [
+ // T52785 do not update any other pages on a null edit
+ 'recursive' => $this->options['changed']
+ ] );
+ } );
// TODO: MCR: check if *any* changed slot supports categories!
if ( $this->rcWatchCategoryMembership
}
// TODO: replace legacy hook! Use a listener on PageEventEmitter instead!
+ // @note: Extensions should *avoid* calling getCannonicalParserOutput() when using
+ // this hook whenever possible in order to avoid unnecessary additional parses.
$editInfo = $this->getPreparedEdit();
- Hooks::run( 'ArticleEditUpdates', [ &$wikiPage, &$editInfo, $this->options['changed'] ] );
+ Hooks::run( 'ArticleEditUpdates',
+ [ &$wikiPage, &$editInfo, $this->options['changed'] ] );
// TODO: replace legacy hook! Use a listener on PageEventEmitter instead!
if ( Hooks::run( 'ArticleEditUpdatesDeleteFromRecentchanges', [ &$wikiPage ] ) ) {
}
}
}
-
-/**
- * Stub object for the user language. Assigned to the $wgLang global.
- */
-class StubUserLang extends StubObject {
-
- public function __construct() {
- parent::__construct( 'wgLang' );
- }
-
- /**
- * Call Language::findVariantLink after unstubbing $wgLang.
- *
- * This method is implemented with a full signature rather than relying on
- * __call so that the pass-by-reference signature of the proxied method is
- * honored.
- *
- * @param string &$link The name of the link
- * @param Title &$nt The title object of the link
- * @param bool $ignoreOtherCond To disable other conditions when
- * we need to transclude a template or update a category's link
- */
- public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
- global $wgLang;
- $this->_unstub( 'findVariantLink', 3 );
- $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
- }
-
- /**
- * @return Language
- */
- public function _newObject() {
- return RequestContext::getMain()->getLanguage();
- }
-}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Stub object for the user language. Assigned to the $wgLang global.
+ */
+class StubUserLang extends StubObject {
+
+ public function __construct() {
+ parent::__construct( 'wgLang' );
+ }
+
+ /**
+ * Call Language::findVariantLink after unstubbing $wgLang.
+ *
+ * This method is implemented with a full signature rather than relying on
+ * __call so that the pass-by-reference signature of the proxied method is
+ * honored.
+ *
+ * @param string &$link The name of the link
+ * @param Title &$nt The title object of the link
+ * @param bool $ignoreOtherCond To disable other conditions when
+ * we need to transclude a template or update a category's link
+ */
+ public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
+ global $wgLang;
+ $this->_unstub( 'findVariantLink', 3 );
+ $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
+ }
+
+ /**
+ * @return Language
+ */
+ public function _newObject() {
+ return RequestContext::getMain()->getLanguage();
+ }
+}
/**
* Create a new Title for the Main Page
*
+ * This uses the 'mainpage' interface message, which could be specified in
+ * `$wgForceUIMsgAsContentMsg`. If that is the case, then calling this method
+ * will use the user language, which would involve initialising the session
+ * via `RequestContext::getMain()->getLanguage()`. For session-less endpoints,
+ * be sure to pass in a MessageLocalizer (such as your own RequestContext,
+ * or ResourceloaderContext) to prevent an error.
+ *
* @note The Title instance returned by this method is not guaranteed to be a fresh instance.
* It may instead be a cached instance created previously, with references to it remaining
* elsewhere.
*
- * @return Title The new object
+ * @param MessageLocalizer|null $localizer An optional context to use (since 1.34)
+ * @return Title
*/
- public static function newMainPage() {
- $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
- // Don't give fatal errors if the message is broken
+ public static function newMainPage( MessageLocalizer $localizer = null ) {
+ if ( $localizer ) {
+ $msg = $localizer->msg( 'mainpage' );
+ } else {
+ $msg = wfMessage( 'mainpage' );
+ }
+
+ $title = self::newFromText( $msg->inContentLanguage()->text() );
+
+ // Every page renders at least one link to the Main Page (e.g. sidebar).
+ // If the localised value is invalid, don't produce fatal errors that
+ // would make the wiki inaccessible (and hard to fix the invalid message).
+ // Gracefully fallback...
if ( !$title ) {
$title = self::newFromText( 'Main Page' );
}
*/
private function getGPCVal( $arr, $name, $default ) {
# PHP is so nice to not touch input data, except sometimes:
- # https://secure.php.net/variables.external#language.variables.external.dot-in-names
+ # https://www.php.net/variables.external#language.variables.external.dot-in-names
# Work around PHP *feature* to avoid *bugs* elsewhere.
$name = strtr( $name, '.', '_' );
if ( isset( $arr[$name] ) ) {
/**
* Return the upload error. See link for explanation
- * https://secure.php.net/manual/en/features.file-upload.errors.php
+ * https://www.php.net/manual/en/features.file-upload.errors.php
*
* @return int One of the UPLOAD_ constants, 0 if non-existent
*/
}
// Handle atom/RSS feeds.
- $feedType = $request->getVal( 'feed' );
- if ( $feedType ) {
+ $feedType = $request->getRawVal( 'feed' );
+ if ( $feedType !== null ) {
$this->feed( $feedType );
-
return;
}
}
}
+ /**
+ * @inheritDoc
+ */
+ function getDefaultQuery() {
+ parent::getDefaultQuery();
+ unset( $this->mDefaultQuery['date-range-to'] );
+ return $this->mDefaultQuery;
+ }
+
/**
* This is called if a write operation is possible from the generated HTML
* @param bool $enable
/**
* Fetch the standard parameters this helper recognizes
* @param string $action AuthManager action
- * @param string $param,... Parameters to use
+ * @param string ...$wantedParams Parameters to use
* @return array
*/
- public static function getStandardParams( $action, $param /* ... */ ) {
+ public static function getStandardParams( $action, ...$wantedParams ) {
$params = [
'requests' => [
ApiBase::PARAM_TYPE => 'string',
];
$ret = [];
- $wantedParams = func_get_args();
- array_shift( $wantedParams );
foreach ( $wantedParams as $name ) {
if ( isset( $params[$name] ) ) {
$ret[$name] = $params[$name];
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $this->params['target'] ) ] );
}
- $feed = new ChangesFeed( $feedFormat, false );
+ $feed = new ChangesFeed( $feedFormat );
$feedObj = $feed->getFeedObject(
$this->msg( 'recentchangeslinked-title', $title->getPrefixedText() )
->inContentLanguage()->text(),
SpecialPage::getTitleFor( 'Recentchangeslinked' )->getFullURL()
);
} else {
- $feed = new ChangesFeed( $feedFormat, 'rcfeed' );
+ $feed = new ChangesFeed( $feedFormat );
$feedObj = $feed->getFeedObject(
$this->msg( 'recentchanges' )->inContentLanguage()->text(),
$this->msg( 'recentchanges-feed-description' )->inContentLanguage()->text(),
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2010 Bryan Tong Minh and Brion Vibber
+ *
+ * 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
+ */
+
+class ApiFormatXmlRsd extends ApiFormatXml {
+ public function __construct( ApiMain $main, $format ) {
+ parent::__construct( $main, $format );
+ $this->setRootElement( 'rsd' );
+ }
+
+ public function getMimeType() {
+ return 'application/rsd+xml';
+ }
+
+ public static function recXmlPrint( $name, $value, $indent, $attributes = [] ) {
+ unset( $attributes['_idx'] );
+ return parent::recXmlPrint( $name, $value, $indent, $attributes );
+ }
+}
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Import';
}
}
-
-/**
- * Import reporter for the API
- * @ingroup API
- */
-class ApiImportReporter extends ImportReporter {
- private $mResultArr = [];
-
- /**
- * @param Title $title
- * @param Title $origTitle
- * @param int $revisionCount
- * @param int $successCount
- * @param array $pageInfo
- * @return void
- */
- public function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
- // Add a result entry
- $r = [];
-
- if ( $title === null ) {
- # Invalid or non-importable title
- $r['title'] = $pageInfo['title'];
- $r['invalid'] = true;
- } else {
- ApiQueryBase::addTitleInfo( $r, $title );
- $r['revisions'] = (int)$successCount;
- }
-
- $this->mResultArr[] = $r;
-
- // Piggyback on the parent to do the logging
- parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo );
- }
-
- public function getData() {
- return $this->mResultArr;
- }
-}
--- /dev/null
+<?php
+/**
+ * Copyright © 2009 Roan Kattouw "<Firstname>.<Lastname>@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
+ */
+
+/**
+ * Import reporter for the API
+ * @ingroup API
+ */
+class ApiImportReporter extends ImportReporter {
+ private $mResultArr = [];
+
+ /**
+ * @param Title $title
+ * @param Title $origTitle
+ * @param int $revisionCount
+ * @param int $successCount
+ * @param array $pageInfo
+ * @return void
+ */
+ public function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
+ // Add a result entry
+ $r = [];
+
+ if ( $title === null ) {
+ # Invalid or non-importable title
+ $r['title'] = $pageInfo['title'];
+ $r['invalid'] = true;
+ } else {
+ ApiQueryBase::addTitleInfo( $r, $title );
+ $r['revisions'] = (int)$successCount;
+ }
+
+ $this->mResultArr[] = $r;
+
+ // Piggyback on the parent to do the logging
+ parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo );
+ }
+
+ public function getData() {
+ return $this->mResultArr;
+ }
+}
}
}
}
-
-/**
- * @ingroup API
- */
-class ApiOpenSearchFormatJson extends ApiFormatJson {
- private $warningsAsError = false;
-
- public function __construct( ApiMain $main, $fm, $warningsAsError ) {
- parent::__construct( $main, "json$fm" );
- $this->warningsAsError = $warningsAsError;
- }
-
- public function execute() {
- $result = $this->getResult();
- if ( !$result->getResultData( 'error' ) && !$result->getResultData( 'errors' ) ) {
- // Ignore warnings or treat as errors, as requested
- $warnings = $result->removeValue( 'warnings', null );
- if ( $this->warningsAsError && $warnings ) {
- $this->dieWithError(
- 'apierror-opensearch-json-warnings',
- 'warnings',
- [ 'warnings' => $warnings ]
- );
- }
-
- // Ignore any other unexpected keys (e.g. from $wgDebugToolbar)
- $remove = array_keys( array_diff_key(
- $result->getResultData(),
- [ 0 => 'search', 1 => 'terms', 2 => 'descriptions', 3 => 'urls' ]
- ) );
- foreach ( $remove as $key ) {
- $result->removeValue( $key, null );
- }
- }
-
- parent::execute();
- }
-}
--- /dev/null
+<?php
+/**
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
+ * Copyright © 2008 Brion Vibber <brion@wikimedia.org>
+ * Copyright © 2014 Wikimedia Foundation and contributors
+ *
+ * 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 API
+ */
+class ApiOpenSearchFormatJson extends ApiFormatJson {
+ private $warningsAsError = false;
+
+ public function __construct( ApiMain $main, $fm, $warningsAsError ) {
+ parent::__construct( $main, "json$fm" );
+ $this->warningsAsError = $warningsAsError;
+ }
+
+ public function execute() {
+ $result = $this->getResult();
+ if ( !$result->getResultData( 'error' ) && !$result->getResultData( 'errors' ) ) {
+ // Ignore warnings or treat as errors, as requested
+ $warnings = $result->removeValue( 'warnings', null );
+ if ( $this->warningsAsError && $warnings ) {
+ $this->dieWithError(
+ 'apierror-opensearch-json-warnings',
+ 'warnings',
+ [ 'warnings' => $warnings ]
+ );
+ }
+
+ // Ignore any other unexpected keys (e.g. from $wgDebugToolbar)
+ $remove = array_keys( array_diff_key(
+ $result->getResultData(),
+ [ 0 => 'search', 1 => 'terms', 2 => 'descriptions', 3 => 'urls' ]
+ ) );
+ foreach ( $remove as $key ) {
+ $result->removeValue( $key, null );
+ }
+ }
+
+ parent::execute();
+ }
+}
return $outputData;
}
}
-
-class ApiFormatXmlRsd extends ApiFormatXml {
- public function __construct( ApiMain $main, $format ) {
- parent::__construct( $main, $format );
- $this->setRootElement( 'rsd' );
- }
-
- public function getMimeType() {
- return 'application/rsd+xml';
- }
-
- public static function recXmlPrint( $name, $value, $indent, $attributes = [] ) {
- unset( $attributes['_idx'] );
- return parent::recXmlPrint( $name, $value, $indent, $attributes );
- }
-}
"apihelp-main-param-smaxage": "<code>s-maxage</code> HTTP キャッシュ コントロール ヘッダー に、この秒数を設定します。エラーがキャッシュされることはありません。",
"apihelp-main-param-maxage": "<code>max-age</code> HTTP キャッシュ コントロール ヘッダー に、この秒数を設定します。エラーがキャッシュされることはありません。",
"apihelp-main-param-assert": "<kbd>user</kbd> を設定した場合は利用者がログイン済みかどうかを、<kbd>bot</kbd> を指定した場合はボット権限があるかどうかを、それぞれ検証します。",
- "apihelp-main-param-assertuser": "現在のユーザーが指定されたユーザーであることを確認します。",
+ "apihelp-main-param-assertuser": "現在の利用者が指定された利用者であることを確認します。",
"apihelp-main-param-requestid": "任意の値を指定でき、その値が結果に含められます。リクエストを識別するために使用できます。",
"apihelp-main-param-servedby": "リクエストを処理したホスト名を結果に含めます。",
"apihelp-main-param-curtimestamp": "現在のタイムスタンプを結果に含めます。",
"apihelp-import-extended-description": "<var>xml</var> パラメーターでファイルを送信する場合、ファイルのアップロードとしてHTTP POSTされなければならない (例えば、multipart/form-dataを使用する) 点に注意してください。",
"apihelp-import-param-summary": "記録されるページ取り込みの要約。",
"apihelp-import-param-xml": "XMLファイルをアップロード",
- "apihelp-import-param-assignknownusers": "指定されたユーザーがこのウィキに存在する場合そのユーザーに編集を割り当てる",
+ "apihelp-import-param-assignknownusers": "指定された利用者がこのウィキに存在する場合その利用者に編集を割り当てる。",
"apihelp-import-param-interwikisource": "ウィキ間の取り込みの場合: 取り込み元のウィキ。",
"apihelp-import-param-interwikipage": "ウィキ間の取り込みの場合: 取り込むページ。",
"apihelp-import-param-fullhistory": "ウィキ間の取り込みの場合: 現在の版のみではなく完全な履歴を取り込む。",
"apierror-botsnotsupported": "この API インターフェースはボットをサポートしていません。",
"apierror-emptypage": "内容がないページの新規作成は許可されていません。",
"apierror-filedoesnotexist": "ファイルが存在しません。",
- "apierror-invaliduser": "無効なユーザー名「$1」。",
+ "apierror-invaliduser": "無効な利用者名「$1」。",
"apierror-missingparam": "パラメーター <var>$1</var> を設定してください。",
"apierror-mustbeloggedin": "$1にログインしている必要があります。",
"apierror-noimageredirect": "画像のリダイレクトを作成する権限がありません。",
abstract class AbstractRestriction implements Restriction {
/**
- * @var string
+ * String constant identifying the type of restriction. Expected to be overriden in subclasses
+ * with a non-empty string value.
*/
const TYPE = '';
/**
- * @var int
+ * Numeric type identifier. Expected to be overriden in subclasses with a non-zero integer
+ * number. Must not exceed 127 to fit into a TINYINT database field.
*/
const TYPE_ID = 0;
* Gets the id of the type of restriction. This id is used in the database.
*
* @since 1.33
- * @return string
+ * @return int
*/
public static function getTypeId();
--- /dev/null
+<?php
+/**
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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
+ */
+
+/**
+ * Generate an Atom feed.
+ *
+ * @ingroup Feed
+ */
+class AtomFeed extends ChannelFeed {
+ /**
+ * Format a date given timestamp, if one is given.
+ *
+ * @param string|int|null $timestamp
+ * @return string|null
+ */
+ function formatTime( $timestamp ) {
+ if ( $timestamp ) {
+ // need to use RFC 822 time format at least for rss2.0
+ return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $timestamp ) );
+ }
+ }
+
+ /**
+ * Outputs a basic header for Atom 1.0 feeds.
+ */
+ function outHeader() {
+ global $wgVersion;
+ $this->outXmlHeader();
+ // Manually escaping rather than letting Mustache do it because Mustache
+ // uses htmlentities, which does not work with XML
+ $templateParams = [
+ 'language' => $this->xmlEncode( $this->getLanguage() ),
+ 'feedID' => $this->getFeedId(),
+ 'title' => $this->getTitle(),
+ 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ),
+ 'selfUrl' => $this->getSelfUrl(),
+ 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) ),
+ 'description' => $this->getDescription(),
+ 'version' => $this->xmlEncode( $wgVersion ),
+ ];
+ print $this->templateParser->processTemplate( 'AtomHeader', $templateParams );
+ }
+
+ /**
+ * Atom 1.0 requires a unique, opaque IRI as a unique identifier
+ * for every feed we create. For now just use the URL, but who
+ * can tell if that's right? If we put options on the feed, do we
+ * have to change the id? Maybe? Maybe not.
+ *
+ * @return string
+ */
+ private function getFeedId() {
+ return $this->getSelfUrl();
+ }
+
+ /**
+ * Atom 1.0 requests a self-reference to the feed.
+ * @return string
+ */
+ private function getSelfUrl() {
+ global $wgRequest;
+ return htmlspecialchars( $wgRequest->getFullRequestURL() );
+ }
+
+ /**
+ * Output a given item.
+ * @param FeedItem $item
+ */
+ function outItem( $item ) {
+ global $wgMimeType;
+ // Manually escaping rather than letting Mustache do it because Mustache
+ // uses htmlentities, which does not work with XML
+ $templateParams = [
+ "uniqueID" => $item->getUniqueID(),
+ "title" => $item->getTitle(),
+ "mimeType" => $this->xmlEncode( $wgMimeType ),
+ "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ),
+ "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ),
+ "description" => $item->getDescription(),
+ "author" => $item->getAuthor()
+ ];
+ print $this->templateParser->processTemplate( 'AtomItem', $templateParams );
+ }
+
+ /**
+ * Outputs the footer for Atom 1.0 feed (basically '\</feed\>').
+ */
+ function outFooter() {
+ print "</feed>";
+ }
+}
* @file
*/
-use Wikimedia\Rdbms\ResultWrapper;
-use MediaWiki\MediaWikiServices;
-
/**
- * Feed to Special:RecentChanges and Special:RecentChangesLiked
+ * Feed to Special:RecentChanges and Special:RecentChangesLinked.
*
* @ingroup Feed
*/
class ChangesFeed {
- public $format, $type, $titleMsg, $descMsg;
+ private $format;
/**
* @param string $format Feed's format (either 'rss' or 'atom')
- * @param string $type Type of feed (for cache keys)
*/
- public function __construct( $format, $type ) {
+ public function __construct( $format ) {
$this->format = $format;
- $this->type = $type;
}
/**
$feedTitle, htmlspecialchars( $description ), $url );
}
- /**
- * Generates feed's content
- *
- * @param ChannelFeed $feed ChannelFeed subclass object (generally the one returned
- * by getFeedObject())
- * @param ResultWrapper $rows ResultWrapper object with rows in recentchanges table
- * @param int $lastmod Timestamp of the last item in the recentchanges table (only
- * used for the cache key)
- * @param FormOptions $opts As in SpecialRecentChanges::getDefaultOptions()
- * @return null|bool True or null
- */
- public function execute( $feed, $rows, $lastmod, $opts ) {
- global $wgLang, $wgRenderHashAppend;
-
- if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
- return null;
- }
-
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $optionsHash = md5( serialize( $opts->getAllValues() ) ) . $wgRenderHashAppend;
- $timekey = $cache->makeKey(
- $this->type, $this->format, $wgLang->getCode(), $optionsHash, 'timestamp' );
- $key = $cache->makeKey( $this->type, $this->format, $wgLang->getCode(), $optionsHash );
-
- FeedUtils::checkPurge( $timekey, $key );
-
- /**
- * Bumping around loading up diffs can be pretty slow, so where
- * possible we want to cache the feed output so the next visitor
- * gets it quick too.
- */
- $cachedFeed = $this->loadFromCache( $lastmod, $timekey, $key );
- if ( is_string( $cachedFeed ) ) {
- wfDebug( "RC: Outputting cached feed\n" );
- $feed->httpHeaders();
- echo $cachedFeed;
- } else {
- wfDebug( "RC: rendering new feed and caching it\n" );
- ob_start();
- self::generateFeed( $rows, $feed );
- $cachedFeed = ob_get_contents();
- ob_end_flush();
- $this->saveToCache( $cachedFeed, $timekey, $key );
- }
- return true;
- }
-
- /**
- * Save to feed result to cache
- *
- * @param string $feed Feed's content
- * @param string $timekey Memcached key of the last modification
- * @param string $key Memcached key of the content
- */
- public function saveToCache( $feed, $timekey, $key ) {
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $cache->set( $key, $feed, $cache::TTL_DAY );
- $cache->set( $timekey, wfTimestamp( TS_MW ), $cache::TTL_DAY );
- }
-
- /**
- * Try to load the feed result from cache
- *
- * @param int $lastmod Timestamp of the last item in the recentchanges table
- * @param string $timekey Memcached key of the last modification
- * @param string $key Memcached key of the content
- * @return string|bool Feed's content on cache hit or false on cache miss
- */
- public function loadFromCache( $lastmod, $timekey, $key ) {
- global $wgFeedCacheTimeout, $wgOut;
-
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $feedLastmod = $cache->get( $timekey );
-
- if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
- /**
- * If the cached feed was rendered very recently, we may
- * go ahead and use it even if there have been edits made
- * since it was rendered. This keeps a swarm of requests
- * from being too bad on a super-frequently edited wiki.
- */
-
- $feedAge = time() - wfTimestamp( TS_UNIX, $feedLastmod );
- $feedLastmodUnix = wfTimestamp( TS_UNIX, $feedLastmod );
- $lastmodUnix = wfTimestamp( TS_UNIX, $lastmod );
-
- if ( $feedAge < $wgFeedCacheTimeout || $feedLastmodUnix > $lastmodUnix ) {
- wfDebug( "RC: loading feed from cache ($key; $feedLastmod; $lastmod)...\n" );
- if ( $feedLastmodUnix < $lastmodUnix ) {
- $wgOut->setLastModified( $feedLastmod ); // T23916
- }
- return $cache->get( $key );
- } else {
- wfDebug( "RC: cached feed timestamp check failed ($feedLastmod; $lastmod)\n" );
- }
- }
- return false;
- }
-
- /**
- * Generate the feed items given a row from the database, printing the feed.
- * @param object $rows IDatabase resource with recentchanges rows
- * @param ChannelFeed &$feed
- */
- public static function generateFeed( $rows, &$feed ) {
- $items = self::buildItems( $rows );
- $feed->outHeader();
- foreach ( $items as $item ) {
- $feed->outItem( $item );
- }
- $feed->outFooter();
- }
-
/**
* Generate the feed items given a row from the database.
* @param object $rows IDatabase resource with recentchanges rows
--- /dev/null
+<?php
+/**
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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
+ */
+
+/**
+ * Class to support the outputting of syndication feeds in Atom and RSS format.
+ *
+ * @ingroup Feed
+ */
+abstract class ChannelFeed extends FeedItem {
+
+ /** @var TemplateParser */
+ protected $templateParser;
+
+ /**
+ * @param string|Title $title Feed's title
+ * @param string $description
+ * @param string $url URL uniquely designating the feed.
+ * @param string $date Feed's date
+ * @param string $author Author's user name
+ * @param string $comments
+ */
+ function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) {
+ parent::__construct( $title, $description, $url, $date, $author, $comments );
+ $this->templateParser = new TemplateParser();
+ }
+
+ /**
+ * Generate Header of the feed
+ * @par Example:
+ * @code
+ * print "<feed>";
+ * @endcode
+ */
+ abstract public function outHeader();
+
+ /**
+ * Generate an item
+ * @par Example:
+ * @code
+ * print "<item>...</item>";
+ * @endcode
+ * @param FeedItem $item
+ */
+ abstract public function outItem( $item );
+
+ /**
+ * Generate Footer of the feed
+ * @par Example:
+ * @code
+ * print "</feed>";
+ * @endcode
+ */
+ abstract public function outFooter();
+
+ /**
+ * Setup and send HTTP headers. Don't send any content;
+ * content might end up being cached and re-sent with
+ * these same headers later.
+ *
+ * This should be called from the outHeader() method,
+ * but can also be called separately.
+ */
+ public function httpHeaders() {
+ global $wgOut, $wgVaryOnXFP;
+
+ # We take over from $wgOut, excepting its cache header info
+ $wgOut->disable();
+ $mimetype = $this->contentType();
+ header( "Content-type: $mimetype; charset=UTF-8" );
+
+ // Set a sane filename
+ $exts = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer()
+ ->getExtensionsForType( $mimetype );
+ $ext = $exts ? strtok( $exts, ' ' ) : 'xml';
+ header( "Content-Disposition: inline; filename=\"feed.{$ext}\"" );
+
+ if ( $wgVaryOnXFP ) {
+ $wgOut->addVaryHeader( 'X-Forwarded-Proto' );
+ }
+ $wgOut->sendCacheControl();
+ }
+
+ /**
+ * Return an internet media type to be sent in the headers.
+ *
+ * @return string
+ */
+ private function contentType() {
+ global $wgRequest;
+
+ $ctype = $wgRequest->getVal( 'ctype', 'application/xml' );
+ $allowedctypes = [
+ 'application/xml',
+ 'text/xml',
+ 'application/rss+xml',
+ 'application/atom+xml'
+ ];
+
+ return ( in_array( $ctype, $allowedctypes ) ? $ctype : 'application/xml' );
+ }
+
+ /**
+ * Output the initial XML headers.
+ */
+ protected function outXmlHeader() {
+ $this->httpHeaders();
+ echo '<?xml version="1.0"?>' . "\n";
+ }
+}
--- /dev/null
+<?php
+/**
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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
+ */
+
+/**
+ * @defgroup Feed Feed
+ */
+
+/**
+ * A base class for outputting syndication feeds (e.g. RSS and other formats).
+ *
+ * @ingroup Feed
+ */
+class FeedItem {
+ /** @var Title */
+ public $title;
+
+ public $description;
+
+ public $url;
+
+ public $date;
+
+ public $author;
+
+ public $uniqueId;
+
+ public $comments;
+
+ public $rssIsPermalink = false;
+
+ /**
+ * @param string|Title $title Item's title
+ * @param string $description
+ * @param string $url URL uniquely designating the item.
+ * @param string $date Item's date
+ * @param string $author Author's user name
+ * @param string $comments
+ */
+ function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) {
+ $this->title = $title;
+ $this->description = $description;
+ $this->url = $url;
+ $this->uniqueId = $url;
+ $this->date = $date;
+ $this->author = $author;
+ $this->comments = $comments;
+ }
+
+ /**
+ * Encode $string so that it can be safely embedded in a XML document
+ *
+ * @param string $string String to encode
+ * @return string
+ */
+ public function xmlEncode( $string ) {
+ $string = str_replace( "\r\n", "\n", $string );
+ $string = preg_replace( '/[\x00-\x08\x0b\x0c\x0e-\x1f]/', '', $string );
+ return htmlspecialchars( $string );
+ }
+
+ /**
+ * Get the unique id of this item; already xml-encoded
+ * @return string
+ */
+ public function getUniqueID() {
+ $id = $this->getUniqueIdUnescaped();
+ if ( $id ) {
+ return $this->xmlEncode( $id );
+ }
+ }
+
+ /**
+ * Get the unique id of this item, without any escaping
+ * @return string
+ */
+ public function getUniqueIdUnescaped() {
+ if ( $this->uniqueId ) {
+ return wfExpandUrl( $this->uniqueId, PROTO_CURRENT );
+ }
+ }
+
+ /**
+ * Set the unique id of an item
+ *
+ * @param string $uniqueId Unique id for the item
+ * @param bool $rssIsPermalink Set to true if the guid (unique id) is a permalink (RSS feeds only)
+ */
+ public function setUniqueId( $uniqueId, $rssIsPermalink = false ) {
+ $this->uniqueId = $uniqueId;
+ $this->rssIsPermalink = $rssIsPermalink;
+ }
+
+ /**
+ * Get the title of this item; already xml-encoded
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->xmlEncode( $this->title );
+ }
+
+ /**
+ * Get the URL of this item; already xml-encoded
+ *
+ * @return string
+ */
+ public function getUrl() {
+ return $this->xmlEncode( $this->url );
+ }
+
+ /** Get the URL of this item without any escaping
+ *
+ * @return string
+ */
+ public function getUrlUnescaped() {
+ return $this->url;
+ }
+
+ /**
+ * Get the description of this item; already xml-encoded
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->xmlEncode( $this->description );
+ }
+
+ /**
+ * Get the description of this item without any escaping
+ *
+ * @return string
+ */
+ public function getDescriptionUnescaped() {
+ return $this->description;
+ }
+
+ /**
+ * Get the language of this item
+ *
+ * @return string
+ */
+ public function getLanguage() {
+ global $wgLanguageCode;
+ return LanguageCode::bcp47( $wgLanguageCode );
+ }
+
+ /**
+ * Get the date of this item
+ *
+ * @return string
+ */
+ public function getDate() {
+ return $this->date;
+ }
+
+ /**
+ * Get the author of this item; already xml-encoded
+ *
+ * @return string
+ */
+ public function getAuthor() {
+ return $this->xmlEncode( $this->author );
+ }
+
+ /**
+ * Get the author of this item without any escaping
+ *
+ * @return string
+ */
+ public function getAuthorUnescaped() {
+ return $this->author;
+ }
+
+ /**
+ * Get the comment of this item; already xml-encoded
+ *
+ * @return string
+ */
+ public function getComments() {
+ return $this->xmlEncode( $this->comments );
+ }
+
+ /**
+ * Get the comment of this item without any escaping
+ *
+ * @return string
+ */
+ public function getCommentsUnescaped() {
+ return $this->comments;
+ }
+
+ /**
+ * Quickie hack... strip out wikilinks to more legible form from the comment.
+ *
+ * @param string $text Wikitext
+ * @return string
+ */
+ public static function stripComment( $text ) {
+ return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
+ }
+ /**#@-*/
+}
--- /dev/null
+<?php
+/**
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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
+ */
+
+/**
+ * Generate an RSS feed.
+ *
+ * @ingroup Feed
+ */
+class RSSFeed extends ChannelFeed {
+
+ /**
+ * Format a date given a timestamp. If a timestamp is not given, nothing is returned
+ *
+ * @param int|null $ts Timestamp
+ * @return string|null Date string
+ */
+ function formatTime( $ts ) {
+ if ( $ts ) {
+ return gmdate( 'D, d M Y H:i:s \G\M\T', wfTimestamp( TS_UNIX, $ts ) );
+ }
+ }
+
+ /**
+ * Output an RSS 2.0 header
+ */
+ function outHeader() {
+ global $wgVersion;
+
+ $this->outXmlHeader();
+ // Manually escaping rather than letting Mustache do it because Mustache
+ // uses htmlentities, which does not work with XML
+ $templateParams = [
+ 'title' => $this->getTitle(),
+ 'url' => $this->xmlEncode( wfExpandUrl( $this->getUrlUnescaped(), PROTO_CURRENT ) ),
+ 'description' => $this->getDescription(),
+ 'language' => $this->xmlEncode( $this->getLanguage() ),
+ 'version' => $this->xmlEncode( $wgVersion ),
+ 'timestamp' => $this->xmlEncode( $this->formatTime( wfTimestampNow() ) )
+ ];
+ print $this->templateParser->processTemplate( 'RSSHeader', $templateParams );
+ }
+
+ /**
+ * Output an RSS 2.0 item
+ * @param FeedItem $item Item to be output
+ */
+ function outItem( $item ) {
+ // Manually escaping rather than letting Mustache do it because Mustache
+ // uses htmlentities, which does not work with XML
+ $templateParams = [
+ "title" => $item->getTitle(),
+ "url" => $this->xmlEncode( wfExpandUrl( $item->getUrlUnescaped(), PROTO_CURRENT ) ),
+ "permalink" => $item->rssIsPermalink,
+ "uniqueID" => $item->getUniqueID(),
+ "description" => $item->getDescription(),
+ "date" => $this->xmlEncode( $this->formatTime( $item->getDate() ) ),
+ "author" => $item->getAuthor()
+ ];
+ $comments = $item->getCommentsUnescaped();
+ if ( $comments ) {
+ $commentsEscaped = $this->xmlEncode( wfExpandUrl( $comments, PROTO_CURRENT ) );
+ $templateParams["comments"] = $commentsEscaped;
+ }
+ print $this->templateParser->processTemplate( 'RSSItem', $templateParams );
+ }
+
+ /**
+ * Output an RSS 2.0 footer
+ */
+ function outFooter() {
+ print "</channel></rss>";
+ }
+}
+++ /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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * Construct objects from configuration instructions.
- *
- * @deprecated since 1.31, use \Wikimedia\ObjectFactory instead
- */
-class ObjectFactory extends \Wikimedia\ObjectFactory {
-}
+++ /dev/null
-<?php
-/**
- * Back-compat for pre-librarized XMP classes
- *
- * 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.
- */
-
-use Wikimedia\XMPReader\Info;
-use Wikimedia\XMPReader\Reader;
-use Wikimedia\XMPReader\Validate;
-
-/**
- * @deprecated since 1.32
- */
-class XMPInfo extends Info {
-}
-
-/**
- * @deprecated since 1.32
- */
-class XMPReader extends Reader {
-}
-
-/**
- * @deprecated since 1.32
- */
-class XMPValidate extends Validate {
-}
return Html::rawElement( 'td', [], $this->arrayTable( $val ) );
}
- return Html::element( 'td', [ 'class' => 'value' ], $this->primitiveValue( $val ) );
+ return Html::element( 'td', [ 'class' => 'mw-json-value' ], $this->primitiveValue( $val ) );
}
/**
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\IDatabase;
-use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\Rdbms\ILoadBalancer;
/**
* Base class for objects that allow access to other wiki's databases using
*
* @since 1.21
*
- * @return LoadBalancer The database load balancer object
+ * @return ILoadBalancer The database load balancer object
*/
public function getLoadBalancer() {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$this->keywordTableMap = $params['keywordTableMap'] ?? [];
$params['tablePrefix'] = strtoupper( $params['tablePrefix'] );
parent::__construct( $params );
- Hooks::run( 'DatabaseOraclePostInit', [ $this ] );
}
function __destruct() {
* Return sequence_name if table has a sequence
*
* @param string $table
- * @return bool
+ * @return string[]|bool
*/
private function getSequenceData( $table ) {
if ( $this->sequenceData == null ) {
+++ /dev/null
-<?php
-/**
- * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
- *
- * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
- * You may copy this code freely under the conditions of the GPL.
- *
- * 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 DifferenceEngine
- * @defgroup DifferenceEngine DifferenceEngine
- */
-
-/**
- * The base class for all other DiffOp classes.
- *
- * The classes that extend DiffOp are: DiffOpCopy, DiffOpDelete, DiffOpAdd and
- * DiffOpChange. FakeDiffOp also extends DiffOp, but it is not located in this file.
- *
- * @private
- * @ingroup DifferenceEngine
- */
-abstract class DiffOp {
-
- /**
- * @var string
- */
- public $type;
-
- /**
- * @var string[]
- */
- public $orig;
-
- /**
- * @var string[]
- */
- public $closing;
-
- /**
- * @return string
- */
- public function getType() {
- return $this->type;
- }
-
- /**
- * @return string[]
- */
- public function getOrig() {
- return $this->orig;
- }
-
- /**
- * @param int|null $i
- * @return string[]|string|null
- */
- public function getClosing( $i = null ) {
- if ( $i === null ) {
- return $this->closing;
- }
- if ( array_key_exists( $i, $this->closing ) ) {
- return $this->closing[$i];
- }
- return null;
- }
-
- abstract public function reverse();
-
- /**
- * @return int
- */
- public function norig() {
- return $this->orig ? count( $this->orig ) : 0;
- }
-
- /**
- * @return int
- */
- public function nclosing() {
- return $this->closing ? count( $this->closing ) : 0;
- }
-}
-
-/**
- * Extends DiffOp. Used to mark strings that have been
- * copied from one string array to the other.
- *
- * @private
- * @ingroup DifferenceEngine
- */
-class DiffOpCopy extends DiffOp {
- public $type = 'copy';
-
- public function __construct( $orig, $closing = false ) {
- if ( !is_array( $closing ) ) {
- $closing = $orig;
- }
- $this->orig = $orig;
- $this->closing = $closing;
- }
-
- /**
- * @return DiffOpCopy
- */
- public function reverse() {
- return new DiffOpCopy( $this->closing, $this->orig );
- }
-}
-
-/**
- * Extends DiffOp. Used to mark strings that have been
- * deleted from the first string array.
- *
- * @private
- * @ingroup DifferenceEngine
- */
-class DiffOpDelete extends DiffOp {
- public $type = 'delete';
-
- public function __construct( $lines ) {
- $this->orig = $lines;
- $this->closing = false;
- }
-
- /**
- * @return DiffOpAdd
- */
- public function reverse() {
- return new DiffOpAdd( $this->orig );
- }
-}
-
-/**
- * Extends DiffOp. Used to mark strings that have been
- * added from the first string array.
- *
- * @private
- * @ingroup DifferenceEngine
- */
-class DiffOpAdd extends DiffOp {
- public $type = 'add';
-
- public function __construct( $lines ) {
- $this->closing = $lines;
- $this->orig = false;
- }
-
- /**
- * @return DiffOpDelete
- */
- public function reverse() {
- return new DiffOpDelete( $this->closing );
- }
-}
-
-/**
- * Extends DiffOp. Used to mark strings that have been
- * changed from the first string array (both added and subtracted).
- *
- * @private
- * @ingroup DifferenceEngine
- */
-class DiffOpChange extends DiffOp {
- public $type = 'change';
-
- public function __construct( $orig, $closing ) {
- $this->orig = $orig;
- $this->closing = $closing;
- }
-
- /**
- * @return DiffOpChange
- */
- public function reverse() {
- return new DiffOpChange( $this->closing, $this->orig );
- }
-}
-
-/**
- * Class representing a 'diff' between two sequences of strings.
- * @todo document
- * @private
- * @ingroup DifferenceEngine
- */
-class Diff {
-
- /**
- * @var DiffOp[]
- */
- public $edits;
-
- /**
- * @var int If this diff complexity is exceeded, a ComplexityException is thrown
- * 0 means no limit.
- */
- protected $bailoutComplexity = 0;
-
- /**
- * Computes diff between sequences of strings.
- *
- * @param string[] $from_lines An array of strings.
- * Typically these are lines from a file.
- * @param string[] $to_lines An array of strings.
- * @throws \MediaWiki\Diff\ComplexityException
- */
- public function __construct( $from_lines, $to_lines ) {
- $eng = new DiffEngine;
- $eng->setBailoutComplexity( $this->bailoutComplexity );
- $this->edits = $eng->diff( $from_lines, $to_lines );
- }
-
- /**
- * @return DiffOp[]
- */
- public function getEdits() {
- return $this->edits;
- }
-
- /**
- * Compute reversed Diff.
- *
- * SYNOPSIS:
- *
- * $diff = new Diff($lines1, $lines2);
- * $rev = $diff->reverse();
- *
- * @return Object A Diff object representing the inverse of the
- * original diff.
- */
- public function reverse() {
- $rev = $this;
- $rev->edits = [];
- /** @var DiffOp $edit */
- foreach ( $this->edits as $edit ) {
- $rev->edits[] = $edit->reverse();
- }
-
- return $rev;
- }
-
- /**
- * Check for empty diff.
- *
- * @return bool True if two sequences were identical.
- */
- public function isEmpty() {
- foreach ( $this->edits as $edit ) {
- if ( $edit->type != 'copy' ) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Compute the length of the Longest Common Subsequence (LCS).
- *
- * This is mostly for diagnostic purposed.
- *
- * @return int The length of the LCS.
- */
- public function lcs() {
- $lcs = 0;
- foreach ( $this->edits as $edit ) {
- if ( $edit->type == 'copy' ) {
- $lcs += count( $edit->orig );
- }
- }
-
- return $lcs;
- }
-
- /**
- * Get the original set of lines.
- *
- * This reconstructs the $from_lines parameter passed to the
- * constructor.
- *
- * @return string[] The original sequence of strings.
- */
- public function orig() {
- $lines = [];
-
- foreach ( $this->edits as $edit ) {
- if ( $edit->orig ) {
- array_splice( $lines, count( $lines ), 0, $edit->orig );
- }
- }
-
- return $lines;
- }
-
- /**
- * Get the closing set of lines.
- *
- * This reconstructs the $to_lines parameter passed to the
- * constructor.
- *
- * @return string[] The sequence of strings.
- */
- public function closing() {
- $lines = [];
-
- foreach ( $this->edits as $edit ) {
- if ( $edit->closing ) {
- array_splice( $lines, count( $lines ), 0, $edit->closing );
- }
- }
-
- return $lines;
- }
-}
-
-/**
- * @deprecated Alias for WordAccumulator, to be soon removed
- */
-class HWLDFWordAccumulator extends MediaWiki\Diff\WordAccumulator {
-}
--- /dev/null
+<?php
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Class representing a 'diff' between two sequences of strings.
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class Diff {
+
+ /**
+ * @var DiffOp[]
+ */
+ public $edits;
+
+ /**
+ * @var int If this diff complexity is exceeded, a ComplexityException is thrown
+ * 0 means no limit.
+ */
+ protected $bailoutComplexity = 0;
+
+ /**
+ * Computes diff between sequences of strings.
+ *
+ * @param string[] $from_lines An array of strings.
+ * Typically these are lines from a file.
+ * @param string[] $to_lines An array of strings.
+ * @throws \MediaWiki\Diff\ComplexityException
+ */
+ public function __construct( $from_lines, $to_lines ) {
+ $eng = new DiffEngine;
+ $eng->setBailoutComplexity( $this->bailoutComplexity );
+ $this->edits = $eng->diff( $from_lines, $to_lines );
+ }
+
+ /**
+ * @return DiffOp[]
+ */
+ public function getEdits() {
+ return $this->edits;
+ }
+
+ /**
+ * Compute reversed Diff.
+ *
+ * SYNOPSIS:
+ *
+ * $diff = new Diff($lines1, $lines2);
+ * $rev = $diff->reverse();
+ *
+ * @return Object A Diff object representing the inverse of the
+ * original diff.
+ */
+ public function reverse() {
+ $rev = $this;
+ $rev->edits = [];
+ /** @var DiffOp $edit */
+ foreach ( $this->edits as $edit ) {
+ $rev->edits[] = $edit->reverse();
+ }
+
+ return $rev;
+ }
+
+ /**
+ * Check for empty diff.
+ *
+ * @return bool True if two sequences were identical.
+ */
+ public function isEmpty() {
+ foreach ( $this->edits as $edit ) {
+ if ( $edit->type != 'copy' ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Compute the length of the Longest Common Subsequence (LCS).
+ *
+ * This is mostly for diagnostic purposed.
+ *
+ * @return int The length of the LCS.
+ */
+ public function lcs() {
+ $lcs = 0;
+ foreach ( $this->edits as $edit ) {
+ if ( $edit->type == 'copy' ) {
+ $lcs += count( $edit->orig );
+ }
+ }
+
+ return $lcs;
+ }
+
+ /**
+ * Get the original set of lines.
+ *
+ * This reconstructs the $from_lines parameter passed to the
+ * constructor.
+ *
+ * @return string[] The original sequence of strings.
+ */
+ public function orig() {
+ $lines = [];
+
+ foreach ( $this->edits as $edit ) {
+ if ( $edit->orig ) {
+ array_splice( $lines, count( $lines ), 0, $edit->orig );
+ }
+ }
+
+ return $lines;
+ }
+
+ /**
+ * Get the closing set of lines.
+ *
+ * This reconstructs the $to_lines parameter passed to the
+ * constructor.
+ *
+ * @return string[] The sequence of strings.
+ */
+ public function closing() {
+ $lines = [];
+
+ foreach ( $this->edits as $edit ) {
+ if ( $edit->closing ) {
+ array_splice( $lines, count( $lines ), 0, $edit->closing );
+ }
+ }
+
+ return $lines;
+ }
+}
--- /dev/null
+<?php
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ * @defgroup DifferenceEngine DifferenceEngine
+ */
+
+/**
+ * The base class for all other DiffOp classes.
+ *
+ * The classes that extend DiffOp are: DiffOpCopy, DiffOpDelete, DiffOpAdd and
+ * DiffOpChange. FakeDiffOp also extends DiffOp, but it is not located in this file.
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+abstract class DiffOp {
+
+ /**
+ * @var string
+ */
+ public $type;
+
+ /**
+ * @var string[]
+ */
+ public $orig;
+
+ /**
+ * @var string[]
+ */
+ public $closing;
+
+ /**
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getOrig() {
+ return $this->orig;
+ }
+
+ /**
+ * @param int|null $i
+ * @return string[]|string|null
+ */
+ public function getClosing( $i = null ) {
+ if ( $i === null ) {
+ return $this->closing;
+ }
+ if ( array_key_exists( $i, $this->closing ) ) {
+ return $this->closing[$i];
+ }
+ return null;
+ }
+
+ abstract public function reverse();
+
+ /**
+ * @return int
+ */
+ public function norig() {
+ return $this->orig ? count( $this->orig ) : 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function nclosing() {
+ return $this->closing ? count( $this->closing ) : 0;
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Extends DiffOp. Used to mark strings that have been
+ * added from the first string array.
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class DiffOpAdd extends DiffOp {
+ public $type = 'add';
+
+ public function __construct( $lines ) {
+ $this->closing = $lines;
+ $this->orig = false;
+ }
+
+ /**
+ * @return DiffOpDelete
+ */
+ public function reverse() {
+ return new DiffOpDelete( $this->closing );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Extends DiffOp. Used to mark strings that have been
+ * changed from the first string array (both added and subtracted).
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class DiffOpChange extends DiffOp {
+ public $type = 'change';
+
+ public function __construct( $orig, $closing ) {
+ $this->orig = $orig;
+ $this->closing = $closing;
+ }
+
+ /**
+ * @return DiffOpChange
+ */
+ public function reverse() {
+ return new DiffOpChange( $this->closing, $this->orig );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Extends DiffOp. Used to mark strings that have been
+ * copied from one string array to the other.
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class DiffOpCopy extends DiffOp {
+ public $type = 'copy';
+
+ public function __construct( $orig, $closing = false ) {
+ if ( !is_array( $closing ) ) {
+ $closing = $orig;
+ }
+ $this->orig = $orig;
+ $this->closing = $closing;
+ }
+
+ /**
+ * @return DiffOpCopy
+ */
+ public function reverse() {
+ return new DiffOpCopy( $this->closing, $this->orig );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A PHP diff engine for phpwiki. (Taken from phpwiki-1.3.3)
+ *
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * 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 DifferenceEngine
+ */
+
+/**
+ * Extends DiffOp. Used to mark strings that have been
+ * deleted from the first string array.
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class DiffOpDelete extends DiffOp {
+ public $type = 'delete';
+
+ public function __construct( $lines ) {
+ $this->orig = $lines;
+ $this->closing = false;
+ }
+
+ /**
+ * @return DiffOpAdd
+ */
+ public function reverse() {
+ return new DiffOpAdd( $this->orig );
+ }
+}
$slotContents = $this->getSlotContents();
$this->slotDiffRenderers = array_map( function ( $contents ) {
- /** @var $content Content */
+ /** @var Content $content */
$content = $contents['new'] ?: $contents['old'];
return $content->getContentHandler()->getSlotDiffRenderer( $this->getContext() );
}, $slotContents );
* @return string
*/
public static function diff( $oldText, $newText ) {
- /** @var $slotDiffRenderer TextSlotDiffRenderer */
+ /** @var TextSlotDiffRenderer $slotDiffRenderer */
$slotDiffRenderer = ContentHandler::getForModelID( CONTENT_MODEL_TEXT )
->getSlotDiffRenderer( RequestContext::getMain() );
return $slotDiffRenderer->getTextDiff( $oldText, $newText );
use Content;
use ParserOptions;
+use RuntimeException;
use ParserOutput;
/**
* @since 1.30
*/
class PreparedEdit {
-
/**
* Time this prepared edit was made
*
*
* @var ParserOutput|null
*/
- public $output;
+ private $canonicalOutput;
/**
* Content that is being saved (before PST)
*/
public $oldContent;
+ /**
+ * Lazy-loading callback to get canonical ParserOutput object
+ *
+ * @var callable
+ */
+ public $parserOutputCallback;
+
+ /**
+ * @return ParserOutput Canonical parser output
+ */
+ public function getOutput() {
+ if ( !$this->canonicalOutput ) {
+ $this->canonicalOutput = call_user_func( $this->parserOutputCallback );
+ }
+
+ return $this->canonicalOutput;
+ }
+
+ /**
+ * Fetch the ParserOutput via a lazy-loaded callback (for backwards compatibility).
+ *
+ * @deprecated since 1.33
+ * @param string $name
+ * @return mixed
+ */
+ function __get( $name ) {
+ if ( $name === 'output' ) {
+ return $this->getOutput();
+ }
+
+ throw new RuntimeException( "Undefined field $name." );
+ }
}
* @param string $key Message name
* @param string $fallback Default message if the message cache can't be
* called by the exception
- * The function also has other parameters that are arguments for the message
+ * @param mixed ...$params To pass to wfMessage()
* @return string Message with arguments replaced
*/
- public function msg( $key, $fallback /*[, params...] */ ) {
+ public function msg( $key, $fallback, ...$params ) {
global $wgSitename;
- $args = array_slice( func_get_args(), 2 );
// FIXME: Keep logic in sync with MWExceptionRenderer::msg.
$res = false;
if ( $this->useMessageCache() ) {
try {
- $res = wfMessage( $key, $args )->text();
+ $res = wfMessage( $key, $params )->text();
} catch ( Exception $e ) {
}
}
if ( $res === false ) {
- $res = wfMsgReplaceArgs( $fallback, $args );
+ $res = wfMsgReplaceArgs( $fallback, $params );
// If an exception happens inside message rendering,
// {{SITENAME}} sometimes won't be replaced.
$res = strtr( $res, [
* @param string $key Message name
* @param string $fallback Default message if the message cache can't be
* called by the exception
- * The function also has other parameters that are arguments for the message
+ * @param mixed ...$params To pass to wfMessage()
* @return string Message with arguments replaced
*/
- private static function msg( $key, $fallback /*[, params...] */ ) {
+ private static function msg( $key, $fallback, ...$params ) {
global $wgSitename;
- $args = array_slice( func_get_args(), 2 );
// FIXME: Keep logic in sync with MWException::msg.
try {
- $res = wfMessage( $key, $args )->text();
+ $res = wfMessage( $key, $params )->text();
} catch ( Exception $e ) {
- $res = wfMsgReplaceArgs( $fallback, $args );
+ $res = wfMsgReplaceArgs( $fallback, $params );
// If an exception happens inside message rendering,
// {{SITENAME}} sometimes won't be replaced.
$res = strtr( $res, [
function __destruct() {
$this->unlock();
}
-} // LocalFile class
-
-# ------------------------------------------------------------------------------
-
-/**
- * Helper class for file deletion
- * @ingroup FileAbstraction
- */
-class LocalFileDeleteBatch {
- /** @var LocalFile */
- private $file;
-
- /** @var string */
- private $reason;
-
- /** @var array */
- private $srcRels = [];
-
- /** @var array */
- private $archiveUrls = [];
-
- /** @var array Items to be processed in the deletion batch */
- private $deletionBatch;
-
- /** @var bool Whether to suppress all suppressable fields when deleting */
- private $suppress;
-
- /** @var Status */
- private $status;
-
- /** @var User */
- private $user;
-
- /**
- * @param File $file
- * @param string $reason
- * @param bool $suppress
- * @param User|null $user
- */
- function __construct( File $file, $reason = '', $suppress = false, $user = null ) {
- $this->file = $file;
- $this->reason = $reason;
- $this->suppress = $suppress;
- if ( $user ) {
- $this->user = $user;
- } else {
- global $wgUser;
- $this->user = $wgUser;
- }
- $this->status = $file->repo->newGood();
- }
-
- public function addCurrent() {
- $this->srcRels['.'] = $this->file->getRel();
- }
-
- /**
- * @param string $oldName
- */
- public function addOld( $oldName ) {
- $this->srcRels[$oldName] = $this->file->getArchiveRel( $oldName );
- $this->archiveUrls[] = $this->file->getArchiveUrl( $oldName );
- }
-
- /**
- * Add the old versions of the image to the batch
- * @return string[] List of archive names from old versions
- */
- public function addOlds() {
- $archiveNames = [];
-
- $dbw = $this->file->repo->getMasterDB();
- $result = $dbw->select( 'oldimage',
- [ 'oi_archive_name' ],
- [ 'oi_name' => $this->file->getName() ],
- __METHOD__
- );
-
- foreach ( $result as $row ) {
- $this->addOld( $row->oi_archive_name );
- $archiveNames[] = $row->oi_archive_name;
- }
-
- return $archiveNames;
- }
-
- /**
- * @return array
- */
- protected function getOldRels() {
- if ( !isset( $this->srcRels['.'] ) ) {
- $oldRels =& $this->srcRels;
- $deleteCurrent = false;
- } else {
- $oldRels = $this->srcRels;
- unset( $oldRels['.'] );
- $deleteCurrent = true;
- }
-
- return [ $oldRels, $deleteCurrent ];
- }
-
- /**
- * @return array
- */
- protected function getHashes() {
- $hashes = [];
- list( $oldRels, $deleteCurrent ) = $this->getOldRels();
-
- if ( $deleteCurrent ) {
- $hashes['.'] = $this->file->getSha1();
- }
-
- if ( count( $oldRels ) ) {
- $dbw = $this->file->repo->getMasterDB();
- $res = $dbw->select(
- 'oldimage',
- [ 'oi_archive_name', 'oi_sha1' ],
- [ 'oi_archive_name' => array_keys( $oldRels ),
- 'oi_name' => $this->file->getName() ], // performance
- __METHOD__
- );
-
- foreach ( $res as $row ) {
- if ( rtrim( $row->oi_sha1, "\0" ) === '' ) {
- // Get the hash from the file
- $oldUrl = $this->file->getArchiveVirtualUrl( $row->oi_archive_name );
- $props = $this->file->repo->getFileProps( $oldUrl );
-
- if ( $props['fileExists'] ) {
- // Upgrade the oldimage row
- $dbw->update( 'oldimage',
- [ 'oi_sha1' => $props['sha1'] ],
- [ 'oi_name' => $this->file->getName(), 'oi_archive_name' => $row->oi_archive_name ],
- __METHOD__ );
- $hashes[$row->oi_archive_name] = $props['sha1'];
- } else {
- $hashes[$row->oi_archive_name] = false;
- }
- } else {
- $hashes[$row->oi_archive_name] = $row->oi_sha1;
- }
- }
- }
-
- $missing = array_diff_key( $this->srcRels, $hashes );
-
- foreach ( $missing as $name => $rel ) {
- $this->status->error( 'filedelete-old-unregistered', $name );
- }
-
- foreach ( $hashes as $name => $hash ) {
- if ( !$hash ) {
- $this->status->error( 'filedelete-missing', $this->srcRels[$name] );
- unset( $hashes[$name] );
- }
- }
-
- return $hashes;
- }
-
- protected function doDBInserts() {
- global $wgActorTableSchemaMigrationStage;
-
- $now = time();
- $dbw = $this->file->repo->getMasterDB();
-
- $commentStore = MediaWikiServices::getInstance()->getCommentStore();
- $actorMigration = ActorMigration::newMigration();
-
- $encTimestamp = $dbw->addQuotes( $dbw->timestamp( $now ) );
- $encUserId = $dbw->addQuotes( $this->user->getId() );
- $encGroup = $dbw->addQuotes( 'deleted' );
- $ext = $this->file->getExtension();
- $dotExt = $ext === '' ? '' : ".$ext";
- $encExt = $dbw->addQuotes( $dotExt );
- list( $oldRels, $deleteCurrent ) = $this->getOldRels();
-
- // Bitfields to further suppress the content
- if ( $this->suppress ) {
- $bitfield = Revision::SUPPRESSED_ALL;
- } else {
- $bitfield = 'oi_deleted';
- }
-
- if ( $deleteCurrent ) {
- $tables = [ 'image' ];
- $fields = [
- 'fa_storage_group' => $encGroup,
- 'fa_storage_key' => $dbw->conditional(
- [ 'img_sha1' => '' ],
- $dbw->addQuotes( '' ),
- $dbw->buildConcat( [ "img_sha1", $encExt ] )
- ),
- 'fa_deleted_user' => $encUserId,
- 'fa_deleted_timestamp' => $encTimestamp,
- 'fa_deleted' => $this->suppress ? $bitfield : 0,
- 'fa_name' => 'img_name',
- 'fa_archive_name' => 'NULL',
- 'fa_size' => 'img_size',
- 'fa_width' => 'img_width',
- 'fa_height' => 'img_height',
- 'fa_metadata' => 'img_metadata',
- 'fa_bits' => 'img_bits',
- 'fa_media_type' => 'img_media_type',
- 'fa_major_mime' => 'img_major_mime',
- 'fa_minor_mime' => 'img_minor_mime',
- 'fa_description_id' => 'img_description_id',
- 'fa_timestamp' => 'img_timestamp',
- 'fa_sha1' => 'img_sha1'
- ];
- $joins = [];
-
- $fields += array_map(
- [ $dbw, 'addQuotes' ],
- $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 );
- }
-
- if ( count( $oldRels ) ) {
- $fileQuery = OldLocalFile::getQueryInfo();
- $res = $dbw->select(
- $fileQuery['tables'],
- $fileQuery['fields'],
- [
- 'oi_name' => $this->file->getName(),
- 'oi_archive_name' => array_keys( $oldRels )
- ],
- __METHOD__,
- [ 'FOR UPDATE' ],
- $fileQuery['joins']
- );
- $rowsInsert = [];
- if ( $res->numRows() ) {
- $reason = $commentStore->createComment( $dbw, $this->reason );
- foreach ( $res as $row ) {
- $comment = $commentStore->getComment( 'oi_description', $row );
- $user = User::newFromAnyId( $row->oi_user, $row->oi_user_text, $row->oi_actor );
- $rowsInsert[] = [
- // Deletion-specific fields
- 'fa_storage_group' => 'deleted',
- 'fa_storage_key' => ( $row->oi_sha1 === '' )
- ? ''
- : "{$row->oi_sha1}{$dotExt}",
- 'fa_deleted_user' => $this->user->getId(),
- 'fa_deleted_timestamp' => $dbw->timestamp( $now ),
- // Counterpart fields
- 'fa_deleted' => $this->suppress ? $bitfield : $row->oi_deleted,
- 'fa_name' => $row->oi_name,
- 'fa_archive_name' => $row->oi_archive_name,
- 'fa_size' => $row->oi_size,
- 'fa_width' => $row->oi_width,
- 'fa_height' => $row->oi_height,
- 'fa_metadata' => $row->oi_metadata,
- 'fa_bits' => $row->oi_bits,
- 'fa_media_type' => $row->oi_media_type,
- 'fa_major_mime' => $row->oi_major_mime,
- 'fa_minor_mime' => $row->oi_minor_mime,
- 'fa_timestamp' => $row->oi_timestamp,
- 'fa_sha1' => $row->oi_sha1
- ] + $commentStore->insert( $dbw, 'fa_deleted_reason', $reason )
- + $commentStore->insert( $dbw, 'fa_description', $comment )
- + $actorMigration->getInsertValues( $dbw, 'fa_user', $user );
- }
- }
-
- $dbw->insert( 'filearchive', $rowsInsert, __METHOD__ );
- }
- }
-
- function doDBDeletes() {
- $dbw = $this->file->repo->getMasterDB();
- list( $oldRels, $deleteCurrent ) = $this->getOldRels();
-
- if ( count( $oldRels ) ) {
- $dbw->delete( 'oldimage',
- [
- 'oi_name' => $this->file->getName(),
- 'oi_archive_name' => array_keys( $oldRels )
- ], __METHOD__ );
- }
-
- if ( $deleteCurrent ) {
- $dbw->delete( 'image', [ 'img_name' => $this->file->getName() ], __METHOD__ );
- }
- }
-
- /**
- * Run the transaction
- * @return Status
- */
- public function execute() {
- $repo = $this->file->getRepo();
- $this->file->lock();
-
- // Prepare deletion batch
- $hashes = $this->getHashes();
- $this->deletionBatch = [];
- $ext = $this->file->getExtension();
- $dotExt = $ext === '' ? '' : ".$ext";
-
- foreach ( $this->srcRels as $name => $srcRel ) {
- // Skip files that have no hash (e.g. missing DB record, or sha1 field and file source)
- if ( isset( $hashes[$name] ) ) {
- $hash = $hashes[$name];
- $key = $hash . $dotExt;
- $dstRel = $repo->getDeletedHashPath( $key ) . $key;
- $this->deletionBatch[$name] = [ $srcRel, $dstRel ];
- }
- }
-
- if ( !$repo->hasSha1Storage() ) {
- // Removes non-existent file from the batch, so we don't get errors.
- // This also handles files in the 'deleted' zone deleted via revision deletion.
- $checkStatus = $this->removeNonexistentFiles( $this->deletionBatch );
- if ( !$checkStatus->isGood() ) {
- $this->status->merge( $checkStatus );
- return $this->status;
- }
- $this->deletionBatch = $checkStatus->value;
-
- // Execute the file deletion batch
- $status = $this->file->repo->deleteBatch( $this->deletionBatch );
- if ( !$status->isGood() ) {
- $this->status->merge( $status );
- }
- }
-
- if ( !$this->status->isOK() ) {
- // Critical file deletion error; abort
- $this->file->unlock();
-
- return $this->status;
- }
-
- // Copy the image/oldimage rows to filearchive
- $this->doDBInserts();
- // Delete image/oldimage rows
- $this->doDBDeletes();
-
- // Commit and return
- $this->file->unlock();
-
- return $this->status;
- }
-
- /**
- * Removes non-existent files from a deletion batch.
- * @param array $batch
- * @return Status
- */
- protected function removeNonexistentFiles( $batch ) {
- $files = $newBatch = [];
-
- foreach ( $batch as $batchItem ) {
- list( $src, ) = $batchItem;
- $files[$src] = $this->file->repo->getVirtualUrl( 'public' ) . '/' . rawurlencode( $src );
- }
-
- $result = $this->file->repo->fileExistsBatch( $files );
- if ( in_array( null, $result, true ) ) {
- return Status::newFatal( 'backend-fail-internal',
- $this->file->repo->getBackend()->getName() );
- }
-
- foreach ( $batch as $batchItem ) {
- if ( $result[$batchItem[0]] ) {
- $newBatch[] = $batchItem;
- }
- }
-
- return Status::newGood( $newBatch );
- }
-}
-
-# ------------------------------------------------------------------------------
-
-/**
- * Helper class for file undeletion
- * @ingroup FileAbstraction
- */
-class LocalFileRestoreBatch {
- /** @var LocalFile */
- private $file;
-
- /** @var string[] List of file IDs to restore */
- private $cleanupBatch;
-
- /** @var string[] List of file IDs to restore */
- private $ids;
-
- /** @var bool Add all revisions of the file */
- private $all;
-
- /** @var bool Whether to remove all settings for suppressed fields */
- private $unsuppress = false;
-
- /**
- * @param File $file
- * @param bool $unsuppress
- */
- function __construct( File $file, $unsuppress = false ) {
- $this->file = $file;
- $this->cleanupBatch = [];
- $this->ids = [];
- $this->unsuppress = $unsuppress;
- }
-
- /**
- * Add a file by ID
- * @param int $fa_id
- */
- public function addId( $fa_id ) {
- $this->ids[] = $fa_id;
- }
-
- /**
- * Add a whole lot of files by ID
- * @param int[] $ids
- */
- public function addIds( $ids ) {
- $this->ids = array_merge( $this->ids, $ids );
- }
-
- /**
- * Add all revisions of the file
- */
- public function addAll() {
- $this->all = true;
- }
-
- /**
- * Run the transaction, except the cleanup batch.
- * The cleanup batch should be run in a separate transaction, because it locks different
- * rows and there's no need to keep the image row locked while it's acquiring those locks
- * The caller may have its own transaction open.
- * So we save the batch and let the caller call cleanup()
- * @return Status
- */
- public function execute() {
- /** @var Language */
- global $wgLang;
-
- $repo = $this->file->getRepo();
- if ( !$this->all && !$this->ids ) {
- // Do nothing
- return $repo->newGood();
- }
-
- $lockOwnsTrx = $this->file->lock();
-
- $dbw = $this->file->repo->getMasterDB();
-
- $commentStore = MediaWikiServices::getInstance()->getCommentStore();
- $actorMigration = ActorMigration::newMigration();
-
- $status = $this->file->repo->newGood();
-
- $exists = (bool)$dbw->selectField( 'image', '1',
- [ 'img_name' => $this->file->getName() ],
- __METHOD__,
- // The lock() should already prevents changes, but this still may need
- // to bypass any transaction snapshot. However, if lock() started the
- // trx (which it probably did) then snapshot is post-lock and up-to-date.
- $lockOwnsTrx ? [] : [ 'LOCK IN SHARE MODE' ]
- );
-
- // Fetch all or selected archived revisions for the file,
- // sorted from the most recent to the oldest.
- $conditions = [ 'fa_name' => $this->file->getName() ];
-
- if ( !$this->all ) {
- $conditions['fa_id'] = $this->ids;
- }
-
- $arFileQuery = ArchivedFile::getQueryInfo();
- $result = $dbw->select(
- $arFileQuery['tables'],
- $arFileQuery['fields'],
- $conditions,
- __METHOD__,
- [ 'ORDER BY' => 'fa_timestamp DESC' ],
- $arFileQuery['joins']
- );
-
- $idsPresent = [];
- $storeBatch = [];
- $insertBatch = [];
- $insertCurrent = false;
- $deleteIds = [];
- $first = true;
- $archiveNames = [];
-
- foreach ( $result as $row ) {
- $idsPresent[] = $row->fa_id;
-
- if ( $row->fa_name != $this->file->getName() ) {
- $status->error( 'undelete-filename-mismatch', $wgLang->timeanddate( $row->fa_timestamp ) );
- $status->failCount++;
- continue;
- }
-
- if ( $row->fa_storage_key == '' ) {
- // Revision was missing pre-deletion
- $status->error( 'undelete-bad-store-key', $wgLang->timeanddate( $row->fa_timestamp ) );
- $status->failCount++;
- continue;
- }
-
- $deletedRel = $repo->getDeletedHashPath( $row->fa_storage_key ) .
- $row->fa_storage_key;
- $deletedUrl = $repo->getVirtualUrl() . '/deleted/' . $deletedRel;
-
- if ( isset( $row->fa_sha1 ) ) {
- $sha1 = $row->fa_sha1;
- } else {
- // old row, populate from key
- $sha1 = LocalRepo::getHashFromKey( $row->fa_storage_key );
- }
-
- # Fix leading zero
- if ( strlen( $sha1 ) == 32 && $sha1[0] == '0' ) {
- $sha1 = substr( $sha1, 1 );
- }
-
- if ( is_null( $row->fa_major_mime ) || $row->fa_major_mime == 'unknown'
- || is_null( $row->fa_minor_mime ) || $row->fa_minor_mime == 'unknown'
- || is_null( $row->fa_media_type ) || $row->fa_media_type == 'UNKNOWN'
- || is_null( $row->fa_metadata )
- ) {
- // Refresh our metadata
- // Required for a new current revision; nice for older ones too. :)
- $props = RepoGroup::singleton()->getFileProps( $deletedUrl );
- } else {
- $props = [
- 'minor_mime' => $row->fa_minor_mime,
- 'major_mime' => $row->fa_major_mime,
- 'media_type' => $row->fa_media_type,
- 'metadata' => $row->fa_metadata
- ];
- }
-
- $comment = $commentStore->getComment( 'fa_description', $row );
- $user = User::newFromAnyId( $row->fa_user, $row->fa_user_text, $row->fa_actor );
- if ( $first && !$exists ) {
- // This revision will be published as the new current version
- $destRel = $this->file->getRel();
- $commentFields = $commentStore->insert( $dbw, 'img_description', $comment );
- $actorFields = $actorMigration->getInsertValues( $dbw, 'img_user', $user );
- $insertCurrent = [
- 'img_name' => $row->fa_name,
- 'img_size' => $row->fa_size,
- 'img_width' => $row->fa_width,
- 'img_height' => $row->fa_height,
- 'img_metadata' => $props['metadata'],
- 'img_bits' => $row->fa_bits,
- 'img_media_type' => $props['media_type'],
- 'img_major_mime' => $props['major_mime'],
- 'img_minor_mime' => $props['minor_mime'],
- 'img_timestamp' => $row->fa_timestamp,
- 'img_sha1' => $sha1
- ] + $commentFields + $actorFields;
-
- // The live (current) version cannot be hidden!
- if ( !$this->unsuppress && $row->fa_deleted ) {
- $status->fatal( 'undeleterevdel' );
- $this->file->unlock();
- return $status;
- }
- } else {
- $archiveName = $row->fa_archive_name;
-
- if ( $archiveName == '' ) {
- // This was originally a current version; we
- // have to devise a new archive name for it.
- // Format is <timestamp of archiving>!<name>
- $timestamp = wfTimestamp( TS_UNIX, $row->fa_deleted_timestamp );
-
- do {
- $archiveName = wfTimestamp( TS_MW, $timestamp ) . '!' . $row->fa_name;
- $timestamp++;
- } while ( isset( $archiveNames[$archiveName] ) );
- }
-
- $archiveNames[$archiveName] = true;
- $destRel = $this->file->getArchiveRel( $archiveName );
- $insertBatch[] = [
- 'oi_name' => $row->fa_name,
- 'oi_archive_name' => $archiveName,
- 'oi_size' => $row->fa_size,
- 'oi_width' => $row->fa_width,
- 'oi_height' => $row->fa_height,
- 'oi_bits' => $row->fa_bits,
- 'oi_timestamp' => $row->fa_timestamp,
- 'oi_metadata' => $props['metadata'],
- 'oi_media_type' => $props['media_type'],
- 'oi_major_mime' => $props['major_mime'],
- 'oi_minor_mime' => $props['minor_mime'],
- 'oi_deleted' => $this->unsuppress ? 0 : $row->fa_deleted,
- 'oi_sha1' => $sha1
- ] + $commentStore->insert( $dbw, 'oi_description', $comment )
- + $actorMigration->getInsertValues( $dbw, 'oi_user', $user );
- }
-
- $deleteIds[] = $row->fa_id;
-
- if ( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) {
- // private files can stay where they are
- $status->successCount++;
- } else {
- $storeBatch[] = [ $deletedUrl, 'public', $destRel ];
- $this->cleanupBatch[] = $row->fa_storage_key;
- }
-
- $first = false;
- }
-
- unset( $result );
-
- // Add a warning to the status object for missing IDs
- $missingIds = array_diff( $this->ids, $idsPresent );
-
- foreach ( $missingIds as $id ) {
- $status->error( 'undelete-missing-filearchive', $id );
- }
-
- if ( !$repo->hasSha1Storage() ) {
- // Remove missing files from batch, so we don't get errors when undeleting them
- $checkStatus = $this->removeNonexistentFiles( $storeBatch );
- if ( !$checkStatus->isGood() ) {
- $status->merge( $checkStatus );
- return $status;
- }
- $storeBatch = $checkStatus->value;
-
- // Run the store batch
- // Use the OVERWRITE_SAME flag to smooth over a common error
- $storeStatus = $this->file->repo->storeBatch( $storeBatch, FileRepo::OVERWRITE_SAME );
- $status->merge( $storeStatus );
-
- if ( !$status->isGood() ) {
- // Even if some files could be copied, fail entirely as that is the
- // easiest thing to do without data loss
- $this->cleanupFailedBatch( $storeStatus, $storeBatch );
- $status->setOK( false );
- $this->file->unlock();
-
- return $status;
- }
- }
-
- // Run the DB updates
- // Because we have locked the image row, key conflicts should be rare.
- // If they do occur, we can roll back the transaction at this time with
- // no data loss, but leaving unregistered files scattered throughout the
- // public zone.
- // This is not ideal, which is why it's important to lock the image row.
- if ( $insertCurrent ) {
- $dbw->insert( 'image', $insertCurrent, __METHOD__ );
- }
-
- if ( $insertBatch ) {
- $dbw->insert( 'oldimage', $insertBatch, __METHOD__ );
- }
-
- if ( $deleteIds ) {
- $dbw->delete( 'filearchive',
- [ 'fa_id' => $deleteIds ],
- __METHOD__ );
- }
-
- // If store batch is empty (all files are missing), deletion is to be considered successful
- if ( $status->successCount > 0 || !$storeBatch || $repo->hasSha1Storage() ) {
- if ( !$exists ) {
- wfDebug( __METHOD__ . " restored {$status->successCount} items, creating a new current\n" );
-
- DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => 1 ] ) );
-
- $this->file->purgeEverything();
- } else {
- wfDebug( __METHOD__ . " restored {$status->successCount} as archived versions\n" );
- $this->file->purgeDescription();
- }
- }
-
- $this->file->unlock();
-
- return $status;
- }
-
- /**
- * Removes non-existent files from a store batch.
- * @param array $triplets
- * @return Status
- */
- protected function removeNonexistentFiles( $triplets ) {
- $files = $filteredTriplets = [];
- foreach ( $triplets as $file ) {
- $files[$file[0]] = $file[0];
- }
-
- $result = $this->file->repo->fileExistsBatch( $files );
- if ( in_array( null, $result, true ) ) {
- return Status::newFatal( 'backend-fail-internal',
- $this->file->repo->getBackend()->getName() );
- }
-
- foreach ( $triplets as $file ) {
- if ( $result[$file[0]] ) {
- $filteredTriplets[] = $file;
- }
- }
-
- return Status::newGood( $filteredTriplets );
- }
-
- /**
- * Removes non-existent files from a cleanup batch.
- * @param string[] $batch
- * @return string[]
- */
- protected function removeNonexistentFromCleanup( $batch ) {
- $files = $newBatch = [];
- $repo = $this->file->repo;
-
- foreach ( $batch as $file ) {
- $files[$file] = $repo->getVirtualUrl( 'deleted' ) . '/' .
- rawurlencode( $repo->getDeletedHashPath( $file ) . $file );
- }
-
- $result = $repo->fileExistsBatch( $files );
-
- foreach ( $batch as $file ) {
- if ( $result[$file] ) {
- $newBatch[] = $file;
- }
- }
-
- return $newBatch;
- }
-
- /**
- * Delete unused files in the deleted zone.
- * This should be called from outside the transaction in which execute() was called.
- * @return Status
- */
- public function cleanup() {
- if ( !$this->cleanupBatch ) {
- return $this->file->repo->newGood();
- }
-
- $this->cleanupBatch = $this->removeNonexistentFromCleanup( $this->cleanupBatch );
-
- $status = $this->file->repo->cleanupDeletedBatch( $this->cleanupBatch );
-
- return $status;
- }
-
- /**
- * Cleanup a failed batch. The batch was only partially successful, so
- * rollback by removing all items that were successfully copied.
- *
- * @param Status $storeStatus
- * @param array[] $storeBatch
- */
- protected function cleanupFailedBatch( $storeStatus, $storeBatch ) {
- $cleanupBatch = [];
-
- foreach ( $storeStatus->success as $i => $success ) {
- // Check if this item of the batch was successfully copied
- if ( $success ) {
- // Item was successfully copied and needs to be removed again
- // Extract ($dstZone, $dstRel) from the batch
- $cleanupBatch[] = [ $storeBatch[$i][1], $storeBatch[$i][2] ];
- }
- }
- $this->file->repo->cleanupBatch( $cleanupBatch );
- }
-}
-
-# ------------------------------------------------------------------------------
-
-/**
- * Helper class for file movement
- * @ingroup FileAbstraction
- */
-class LocalFileMoveBatch {
- /** @var LocalFile */
- protected $file;
-
- /** @var Title */
- protected $target;
-
- protected $cur;
-
- protected $olds;
-
- protected $oldCount;
-
- protected $archive;
-
- /** @var IDatabase */
- protected $db;
-
- /**
- * @param File $file
- * @param Title $target
- */
- function __construct( File $file, Title $target ) {
- $this->file = $file;
- $this->target = $target;
- $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() );
- $this->newHash = $this->file->repo->getHashPath( $this->target->getDBkey() );
- $this->oldName = $this->file->getName();
- $this->newName = $this->file->repo->getNameFromTitle( $this->target );
- $this->oldRel = $this->oldHash . $this->oldName;
- $this->newRel = $this->newHash . $this->newName;
- $this->db = $file->getRepo()->getMasterDB();
- }
-
- /**
- * Add the current image to the batch
- */
- public function addCurrent() {
- $this->cur = [ $this->oldRel, $this->newRel ];
- }
-
- /**
- * Add the old versions of the image to the batch
- * @return string[] List of archive names from old versions
- */
- public function addOlds() {
- $archiveBase = 'archive';
- $this->olds = [];
- $this->oldCount = 0;
- $archiveNames = [];
-
- $result = $this->db->select( 'oldimage',
- [ 'oi_archive_name', 'oi_deleted' ],
- [ 'oi_name' => $this->oldName ],
- __METHOD__,
- [ 'LOCK IN SHARE MODE' ] // ignore snapshot
- );
-
- foreach ( $result as $row ) {
- $archiveNames[] = $row->oi_archive_name;
- $oldName = $row->oi_archive_name;
- $bits = explode( '!', $oldName, 2 );
-
- if ( count( $bits ) != 2 ) {
- wfDebug( "Old file name missing !: '$oldName' \n" );
- continue;
- }
-
- list( $timestamp, $filename ) = $bits;
-
- if ( $this->oldName != $filename ) {
- wfDebug( "Old file name doesn't match: '$oldName' \n" );
- continue;
- }
-
- $this->oldCount++;
-
- // Do we want to add those to oldCount?
- if ( $row->oi_deleted & File::DELETED_FILE ) {
- continue;
- }
-
- $this->olds[] = [
- "{$archiveBase}/{$this->oldHash}{$oldName}",
- "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"
- ];
- }
-
- return $archiveNames;
- }
-
- /**
- * Perform the move.
- * @return Status
- */
- public function execute() {
- $repo = $this->file->repo;
- $status = $repo->newGood();
- $destFile = wfLocalFile( $this->target );
-
- $this->file->lock();
- $destFile->lock(); // quickly fail if destination is not available
-
- $triplets = $this->getMoveTriplets();
- $checkStatus = $this->removeNonexistentFiles( $triplets );
- if ( !$checkStatus->isGood() ) {
- $destFile->unlock();
- $this->file->unlock();
- $status->merge( $checkStatus ); // couldn't talk to file backend
- return $status;
- }
- $triplets = $checkStatus->value;
-
- // Verify the file versions metadata in the DB.
- $statusDb = $this->verifyDBUpdates();
- if ( !$statusDb->isGood() ) {
- $destFile->unlock();
- $this->file->unlock();
- $statusDb->setOK( false );
-
- return $statusDb;
- }
-
- if ( !$repo->hasSha1Storage() ) {
- // Copy the files into their new location.
- // If a prior process fataled copying or cleaning up files we tolerate any
- // of the existing files if they are identical to the ones being stored.
- $statusMove = $repo->storeBatch( $triplets, FileRepo::OVERWRITE_SAME );
- wfDebugLog( 'imagemove', "Moved files for {$this->file->getName()}: " .
- "{$statusMove->successCount} successes, {$statusMove->failCount} failures" );
- if ( !$statusMove->isGood() ) {
- // Delete any files copied over (while the destination is still locked)
- $this->cleanupTarget( $triplets );
- $destFile->unlock();
- $this->file->unlock();
- wfDebugLog( 'imagemove', "Error in moving files: "
- . $statusMove->getWikiText( false, false, 'en' ) );
- $statusMove->setOK( false );
-
- return $statusMove;
- }
- $status->merge( $statusMove );
- }
-
- // Rename the file versions metadata in the DB.
- $this->doDBUpdates();
-
- wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: " .
- "{$statusDb->successCount} successes, {$statusDb->failCount} failures" );
-
- $destFile->unlock();
- $this->file->unlock();
-
- // Everything went ok, remove the source files
- $this->cleanupSource( $triplets );
-
- $status->merge( $statusDb );
-
- return $status;
- }
-
- /**
- * Verify the database updates and return a new Status indicating how
- * many rows would be updated.
- *
- * @return Status
- */
- protected function verifyDBUpdates() {
- $repo = $this->file->repo;
- $status = $repo->newGood();
- $dbw = $this->db;
-
- $hasCurrent = $dbw->lockForUpdate(
- 'image',
- [ 'img_name' => $this->oldName ],
- __METHOD__
- );
- $oldRowCount = $dbw->lockForUpdate(
- 'oldimage',
- [ 'oi_name' => $this->oldName ],
- __METHOD__
- );
-
- if ( $hasCurrent ) {
- $status->successCount++;
- } else {
- $status->failCount++;
- }
- $status->successCount += $oldRowCount;
- // T36934: oldCount is based on files that actually exist.
- // There may be more DB rows than such files, in which case $affected
- // can be greater than $total. We use max() to avoid negatives here.
- $status->failCount += max( 0, $this->oldCount - $oldRowCount );
- if ( $status->failCount ) {
- $status->error( 'imageinvalidfilename' );
- }
-
- return $status;
- }
-
- /**
- * Do the database updates and return a new Status indicating how
- * many rows where updated.
- */
- protected function doDBUpdates() {
- $dbw = $this->db;
-
- // Update current image
- $dbw->update(
- 'image',
- [ 'img_name' => $this->newName ],
- [ 'img_name' => $this->oldName ],
- __METHOD__
- );
-
- // Update old images
- $dbw->update(
- 'oldimage',
- [
- 'oi_name' => $this->newName,
- 'oi_archive_name = ' . $dbw->strreplace( 'oi_archive_name',
- $dbw->addQuotes( $this->oldName ), $dbw->addQuotes( $this->newName ) ),
- ],
- [ 'oi_name' => $this->oldName ],
- __METHOD__
- );
- }
-
- /**
- * Generate triplets for FileRepo::storeBatch().
- * @return array[]
- */
- protected function getMoveTriplets() {
- $moves = array_merge( [ $this->cur ], $this->olds );
- $triplets = []; // The format is: (srcUrl, destZone, destUrl)
-
- foreach ( $moves as $move ) {
- // $move: (oldRelativePath, newRelativePath)
- $srcUrl = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $move[0] );
- $triplets[] = [ $srcUrl, 'public', $move[1] ];
- wfDebugLog(
- 'imagemove',
- "Generated move triplet for {$this->file->getName()}: {$srcUrl} :: public :: {$move[1]}"
- );
- }
-
- return $triplets;
- }
-
- /**
- * Removes non-existent files from move batch.
- * @param array $triplets
- * @return Status
- */
- protected function removeNonexistentFiles( $triplets ) {
- $files = [];
-
- foreach ( $triplets as $file ) {
- $files[$file[0]] = $file[0];
- }
-
- $result = $this->file->repo->fileExistsBatch( $files );
- if ( in_array( null, $result, true ) ) {
- return Status::newFatal( 'backend-fail-internal',
- $this->file->repo->getBackend()->getName() );
- }
-
- $filteredTriplets = [];
- foreach ( $triplets as $file ) {
- if ( $result[$file[0]] ) {
- $filteredTriplets[] = $file;
- } else {
- wfDebugLog( 'imagemove', "File {$file[0]} does not exist" );
- }
- }
-
- return Status::newGood( $filteredTriplets );
- }
-
- /**
- * Cleanup a partially moved array of triplets by deleting the target
- * files. Called if something went wrong half way.
- * @param array[] $triplets
- */
- protected function cleanupTarget( $triplets ) {
- // Create dest pairs from the triplets
- $pairs = [];
- foreach ( $triplets as $triplet ) {
- // $triplet: (old source virtual URL, dst zone, dest rel)
- $pairs[] = [ $triplet[1], $triplet[2] ];
- }
-
- $this->file->repo->cleanupBatch( $pairs );
- }
-
- /**
- * Cleanup a fully moved array of triplets by deleting the source files.
- * Called at the end of the move process if everything else went ok.
- * @param array[] $triplets
- */
- protected function cleanupSource( $triplets ) {
- // Create source file names from the triplets
- $files = [];
- foreach ( $triplets as $triplet ) {
- $files[] = $triplet[0];
- }
-
- $this->file->repo->cleanupBatch( $files );
- }
-}
-
-class LocalFileLockError extends ErrorPageError {
- public function __construct( Status $status ) {
- parent::__construct(
- 'actionfailed',
- $status->getMessage()
- );
- }
-
- public function report() {
- global $wgOut;
- $wgOut->setStatusCode( 429 );
- parent::report();
- }
}
--- /dev/null
+<?php
+/**
+ * Local file in the wiki's own database.
+ *
+ * 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 FileAbstraction
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Helper class for file deletion
+ * @ingroup FileAbstraction
+ */
+class LocalFileDeleteBatch {
+ /** @var LocalFile */
+ private $file;
+
+ /** @var string */
+ private $reason;
+
+ /** @var array */
+ private $srcRels = [];
+
+ /** @var array */
+ private $archiveUrls = [];
+
+ /** @var array Items to be processed in the deletion batch */
+ private $deletionBatch;
+
+ /** @var bool Whether to suppress all suppressable fields when deleting */
+ private $suppress;
+
+ /** @var Status */
+ private $status;
+
+ /** @var User */
+ private $user;
+
+ /**
+ * @param File $file
+ * @param string $reason
+ * @param bool $suppress
+ * @param User|null $user
+ */
+ function __construct( File $file, $reason = '', $suppress = false, $user = null ) {
+ $this->file = $file;
+ $this->reason = $reason;
+ $this->suppress = $suppress;
+ if ( $user ) {
+ $this->user = $user;
+ } else {
+ global $wgUser;
+ $this->user = $wgUser;
+ }
+ $this->status = $file->repo->newGood();
+ }
+
+ public function addCurrent() {
+ $this->srcRels['.'] = $this->file->getRel();
+ }
+
+ /**
+ * @param string $oldName
+ */
+ public function addOld( $oldName ) {
+ $this->srcRels[$oldName] = $this->file->getArchiveRel( $oldName );
+ $this->archiveUrls[] = $this->file->getArchiveUrl( $oldName );
+ }
+
+ /**
+ * Add the old versions of the image to the batch
+ * @return string[] List of archive names from old versions
+ */
+ public function addOlds() {
+ $archiveNames = [];
+
+ $dbw = $this->file->repo->getMasterDB();
+ $result = $dbw->select( 'oldimage',
+ [ 'oi_archive_name' ],
+ [ 'oi_name' => $this->file->getName() ],
+ __METHOD__
+ );
+
+ foreach ( $result as $row ) {
+ $this->addOld( $row->oi_archive_name );
+ $archiveNames[] = $row->oi_archive_name;
+ }
+
+ return $archiveNames;
+ }
+
+ /**
+ * @return array
+ */
+ protected function getOldRels() {
+ if ( !isset( $this->srcRels['.'] ) ) {
+ $oldRels =& $this->srcRels;
+ $deleteCurrent = false;
+ } else {
+ $oldRels = $this->srcRels;
+ unset( $oldRels['.'] );
+ $deleteCurrent = true;
+ }
+
+ return [ $oldRels, $deleteCurrent ];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getHashes() {
+ $hashes = [];
+ list( $oldRels, $deleteCurrent ) = $this->getOldRels();
+
+ if ( $deleteCurrent ) {
+ $hashes['.'] = $this->file->getSha1();
+ }
+
+ if ( count( $oldRels ) ) {
+ $dbw = $this->file->repo->getMasterDB();
+ $res = $dbw->select(
+ 'oldimage',
+ [ 'oi_archive_name', 'oi_sha1' ],
+ [ 'oi_archive_name' => array_keys( $oldRels ),
+ 'oi_name' => $this->file->getName() ], // performance
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ if ( rtrim( $row->oi_sha1, "\0" ) === '' ) {
+ // Get the hash from the file
+ $oldUrl = $this->file->getArchiveVirtualUrl( $row->oi_archive_name );
+ $props = $this->file->repo->getFileProps( $oldUrl );
+
+ if ( $props['fileExists'] ) {
+ // Upgrade the oldimage row
+ $dbw->update( 'oldimage',
+ [ 'oi_sha1' => $props['sha1'] ],
+ [ 'oi_name' => $this->file->getName(), 'oi_archive_name' => $row->oi_archive_name ],
+ __METHOD__ );
+ $hashes[$row->oi_archive_name] = $props['sha1'];
+ } else {
+ $hashes[$row->oi_archive_name] = false;
+ }
+ } else {
+ $hashes[$row->oi_archive_name] = $row->oi_sha1;
+ }
+ }
+ }
+
+ $missing = array_diff_key( $this->srcRels, $hashes );
+
+ foreach ( $missing as $name => $rel ) {
+ $this->status->error( 'filedelete-old-unregistered', $name );
+ }
+
+ foreach ( $hashes as $name => $hash ) {
+ if ( !$hash ) {
+ $this->status->error( 'filedelete-missing', $this->srcRels[$name] );
+ unset( $hashes[$name] );
+ }
+ }
+
+ return $hashes;
+ }
+
+ protected function doDBInserts() {
+ global $wgActorTableSchemaMigrationStage;
+
+ $now = time();
+ $dbw = $this->file->repo->getMasterDB();
+
+ $commentStore = MediaWikiServices::getInstance()->getCommentStore();
+ $actorMigration = ActorMigration::newMigration();
+
+ $encTimestamp = $dbw->addQuotes( $dbw->timestamp( $now ) );
+ $encUserId = $dbw->addQuotes( $this->user->getId() );
+ $encGroup = $dbw->addQuotes( 'deleted' );
+ $ext = $this->file->getExtension();
+ $dotExt = $ext === '' ? '' : ".$ext";
+ $encExt = $dbw->addQuotes( $dotExt );
+ list( $oldRels, $deleteCurrent ) = $this->getOldRels();
+
+ // Bitfields to further suppress the content
+ if ( $this->suppress ) {
+ $bitfield = Revision::SUPPRESSED_ALL;
+ } else {
+ $bitfield = 'oi_deleted';
+ }
+
+ if ( $deleteCurrent ) {
+ $tables = [ 'image' ];
+ $fields = [
+ 'fa_storage_group' => $encGroup,
+ 'fa_storage_key' => $dbw->conditional(
+ [ 'img_sha1' => '' ],
+ $dbw->addQuotes( '' ),
+ $dbw->buildConcat( [ "img_sha1", $encExt ] )
+ ),
+ 'fa_deleted_user' => $encUserId,
+ 'fa_deleted_timestamp' => $encTimestamp,
+ 'fa_deleted' => $this->suppress ? $bitfield : 0,
+ 'fa_name' => 'img_name',
+ 'fa_archive_name' => 'NULL',
+ 'fa_size' => 'img_size',
+ 'fa_width' => 'img_width',
+ 'fa_height' => 'img_height',
+ 'fa_metadata' => 'img_metadata',
+ 'fa_bits' => 'img_bits',
+ 'fa_media_type' => 'img_media_type',
+ 'fa_major_mime' => 'img_major_mime',
+ 'fa_minor_mime' => 'img_minor_mime',
+ 'fa_description_id' => 'img_description_id',
+ 'fa_timestamp' => 'img_timestamp',
+ 'fa_sha1' => 'img_sha1'
+ ];
+ $joins = [];
+
+ $fields += array_map(
+ [ $dbw, 'addQuotes' ],
+ $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 );
+ }
+
+ if ( count( $oldRels ) ) {
+ $fileQuery = OldLocalFile::getQueryInfo();
+ $res = $dbw->select(
+ $fileQuery['tables'],
+ $fileQuery['fields'],
+ [
+ 'oi_name' => $this->file->getName(),
+ 'oi_archive_name' => array_keys( $oldRels )
+ ],
+ __METHOD__,
+ [ 'FOR UPDATE' ],
+ $fileQuery['joins']
+ );
+ $rowsInsert = [];
+ if ( $res->numRows() ) {
+ $reason = $commentStore->createComment( $dbw, $this->reason );
+ foreach ( $res as $row ) {
+ $comment = $commentStore->getComment( 'oi_description', $row );
+ $user = User::newFromAnyId( $row->oi_user, $row->oi_user_text, $row->oi_actor );
+ $rowsInsert[] = [
+ // Deletion-specific fields
+ 'fa_storage_group' => 'deleted',
+ 'fa_storage_key' => ( $row->oi_sha1 === '' )
+ ? ''
+ : "{$row->oi_sha1}{$dotExt}",
+ 'fa_deleted_user' => $this->user->getId(),
+ 'fa_deleted_timestamp' => $dbw->timestamp( $now ),
+ // Counterpart fields
+ 'fa_deleted' => $this->suppress ? $bitfield : $row->oi_deleted,
+ 'fa_name' => $row->oi_name,
+ 'fa_archive_name' => $row->oi_archive_name,
+ 'fa_size' => $row->oi_size,
+ 'fa_width' => $row->oi_width,
+ 'fa_height' => $row->oi_height,
+ 'fa_metadata' => $row->oi_metadata,
+ 'fa_bits' => $row->oi_bits,
+ 'fa_media_type' => $row->oi_media_type,
+ 'fa_major_mime' => $row->oi_major_mime,
+ 'fa_minor_mime' => $row->oi_minor_mime,
+ 'fa_timestamp' => $row->oi_timestamp,
+ 'fa_sha1' => $row->oi_sha1
+ ] + $commentStore->insert( $dbw, 'fa_deleted_reason', $reason )
+ + $commentStore->insert( $dbw, 'fa_description', $comment )
+ + $actorMigration->getInsertValues( $dbw, 'fa_user', $user );
+ }
+ }
+
+ $dbw->insert( 'filearchive', $rowsInsert, __METHOD__ );
+ }
+ }
+
+ function doDBDeletes() {
+ $dbw = $this->file->repo->getMasterDB();
+ list( $oldRels, $deleteCurrent ) = $this->getOldRels();
+
+ if ( count( $oldRels ) ) {
+ $dbw->delete( 'oldimage',
+ [
+ 'oi_name' => $this->file->getName(),
+ 'oi_archive_name' => array_keys( $oldRels )
+ ], __METHOD__ );
+ }
+
+ if ( $deleteCurrent ) {
+ $dbw->delete( 'image', [ 'img_name' => $this->file->getName() ], __METHOD__ );
+ }
+ }
+
+ /**
+ * Run the transaction
+ * @return Status
+ */
+ public function execute() {
+ $repo = $this->file->getRepo();
+ $this->file->lock();
+
+ // Prepare deletion batch
+ $hashes = $this->getHashes();
+ $this->deletionBatch = [];
+ $ext = $this->file->getExtension();
+ $dotExt = $ext === '' ? '' : ".$ext";
+
+ foreach ( $this->srcRels as $name => $srcRel ) {
+ // Skip files that have no hash (e.g. missing DB record, or sha1 field and file source)
+ if ( isset( $hashes[$name] ) ) {
+ $hash = $hashes[$name];
+ $key = $hash . $dotExt;
+ $dstRel = $repo->getDeletedHashPath( $key ) . $key;
+ $this->deletionBatch[$name] = [ $srcRel, $dstRel ];
+ }
+ }
+
+ if ( !$repo->hasSha1Storage() ) {
+ // Removes non-existent file from the batch, so we don't get errors.
+ // This also handles files in the 'deleted' zone deleted via revision deletion.
+ $checkStatus = $this->removeNonexistentFiles( $this->deletionBatch );
+ if ( !$checkStatus->isGood() ) {
+ $this->status->merge( $checkStatus );
+ return $this->status;
+ }
+ $this->deletionBatch = $checkStatus->value;
+
+ // Execute the file deletion batch
+ $status = $this->file->repo->deleteBatch( $this->deletionBatch );
+ if ( !$status->isGood() ) {
+ $this->status->merge( $status );
+ }
+ }
+
+ if ( !$this->status->isOK() ) {
+ // Critical file deletion error; abort
+ $this->file->unlock();
+
+ return $this->status;
+ }
+
+ // Copy the image/oldimage rows to filearchive
+ $this->doDBInserts();
+ // Delete image/oldimage rows
+ $this->doDBDeletes();
+
+ // Commit and return
+ $this->file->unlock();
+
+ return $this->status;
+ }
+
+ /**
+ * Removes non-existent files from a deletion batch.
+ * @param array $batch
+ * @return Status
+ */
+ protected function removeNonexistentFiles( $batch ) {
+ $files = $newBatch = [];
+
+ foreach ( $batch as $batchItem ) {
+ list( $src, ) = $batchItem;
+ $files[$src] = $this->file->repo->getVirtualUrl( 'public' ) . '/' . rawurlencode( $src );
+ }
+
+ $result = $this->file->repo->fileExistsBatch( $files );
+ if ( in_array( null, $result, true ) ) {
+ return Status::newFatal( 'backend-fail-internal',
+ $this->file->repo->getBackend()->getName() );
+ }
+
+ foreach ( $batch as $batchItem ) {
+ if ( $result[$batchItem[0]] ) {
+ $newBatch[] = $batchItem;
+ }
+ }
+
+ return Status::newGood( $newBatch );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Local file in the wiki's own database.
+ *
+ * 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 FileAbstraction
+ */
+
+class LocalFileLockError extends ErrorPageError {
+ public function __construct( Status $status ) {
+ parent::__construct(
+ 'actionfailed',
+ $status->getMessage()
+ );
+ }
+
+ public function report() {
+ global $wgOut;
+ $wgOut->setStatusCode( 429 );
+ parent::report();
+ }
+}
--- /dev/null
+<?php
+/**
+ * Local file in the wiki's own database.
+ *
+ * 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 FileAbstraction
+ */
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Helper class for file movement
+ * @ingroup FileAbstraction
+ */
+class LocalFileMoveBatch {
+ /** @var LocalFile */
+ protected $file;
+
+ /** @var Title */
+ protected $target;
+
+ protected $cur;
+
+ protected $olds;
+
+ protected $oldCount;
+
+ protected $archive;
+
+ /** @var IDatabase */
+ protected $db;
+
+ /**
+ * @param File $file
+ * @param Title $target
+ */
+ function __construct( File $file, Title $target ) {
+ $this->file = $file;
+ $this->target = $target;
+ $this->oldHash = $this->file->repo->getHashPath( $this->file->getName() );
+ $this->newHash = $this->file->repo->getHashPath( $this->target->getDBkey() );
+ $this->oldName = $this->file->getName();
+ $this->newName = $this->file->repo->getNameFromTitle( $this->target );
+ $this->oldRel = $this->oldHash . $this->oldName;
+ $this->newRel = $this->newHash . $this->newName;
+ $this->db = $file->getRepo()->getMasterDB();
+ }
+
+ /**
+ * Add the current image to the batch
+ */
+ public function addCurrent() {
+ $this->cur = [ $this->oldRel, $this->newRel ];
+ }
+
+ /**
+ * Add the old versions of the image to the batch
+ * @return string[] List of archive names from old versions
+ */
+ public function addOlds() {
+ $archiveBase = 'archive';
+ $this->olds = [];
+ $this->oldCount = 0;
+ $archiveNames = [];
+
+ $result = $this->db->select( 'oldimage',
+ [ 'oi_archive_name', 'oi_deleted' ],
+ [ 'oi_name' => $this->oldName ],
+ __METHOD__,
+ [ 'LOCK IN SHARE MODE' ] // ignore snapshot
+ );
+
+ foreach ( $result as $row ) {
+ $archiveNames[] = $row->oi_archive_name;
+ $oldName = $row->oi_archive_name;
+ $bits = explode( '!', $oldName, 2 );
+
+ if ( count( $bits ) != 2 ) {
+ wfDebug( "Old file name missing !: '$oldName' \n" );
+ continue;
+ }
+
+ list( $timestamp, $filename ) = $bits;
+
+ if ( $this->oldName != $filename ) {
+ wfDebug( "Old file name doesn't match: '$oldName' \n" );
+ continue;
+ }
+
+ $this->oldCount++;
+
+ // Do we want to add those to oldCount?
+ if ( $row->oi_deleted & File::DELETED_FILE ) {
+ continue;
+ }
+
+ $this->olds[] = [
+ "{$archiveBase}/{$this->oldHash}{$oldName}",
+ "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"
+ ];
+ }
+
+ return $archiveNames;
+ }
+
+ /**
+ * Perform the move.
+ * @return Status
+ */
+ public function execute() {
+ $repo = $this->file->repo;
+ $status = $repo->newGood();
+ $destFile = wfLocalFile( $this->target );
+
+ $this->file->lock();
+ $destFile->lock(); // quickly fail if destination is not available
+
+ $triplets = $this->getMoveTriplets();
+ $checkStatus = $this->removeNonexistentFiles( $triplets );
+ if ( !$checkStatus->isGood() ) {
+ $destFile->unlock();
+ $this->file->unlock();
+ $status->merge( $checkStatus ); // couldn't talk to file backend
+ return $status;
+ }
+ $triplets = $checkStatus->value;
+
+ // Verify the file versions metadata in the DB.
+ $statusDb = $this->verifyDBUpdates();
+ if ( !$statusDb->isGood() ) {
+ $destFile->unlock();
+ $this->file->unlock();
+ $statusDb->setOK( false );
+
+ return $statusDb;
+ }
+
+ if ( !$repo->hasSha1Storage() ) {
+ // Copy the files into their new location.
+ // If a prior process fataled copying or cleaning up files we tolerate any
+ // of the existing files if they are identical to the ones being stored.
+ $statusMove = $repo->storeBatch( $triplets, FileRepo::OVERWRITE_SAME );
+ wfDebugLog( 'imagemove', "Moved files for {$this->file->getName()}: " .
+ "{$statusMove->successCount} successes, {$statusMove->failCount} failures" );
+ if ( !$statusMove->isGood() ) {
+ // Delete any files copied over (while the destination is still locked)
+ $this->cleanupTarget( $triplets );
+ $destFile->unlock();
+ $this->file->unlock();
+ wfDebugLog( 'imagemove', "Error in moving files: "
+ . $statusMove->getWikiText( false, false, 'en' ) );
+ $statusMove->setOK( false );
+
+ return $statusMove;
+ }
+ $status->merge( $statusMove );
+ }
+
+ // Rename the file versions metadata in the DB.
+ $this->doDBUpdates();
+
+ wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: " .
+ "{$statusDb->successCount} successes, {$statusDb->failCount} failures" );
+
+ $destFile->unlock();
+ $this->file->unlock();
+
+ // Everything went ok, remove the source files
+ $this->cleanupSource( $triplets );
+
+ $status->merge( $statusDb );
+
+ return $status;
+ }
+
+ /**
+ * Verify the database updates and return a new Status indicating how
+ * many rows would be updated.
+ *
+ * @return Status
+ */
+ protected function verifyDBUpdates() {
+ $repo = $this->file->repo;
+ $status = $repo->newGood();
+ $dbw = $this->db;
+
+ $hasCurrent = $dbw->lockForUpdate(
+ 'image',
+ [ 'img_name' => $this->oldName ],
+ __METHOD__
+ );
+ $oldRowCount = $dbw->lockForUpdate(
+ 'oldimage',
+ [ 'oi_name' => $this->oldName ],
+ __METHOD__
+ );
+
+ if ( $hasCurrent ) {
+ $status->successCount++;
+ } else {
+ $status->failCount++;
+ }
+ $status->successCount += $oldRowCount;
+ // T36934: oldCount is based on files that actually exist.
+ // There may be more DB rows than such files, in which case $affected
+ // can be greater than $total. We use max() to avoid negatives here.
+ $status->failCount += max( 0, $this->oldCount - $oldRowCount );
+ if ( $status->failCount ) {
+ $status->error( 'imageinvalidfilename' );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Do the database updates and return a new Status indicating how
+ * many rows where updated.
+ */
+ protected function doDBUpdates() {
+ $dbw = $this->db;
+
+ // Update current image
+ $dbw->update(
+ 'image',
+ [ 'img_name' => $this->newName ],
+ [ 'img_name' => $this->oldName ],
+ __METHOD__
+ );
+
+ // Update old images
+ $dbw->update(
+ 'oldimage',
+ [
+ 'oi_name' => $this->newName,
+ 'oi_archive_name = ' . $dbw->strreplace( 'oi_archive_name',
+ $dbw->addQuotes( $this->oldName ), $dbw->addQuotes( $this->newName ) ),
+ ],
+ [ 'oi_name' => $this->oldName ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * Generate triplets for FileRepo::storeBatch().
+ * @return array[]
+ */
+ protected function getMoveTriplets() {
+ $moves = array_merge( [ $this->cur ], $this->olds );
+ $triplets = []; // The format is: (srcUrl, destZone, destUrl)
+
+ foreach ( $moves as $move ) {
+ // $move: (oldRelativePath, newRelativePath)
+ $srcUrl = $this->file->repo->getVirtualUrl() . '/public/' . rawurlencode( $move[0] );
+ $triplets[] = [ $srcUrl, 'public', $move[1] ];
+ wfDebugLog(
+ 'imagemove',
+ "Generated move triplet for {$this->file->getName()}: {$srcUrl} :: public :: {$move[1]}"
+ );
+ }
+
+ return $triplets;
+ }
+
+ /**
+ * Removes non-existent files from move batch.
+ * @param array $triplets
+ * @return Status
+ */
+ protected function removeNonexistentFiles( $triplets ) {
+ $files = [];
+
+ foreach ( $triplets as $file ) {
+ $files[$file[0]] = $file[0];
+ }
+
+ $result = $this->file->repo->fileExistsBatch( $files );
+ if ( in_array( null, $result, true ) ) {
+ return Status::newFatal( 'backend-fail-internal',
+ $this->file->repo->getBackend()->getName() );
+ }
+
+ $filteredTriplets = [];
+ foreach ( $triplets as $file ) {
+ if ( $result[$file[0]] ) {
+ $filteredTriplets[] = $file;
+ } else {
+ wfDebugLog( 'imagemove', "File {$file[0]} does not exist" );
+ }
+ }
+
+ return Status::newGood( $filteredTriplets );
+ }
+
+ /**
+ * Cleanup a partially moved array of triplets by deleting the target
+ * files. Called if something went wrong half way.
+ * @param array[] $triplets
+ */
+ protected function cleanupTarget( $triplets ) {
+ // Create dest pairs from the triplets
+ $pairs = [];
+ foreach ( $triplets as $triplet ) {
+ // $triplet: (old source virtual URL, dst zone, dest rel)
+ $pairs[] = [ $triplet[1], $triplet[2] ];
+ }
+
+ $this->file->repo->cleanupBatch( $pairs );
+ }
+
+ /**
+ * Cleanup a fully moved array of triplets by deleting the source files.
+ * Called at the end of the move process if everything else went ok.
+ * @param array[] $triplets
+ */
+ protected function cleanupSource( $triplets ) {
+ // Create source file names from the triplets
+ $files = [];
+ foreach ( $triplets as $triplet ) {
+ $files[] = $triplet[0];
+ }
+
+ $this->file->repo->cleanupBatch( $files );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Local file in the wiki's own database.
+ *
+ * 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 FileAbstraction
+ */
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Helper class for file undeletion
+ * @ingroup FileAbstraction
+ */
+class LocalFileRestoreBatch {
+ /** @var LocalFile */
+ private $file;
+
+ /** @var string[] List of file IDs to restore */
+ private $cleanupBatch;
+
+ /** @var string[] List of file IDs to restore */
+ private $ids;
+
+ /** @var bool Add all revisions of the file */
+ private $all;
+
+ /** @var bool Whether to remove all settings for suppressed fields */
+ private $unsuppress = false;
+
+ /**
+ * @param File $file
+ * @param bool $unsuppress
+ */
+ function __construct( File $file, $unsuppress = false ) {
+ $this->file = $file;
+ $this->cleanupBatch = [];
+ $this->ids = [];
+ $this->unsuppress = $unsuppress;
+ }
+
+ /**
+ * Add a file by ID
+ * @param int $fa_id
+ */
+ public function addId( $fa_id ) {
+ $this->ids[] = $fa_id;
+ }
+
+ /**
+ * Add a whole lot of files by ID
+ * @param int[] $ids
+ */
+ public function addIds( $ids ) {
+ $this->ids = array_merge( $this->ids, $ids );
+ }
+
+ /**
+ * Add all revisions of the file
+ */
+ public function addAll() {
+ $this->all = true;
+ }
+
+ /**
+ * Run the transaction, except the cleanup batch.
+ * The cleanup batch should be run in a separate transaction, because it locks different
+ * rows and there's no need to keep the image row locked while it's acquiring those locks
+ * The caller may have its own transaction open.
+ * So we save the batch and let the caller call cleanup()
+ * @return Status
+ */
+ public function execute() {
+ /** @var Language */
+ global $wgLang;
+
+ $repo = $this->file->getRepo();
+ if ( !$this->all && !$this->ids ) {
+ // Do nothing
+ return $repo->newGood();
+ }
+
+ $lockOwnsTrx = $this->file->lock();
+
+ $dbw = $this->file->repo->getMasterDB();
+
+ $commentStore = MediaWikiServices::getInstance()->getCommentStore();
+ $actorMigration = ActorMigration::newMigration();
+
+ $status = $this->file->repo->newGood();
+
+ $exists = (bool)$dbw->selectField( 'image', '1',
+ [ 'img_name' => $this->file->getName() ],
+ __METHOD__,
+ // The lock() should already prevents changes, but this still may need
+ // to bypass any transaction snapshot. However, if lock() started the
+ // trx (which it probably did) then snapshot is post-lock and up-to-date.
+ $lockOwnsTrx ? [] : [ 'LOCK IN SHARE MODE' ]
+ );
+
+ // Fetch all or selected archived revisions for the file,
+ // sorted from the most recent to the oldest.
+ $conditions = [ 'fa_name' => $this->file->getName() ];
+
+ if ( !$this->all ) {
+ $conditions['fa_id'] = $this->ids;
+ }
+
+ $arFileQuery = ArchivedFile::getQueryInfo();
+ $result = $dbw->select(
+ $arFileQuery['tables'],
+ $arFileQuery['fields'],
+ $conditions,
+ __METHOD__,
+ [ 'ORDER BY' => 'fa_timestamp DESC' ],
+ $arFileQuery['joins']
+ );
+
+ $idsPresent = [];
+ $storeBatch = [];
+ $insertBatch = [];
+ $insertCurrent = false;
+ $deleteIds = [];
+ $first = true;
+ $archiveNames = [];
+
+ foreach ( $result as $row ) {
+ $idsPresent[] = $row->fa_id;
+
+ if ( $row->fa_name != $this->file->getName() ) {
+ $status->error( 'undelete-filename-mismatch', $wgLang->timeanddate( $row->fa_timestamp ) );
+ $status->failCount++;
+ continue;
+ }
+
+ if ( $row->fa_storage_key == '' ) {
+ // Revision was missing pre-deletion
+ $status->error( 'undelete-bad-store-key', $wgLang->timeanddate( $row->fa_timestamp ) );
+ $status->failCount++;
+ continue;
+ }
+
+ $deletedRel = $repo->getDeletedHashPath( $row->fa_storage_key ) .
+ $row->fa_storage_key;
+ $deletedUrl = $repo->getVirtualUrl() . '/deleted/' . $deletedRel;
+
+ if ( isset( $row->fa_sha1 ) ) {
+ $sha1 = $row->fa_sha1;
+ } else {
+ // old row, populate from key
+ $sha1 = LocalRepo::getHashFromKey( $row->fa_storage_key );
+ }
+
+ # Fix leading zero
+ if ( strlen( $sha1 ) == 32 && $sha1[0] == '0' ) {
+ $sha1 = substr( $sha1, 1 );
+ }
+
+ if ( is_null( $row->fa_major_mime ) || $row->fa_major_mime == 'unknown'
+ || is_null( $row->fa_minor_mime ) || $row->fa_minor_mime == 'unknown'
+ || is_null( $row->fa_media_type ) || $row->fa_media_type == 'UNKNOWN'
+ || is_null( $row->fa_metadata )
+ ) {
+ // Refresh our metadata
+ // Required for a new current revision; nice for older ones too. :)
+ $props = RepoGroup::singleton()->getFileProps( $deletedUrl );
+ } else {
+ $props = [
+ 'minor_mime' => $row->fa_minor_mime,
+ 'major_mime' => $row->fa_major_mime,
+ 'media_type' => $row->fa_media_type,
+ 'metadata' => $row->fa_metadata
+ ];
+ }
+
+ $comment = $commentStore->getComment( 'fa_description', $row );
+ $user = User::newFromAnyId( $row->fa_user, $row->fa_user_text, $row->fa_actor );
+ if ( $first && !$exists ) {
+ // This revision will be published as the new current version
+ $destRel = $this->file->getRel();
+ $commentFields = $commentStore->insert( $dbw, 'img_description', $comment );
+ $actorFields = $actorMigration->getInsertValues( $dbw, 'img_user', $user );
+ $insertCurrent = [
+ 'img_name' => $row->fa_name,
+ 'img_size' => $row->fa_size,
+ 'img_width' => $row->fa_width,
+ 'img_height' => $row->fa_height,
+ 'img_metadata' => $props['metadata'],
+ 'img_bits' => $row->fa_bits,
+ 'img_media_type' => $props['media_type'],
+ 'img_major_mime' => $props['major_mime'],
+ 'img_minor_mime' => $props['minor_mime'],
+ 'img_timestamp' => $row->fa_timestamp,
+ 'img_sha1' => $sha1
+ ] + $commentFields + $actorFields;
+
+ // The live (current) version cannot be hidden!
+ if ( !$this->unsuppress && $row->fa_deleted ) {
+ $status->fatal( 'undeleterevdel' );
+ $this->file->unlock();
+ return $status;
+ }
+ } else {
+ $archiveName = $row->fa_archive_name;
+
+ if ( $archiveName == '' ) {
+ // This was originally a current version; we
+ // have to devise a new archive name for it.
+ // Format is <timestamp of archiving>!<name>
+ $timestamp = wfTimestamp( TS_UNIX, $row->fa_deleted_timestamp );
+
+ do {
+ $archiveName = wfTimestamp( TS_MW, $timestamp ) . '!' . $row->fa_name;
+ $timestamp++;
+ } while ( isset( $archiveNames[$archiveName] ) );
+ }
+
+ $archiveNames[$archiveName] = true;
+ $destRel = $this->file->getArchiveRel( $archiveName );
+ $insertBatch[] = [
+ 'oi_name' => $row->fa_name,
+ 'oi_archive_name' => $archiveName,
+ 'oi_size' => $row->fa_size,
+ 'oi_width' => $row->fa_width,
+ 'oi_height' => $row->fa_height,
+ 'oi_bits' => $row->fa_bits,
+ 'oi_timestamp' => $row->fa_timestamp,
+ 'oi_metadata' => $props['metadata'],
+ 'oi_media_type' => $props['media_type'],
+ 'oi_major_mime' => $props['major_mime'],
+ 'oi_minor_mime' => $props['minor_mime'],
+ 'oi_deleted' => $this->unsuppress ? 0 : $row->fa_deleted,
+ 'oi_sha1' => $sha1
+ ] + $commentStore->insert( $dbw, 'oi_description', $comment )
+ + $actorMigration->getInsertValues( $dbw, 'oi_user', $user );
+ }
+
+ $deleteIds[] = $row->fa_id;
+
+ if ( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) {
+ // private files can stay where they are
+ $status->successCount++;
+ } else {
+ $storeBatch[] = [ $deletedUrl, 'public', $destRel ];
+ $this->cleanupBatch[] = $row->fa_storage_key;
+ }
+
+ $first = false;
+ }
+
+ unset( $result );
+
+ // Add a warning to the status object for missing IDs
+ $missingIds = array_diff( $this->ids, $idsPresent );
+
+ foreach ( $missingIds as $id ) {
+ $status->error( 'undelete-missing-filearchive', $id );
+ }
+
+ if ( !$repo->hasSha1Storage() ) {
+ // Remove missing files from batch, so we don't get errors when undeleting them
+ $checkStatus = $this->removeNonexistentFiles( $storeBatch );
+ if ( !$checkStatus->isGood() ) {
+ $status->merge( $checkStatus );
+ return $status;
+ }
+ $storeBatch = $checkStatus->value;
+
+ // Run the store batch
+ // Use the OVERWRITE_SAME flag to smooth over a common error
+ $storeStatus = $this->file->repo->storeBatch( $storeBatch, FileRepo::OVERWRITE_SAME );
+ $status->merge( $storeStatus );
+
+ if ( !$status->isGood() ) {
+ // Even if some files could be copied, fail entirely as that is the
+ // easiest thing to do without data loss
+ $this->cleanupFailedBatch( $storeStatus, $storeBatch );
+ $status->setOK( false );
+ $this->file->unlock();
+
+ return $status;
+ }
+ }
+
+ // Run the DB updates
+ // Because we have locked the image row, key conflicts should be rare.
+ // If they do occur, we can roll back the transaction at this time with
+ // no data loss, but leaving unregistered files scattered throughout the
+ // public zone.
+ // This is not ideal, which is why it's important to lock the image row.
+ if ( $insertCurrent ) {
+ $dbw->insert( 'image', $insertCurrent, __METHOD__ );
+ }
+
+ if ( $insertBatch ) {
+ $dbw->insert( 'oldimage', $insertBatch, __METHOD__ );
+ }
+
+ if ( $deleteIds ) {
+ $dbw->delete( 'filearchive',
+ [ 'fa_id' => $deleteIds ],
+ __METHOD__ );
+ }
+
+ // If store batch is empty (all files are missing), deletion is to be considered successful
+ if ( $status->successCount > 0 || !$storeBatch || $repo->hasSha1Storage() ) {
+ if ( !$exists ) {
+ wfDebug( __METHOD__ . " restored {$status->successCount} items, creating a new current\n" );
+
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => 1 ] ) );
+
+ $this->file->purgeEverything();
+ } else {
+ wfDebug( __METHOD__ . " restored {$status->successCount} as archived versions\n" );
+ $this->file->purgeDescription();
+ }
+ }
+
+ $this->file->unlock();
+
+ return $status;
+ }
+
+ /**
+ * Removes non-existent files from a store batch.
+ * @param array $triplets
+ * @return Status
+ */
+ protected function removeNonexistentFiles( $triplets ) {
+ $files = $filteredTriplets = [];
+ foreach ( $triplets as $file ) {
+ $files[$file[0]] = $file[0];
+ }
+
+ $result = $this->file->repo->fileExistsBatch( $files );
+ if ( in_array( null, $result, true ) ) {
+ return Status::newFatal( 'backend-fail-internal',
+ $this->file->repo->getBackend()->getName() );
+ }
+
+ foreach ( $triplets as $file ) {
+ if ( $result[$file[0]] ) {
+ $filteredTriplets[] = $file;
+ }
+ }
+
+ return Status::newGood( $filteredTriplets );
+ }
+
+ /**
+ * Removes non-existent files from a cleanup batch.
+ * @param string[] $batch
+ * @return string[]
+ */
+ protected function removeNonexistentFromCleanup( $batch ) {
+ $files = $newBatch = [];
+ $repo = $this->file->repo;
+
+ foreach ( $batch as $file ) {
+ $files[$file] = $repo->getVirtualUrl( 'deleted' ) . '/' .
+ rawurlencode( $repo->getDeletedHashPath( $file ) . $file );
+ }
+
+ $result = $repo->fileExistsBatch( $files );
+
+ foreach ( $batch as $file ) {
+ if ( $result[$file] ) {
+ $newBatch[] = $file;
+ }
+ }
+
+ return $newBatch;
+ }
+
+ /**
+ * Delete unused files in the deleted zone.
+ * This should be called from outside the transaction in which execute() was called.
+ * @return Status
+ */
+ public function cleanup() {
+ if ( !$this->cleanupBatch ) {
+ return $this->file->repo->newGood();
+ }
+
+ $this->cleanupBatch = $this->removeNonexistentFromCleanup( $this->cleanupBatch );
+
+ $status = $this->file->repo->cleanupDeletedBatch( $this->cleanupBatch );
+
+ return $status;
+ }
+
+ /**
+ * Cleanup a failed batch. The batch was only partially successful, so
+ * rollback by removing all items that were successfully copied.
+ *
+ * @param Status $storeStatus
+ * @param array[] $storeBatch
+ */
+ protected function cleanupFailedBatch( $storeStatus, $storeBatch ) {
+ $cleanupBatch = [];
+
+ foreach ( $storeStatus->success as $i => $success ) {
+ // Check if this item of the batch was successfully copied
+ if ( $success ) {
+ // Item was successfully copied and needs to be removed again
+ // Extract ($dstZone, $dstRel) from the batch
+ $cleanupBatch[] = [ $storeBatch[$i][1], $storeBatch[$i][2] ];
+ }
+ }
+ $this->file->repo->cleanupBatch( $cleanupBatch );
+ }
+}
--- /dev/null
+<?php
+
+class HTMLFormActionFieldLayout extends OOUI\ActionFieldLayout {
+ use HTMLFormElement;
+
+ public function __construct( $fieldWidget, $buttonWidget = false, array $config = [] ) {
+ parent::__construct( $fieldWidget, $buttonWidget, $config );
+
+ // Traits
+ $this->initializeHTMLFormElement( $config );
+ }
+
+ protected function getJavaScriptClassName() {
+ return 'mw.htmlform.ActionFieldLayout';
+ }
+}
} );
}
}
-
-class HTMLFormFieldLayout extends OOUI\FieldLayout {
- use HTMLFormElement;
-
- public function __construct( $fieldWidget, array $config = [] ) {
- parent::__construct( $fieldWidget, $config );
-
- // Traits
- $this->initializeHTMLFormElement( $config );
- }
-
- protected function getJavaScriptClassName() {
- return 'mw.htmlform.FieldLayout';
- }
-}
-
-class HTMLFormActionFieldLayout extends OOUI\ActionFieldLayout {
- use HTMLFormElement;
-
- public function __construct( $fieldWidget, $buttonWidget = false, array $config = [] ) {
- parent::__construct( $fieldWidget, $buttonWidget, $config );
-
- // Traits
- $this->initializeHTMLFormElement( $config );
- }
-
- protected function getJavaScriptClassName() {
- return 'mw.htmlform.ActionFieldLayout';
- }
-}
--- /dev/null
+<?php
+
+class HTMLFormFieldLayout extends OOUI\FieldLayout {
+ use HTMLFormElement;
+
+ public function __construct( $fieldWidget, array $config = [] ) {
+ parent::__construct( $fieldWidget, $config );
+
+ // Traits
+ $this->initializeHTMLFormElement( $config );
+ }
+
+ protected function getJavaScriptClassName() {
+ return 'mw.htmlform.FieldLayout';
+ }
+}
'type' => $this->mButtonType,
'label' => $this->mButtonValue,
'flags' => $this->mButtonFlags,
+ 'id' => $this->mButtonId,
] + OOUI\Element::configFromHtmlAttributes(
$this->getAttributes( [ 'disabled', 'tabindex' ] )
) );
case 'url':
$type = $this->mParams['type'];
break;
+ case 'textwithbutton':
+ $type = $this->mParams['inputtype'] ?? 'text';
+ break;
}
}
if ( !Http::$httpEngine ) {
Http::$httpEngine = 'guzzle';
} elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
- throw new DomainException( __METHOD__ . ': curl (https://secure.php.net/curl) is not ' .
+ throw new DomainException( __METHOD__ . ': curl (https://www.php.net/curl) is not ' .
'installed, but Http::$httpEngine is set to "curl"' );
}
throw new DomainException( __METHOD__ . ': allow_url_fopen ' .
'needs to be enabled for pure PHP http requests to ' .
'work. If possible, curl should be used instead. See ' .
- 'https://secure.php.net/curl.'
+ 'https://www.php.net/curl.'
);
}
return new PhpHttpRequest( $url, $options, $caller, Profiler::instance() );
wfDebug( "IMPORT: $data\n" );
}
- public function notice( $msg /*, $param, ...*/ ) {
- $params = func_get_args();
- array_shift( $params );
-
+ public function notice( $msg, ...$params ) {
if ( is_callable( $this->mNoticeCallback ) ) {
call_user_func( $this->mNoticeCallback, $msg, $params );
} else { # No ImportReporter -> CLI
}
}
- $args = func_get_args();
- return Hooks::run( 'AfterImportPage', $args );
+ return Hooks::run( 'AfterImportPage', func_get_args() );
}
/**
private function pageOutCallback( $title, $foreignTitle, $revCount,
$sucCount, $pageInfo ) {
if ( isset( $this->mPageOutCallback ) ) {
- $args = func_get_args();
- call_user_func_array( $this->mPageOutCallback, $args );
+ call_user_func_array( $this->mPageOutCallback, func_get_args() );
}
}
$this->showMessage( 'config-install-step-done' );
}
- public function showMessage( $msg /*, ... */ ) {
- echo $this->getMessageText( func_get_args() ) . "\n";
+ public function showMessage( $msg, ...$params ) {
+ echo $this->getMessageText( $msg, $params ) . "\n";
flush();
}
- public function showError( $msg /*, ... */ ) {
- echo "***{$this->getMessageText( func_get_args() )}***\n";
+ public function showError( $msg, ...$params ) {
+ echo "***{$this->getMessageText( $msg, $params )}***\n";
flush();
}
/**
+ * @param string $msg
* @param array $params
*
* @return string
*/
- protected function getMessageText( $params ) {
- $msg = array_shift( $params );
-
+ protected function getMessageText( $msg, $params ) {
$text = wfMessage( $msg, $params )->parse();
$text = preg_replace( '/<a href="(.*?)".*?>(.*?)<\/a>/', '$2 <$1>', $text );
/**
* The Installer object.
*
- * @todo Naming this parent is confusing, 'installer' would be clearer.
- *
* @var WebInstaller
*/
public $parent;
*/
protected function rebuildLocalisationCache() {
/**
- * @var $cl RebuildLocalisationCache
+ * @var RebuildLocalisationCache $cl
*/
$cl = $this->maintenance->runChild(
RebuildLocalisationCache::class, 'rebuildLocalisationCache.php'
* The messages will be in wikitext format, which will be converted to an
* output format such as HTML or text before being sent to the user.
* @param string $msg
+ * @param mixed ...$params
*/
- abstract public function showMessage( $msg /*, ... */ );
+ abstract public function showMessage( $msg, ...$params );
/**
* Same as showMessage(), but for displaying errors
* @param string $msg
+ * @param mixed ...$params
*/
- abstract public function showError( $msg /*, ... */ );
+ abstract public function showError( $msg, ...$params );
/**
* Show a message to the installing user by using a Status object
return $status;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
return;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
$conn->selectDB( $this->getVar( 'wgDBname' ) );
return $status;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
return;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
$conn->selectDB( $this->getVar( 'wgDBname' ) );
$status = $this->getConnection();
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
+++ /dev/null
-<?php
-/**
- * Classes for self-contained tests for known bugs in 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * @defgroup PHPBugTests PHP known bugs tests
- */
-/**
- * Test for PHP+libxml2 bug which breaks XML input subtly with certain versions.
- * Known fixed with PHP 5.2.9 + libxml2-2.7.3
- * @see https://bugs.php.net/bug.php?id=45996
- * @ingroup PHPBugTests
- */
-class PhpXmlBugTester {
- private $parsedData = '';
- public $ok = false;
-
- public function __construct() {
- $charData = '<b>c</b>';
- $xml = '<a>' . htmlspecialchars( $charData ) . '</a>';
-
- $parser = xml_parser_create();
- xml_set_character_data_handler( $parser, [ $this, 'chardata' ] );
- $parsedOk = xml_parse( $parser, $xml, true );
- $this->ok = $parsedOk && ( $this->parsedData == $charData );
- }
-
- public function chardata( $parser, $data ) {
- $this->parsedData .= $data;
- }
-}
--- /dev/null
+<?php
+/**
+ * Classes for self-contained tests for known bugs in 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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * @defgroup PHPBugTests PHP known bugs tests
+ */
+/**
+ * Test for PHP+libxml2 bug which breaks XML input subtly with certain versions.
+ * Known fixed with PHP 5.2.9 + libxml2-2.7.3
+ * @see https://bugs.php.net/bug.php?id=45996
+ * @ingroup PHPBugTests
+ */
+class PhpXmlBugTester {
+ private $parsedData = '';
+ public $ok = false;
+
+ public function __construct() {
+ $charData = '<b>c</b>';
+ $xml = '<a>' . htmlspecialchars( $charData ) . '</a>';
+
+ $parser = xml_parser_create();
+ xml_set_character_data_handler( $parser, [ $this, 'chardata' ] );
+ $parsedOk = xml_parse( $parser, $xml, true );
+ $this->ok = $parsedOk && ( $this->parsedData == $charData );
+ }
+
+ public function chardata( $parser, $data ) {
+ $this->parsedData .= $data;
+ }
+}
return $status;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
if ( $status->isOK() ) {
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
$conn->clearFlag( DBO_TRX );
$status = $this->openPgConnection( 'create-schema' );
if ( $status->isOK() ) {
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
$safeRole = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) );
return false;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
$superuser = $this->getVar( '_InstallUser' );
return $status;
}
/**
- * @var $conn Database
+ * @var Database $conn
*/
$conn = $status->value;
* Show an error message in a box. Parameters are like wfMessage(), or
* alternatively, pass a Message object in.
* @param string|Message $msg
+ * @param mixed ...$params
*/
- public function showError( $msg /*...*/ ) {
+ public function showError( $msg, ...$params ) {
if ( !( $msg instanceof Message ) ) {
- $args = func_get_args();
- array_shift( $args );
- $args = array_map( 'htmlspecialchars', $args );
- $msg = wfMessage( $msg, $args );
+ $msg = wfMessage(
+ $msg,
+ array_map( 'htmlspecialchars', $params )
+ );
}
$text = $msg->useDatabase( false )->plain();
$this->output->addHTML( $this->getErrorBox( $text ) );
* @param string $msg
* @return string
*/
- public function getHelpBox( $msg /*, ... */ ) {
- $args = func_get_args();
- array_shift( $args );
+ public function getHelpBox( $msg, ...$args ) {
$args = array_map( 'htmlspecialchars', $args );
$text = wfMessage( $msg, $args )->useDatabase( false )->plain();
$html = $this->parse( $text, true );
/**
* Output a help box.
* @param string $msg Key for wfMessage()
+ * @param mixed ...$params
*/
- public function showHelpBox( $msg /*, ... */ ) {
- $args = func_get_args();
- $html = $this->getHelpBox( ...$args );
+ public function showHelpBox( $msg, ...$params ) {
+ $html = $this->getHelpBox( $msg, ...$params );
$this->output->addHTML( $html );
}
* Output looks like a list.
*
* @param string $msg
+ * @param mixed ...$params
*/
- public function showMessage( $msg /*, ... */ ) {
- $args = func_get_args();
- array_shift( $args );
+ public function showMessage( $msg, ...$params ) {
$html = '<div class="config-message">' .
- $this->parse( wfMessage( $msg, $args )->useDatabase( false )->plain() ) .
+ $this->parse( wfMessage( $msg, $params )->useDatabase( false )->plain() ) .
"</div>\n";
$this->output->addHTML( $html );
}
"config-pcre-no-utf8": "<strong>فادح:</strong> يبدو أن وحدة PCRE في PHP يتم تجميعها بدون دعم PCRE_UTF8، \nيتطلب ميدياويكي دعم UTF-8 ليعمل بشكل صحيح.",
"config-memory-raised": "<code>memory_limit</code> في PHP $1 ومرتفعة إلى $2.",
"config-memory-bad": "<strong>تحذير:</strong> في PHP <code>memory_limit</code> $1،\nهذا على الارجح منخفض جدا;\nقد يفشل التثبيت!",
- "config-apc": "تثبيت [https://secure.php.net/apc APC]",
- "config-apcu": "[https://secure.php.net/apcu APCu] مثبت",
+ "config-apc": "تثبيت [https://www.php.net/apc APC]",
+ "config-apcu": "[https://www.php.net/apcu APCu] مثبت",
"config-wincache": "تثبيت [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]",
- "config-no-cache-apcu": "<strong>تحذير:</strong> تعذر العثور على [https://secure.php.net/apcu APCu] أو [https://www.iis.net/downloads/microsoft/wincache-extension WinCache];\nكائن التخزين المؤقت غير ممكّّن.",
+ "config-no-cache-apcu": "<strong>تحذير:</strong> تعذر العثور على [https://www.php.net/apcu APCu] أو [https://www.iis.net/downloads/microsoft/wincache-extension WinCache];\nكائن التخزين المؤقت غير ممكّّن.",
"config-mod-security": "<strong>تحذير:</strong> تم تمكين خادم الويب الخاص بك [https://modsecurity.org/ mod_security]/mod_security2، العديد من التكوينات الشائعة له سوف تتسبب في مشاكل لميدياويكي والبرامج الأخرى التي تسمح للمستخدمين بنشر محتوى عشوائي; \nإذا كان ذلك ممكنا، يجب تعطيل هذا، أو يمكنك الرجوع إلى [https://modsecurity.org/documentation/ توثيق mod_security] أو الاتصال بدعم مضيفك إذا واجهتك أخطاء عشوائية.",
"config-diff3-bad": "أداة جنو diff3 لمقارنة النصوص غير موجودة. يمكنك تجاهل هذا الآن، لكن ربما تصادف تضاربات تحريرية أكثر من المعتاد.",
"config-git": "العثور على برنامج التحكم في إصدار جت <code>$1</code>.",
"config-type-oracle": "أوراكل",
"config-type-mssql": "خادم SQL لميكروسوفت",
"config-support-info": "ميدياويكي يدعم نظم قواعد البيانات التالية: $1 إذا كنت لا ترى نظام قاعدة البيانات الذي تحاول استخدامه مدرجًا أدناه، اتبع الإرشادات المرتبطة فوق لتمكين الدعم.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] هو الهدف الأساسي لميدياويكي وأفضل دعم له، يعمل ميدياويكي أيضا مع [{{int:version-db-mariadb-url}} MariaDB] و[{{int:version-db-percona-url}} Percona Server]، وهما متوافقان مع MySQL، ([https://secure.php.net/manual/en/mysqli.installation.php كيفية تجميع PHP مع دعم MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] هو نظام قاعدة بيانات مفتوح المصدر مشهور كبديل لـMySQL ([https://secure.php.net/manual/en/pgsql.installation.php كيفية تجميع PHP مع دعم PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] هو نظام قاعدة بيانات خفيف مدعوم بشكل جيد. ([https://secure.php.net/manual/en/pdo.installation.php كيفية ترجمة PHP باستخدام دعم SQLite]، يستخدم PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] هي قاعدة بيانات مؤسسة تجارية. ([https://secure.php.net/manual/en/oci8.installation.php كيفية تجميع PHP مع دعم OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] هي قاعدة بيانات مؤسسة تجارية لويندوز. ([https://secure.php.net/manual/en/sqlsrv.installation.php كيفية تجميع PHP مع دعم SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] هو الهدف الأساسي لميدياويكي وأفضل دعم له، يعمل ميدياويكي أيضا مع [{{int:version-db-mariadb-url}} MariaDB] و[{{int:version-db-percona-url}} Percona Server]، وهما متوافقان مع MySQL، ([https://www.php.net/manual/en/mysqli.installation.php كيفية تجميع PHP مع دعم MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] هو نظام قاعدة بيانات مفتوح المصدر مشهور كبديل لـMySQL ([https://www.php.net/manual/en/pgsql.installation.php كيفية تجميع PHP مع دعم PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] هو نظام قاعدة بيانات خفيف مدعوم بشكل جيد. ([https://www.php.net/manual/en/pdo.installation.php كيفية ترجمة PHP باستخدام دعم SQLite]، يستخدم PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] هي قاعدة بيانات مؤسسة تجارية. ([https://www.php.net/manual/en/oci8.installation.php كيفية تجميع PHP مع دعم OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] هي قاعدة بيانات مؤسسة تجارية لويندوز. ([https://www.php.net/manual/en/sqlsrv.installation.php كيفية تجميع PHP مع دعم SQLSRV])",
"config-header-mysql": "إعدادات MariaDB/MySQL",
"config-header-postgres": "إعدادات PostgreSQL",
"config-header-sqlite": "إعدادات SQLite",
"config-license-help": "يضع العديد من الويكيات العامة جميع المساهمات تحت [https://freedomdefined.org/Definition خصة حرة]، \nوهذا يساعد على خلق شعور بالملكية المجتمعية ويشجع على المساهمة طويلة الأجل، \nليس من الضروري عموما استخدام ويكي خاص أو شركة. \n\nإذا كنت تريد أن تكون قادرا على استخدام النص من ويكيبيديا، وتريد أن تكون ويكيبيديا قادرة على قبول النص الذي تم نسخه من الويكي الخاص بك، فيجب عليك اختيار <strong>{{int:config-license-cc-by-sa}}</strong>. \n\nاستخدمت ويكيبيديا في السابق رخصة جنو للوثائق الحرة، \nGFDL هو ترخيص صالح، ولكن من الصعب فهمه، \nمن الصعب أيضا إعادة استخدام المحتوى المرخص بموجب GFDL.",
"config-email-settings": "إعدادات البريد الإلكتروني",
"config-enable-email": "تمكين البريد الإلكتروني الصادرة",
- "config-enable-email-help": "إذا كنت تريد إرسال بريد إلكتروني إلى العمل، [Config-dbsupport-oracle/manual/en/mail.configuration.php إعدات بريد PHP's] تحتاج لأن يتم تكوينها بشكل صحيح. إذا كنت لا تريد أيا من ميزات البريد الإلكتروني، يمكنك تعطيلها هنا.",
+ "config-enable-email-help": "إذا كنت تريد إرسال بريد إلكتروني إلى العمل، [https://www.php.net/manual/en/mail.configuration.php إعدات بريد PHP] تحتاج لأن يتم تكوينها بشكل صحيح. إذا كنت لا تريد أيا من ميزات البريد الإلكتروني، يمكنك تعطيلها هنا.",
"config-email-user": "تفعيل البريد الإلكتروني من المستخدم إلى مستخدم آخر",
"config-email-user-help": "يتيح لكل المستخدمين إرسال رسائل بريد إلكتروني إلى بعضهم البعض إذا فعَّلوا هذا الخيار في تفضيلاتهم.",
"config-email-usertalk": "فعل إخطارات صفحات نقاش المستخدمين",
"config-pcre-no-utf8": "<strong>Erru fatal:</strong> Paez que'l módulu PCRE de PHP foi compiláu ensin el soporte PCRE_UTF8.\nMediaWiki requier compatibilidá con UTF_8 pa furrular correutamente.",
"config-memory-raised": "El parámetru <code>memory_limit</code> de PHP ye $1. Auméntase a $2.",
"config-memory-bad": "<strong>Alvertencia:</strong>: el parámetru <code>memory_limit</code> de PHP ye $1.\nProbablemente sía demasiáu baxu.\n¡La instalación puede fallar!",
- "config-apc": "[https://secure.php.net/apc APC] ta instaláu",
- "config-apcu": "[https://secure.php.net/apcu APCu] ta instaláu",
+ "config-apc": "[https://www.php.net/apc APC] ta instaláu",
+ "config-apcu": "[https://www.php.net/apcu APCu] ta instaláu",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ta instaláu",
- "config-no-cache-apcu": "<strong>Atención:</strong> Nun pudo alcontrase [https://secure.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLa caché d'oxetos nun ta activada.",
+ "config-no-cache-apcu": "<strong>Atención:</strong> Nun pudo alcontrase [https://www.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLa caché d'oxetos nun ta activada.",
"config-mod-security": "<strong>Alvertencia:</strong> El to servidor web tien activáu [https://modsecurity.org/mod_security]/mod_security2 .Munches de les sos configuraciones comunes pueden causar problemes a MediaWiki o otru software que dexe a los usuarios publicar conteníu arbitrario. De ser posible, tendríes de desactivalo. Si non, consulta la [https://modsecurity.org/documentation/ mod_security documentation] o contacta col alministrador del to servidor si atopes erros aleatorios.",
"config-diff3-bad": "Nun s'alcontró la utilidá de comparanza de testos GNU diff3. Pues inorar esto pel momentu pero podríes tener problemes d'edición davezu.",
"config-git": "Alcontróse'l software de control de versiones Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki ye compatible colos siguientes sistemes de bases de datos:\n\n$1\n\nSi nun atopes na llista el sistema de base de datos que tas intentando utilizar, sigue les instrucciones enllazaes enriba p'activar la compatibilidá.",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ye un sistema comercial de base de datos empresariales pa Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Cómo compilar PHP con compatibilidá pa SQLSRV])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ye un sistema comercial de base de datos empresariales pa Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Cómo compilar PHP con compatibilidá pa SQLSRV])",
"config-header-mysql": "Axustes de MariaDB/MySQL",
"config-header-postgres": "Axustes de PostgreSQL",
"config-header-sqlite": "Axustes de SQLite",
"config-license-cc-choose": "Escoyer una llicencia Creative Commons personalizada",
"config-email-settings": "Configuración de corréu electrónicu",
"config-enable-email": "Activar el corréu electrónicu de salida",
- "config-enable-email-help": "Si quies que'l corréu electrónicu funcione, les [https://secure.php.net/manual/en/mail.configuration.php preferencies de corréu de PHP] tienen de tar configuraes correutamente.\nSi nun quies les funciones de corréu electrónicu, puedes desactivales equí.",
+ "config-enable-email-help": "Si quies que'l corréu electrónicu funcione, les [https://www.php.net/manual/en/mail.configuration.php preferencies de corréu de PHP] tienen de tar configuraes correutamente.\nSi nun quies les funciones de corréu electrónicu, puedes desactivales equí.",
"config-email-user": "Activar el corréu electrónicu ente usuarios",
"config-logo": "URL del logo:",
"config-instantcommons": "Activar Instant Commons",
"config-pcre-no-utf8": "'''Фаталь хата'''. PHP өсөн PCRE модуле PCRE_UTF8 менән яраҡлыштырылмаған.\nMediaWiki дөрөҫ эшләһен өсөн UTF-8 талап ителә.",
"config-memory-raised": "Хәтер сикләнгән PHP (<code>memory_limit</code>) $1 $2 тиклем арттырылған.",
"config-memory-bad": "'''Иғтибар:''' PHP күләме <code>memory_limit</code> $1 тәшкил итә.\nБәлки, был саманан тыш аҙҙыр. \nҠуйылыштың уңышһыҙлыҡҡа осрауы бар!",
- "config-apc": "[https://secure.php.net/apc APC] урынлаштырылды",
+ "config-apc": "[https://www.php.net/apc APC] урынлаштырылды",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] урынлыштырылды",
- "config-no-cache-apcu": "'''Иғтибар:''' [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] табылманы йәки [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nОбъекттарҙы кэшлау һүндереләсәк..",
+ "config-no-cache-apcu": "'''Иғтибар:''' [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] табылманы йәки [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nОбъекттарҙы кэшлау һүндереләсәк..",
"config-mod-security": "<strong>Иғтибар</strong>: һеҙҙең веб-серверығыҙҙа [https://modsecurity.org/ mod_security]/mod_security2 ҡабыҙылған. Уның күп кенә стандарт көйләүҙәре MediaWiki йәки бүтән ПО ҡулланыусыларға серверға ирекле контент ебәрегрә мөмкинлек буйынса проблемалар тыуҙырыуы мөмкин.\nКөтөлмәгән хаталарға тап булһағыҙ, ошонда [https://modsecurity.org/documentation/ документации mod_security]йәки үҙегеҙҙең хостинг-провайдерығыҙға мөрәжәғәт итегеҙ.",
"config-diff3-bad": "GNU diff3 табылманы.",
"config-git": "Git өлгөләрҙе контролләү системаһы табылды: <code>$1</code>.",
"config-type-mysql": "MySQL (йәки тура килгән)",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki -ла түбәндәге СУБД бар:\n\n$1\n\nӘгәр мәғлүмәт һаҡлау системаһын исемлектә күрмәһәгеҙ, рөхсәт алыу өсөн өҫтәге һылтанмалағы инструкция буйынса эш итегеҙ.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] — MediaWiki-ҙың иҫ яҡшы эшләгән төп мәғлүмәттәр базаһы. MediaWiki шулай уҡ MySQL-тап килгән [{{int:version-db-mariadb-url}} MariaDB] һәм [{{int:version-db-percona-url}} Percona Server] менән эшләй. ([https://secure.php.net/manual/ru/mysql.installation.php MySQL-ярҙамында PHP туплау инструкцияһы])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — СУБД-ның популяр открыткаһы, MySQL өсөн альтернатива.\nТөҙәтелмәгән хаталар булыуы мөмкин, эш схемаһында ҡулланыу тәҡдим ителмәй. ([https://secure.php.net/manual/en/pgsql.installation.php PostgreSQL рөхсәт ителгән РНР йыйыу инструкцияһы]).",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] — MediaWiki-ҙың иҫ яҡшы эшләгән төп мәғлүмәттәр базаһы. MediaWiki шулай уҡ MySQL-тап килгән [{{int:version-db-mariadb-url}} MariaDB] һәм [{{int:version-db-percona-url}} Percona Server] менән эшләй. ([https://www.php.net/manual/ru/mysql.installation.php MySQL-ярҙамында PHP туплау инструкцияһы])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — СУБД-ның популяр открыткаһы, MySQL өсөн альтернатива.\nТөҙәтелмәгән хаталар булыуы мөмкин, эш схемаһында ҡулланыу тәҡдим ителмәй. ([https://www.php.net/manual/en/pgsql.installation.php PostgreSQL рөхсәт ителгән РНР йыйыу инструкцияһы]).",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — яҡшы һәм еңел мәғлүмәт базаһы системаһы. ([http://www.php.net/manual/ru/pdo.installation.php собрать PHP SQLite] PDO менән эшләй торған инструкция)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] — предприятие масштабындаға коммерция базыһы. ([http://www.php.net/manual/ru/oci8.installation.php OCI8 ярҙамындағы РНР нисек йыйырға])",
- "config-dbsupport-mssql": "* [{{int:version-db-oracle-url}} Oracle] — предприятие масштабындаға Windows өсөн коммерция базыһы. ([https://secure.php.net/manual/en/sqlsrv.installation.php OCI8 ярҙамындағы РНР нисек йыйырға])",
+ "config-dbsupport-mssql": "* [{{int:version-db-oracle-url}} Oracle] — предприятие масштабындаға Windows өсөн коммерция базыһы. ([https://www.php.net/manual/en/sqlsrv.installation.php OCI8 ярҙамындағы РНР нисек йыйырға])",
"config-header-mysql": "MySQL көйләү",
"config-header-postgres": "PostgreSQL көйләү",
"config-header-sqlite": "SQLite көйләү",
"config-pcre-no-utf8": "'''Фатальная памылка''': модуль PCRE для PHP скампіляваны без падтрымкі PCRE_UTF8.\nMediaWiki патрабуе падтрымкі UTF-8 для слушнай працы.",
"config-memory-raised": "Абмежаваньне на даступную для PHP памяць <code>memory_limit</code> было падвышанае з $1 да $2.",
"config-memory-bad": "'''Папярэджаньне:''' памер PHP <code>memory_limit</code> складае $1.\nВерагодна, гэта вельмі мала.\nУсталяваньне можа быць няўдалым!",
- "config-apc": "[https://secure.php.net/apc APC] усталяваны",
- "config-apcu": "[https://secure.php.net/apcu APCu] ўсталяваны",
+ "config-apc": "[https://www.php.net/apc APC] усталяваны",
+ "config-apcu": "[https://www.php.net/apcu APCu] ўсталяваны",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] усталяваны",
- "config-no-cache-apcu": "<strong>Папярэджаньне:</strong> ня знойдзеныя [https://secure.php.net/apcu APCu] ці [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]. Кэшаваньне аб’ектаў адключанае.",
+ "config-no-cache-apcu": "<strong>Папярэджаньне:</strong> ня знойдзеныя [https://www.php.net/apcu APCu] ці [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]. Кэшаваньне аб’ектаў адключанае.",
"config-mod-security": "'''Папярэджаньне''': на Вашым ўэб-сэрверы ўключаны [https://modsecurity.org/ mod_security]. У выпадку няслушнай наладцы, ён можа стаць прычынай праблемаў для MediaWiki ці іншага праграмнага забесьпячэньня, якое дазваляе ўдзельнікам дасылаць на сэрвэр любы зьмест.\nГлядзіце [https://modsecurity.org/documentation/ дакумэнтацыю mod_security] ці зьвярніцеся ў падтрымку Вашага хосту, калі ў Вас узьнікаюць выпадковыя праблемы.",
"config-diff3-bad": "Праграма параўнаньня тэксту GNU diff3 ня знойдзеная. Вы можаце праігнараваць гэта, але можаце сутыкнуцца з канфліктамі рэдагаваньня больш часта.",
"config-git": "Знойдзеная сыстэма канстролю вэрсіяў Git: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki падтрымлівае наступныя сыстэмы базаў зьвестак:\n\n$1\n\nКалі Вы ня бачыце сыстэму базаў зьвестак, якую Вы спрабуеце выкарыстоўваць ў сьпісе ніжэй, перайдзіце па спасылцы інструкцыі, якая знаходзіцца ніжэй, каб уключыць падтрымку.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] зьяўляецца галоўнай мэтай MediaWiki і падтрымліваецца найлепш. MediaWiki таксама працуе з [{{int:version-db-mysql-url}} MySQL] і [{{int:version-db-percona-url}} Percona Server], якія сумяшчальныя з MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Як скампіляваць PHP з падтрымкай MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — папулярная сыстэма базы зьвестак з адкрытым кодам, якая зьяўляецца альтэрнатывай MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Як кампіляваць PHP з падтрымкай PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — невялікая сыстэма базы зьвестак, якая мае вельмі добрую падтрымку. ([https://secure.php.net/manual/en/pdo.installation.php Як кампіляваць PHP з падтрымкай SQLite], выкарыстоўвае PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] зьяўляецца камэрцыйнай прафэсійнай базай зьвестак. ([https://secure.php.net/manual/en/oci8.installation.php Як скампіляваць PHP з падтрымкай OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — камэрцыйная база зьвестак для Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Як скампіляваць PHP з падтрымкай SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] зьяўляецца галоўнай мэтай MediaWiki і падтрымліваецца найлепш. MediaWiki таксама працуе з [{{int:version-db-mysql-url}} MySQL] і [{{int:version-db-percona-url}} Percona Server], якія сумяшчальныя з MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Як скампіляваць PHP з падтрымкай MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — папулярная сыстэма базы зьвестак з адкрытым кодам, якая зьяўляецца альтэрнатывай MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Як кампіляваць PHP з падтрымкай PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — невялікая сыстэма базы зьвестак, якая мае вельмі добрую падтрымку. ([https://www.php.net/manual/en/pdo.installation.php Як кампіляваць PHP з падтрымкай SQLite], выкарыстоўвае PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] зьяўляецца камэрцыйнай прафэсійнай базай зьвестак. ([https://www.php.net/manual/en/oci8.installation.php Як скампіляваць PHP з падтрымкай OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — камэрцыйная база зьвестак для Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Як скампіляваць PHP з падтрымкай SQLSRV])",
"config-header-mysql": "Налады MariaDB/MySQL",
"config-header-postgres": "Налады PostgreSQL",
"config-header-sqlite": "Налады SQLite",
"config-license-help": "Шматлікія адкрытыя вікі публікуюць увесь унёсак у праект на ўмовах [https://freedomdefined.org/Definition вольнай ліцэнзіі].\nГэта дазваляе ствараць эфэкт супольнай уласнасьці і садзейнічае доўгатэрміноваму ўнёску.\nДля прыватных і карпаратыўных вікі гэта не зьяўляецца неабходнасьцю.\n\nКалі Вы жадаеце выкарыстоўваць тэкст зь Вікіпэдыі, і жадаеце, каб Вікіпэдыя магла прымаць тэксты, скапіяваныя з Вашай вікі, Вам неабходна выбраць ліцэнзію <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nРаней Вікіпэдыя выкарыстоўвала ліцэнзію GNU Free Documentation.\nЯна ўсё яшчэ дзейнічае, але яна ўтрымлівае некаторыя моманты, якія ўскладняюць паўторнае выкарыстаньне і інтэрпрэтацыю матэрыялаў.",
"config-email-settings": "Налады электроннай пошты",
"config-enable-email": "Дазволіць выходзячыя электронныя лісты",
- "config-enable-email-help": "Калі вы жадаеце, каб працавала электронная пошта, неабходна сканфігураваць PHP [https://secure.php.net/manual/en/mail.configuration.php адпаведным чынам].\nКалі вы не жадаеце выкарыстоўваць магчымасьці электроннай пошты, вы можаце іх адключыць тут.",
+ "config-enable-email-help": "Калі вы жадаеце, каб працавала электронная пошта, неабходна сканфігураваць PHP [https://www.php.net/manual/en/mail.configuration.php адпаведным чынам].\nКалі вы не жадаеце выкарыстоўваць магчымасьці электроннай пошты, вы можаце іх адключыць тут.",
"config-email-user": "Дазволіць электронную пошту для сувязі паміж удзельнікамі",
"config-email-user-help": "Дазволіць усім удзельнікам дасылаць адзін аднаму электронныя лісты, калі ўключаная адпаведная магчымасьць ў іх наладах.",
"config-email-usertalk": "Уключыць абвяшчэньні пра паведамленьні на старонцы абмеркаваньня",
"config-pcre-no-utf8": "<strong>Фатално:</strong> Модулът PCRE на PHP изглежда е компилиран без поддръжка на PCRE_UTF8.\nЗа да функционира правилно, МедияУики изисква поддръжка на UTF-8.",
"config-memory-raised": "<code>memory_limit</code> на PHP е $1, увеличаване до $2.",
"config-memory-bad": "<strong>Внимание:</strong> <code>memory_limit</code> на PHP е $1.\nСтойността вероятно е твърде ниска.\nВъзможно е инсталацията да се провали!",
- "config-apc": "[https://secure.php.net/apc APC] е инсталиран",
- "config-apcu": "[https://secure.php.net/apcu APCu] е инсталиран",
+ "config-apc": "[https://www.php.net/apc APC] е инсталиран",
+ "config-apcu": "[https://www.php.net/apcu APCu] е инсталиран",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] е инсталиран",
- "config-no-cache-apcu": "<strong>Внимание:</strong> [https://secure.php.net/apcu APCu] и [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] не могат да бъдат открити.\nКеширането на обекти не е активирано.",
+ "config-no-cache-apcu": "<strong>Внимание:</strong> [https://www.php.net/apcu APCu] и [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] не могат да бъдат открити.\nКеширането на обекти не е активирано.",
"config-mod-security": "<strong>Предупреждение:</strong> [https://modsecurity.org/ mod_security]/mod_security2 е включено на вашия уеб сървър. Много от обичайните му конфигурации пораждат проблеми с МедияУики и друг софтуер, който позволява публикуване на произволно съдържание.\nАко е възможно, моля изключете го. В противен случай се обърнете към [https://modsecurity.org/documentation/ документацията на mod_security] или се свържете с поддръжката на хостинга си, ако се сблъскате със случайни грешки.",
"config-diff3-bad": "Инструментът за сравняване на текст GNU diff3 не беше намерен. Можете да игнорирате това за сега, но конфликтите при редактиране може да бъдат по-чести.",
"config-git": "Налична е системата за контрол на версиите Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL (или съвместима)",
"config-type-mssql": "Microsoft SQL сървър",
"config-support-info": "МедияУики поддържа следните системи за бази от данни:\n\n$1\n\nАко не виждате желаната за използване система в списъка по-долу, следвайте инструкциите за активиране на поддръжка по-горе.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] е най-важна за МедияУики и се поддържа най-добре. МедияУики работи също така с [{{int:version-db-mysql-url}} MySQL] и [{{int:version-db-percona-url}} Percona Server], които са съвместими с MariaDB.\n([https://secure.php.net/manual/en/mysqli.installation.php Как се компилира PHP с поддръжка на MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] е популярна система за управление на бази от данни, алтернатива на MySQL. ([https://secure.php.net/manual/bg/pgsql.installation.php Как се компилира PHP с поддръжка на PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] е олекотена система за бази от данни, която е много добре поддържана. ([https://secure.php.net/manual/en/pdo.installation.php Как се компилира PHP с поддръжка на SQLite], използва PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] е комерсиална корпоративна база от данни. ([https://secure.php.net/manual/en/oci8.installation.php Как се компилира PHP с поддръжка на OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] е комерсиална корпоративна база от данни за Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Как да се компилира PHP с поддръжка на SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] е най-важна за МедияУики и се поддържа най-добре. МедияУики работи също така с [{{int:version-db-mysql-url}} MySQL] и [{{int:version-db-percona-url}} Percona Server], които са съвместими с MariaDB.\n([https://www.php.net/manual/en/mysqli.installation.php Как се компилира PHP с поддръжка на MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] е популярна система за управление на бази от данни, алтернатива на MySQL. ([https://www.php.net/manual/bg/pgsql.installation.php Как се компилира PHP с поддръжка на PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] е олекотена система за бази от данни, която е много добре поддържана. ([https://www.php.net/manual/en/pdo.installation.php Как се компилира PHP с поддръжка на SQLite], използва PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] е комерсиална корпоративна база от данни. ([https://www.php.net/manual/en/oci8.installation.php Как се компилира PHP с поддръжка на OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] е комерсиална корпоративна база от данни за Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Как да се компилира PHP с поддръжка на SQLSRV])",
"config-header-mysql": "Настройки на MariaDB/MySQL",
"config-header-postgres": "Настройки за PostgreSQL",
"config-header-sqlite": "Настройки за SQLite",
"config-license-help": "Много публични уикита поставят всички приноси под [https://freedomdefined.org/Definition/Bg свободен лиценз].\nТова помага за създаването на усещане за общност и насърчава дългосрочните приноси. \nТова не е необходимо като цяло за частно или корпоративно уики.\n\nАко искате да използвате текстове от Уикипедия, и искате Уикипедия да може да приема текстове, копирани от вашето уики, трябва да изберете лиценз <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nЛицензът за свободна документация на GNU е старият лиценз на съдържанието на Уикипедия.\nТой все още е валиден лиценз, но някои негови условия са трудни за разбиране и правят по-сложни повторното използване и интерпретацията.",
"config-email-settings": "Настройки за е-поща",
"config-enable-email": "Разрешаване на изходящи е-писма",
- "config-enable-email-help": "За да работят възможностите за използване на е-поща, необходимо е [https://secure.php.net/manual/en/mail.configuration.ph настройките за поща на PHP] да бъдат конфигурирани правилно.\nАко няма да се използват услугите за е-поща в уикито, те могат да бъдат изключени тук.",
+ "config-enable-email-help": "За да работят възможностите за използване на е-поща, необходимо е [https://www.php.net/manual/en/mail.configuration.ph настройките за поща на PHP] да бъдат конфигурирани правилно.\nАко няма да се използват услугите за е-поща в уикито, те могат да бъдат изключени тук.",
"config-email-user": "Позволяване на потребителите да си изпращат е-писма през уикито",
"config-email-user-help": "Позволяване на потребителите да си изпращат е-писма ако са разрешили това в настройките си.",
"config-email-usertalk": "Оповестяване при промяна на потребителската беседа",
"config-env-php": "পিএইচপি $1 ইন্সটল করা হয়েছে।",
"config-env-hhvm": "HHVM $1 ইনস্টল করা হয়েছে।",
"config-memory-raised": "পিএইচপির <code>memory_limit</code> হচ্ছে $1, বৃদ্ধি পেয়ে $2 হয়েছে।",
- "config-apc": "[https://secure.php.net/apc এপিসি] ইনস্টল করা হয়েছে",
- "config-apcu": "[https://secure.php.net/apcu এপিসিu] ইনস্টল করা হয়েছে",
+ "config-apc": "[https://www.php.net/apc এপিসি] ইনস্টল করা হয়েছে",
+ "config-apcu": "[https://www.php.net/apcu এপিসিu] ইনস্টল করা হয়েছে",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ইনস্টল করা হয়েছে",
"config-diff3-bad": "GNU diff3 লেখা তুলনা করার কার্যকারিতা পাওয়া যায়নি। আপনি এখন এটিকে উপেক্ষা করতে পারেন, তবে এতে আপনি আরো ঘন ঘন সম্পাদনা সংঘাতের মধ্যে পড়তে পারেন।",
"config-git": "Git সংস্করণের নিয়ন্ত্রণ সফটওয়্যার পাওয়া গেছে: <code>$1</code>।",
"config-oracle-temp-ts": "সাময়কি টেবিলস্পেস:",
"config-type-mysql": "MariaDB, MySQL, বা উপযুক্তগুলি",
"config-type-mssql": "মাইক্রোসফট SQL সার্ভার",
- "config-dbsupport-postgres": "* MySQL-এর বিকল্প হিসেবে [{{int:version-db-postgres-url}} PostgreSQL] হচ্ছে একটি জনপ্রিয় মুক্ত উৎসের ডাটাবেস ব্যবস্থা। ([https://secure.php.net/manual/en/pgsql.installation.php PostgreSQL সমর্থনসহ কিভাবে PHP সঙ্কলন করবেন])",
+ "config-dbsupport-postgres": "* MySQL-এর বিকল্প হিসেবে [{{int:version-db-postgres-url}} PostgreSQL] হচ্ছে একটি জনপ্রিয় মুক্ত উৎসের ডাটাবেস ব্যবস্থা। ([https://www.php.net/manual/en/pgsql.installation.php PostgreSQL সমর্থনসহ কিভাবে PHP সঙ্কলন করবেন])",
"config-header-mysql": "MariaDB/MySQL সেটিং",
"config-header-postgres": "PostgreSQL সেটিংস",
"config-header-sqlite": "SQLite সেটিংস",
"config-pcre-no-utf8": "'''Fazi groñs ''': evit doare eo bet kempunet modulenn PCRE PHP hep ar skor PCRE_UTF8.\nEzhomm en deus MediaWiki eus UTF-8 evit mont plaen en-dro.",
"config-memory-raised": "<code>memory_limit</code> ar PHP zo $1, kemmet e $2.",
"config-memory-bad": "'''Diwallit :''' Da $1 emañ arventenn <code>memory_limit</code> PHP.\nRe izel eo moarvat.\nMarteze e c'hwito ar staliadenn !",
- "config-apc": "Staliet eo [https://secure.php.net/apc APC]",
- "config-apcu": "Staliet eo [https://secure.php.net/apcu APCu]",
+ "config-apc": "Staliet eo [https://www.php.net/apc APC]",
+ "config-apcu": "Staliet eo [https://www.php.net/apcu APCu]",
"config-wincache": "Staliet eo [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]",
- "config-no-cache-apcu": "<strong>Taolit pled :</strong> N'eus ket bet gallet kavout [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] pe [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nN'eo ket gweredekaet ar c'hrubuilhañ traezoù.",
+ "config-no-cache-apcu": "<strong>Taolit pled :</strong> N'eus ket bet gallet kavout [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] pe [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nN'eo ket gweredekaet ar c'hrubuilhañ traezoù.",
"config-mod-security": "<strong>Taolit pled :</strong> Gweredekaet eo [https://modsecurity.org/ mod_security]/mod_security2 gant ho servijer web. Ma n'eo ket kfluniet mat e c'hall tegas trubuilhoù da MediaWiki ha meziantoù all a aotre implijerien da ouzhpennañ danvez evel ma karont.\nE kement ha m'eo posupl e tlefe bezañ diweredekaet. A-hend-all, sellit ouzh [https://modsecurity.org/documentation/ mod_security an teuliadur] pe kit e darempred gant skoazell ho herberc'hier m'en em gavit gant fazioù dargouezhek.",
"config-diff3-bad": "N'eo ket bet kavet GNU diff3.",
"config-git": "Kavet eo bet ar meziant kontrolliñ adstummoù Git : <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "Skoret eo ar reizhiadoù diaz titouroù da-heul gant MediaWiki :\n\n$1\n\nMa ne welit ket amañ dindan ar reizhiad diaz titouroù a fell deoc'h ober ganti, heuilhit an titouroù a-us (s.o. al liammoù) evit gweredekaat ar skorañ.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] eo an dibab kentañ evit MediaWiki hag an hini skoret ar gwellañ. Mont a ra MediaWiki en-dro gant [{{int:version-db-mariadb-url}} MariaDB] ha [{{int:version-db-percona-url}} Percona Server] ivez, kenglotus o-daou gant MySQL. ([https://secure.php.net/manual/en/mysqli.installation.php Penaos kempunañ PHP gant skor MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] zo anezhi ur reizhiad diaz roadennoù frank a wirioù brudet-mat a c'haller ober gantañ e plas MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Penaos kempunañ PHP gant skor PostgreSQL])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] eo an dibab kentañ evit MediaWiki hag an hini skoret ar gwellañ. Mont a ra MediaWiki en-dro gant [{{int:version-db-mariadb-url}} MariaDB] ha [{{int:version-db-percona-url}} Percona Server] ivez, kenglotus o-daou gant MySQL. ([https://www.php.net/manual/en/mysqli.installation.php Penaos kempunañ PHP gant skor MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] zo anezhi ur reizhiad diaz roadennoù frank a wirioù brudet-mat a c'haller ober gantañ e plas MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Penaos kempunañ PHP gant skor PostgreSQL])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] zo anezhi ur reizhiad diaz roadennoù skañv skoret eus ar c'hentañ. ([http://www.php.net/manual/en/pdo.installation.php Penaos kempunañ PHP gant skor SQLite], implijout a ra PDO)",
"config-dbsupport-oracle": "* Un embregerezh kenwerzhel diaz roadennoù eo [{{int:version-db-oracle-url}} Oracle]. ([http://www.php.net/manual/en/oci8.installation.php Penaos kempunañ PHP gant skor OCI8])",
- "config-dbsupport-mssql": "* Un embregerezh kenwerzhel diaz roadennoù evit Windows eo [{{int:version-db-mssql-url}} Microsoft SQL Server]. ([https://secure.php.net/manual/en/sqlsrv.installation.php Penaos kempunañ PHP gant skor SQLSRV])",
+ "config-dbsupport-mssql": "* Un embregerezh kenwerzhel diaz roadennoù evit Windows eo [{{int:version-db-mssql-url}} Microsoft SQL Server]. ([https://www.php.net/manual/en/sqlsrv.installation.php Penaos kempunañ PHP gant skor SQLSRV])",
"config-header-mysql": "Arventennoù MySQL",
"config-header-postgres": "Arventennoù PostgreSQL",
"config-header-sqlite": "Arventennoù SQLite",
"config-no-db": "Ne mogu pronaći pogodan upravljački program za bazu podataka! Morate ga instalirati za PHP-bazu.\n{{PLURAL:$2|Podržana je sljedeća vrsta|Podržane su sljedeće vrste}} baze podataka: $1.\n\nAko se sami kompajlirali PHP, omogućite klijent baze podataka u postavkama koristeći, naprimjer, <code>./configure --with-mysqli</code>.\nAko ste instalirali PHP iz paketa za Debian ili Ubuntu, onda također morate instalirati, naprimjer, paket <code>php-mysql</code>.",
"config-memory-raised": "<code>memory_limit</code> za PHP iznosi $1, povišen na $2.",
"config-memory-bad": "<strong>Upozorenje:</strong> <code>memory_limit</code> za PHP iznosi $1.\nOvo je vjerovatno premalo.\nInstalacija možda neće uspjeti!",
- "config-apc": "[https://secure.php.net/apc APC] je instaliran",
- "config-apcu": "[https://secure.php.net/apcu APCu] je instaliran",
+ "config-apc": "[https://www.php.net/apc APC] je instaliran",
+ "config-apcu": "[https://www.php.net/apcu APCu] je instaliran",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je instaliran",
"config-diff3-bad": "GNU diff3 nije pronađen.",
"config-git": "Pronađen je Git program za kontrolu verzija: <code>$1</code>.",
"config-pcre-old": "<strong>Error fatal:</strong> Cal el PCRE $1 o superior.\nEl binari PHP que utilitzeu està enllaçat amb el PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Més informació].",
"config-memory-raised": "El <code>memory_limit</code> del PHP és $1 i s'ha aixecat a $2.",
"config-memory-bad": "<strong>Avís:</strong> El <code>memory_limit</code> del PHP és $1.\nAixò és probablement massa baix.\nLa instal·lació pot fallar!",
- "config-apc": "L'[https://secure.php.net/apc APC] està instal·lat",
- "config-apcu": "L'[https://secure.php.net/apcu APCu] està instal·lat",
+ "config-apc": "L'[https://www.php.net/apc APC] està instal·lat",
+ "config-apcu": "L'[https://www.php.net/apcu APCu] està instal·lat",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] està instal·lat",
"config-diff3-bad": "No s'ha trobat el GNU diff3. Podeu ignorar-ho per ara, però us podeu trobar amb conflictes d'edició més habitualment.",
"config-git": "S'ha trobat el programari de control de versions Git: <code>$1</code>.",
"config-license-cc-choose": "Selecció d'una llicència personalitzada de Creative Commons",
"config-email-settings": "Paràmetres del correu electrònic",
"config-enable-email": "Habilita el correu sortint",
- "config-enable-email-help": "Si voleu que el correu electrònic funcioni, cal configurar [https://secure.php.net/manual/en/mail.configuration.php PHP's els paràmetres de correu] correctament.\nSi no voleu cap funcionalitat de correu, podeu inhabilitar-ho.",
+ "config-enable-email-help": "Si voleu que el correu electrònic funcioni, cal configurar [https://www.php.net/manual/en/mail.configuration.php PHP's els paràmetres de correu] correctament.\nSi no voleu cap funcionalitat de correu, podeu inhabilitar-ho.",
"config-email-user": "Habilita el correu electrònic usuari-a-usuari",
"config-email-user-help": "Permet que tots els usuaris puguin enviar-se correu si ho han habilitat a les preferències.",
"config-email-usertalk": "Habilita la notificació a la pàgina de discussió de l'usuari",
"config-page-existingwiki": "ویکی پێشوو",
"config-restart": "بەڵێ، دەستی پێ بکەرەوە",
"config-env-php": "PHP $1 دامەزراوە.",
- "config-apc": "[https://secure.php.net/apc APC] دامەزراوە",
+ "config-apc": "[https://www.php.net/apc APC] دامەزراوە",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] دامەزراوە",
"config-db-type": "جۆری داتابەیس:",
"config-db-host": "خانەخوێی داتابەیس:",
"config-pcre-no-utf8": "<strong>Kritická chyba:</strong> PHP modul PCRE byl zřejmě přeložen bez podpory PCRE_UTF8.\nMediaWiki vyžaduje ke správné funkci podporu UTF-8.",
"config-memory-raised": "<code>memory_limit</code> v PHP byl nastaven na $1, zvýšen na $2.",
"config-memory-bad": "<strong>Upozornění:</strong> <code>memory_limit</code> je v PHP nastaven na $1.\nTo je pravděpodobně příliš málo.\nInstalace může selhat!",
- "config-apc": "Je nainstalováno [https://secure.php.net/apc APC]",
- "config-apcu": "Je nainstalováno [https://secure.php.net/apcu APCu]",
+ "config-apc": "Je nainstalováno [https://www.php.net/apc APC]",
+ "config-apcu": "Je nainstalováno [https://www.php.net/apcu APCu]",
"config-wincache": "Je nainstalováno [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]",
- "config-no-cache-apcu": "<strong>Upozornění:</strong> Nebylo nalezeno [https://secure.php.net/apcu APCu], \nani [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nCachování objektů není povoleno.",
+ "config-no-cache-apcu": "<strong>Upozornění:</strong> Nebylo nalezeno [https://www.php.net/apcu APCu], \nani [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nCachování objektů není povoleno.",
"config-mod-security": "<strong>Upozornění:</strong> váš webový server má zapnuto [https://modsecurity.org/ mod_security]/mod_security2. Mnoho běžných konfigurací bude způsobovat potíže MediaWiki a dalším programům, které umožňují ukládat libovolný obsah.\nPokud je to možné, mělo by se to vypnout. Jinak se v případě, že narazíte na náhodné chyby, podívejte do [https://modsecurity.org/documentation/ dokumentace mod_security] nebo kontaktujte technickou podporu vašeho poskytovatele.",
"config-diff3-bad": "Nebyl nalezen nástroj pro porovnávání textu GNU diff3. Můžete to zatím ignorovat, ale je možné, že budete častěji narážet na editační konflikty.",
"config-git": "Nalezen software pro správu verzí Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki podporuje následující databázové systémy:\n\n$1\n\nPokud v nabídce níže nevidíte databázový systém, který chcete použít, musíte pro zapnutí podpory následovat instrukce odkázané výše.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je pro MediaWiki hlavní platformou a je podporováno nejlépe. MediaWiki pracuje také s [{{int:version-db-mysql-url}} MySQL] a [{{int:version-db-percona-url}} Percona Server], které jsou s MariaDB kompatibilní. ([https://secure.php.net/manual/en/mysqli.installation.php Jak zkompilovat PHP s podporou MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je populární otevřený databázový systém používaný jako alternativa k MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Jak přeložit PHP s podporou PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je velmi dobře podporovaný odlehčený databázový systém. ([https://secure.php.net/manual/en/pdo.installation.php Jak přeložit PHP s podporou SQLite], používá PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je komerční podniková databáze. ([https://secure.php.net/manual/en/oci8.installation.php Jak přeložit PHP s podporou OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je komerční podniková databáze pro Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Jak přeložit PHP s podporou SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je pro MediaWiki hlavní platformou a je podporováno nejlépe. MediaWiki pracuje také s [{{int:version-db-mysql-url}} MySQL] a [{{int:version-db-percona-url}} Percona Server], které jsou s MariaDB kompatibilní. ([https://www.php.net/manual/en/mysqli.installation.php Jak zkompilovat PHP s podporou MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je populární otevřený databázový systém používaný jako alternativa k MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Jak přeložit PHP s podporou PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je velmi dobře podporovaný odlehčený databázový systém. ([https://www.php.net/manual/en/pdo.installation.php Jak přeložit PHP s podporou SQLite], používá PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je komerční podniková databáze. ([https://www.php.net/manual/en/oci8.installation.php Jak přeložit PHP s podporou OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je komerční podniková databáze pro Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Jak přeložit PHP s podporou SQLSRV])",
"config-header-mysql": "Nastavení MariaDB/MySQL",
"config-header-postgres": "Nastavení PostgreSQL",
"config-header-sqlite": "Nastavení SQLite",
"config-license-help": "Mnoho veřejných wiki všechny příspěvky zveřejňuje pod některou [https://freedomdefined.org/Definition/Cs svobodnou licencí].\nTo pomáhá vytvořit duch komunitního vlastnictví a povzbuzuje dlouhodobé přispívání.\nTo obecně není potřeba u soukromé nebo firemní wiki.\n\nPokud chcete být schopni používat text z Wikipedie a chcete, aby Wikipedie byla schopna přijímat text okopírovaný z vaší wiki, měli byste zvolit <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nDříve Wikipedie používala GNU Free Documentation License.\nGFDL je platná licence, ale složité jí porozumět.\nTaké je komplikované používat obsah licencovaný pod GFDL.",
"config-email-settings": "Nastavení e-mailu",
"config-enable-email": "Zapnout odchozí e-mail",
- "config-enable-email-help": "Pokud chcete, aby e-mail fungoval, je potřeba správně nakonfigurovat [https://secure.php.net/manual/en/mail.configuration.php e-mailová nastavení PHP].\nPokud nechcete žádné e-mailové funkce, můžete je zde vypnout.",
+ "config-enable-email-help": "Pokud chcete, aby e-mail fungoval, je potřeba správně nakonfigurovat [https://www.php.net/manual/en/mail.configuration.php e-mailová nastavení PHP].\nPokud nechcete žádné e-mailové funkce, můžete je zde vypnout.",
"config-email-user": "Umožnit vzájemné e-maily mezi uživateli",
"config-email-user-help": "Umožní všem uživatelům posílat si navzájem e-maily, pokud si to zapnout v uživatelském nastavení.",
"config-email-usertalk": "Umožnit notifikace k uživatelským diskusím",
"config-env-hhvm": "HHVM $1 je wjinastalowóné",
"config-memory-raised": "Paraméter PHP <code>memory_limit</code> $1 òstôł zwikszony do $2.",
"config-apc": "[Http://www.php.net/apc APC] je wjinstalowóny",
- "config-apcu": "[https://secure.php.net/apcu APCu] je wjinstalowóny",
+ "config-apcu": "[https://www.php.net/apcu APCu] je wjinstalowóny",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je wjinstalowóny",
"config-diff3-bad": "Felënk GNU diff3.",
"config-mysql-innodb": "InnoDB",
"config-restart": "Ja, genstart den",
"config-env-php": "PHP $1 er installeret.",
"config-env-hhvm": "HHVM $1 er installeret.",
- "config-apc": "[https://secure.php.net/apc APC] er installeret",
- "config-apcu": "[https://secure.php.net/apcu APCu] er installeret",
+ "config-apc": "[https://www.php.net/apc APC] er installeret",
+ "config-apcu": "[https://www.php.net/apcu APCu] er installeret",
"config-db-type": "Databasetype:",
"config-db-host": "Databasevært:",
"config-db-name": "Databasenavn (ingen bindestreg):",
"config-pcre-no-utf8": "'''Fataler Fehler:''' Das PHP-Modul PCRE scheint ohne PCRE_UTF8-Unterstützung kompiliert worden zu sein.\nMediaWiki benötigt die UTF-8-Unterstützung, um fehlerfrei lauffähig zu sein.",
"config-memory-raised": "Der PHP-Parameter <code>memory_limit</code> betrug $1 und wurde auf $2 erhöht.",
"config-memory-bad": "'''Warnung:''' Der PHP-Parameter <code>memory_limit</code> beträgt $1.\nDieser Wert ist wahrscheinlich zu niedrig.\nDer Installationsvorgang könnte eventuell scheitern!",
- "config-apc": "[https://secure.php.net/apc APC] ist installiert",
- "config-apcu": "[https://secure.php.net/apcu APCu] ist installiert",
+ "config-apc": "[https://www.php.net/apc APC] ist installiert",
+ "config-apcu": "[https://www.php.net/apcu APCu] ist installiert",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ist installiert",
- "config-no-cache-apcu": "<strong>Warnung:</strong> [https://secure.php.net/apcu APCu] oder [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] konnten nicht gefunden werden.\nDer Objektcache ist nicht aktiviert.",
+ "config-no-cache-apcu": "<strong>Warnung:</strong> [https://www.php.net/apcu APCu] oder [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] konnten nicht gefunden werden.\nDer Objektcache ist nicht aktiviert.",
"config-mod-security": "'''Warnung:''' Auf dem Webserver wurde [https://modsecurity.org/ ModSecurity] aktiviert. Sofern falsch konfiguriert, kann dies zu Problemen mit MediaWiki sowie anderer Software auf dem Server führen und es Benutzern ermöglichen, beliebige Inhalte im Wiki einzustellen.\nFür weitere Informationen empfehlen wir die [https://modsecurity.org/documentation/ Dokumentation zu ModSecurity] oder den Kontakt zum Hoster, sofern Fehler auftreten.",
"config-diff3-bad": "Das Textvergleichswerkzeug GNU diff3 wurde nicht gefunden. Du kannst diese Meldung vorerst ignorieren, jedoch kann es vermehrt zu Bearbeitungskonflikten kommen.",
"config-git": "Die Versionsverwaltungssoftware „Git“ wurde gefunden: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki unterstützt die folgenden Datenbanksysteme:\n\n$1\n\nSofern unterhalb nicht das Datenbanksystem angezeigt wird, das verwendet werden soll, muss dieses noch verfügbar gemacht werden. Oben ist zu jedem unterstützten Datenbanksystem ein Link zur entsprechenden Anleitung vorhanden.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] ist das von MediaWiki primär unterstützte Datenbanksystem. MediaWiki funktioniert auch mit [{{int:version-db-mysql-url}} MySQL] und [{{int:version-db-percona-url}} Percona Server], die MariaDB-kompatibel sind. ([https://secure.php.net/manual/en/mysqli.installation.php Anleitung zur Kompilierung von PHP mit MySQL-Unterstützung])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ist ein beliebtes Open-Source-Datenbanksystem und eine Alternative zu MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Anleitung zur Kompilierung von PHP mit PostgreSQL-Unterstützung])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] ist das von MediaWiki primär unterstützte Datenbanksystem. MediaWiki funktioniert auch mit [{{int:version-db-mysql-url}} MySQL] und [{{int:version-db-percona-url}} Percona Server], die MariaDB-kompatibel sind. ([https://www.php.net/manual/en/mysqli.installation.php Anleitung zur Kompilierung von PHP mit MySQL-Unterstützung])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ist ein beliebtes Open-Source-Datenbanksystem und eine Alternative zu MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Anleitung zur Kompilierung von PHP mit PostgreSQL-Unterstützung])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] ist ein verschlanktes Datenbanksystem, das auch gut unterstützt wird ([https://www.php.net/manual/de/pdo.installation.php Anleitung zur Kompilierung von PHP mit SQLite-Unterstützung], verwendet PHP Data Objects (PDO))",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] ist eine kommerzielle Unternehmensdatenbank ([https://www.php.net/manual/en/oci8.installation.php Anleitung zur Kompilierung von PHP mit OCI8-Unterstützung])",
"config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ist eine gewerbliche Unternehmensdatenbank für Windows. ([https://www.php.net/manual/de/sqlsrv.installation.php Anleitung zur Kompilierung von PHP mit SQLSRV-Unterstützung])",
"config-license-help": "Viele öffentliche Wikis publizieren alle Beiträge unter einer [https://freedomdefined.org/Definition/De freien Lizenz.]\nDies trägt dazu bei, ein Gefühl von Gemeinschaft zu schaffen, und ermutigt zu längerfristiger Mitarbeit.\nHingegen ist im Allgemeinen eine freie Lizenz auf geschlossenen Wikis nicht notwendig.\n\nSofern man Texte aus der Wikipedia verwenden möchte und umgekehrt, sollte die Lizenz {{int:config-license-cc-by-sa}} gewählt werden.\n\nDie Wikipedia nutzte vormals die GNU-Lizenz für freie Dokumentation (GFDL).\nDie GFDL ist eine gültige Lizenz, die allerdings schwer zu verstehen ist.\nEs ist zudem schwierig, gemäß dieser Lizenz lizenzierte Inhalte wiederzuverwenden.",
"config-email-settings": "E-Mail-Einstellungen",
"config-enable-email": "Ausgehende E-Mails ermöglichen",
- "config-enable-email-help": "Sofern die E-Mail-Funktionen genutzt werden sollen, müssen die entsprechenden [https://secure.php.net/manual/en/mail.configuration.php PHP-E-Mail-Einstellungen] richtig konfiguriert werden.\nFür den Fall, dass die E-Mail-Funktionen nicht benötigt werden, können sie hier deaktiviert werden.",
+ "config-enable-email-help": "Sofern die E-Mail-Funktionen genutzt werden sollen, müssen die entsprechenden [https://www.php.net/manual/en/mail.configuration.php PHP-E-Mail-Einstellungen] richtig konfiguriert werden.\nFür den Fall, dass die E-Mail-Funktionen nicht benötigt werden, können sie hier deaktiviert werden.",
"config-email-user": "E-Mail-Versand von Benutzer zu Benutzer aktivieren",
"config-email-user-help": "Allen Benutzern ermöglichen, sich gegenseitig E-Mails zu schicken, sofern sie es in ihren Einstellungen aktiviert haben.",
"config-email-usertalk": "Benachrichtigungen zu Änderungen an Benutzerdiskussionsseiten ermöglichen",
"config-pcre-no-utf8": "<strong>Κρίσιμο:</strong> Το PCRE module της PHP φαίνεται να έχει μεταγλωττιστεί χωρίς υποστήριξη PCRE_UTF8.\nΓια τη σωστή λειτουργία του MediaWiki απαιτείται υποστήριξη UTF-8.",
"config-memory-raised": "Το <code>memory_limit</code> της PHP είναι $1 και αυξήθηκε σε $2.",
"config-memory-bad": "<strong>Προειδοποίηση:</strong> το <code>memory_limit</code> της PHP είναι $1.\nΑυτή η τιμή είναι πιθανώς πολύ χαμηλή.\n\nΗ εγκατάσταση ενδέχεται να αποτύχει!",
- "config-apc": "Το [https://secure.php.net/apc APC] είναι εγκατεστημένο",
- "config-apcu": "Το [https://secure.php.net/apcu APCu] είναι εγκατεστημένο",
+ "config-apc": "Το [https://www.php.net/apc APC] είναι εγκατεστημένο",
+ "config-apcu": "Το [https://www.php.net/apcu APCu] είναι εγκατεστημένο",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension Το WinCache] είναι εγκατεστημένο",
- "config-no-cache-apcu": "<strong>Προειδοποίηση:</strong> Αποτυχία εύρεσης του [https://secure.php.net/apcu APCu] ή του [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nΗ αρχειοθέτηση αντικειμένων δεν έχει ενεργοποιηθεί.",
+ "config-no-cache-apcu": "<strong>Προειδοποίηση:</strong> Αποτυχία εύρεσης του [https://www.php.net/apcu APCu] ή του [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nΗ αρχειοθέτηση αντικειμένων δεν έχει ενεργοποιηθεί.",
"config-diff3-bad": "Το GNU diff3 δεν βρέθηκε.",
"config-git": "Βρέθηκε το λογισμικό ελέγχου εκδόσεων Git: <code>$1</code>.",
"config-git-bad": "Το λογισμικό ελέγχου εκδόσεων Git δεν βρέθηκε.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "To MediaWiki υποστηρίζει τα ακόλουθα συστήματα βάσεων δεδομένων:\n\n$1\n\nΑν δεν εμφανίζεται παρακάτω το σύστημα βάσης δεδομένων που θέλετε να χρησιμοποιήσετε, τότε ακολουθήστε τις οδηγίες στον παραπάνω σύνδεσμο για να ενεργοποιήσετε την υποστήριξη.",
- "config-dbsupport-mysql": "* Η [{{int:version-db-mysql-url}} MySQL] είναι ο πρωταρχικός στόχος για το MediaWiki και υποστηρίζεται καλύτερα. Το MediaWiki συνεργάζεται επίσης με τη [{{int:version-db-mariadb-url}} MariaDB] και το [{{int:version-db-percona-url}} διακομιστή Percona], που είναι όλα συμβατά με MySQL. ([https://secure.php.net/manual/en/mysqli.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη MySQL])",
- "config-dbsupport-postgres": "* Η [{{int:version-db-postgres-url}} PostgreSQL] είναι δημοφιλές σύστημα βάσης δεδομένων ανοικτού κώδικα ως εναλλακτική της MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη PostgreSQL])",
+ "config-dbsupport-mysql": "* Η [{{int:version-db-mysql-url}} MySQL] είναι ο πρωταρχικός στόχος για το MediaWiki και υποστηρίζεται καλύτερα. Το MediaWiki συνεργάζεται επίσης με τη [{{int:version-db-mariadb-url}} MariaDB] και το [{{int:version-db-percona-url}} διακομιστή Percona], που είναι όλα συμβατά με MySQL. ([https://www.php.net/manual/en/mysqli.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη MySQL])",
+ "config-dbsupport-postgres": "* Η [{{int:version-db-postgres-url}} PostgreSQL] είναι δημοφιλές σύστημα βάσης δεδομένων ανοικτού κώδικα ως εναλλακτική της MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη PostgreSQL])",
"config-dbsupport-sqlite": "* Η [{{int:version-db-sqlite-url}} SQLite] είναι ένα ελαφρύ σύστημα βάσης δεδομένων που υποστηρίζεται πολύ καλά. ([http://www.php.net/manual/en/pdo.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη SQLite], χρησιμοποιεί PDO)",
"config-dbsupport-oracle": "* Η [{{int:version-db-oracle-url}} Oracle] είναι εμπορική βάση δεδομένων για επιχειρήσεις. ([http://www.php.net/manual/en/oci8.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη OCI8])",
- "config-dbsupport-mssql": "* Ο [{{int:version-db-mssql-url}} Microsoft SQL Server] είναι εμπορική βάση δεδομένων για επιχειρήσεις που λειτουργεί σε Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη SQLSRV])",
+ "config-dbsupport-mssql": "* Ο [{{int:version-db-mssql-url}} Microsoft SQL Server] είναι εμπορική βάση δεδομένων για επιχειρήσεις που λειτουργεί σε Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη SQLSRV])",
"config-header-mysql": "Ρυθμίσεις MySQL",
"config-header-postgres": "Ρυθμίσεις PostgreSQL",
"config-header-sqlite": "Ρυθμίσεις SQLite",
"config-project-namespace-help": "Ακολουθώντας το παράδειγμα της Βικιπαίδειας, πολλά wiki διατηρούν τις σελίδες πολιτικής τους χωριστά από τις σελίδες περιεχομένου τους, σε έναν '''ονοματοχώρο έργου'''.\nΌλοι οι τίτλοι σελίδων σε αυτόν τον ονοματοχώρο ξεκινούν με συγκεκριμένο πρόθεμα, το οποίο μπορείτε να ορίσετε εδώ.\nΣυνήθως, αυτό το πρόθεμα παράγεται από το όνομα του wiki, αλλά δεν μπορεί να περιέχει χαρακτήρες στίξης όπως «#» ή «:».",
"config-admin-box": "Λογαριασμός διαχειριστή",
"config-admin-name": "Το όνομα χρήστη σας:",
- "config-admin-password": "Î\9aÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82:",
+ "config-admin-password": "ΣÏ\85νθημαÏ\84ικÏ\8c:",
"config-admin-password-confirm": "Επανάληψη κωδικού πρόσβασης:",
"config-admin-help": "Εισαγάγετε το προτιμώμενο όνομα χρήστη εδώ, για παράδειγμα «Γιάννης Ιστολόγιος». \nΑυτό είναι το όνομα που θα χρησιμοποιείτε για να συνδέεστε στο wiki.",
"config-admin-name-blank": "Εισαγάγετε όνομα χρήστη διαχειριστή.",
"config-pcre-no-utf8": "<strong>Fatal:</strong> PHP's PCRE module seems to be compiled without PCRE_UTF8 support.\nMediaWiki requires UTF-8 support to function correctly.",
"config-memory-raised": "PHP's <code>memory_limit</code> is $1, raised to $2.",
"config-memory-bad": "<strong>Warning:</strong> PHP's <code>memory_limit</code> is $1.\nThis is probably too low.\nThe installation may fail!",
- "config-apc": "[https://secure.php.net/apc APC] is installed",
- "config-apcu": "[https://secure.php.net/apcu APCu] is installed",
+ "config-apc": "[https://www.php.net/apc APC] is installed",
+ "config-apcu": "[https://www.php.net/apcu APCu] is installed",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] is installed",
- "config-no-cache-apcu": "<strong>Warning:</strong> Could not find [https://secure.php.net/apcu APCu] or [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObject caching is not enabled.",
+ "config-no-cache-apcu": "<strong>Warning:</strong> Could not find [https://www.php.net/apcu APCu] or [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObject caching is not enabled.",
"config-mod-security": "<strong>Warning:</strong> Your web server has [https://modsecurity.org/ mod_security]/mod_security2 enabled. Many common configurations of this will cause problems for MediaWiki and other software that allows users to post arbitrary content.\nIf possible, this should be disabled. Otherwise, refer to [https://modsecurity.org/documentation/ mod_security documentation] or contact your host's support if you encounter random errors.",
"config-diff3-bad": "GNU diff3 text comparison utility not found. You can ignore this for now, but might run into edit conflicts more frequently.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki supports the following database systems:\n\n$1\n\nIf you do not see the database system you are trying to use listed below, then follow the instructions linked above to enable support.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] is the primary target for MediaWiki and is best supported. MediaWiki also works with [{{int:version-db-mysql-url}} MySQL] and [{{int:version-db-percona-url}} Percona Server], which are MariaDB compatible. ([https://secure.php.net/manual/en/mysqli.installation.php How to compile PHP with MySQL support])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is a popular open source database system as an alternative to MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is a lightweight database system that is very well supported. ([https://secure.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], uses PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is a commercial enterprise database. ([https://secure.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is a commercial enterprise database for Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] is the primary target for MediaWiki and is best supported. MediaWiki also works with [{{int:version-db-mysql-url}} MySQL] and [{{int:version-db-percona-url}} Percona Server], which are MariaDB compatible. ([https://www.php.net/manual/en/mysqli.installation.php How to compile PHP with MySQL support])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is a popular open source database system as an alternative to MySQL. ([https://www.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is a lightweight database system that is very well supported. ([https://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], uses PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is a commercial enterprise database. ([https://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is a commercial enterprise database for Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
"config-header-mysql": "MariaDB/MySQL settings",
"config-header-postgres": "PostgreSQL settings",
"config-header-sqlite": "SQLite settings",
"config-license-help": "Many public wikis put all contributions under a [https://freedomdefined.org/Definition free license].\nThis helps to create a sense of community ownership and encourages long-term contribution.\nIt is not generally necessary for a private or corporate wiki.\n\nIf you want to be able to use text from Wikipedia, and you want Wikipedia to be able to accept text copied from your wiki, you should choose <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia previously used the GNU Free Documentation License.\nThe GFDL is a valid license, but it is difficult to understand.\nIt is also difficult to reuse content licensed under the GFDL.",
"config-email-settings": "Email settings",
"config-enable-email": "Enable outbound email",
- "config-enable-email-help": "If you want email to work, [https://secure.php.net/manual/en/mail.configuration.php PHP's mail settings] need to be configured correctly.\nIf you do not want any email features, you can disable them here.",
+ "config-enable-email-help": "If you want email to work, [https://www.php.net/manual/en/mail.configuration.php PHP's mail settings] need to be configured correctly.\nIf you do not want any email features, you can disable them here.",
"config-email-user": "Enable user-to-user email",
"config-email-user-help": "Allow all users to send each other email if they have enabled it in their preferences.",
"config-email-usertalk": "Enable user talk page notification",
"config-env-bad": "La medio estas kontrolita.\nNe eblas instali MediaWiki.",
"config-env-php": "PHP $1 estas instalita.",
"config-env-hhvm": "HHVM $1 estas instalita.",
- "config-apc": "[https://secure.php.net/apc APC] estas instalita",
- "config-apcu": "[https://secure.php.net/apcu APCu] estas instalita",
+ "config-apc": "[https://www.php.net/apc APC] estas instalita",
+ "config-apcu": "[https://www.php.net/apcu APCu] estas instalita",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] estas instalita",
"config-diff3-bad": "GNU diff3 ne estis trovita.",
"config-db-type": "Tipo de datumbazo:",
"config-pcre-no-utf8": "'''Error fatal ''': Parece que el módulo PCRE de PHP fue compilado sin el soporte PCRE_UTF8.\nMediaWiki requiere compatibilidad con UTF-8 para funcionar correctamente.",
"config-memory-raised": "El parámetro <code>memory_limit</code> de PHP es $1. Se aumenta a $2.",
"config-memory-bad": "<strong>Advertencia:</strong> el parámetro <code>memory_limit</code> de PHP es $1.\nProbablemente sea demasiado bajo.\n¡La instalación puede fallar!",
- "config-apc": "[https://secure.php.net/apc APC] está instalado",
- "config-apcu": "[https://secure.php.net/apcu APCu] está instalado",
+ "config-apc": "[https://www.php.net/apc APC] está instalado",
+ "config-apcu": "[https://www.php.net/apcu APCu] está instalado",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] está instalado",
- "config-no-cache-apcu": "<strong>Atención:</strong> no se pudo encontrar [https://secure.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nEl almacenamiento en antememoria de objetos no está activado.",
+ "config-no-cache-apcu": "<strong>Atención:</strong> no se pudo encontrar [https://www.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nEl almacenamiento en antememoria de objetos no está activado.",
"config-mod-security": "<strong>Advertencia:</strong> tu servidor web tiene activado [https://modsecurity.org/ mod_security]/mod_security2. Muchas de sus configuraciones comunes pueden causar problemas a MediaWiki u otro software que permita a los usuarios publicar contenido arbitrario. De ser posible, deberías desactivarlo. Si no, consulta la [https://modsecurity.org/documentation/ documentación de mod_security] o contacta con el administrador de tu servidor si encuentras errores aleatorios.",
"config-diff3-bad": "GNU diff3 no se encuentra.",
"config-git": "Se encontró el software de control de versiones Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki es compatible con los siguientes sistemas de bases de datos:\n\n$1\n\nSi no encuentras en el listado el sistema de base de datos que estás intentando utilizar, sigue las instrucciones enlazadas arriba para activar la compatibilidad.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] es la base de datos mayoritaria para MediaWiki y la que goza de mayor compatibilidad. MediaWiki también funciona con [{{int:version-db-myslql-url}} MySQL] y [{{int:version-db-percona-url}} Percona Server], que son compatibles con MariaDB. ([https://secure.php.net/manual/es/mysql.installation.php Cómo compilar PHP con compatibilidad MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] es un sistema de base de datos popular de código abierto, alternativa a MySQL. ([https://secure.php.net/manual/es/pgsql.installation.php Cómo compilar PHP con compatibilidad PostgreSQL]).",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] es un sistema de base de datos ligero con gran compatibilidad con MediaWiki. ([https://secure.php.net/manual/en/pdo.installation.php Cómo compilar PHP con compatibilidad SQLite], usando PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] es una base de datos comercial a nivel empresarial. ([https://secure.php.net/manual/en/oci8.installation.php Cómo compilar PHP con compatibilidad con OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] es un sistema comercial de base de datos empresariales para Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Cómo compilar PHP con compatibilidad con SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] es la base de datos mayoritaria para MediaWiki y la que goza de mayor compatibilidad. MediaWiki también funciona con [{{int:version-db-myslql-url}} MySQL] y [{{int:version-db-percona-url}} Percona Server], que son compatibles con MariaDB. ([https://www.php.net/manual/es/mysql.installation.php Cómo compilar PHP con compatibilidad MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] es un sistema de base de datos popular de código abierto, alternativa a MySQL. ([https://www.php.net/manual/es/pgsql.installation.php Cómo compilar PHP con compatibilidad PostgreSQL]).",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] es un sistema de base de datos ligero con gran compatibilidad con MediaWiki. ([https://www.php.net/manual/en/pdo.installation.php Cómo compilar PHP con compatibilidad SQLite], usando PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] es una base de datos comercial a nivel empresarial. ([https://www.php.net/manual/en/oci8.installation.php Cómo compilar PHP con compatibilidad con OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] es un sistema comercial de base de datos empresariales para Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Cómo compilar PHP con compatibilidad con SQLSRV])",
"config-header-mysql": "Configuración de MariaDB/MySQL",
"config-header-postgres": "Configuración de PostgreSQL",
"config-header-sqlite": "Configuración de SQLite",
"config-license-help": "Muchos wikis públicos ponen todas las contribuciones bajo una [https://freedomdefined.org/Definition licencia libre].\nEsto ayuda a crear un sentido de propiedad comunitaria y alienta la contribución a largo plazo.\nEsto no es generalmente necesario para un wiki privado o corporativo.\n\nSi deseas poder utilizar texto de Wikipedia, y deseas que Wikipedia pueda aceptar el texto copiado de tu wiki, debes elegir <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia utilizaba anteriormente la licencia de documentación libre de GNU (GFDL).\nLa GFDL es una licencia válida, pero es difícil de entender.\nTambién es difícil reutilizar el contenido licenciado bajo la GFDL.",
"config-email-settings": "Configuración de correo electrónico",
"config-enable-email": "Activar el envío de correos electrónicos",
- "config-enable-email-help": "Si quieres que el correo electrónico funcione, la [https://secure.php.net/manual/en/mail.configuration.php configuración PHP de correo electrónico] debe ser la correcta.\nSi no quieres ninguna funcionalidad de correo electrónico, puedes desactivarlas aquí.",
+ "config-enable-email-help": "Si quieres que el correo electrónico funcione, la [https://www.php.net/manual/en/mail.configuration.php configuración PHP de correo electrónico] debe ser la correcta.\nSi no quieres ninguna funcionalidad de correo electrónico, puedes desactivarlas aquí.",
"config-email-user": "Activar correo electrónico entre usuarios",
"config-email-user-help": "Permitir que todos los usuarios intercambien correos electrónicos si lo han activado en sus preferencias.",
"config-email-usertalk": "Activar notificaciones de páginas de discusión de usuarios",
"config-pcre-no-utf8": "<strong>Fatal:</strong> PHREko PCRE modulua PCRE_UTF8 ko laguntza gabe bildu da.\nMediaWiki-k UTF-8 euskarria behar du behar bezala funtziona dezan.",
"config-memory-raised": "PHP-ko <code>memory_limit</code> $1 da, $2-ra igota.",
"config-memory-bad": "<strong>Warning:</strong> PHPko <code>memory_limit</code> $1 da.\nZiurrenik hau oso baxua da.\nInstalazioa huts egin dezake!",
- "config-apc": "[https://secure.php.net/apc APC] instalatuta dago",
- "config-apcu": "[https://secure.php.net/apcu APCu] instalatuta dago",
+ "config-apc": "[https://www.php.net/apc APC] instalatuta dago",
+ "config-apcu": "[https://www.php.net/apcu APCu] instalatuta dago",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] instalatuta dago",
- "config-no-cache-apcu": "<strong>Warning:</strong> Ezin izan da [https://secure.php.net/apcu APCu] edo [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] aurkitu.\nObjektu katxea ez dago aktibatuta.",
+ "config-no-cache-apcu": "<strong>Warning:</strong> Ezin izan da [https://www.php.net/apcu APCu] edo [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] aurkitu.\nObjektu katxea ez dago aktibatuta.",
"config-mod-security": "<strong>Warning:</strong> Zure web zerbitzariak [https://modsecurity.org/mod_security] / mod_security2 aktibatu du. Honen konfigurazio komun asko sortu ahal dituzte arazoak MediaWikin eta beste software batzuetan, hautazko edukia argitaratzeko aukera ematen dutenei erabiltzaileei.\nAhal izanez gero, desgaitu egin beharko litzateke. Bestela, kontsultatu [https://modsecurity.org/documentation/ mod_security documentation] edo jarri harremanetan zure ostalariarekin ausazko akatsak aurkitzen badituzu.",
"config-diff3-bad": "GNU diff3 ez da aurkitu.",
"config-git": "Git bertsio-kontrol software aurkitu da: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki-k onartzen du hurrengo datu-base sistemak:\n\n$1\n\nListan ez baduzu ikusten erabili nahi duzun sistema, jarraitu goiko argibideak aktibatzeko.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] MediaWikiren lehenengoko helburua da eta primeran babesturik dago. MediaWikik ere [{{int:version-db-mariadb-url}} MariaDB]-rekin egiten du lan baita [{{int:version-db-percona-url}} Percona Server]-kin, MySQL-rekin balio dutenak. ([https://secure.php.net/manual/en/mysqli.installation.php How to compile PHP with MySQL support])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] iturburu irekiko datu basea sistema famatua da MySQL-rako alternatiba bezala. ([https://secure.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] MediaWikiren lehenengoko helburua da eta primeran babesturik dago. MediaWikik ere [{{int:version-db-mariadb-url}} MariaDB]-rekin egiten du lan baita [{{int:version-db-percona-url}} Percona Server]-kin, MySQL-rekin balio dutenak. ([https://www.php.net/manual/en/mysqli.installation.php How to compile PHP with MySQL support])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] iturburu irekiko datu basea sistema famatua da MySQL-rako alternatiba bezala. ([https://www.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] oso ondo onartzen duen datu-basearen sistema arina da.\n ([http://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], uses PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] enpresa komertzial baten datu-basea da. ([http://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] Windows-entzako enpresa komertzial baten datu-basea da. ([https://secure.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] Windows-entzako enpresa komertzial baten datu-basea da. ([https://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
"config-header-mysql": "MySQL hobespenak",
"config-header-postgres": "PostgreSQL hobespenak",
"config-header-sqlite": "SQLite hobespenak",
"config-pcre-no-utf8": "<strong>مخرب:</strong> به نظر میرسد پودمان پیسیآراییِ پیاچپی بدون پشتیبانی پیسیآرایی_یوتیاف۸ تهیه شدهاست.\nمدیاویکی برای درست عمل کردن نیازمند پشتیبانی یوتیاف-۸ است.",
"config-memory-raised": "PHP's <code>memory_limit</code>, نسخهٔ $1 است، به نسخهٔ $2 ارتقاء داده شدهاست.",
"config-memory-bad": "'''هشدار:''' PHP's <code>memory_limit</code> نسخهٔ $1 است.\nاین ممکن است خیلی پایین باشد.\nممکن است نصب با مشکل روبهرو شود.",
- "config-apc": "[https://secure.php.net/apc APC] نصب شدهاست.",
- "config-apcu": "[https://secure.php.net/apcu APCu] نصب شدهاست",
+ "config-apc": "[https://www.php.net/apc APC] نصب شدهاست.",
+ "config-apcu": "[https://www.php.net/apcu APCu] نصب شدهاست",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] نصب شدهاست.",
- "config-no-cache-apcu": "<strong>هشدار:</strong> پیوند [https://secure.php.net/apcu APCu] یا [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] یافت نشد. ذخیره شی فعال نیست.",
+ "config-no-cache-apcu": "<strong>هشدار:</strong> پیوند [https://www.php.net/apcu APCu] یا [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] یافت نشد. ذخیره شی فعال نیست.",
"config-mod-security": "'''هشدار:''' وب سرور شما [https://modsecurity.org/ mod_security] فعال است.اگر اشتباه پیکربندی شده باشد،می تواند باعث ایجاد مشکلاتی برای مدیاویکی یا دیگر نرمافزاری شود که به کاربران اجازه میدهد پیام دلخواه ارسال کنند.\nبه [https://modsecurity.org/documentation/ mod_security documentation] مراجعه کنید یا اگر با خطاهای اتفاقی مواجه شدید با پشتیبانی میزبان خود در تماس باشید.",
"config-diff3-bad": "ابزار مقایسه متن تفاوت۳ گنو پیدا نشد. میتوانید فعلاً این پیام را نادیده بگیرید، اما ممکن است به دفعات بیشتری دچار تعارض ویرایشی شوید.",
"config-git": "کنترل نسخهٔ نرمافزار گیت پیدا شد: <code>$1</code>.",
"config-type-mysql": "MariaDB، مایاسکیوال (یا سازگار)",
"config-type-mssql": "سرور مایکروسافت اسکیوال",
"config-support-info": "مدیاویکی سامانههای پایگاه اطلاعاتی زیر را حمایت میکند:\n$1\nاگر متوجه سامانه پایگاه اطلاعاتی که سعی دارید از فهرست زیر استفاده کنید، نمیشوید، بنابراین دستورالعملهای مرتبط در بالا را برای فعال کردن پشتیبانی دنبال کنید.",
- "config-dbsupport-mysql": "*[{{int:version-db-mariadb-url}} MariaDB] مهمترین هدف برای مدیاویکی است و بهترین پشتیبانی. مدیاویکی همچنین کار میکند با [{{int:version-db-mysql-url}} MariaDB] و [{{int:version-db-percona-url}} Percona Server] که با MariaDB سازگار هستند.([https://secure.php.net/manual/en/mysqli.installation.php چگونه php را با MariaDB کامپایل کنیم])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} پستگرسکیوال] یک سامانه پایگاه اطلاعات متنباز پرطرفدار است که جایگزینی برای مایاسکیوال است. ([https://secure.php.net/manual/en/pgsql.installation.php راهنمای تنظیم کردن پیاچپی به همراه پستگرسکیوال])",
- "config-dbsupport-sqlite": "*[{{int:version-db-sqlite-url}} اسکیولایت] یک سامانه پایگاه اطلاعاتی کم حجمی است که بسیار خوب پشتیبانی شدهاست.\n([https://secure.php.net/manual/en/pdo.installation.php چگونگی کامپایل پیاچپی با اسکیولایت]، از PDO استفاده میکند)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] یک پایگاه اطلاعاتی کار تبلیغاتی است.\n([https://secure.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] یک پایگاه اطلاعاتی موسسهٔ تبلیغاتی برای وینذوز است. ([https://secure.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
+ "config-dbsupport-mysql": "*[{{int:version-db-mariadb-url}} MariaDB] مهمترین هدف برای مدیاویکی است و بهترین پشتیبانی. مدیاویکی همچنین کار میکند با [{{int:version-db-mysql-url}} MariaDB] و [{{int:version-db-percona-url}} Percona Server] که با MariaDB سازگار هستند.([https://www.php.net/manual/en/mysqli.installation.php چگونه php را با MariaDB کامپایل کنیم])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} پستگرسکیوال] یک سامانه پایگاه اطلاعات متنباز پرطرفدار است که جایگزینی برای مایاسکیوال است. ([https://www.php.net/manual/en/pgsql.installation.php راهنمای تنظیم کردن پیاچپی به همراه پستگرسکیوال])",
+ "config-dbsupport-sqlite": "*[{{int:version-db-sqlite-url}} اسکیولایت] یک سامانه پایگاه اطلاعاتی کم حجمی است که بسیار خوب پشتیبانی شدهاست.\n([https://www.php.net/manual/en/pdo.installation.php چگونگی کامپایل پیاچپی با اسکیولایت]، از PDO استفاده میکند)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] یک پایگاه اطلاعاتی کار تبلیغاتی است.\n([https://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] یک پایگاه اطلاعاتی موسسهٔ تبلیغاتی برای وینذوز است. ([https://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
"config-header-mysql": "تنظیمات MariaDB/مایاسکیوال",
"config-header-postgres": "تنظیمات پستگراسکیوال",
"config-header-sqlite": "تنظیمات اسکیولایت",
"config-license-help": "بسیاری از وبگاهها ویرایشهای ها را با [https://freedomdefined.org/Definition اجازهنامهٔ آزاد] منتشر میکنند.\nاین کار به داشتن حس مالکیت جمعی کمک میکند و ویرایشهای طولانی مدت را اشاعه میدهد.\nاین برای ویکیهای خصوصی یا سازمانی الزامی نیست.\n\nاگر شما میخواهید از متون ویکیپدیا استفاده کنید، یا اینکه به ویکیپدیا اجازه دهید از متون شما استفاده کند باید متون خود را با <strong>{{int:config-license-cc-by-sa}}</strong> منتشر کنید.\n\nویکیپدیا در گذشته از اجازهنامهٔ دادههای آزاد گنو استفاده میکرد.\nاین اجازهنامه مورد قبول است، ولی فهم آن آسان نیست.\nهمچنین استفادهٔ دوباره از متون تحت اجازهنامهٔ دادههای آزاد گنو به سختی انجام میگیرد.",
"config-email-settings": "تنظیمات ایمیل",
"config-enable-email": "فعالسازی ایمیل خروجی",
- "config-enable-email-help": "اگر میخواهید ارسال ایمیل کار کند، [https://secure.php.net/manual/en/mail.configuration.php PHP's mail settings] نیازمند پیکربندی صحیح است.\nاگر هیچ قابلیت ایمیلی نمیخواهید، میتوانید آنها را اینجا غیرفعال کنید.",
+ "config-enable-email-help": "اگر میخواهید ارسال ایمیل کار کند، [https://www.php.net/manual/en/mail.configuration.php PHP's mail settings] نیازمند پیکربندی صحیح است.\nاگر هیچ قابلیت ایمیلی نمیخواهید، میتوانید آنها را اینجا غیرفعال کنید.",
"config-email-user": "فعال کردن ایمیل کاربر به کاربر",
"config-email-user-help": "به همهٔ کاربرانی که ارسال ایمیل را در ترجیحات خود فعال کردهاند، اجازه داده خواهد شد که به یکدیگر ایمیل ارسال کنند.",
"config-email-usertalk": "فعال کردن اطلاعرسانی صفحهٔ بحث کاربر",
"config-pcre-no-utf8": "<strong>Vakava virhe:</strong> PHP:n PCRE-moduuli näyttää olevan koottu ilman PCRE_UTF8 tukea.\nMediaWiki edellyttää UTF-8 tukea toimiakseen oikein.",
"config-memory-raised": "PHP:n <code>memory_limit</code> on $1, nostetaan arvoon $2.",
"config-memory-bad": "'''Varoitus:''' PHP:n <code>memory_limit</code> on $1.\nTämä on luultavasti liian alhainen.\nAsennus saattaa epäonnistua!",
- "config-apc": "[https://secure.php.net/apc APC] on asennettu",
- "config-apcu": "[https://secure.php.net/apcu APCu] on asennettu",
+ "config-apc": "[https://www.php.net/apc APC] on asennettu",
+ "config-apcu": "[https://www.php.net/apcu APCu] on asennettu",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] on asennettu",
"config-diff3-bad": "GNU diff3:a ei löytynyt.",
"config-git": "Löydetty Git-versionhallintaohjelmisto: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki tukee seuraavia tietokantajärjestelmiä:\n\n$1\n\nJos et näe tietokantajärjestelmää, jota yrität käyttää, listattuna alhaalla, seuraa yläpuolella olevia ohjeita tuen aktivoimiseksi.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] on MediaWikin ensisijainen kohde ja se on myös parhaiten tuettu. MediaWiki voi myös käyttää [{{int:version-db-mysql-url}} MySQL]- sekä [{{int:version-db-percona-url}} Percona Server]-järjestelmiä, jotka ovat MariaDB-yhteensopivia. ([https://secure.php.net/manual/en/mysqli.installation.php Miten käännetään PHP MySQL-tuella])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] on suosittu avoimen lähdekoodin tietokantajärjestelmä vaihtoehtona MySQL:lle. ([https://secure.php.net/manual/en/pgsql.installation.php Kuinka käännetään PHP PostgreSQL-tuella])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] on kevyt tietokantajärjestelmä, jota tuetaan hyvin. ([https://secure.php.net/manual/en/pdo.installation.php Miten käännetään PHP SQLite-tuella], käyttää PDO:ta)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] on kaupallinen yritystietokanta. ([https://secure.php.net/manual/en/oci8.installation.php Kuinka käännetään PHP OCI8-tuella])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] on kaupallinen yritystietokanta Windowsille. ([https://secure.php.net/manual/en/sqlsrv.installation.php Miten käännetään PHP SQLSRV-tuella])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] on MediaWikin ensisijainen kohde ja se on myös parhaiten tuettu. MediaWiki voi myös käyttää [{{int:version-db-mysql-url}} MySQL]- sekä [{{int:version-db-percona-url}} Percona Server]-järjestelmiä, jotka ovat MariaDB-yhteensopivia. ([https://www.php.net/manual/en/mysqli.installation.php Miten käännetään PHP MySQL-tuella])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] on suosittu avoimen lähdekoodin tietokantajärjestelmä vaihtoehtona MySQL:lle. ([https://www.php.net/manual/en/pgsql.installation.php Kuinka käännetään PHP PostgreSQL-tuella])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] on kevyt tietokantajärjestelmä, jota tuetaan hyvin. ([https://www.php.net/manual/en/pdo.installation.php Miten käännetään PHP SQLite-tuella], käyttää PDO:ta)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] on kaupallinen yritystietokanta. ([https://www.php.net/manual/en/oci8.installation.php Kuinka käännetään PHP OCI8-tuella])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] on kaupallinen yritystietokanta Windowsille. ([https://www.php.net/manual/en/sqlsrv.installation.php Miten käännetään PHP SQLSRV-tuella])",
"config-header-mysql": "MariaDB/MySQL-asetukset",
"config-header-postgres": "PostgreSQL-asetukset",
"config-header-sqlite": "SQLite-asetukset",
"config-license-help": "Monet julkiset wikit käyttävät muokkauksiin [https://freedomdefined.org/Definition vapaata lisenssiä].\nTämä auttaa luomaan yhteisöllisen omistajuuden tunteen ja kannustaa pitkäkestoiseen muokkaamiseen.\nSe ei ole yleensä tarpeen yksityiselle tai yrityksen wikille.\n\nJos haluat pystyä käyttämään tekstiä Wikipediasta, ja haluat Wikipedian pystyvän hyväksymään wikistäsi kopioitua tekstiä, sinun tulisi valita <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia käytti aiemmin GNU Free Documentation Licenseä.\nGFDL on kelvollinen lisenssi, mutta vaikea ymmärtää.\nOn myös vaikeaa käyttää uudelleen GFDL-lisensöityä sisältöä.",
"config-email-settings": "Sähköpostiasetukset",
"config-enable-email": "Ota käyttöön sähköpostien lähetys",
- "config-enable-email-help": "Jotta sähköposti toimii, [https://secure.php.net/manual/en/mail.configuration.php PHP:n sähköpostiasetukset] täytyy asettaa oikein.\nJos et halua käyttää sähköpostiominaisuuksia, ne voi kytkeä pois päältä tästä.",
+ "config-enable-email-help": "Jotta sähköposti toimii, [https://www.php.net/manual/en/mail.configuration.php PHP:n sähköpostiasetukset] täytyy asettaa oikein.\nJos et halua käyttää sähköpostiominaisuuksia, ne voi kytkeä pois päältä tästä.",
"config-email-user": "Ota käyttöön käyttäjältä käyttäjälle sähköpostit",
"config-email-user-help": "Salli käyttäjien lähettää sähköpostia toisilleen jos he ovat ottaneet käyttöön toiminnon asetuksissaan.",
"config-email-usertalk": "Ota käyttöön käyttäjien keskustelusivusta ilmoittaminen",
"config-pcre-no-utf8": "<strong>Erreur fatale :</strong> le module PCRE de PHP semble être compilé sans la prise en charge de PCRE_UTF8.\nMediaWiki a besoin de la gestion d’UTF-8 pour fonctionner correctement.",
"config-memory-raised": "Le paramètre <code>memory_limit</code> de PHP était à $1, porté à $2.",
"config-memory-bad": "<strong>Attention :</strong> Le paramètre <code>memory_limit</code> de PHP est à $1.\nCette valeur est probablement trop faible.\nIl est possible que l’installation échoue !",
- "config-apc": "[https://secure.php.net/apc APC] est installé",
- "config-apcu": "[https://secure.php.net/apcu APCu] est installé",
+ "config-apc": "[https://www.php.net/apc APC] est installé",
+ "config-apcu": "[https://www.php.net/apcu APCu] est installé",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] est installé",
- "config-no-cache-apcu": "<strong>Attention :</strong> impossible de trouver [https://secure.php.net/apcu APCu] ou [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLa mise en cache d’objets n’est pas activée.",
+ "config-no-cache-apcu": "<strong>Attention :</strong> impossible de trouver [https://www.php.net/apcu APCu] ou [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLa mise en cache d’objets n’est pas activée.",
"config-mod-security": "<strong>Attention :</strong> votre serveur web a activé [https://modsecurity.org/ mod_security]/mod_security2 . Dans plusieurs configurations communes cela pose des problèmes à MediaWiki ou à d’autres applications qui permettent aux utilisateurs de publier un contenu quelconque. \nSi possible, ceci devrait être désactivé. Sinon, reportez-vous à [https://modsecurity.org/documentation/ la documentation de mod_security] ou contactez l’assistance de votre hébergeur si vous rencontrez des erreurs aléatoires.",
"config-diff3-bad": "L’utilitaire de comparaison de texte GNU diff3 est introuvable. Vous pouvez l’ignorer pour le moment, mais cela peut provoquer des conflits de modification plus souvent.",
"config-git": "Logiciel de contrôle de version Git trouvé : <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki prend en charge ces systèmes de bases de données :\n\n$1\n\nSi vous ne voyez pas le système de base de données que vous essayez d’utiliser ci-dessous, alors suivez les instructions ci-dessus (voir liens) pour activer la prise en charge.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mysql-url}} MySQL] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec la prise en charge de MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire en ''source ouverte'' qui peut être une alternative à MySQL ([https://secure.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec la prise en charge de PostgreSQL]).",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien pris en charge ([https://secure.php.net/manual/en/pdo.installation.php Comment compiler PHP avec la prise en charge de SQLite], en utilisant PDO).",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] est un système commercial de gestion de base de données d’entreprise. ([https://secure.php.net/manual/en/oci8.installation.php Comment compiler PHP avec la prise en charge d’OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec la prise en charge de SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mysql-url}} MySQL] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec la prise en charge de MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire en ''source ouverte'' qui peut être une alternative à MySQL ([https://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec la prise en charge de PostgreSQL]).",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien pris en charge ([https://www.php.net/manual/en/pdo.installation.php Comment compiler PHP avec la prise en charge de SQLite], en utilisant PDO).",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] est un système commercial de gestion de base de données d’entreprise. ([https://www.php.net/manual/en/oci8.installation.php Comment compiler PHP avec la prise en charge d’OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec la prise en charge de SQLSRV])",
"config-header-mysql": "Paramètres de MariaDB/MySQL",
"config-header-postgres": "Paramètres de PostgreSQL",
"config-header-sqlite": "Paramètres de SQLite",
"config-license-help": "Beaucoup de wikis publics mettent l’ensemble des contributions sous une [https://freedomdefined.org/Definition/Fr licence libre].\nCela contribue à créer un sentiment d’appartenance à une communauté et encourage les contributions sur le long terme.\nCe n’est généralement pas nécessaire pour un wiki privé ou d’entreprise.\n\nSi vous souhaitez utiliser des textes de Wikipédia, et souhaitez que Wikipédia puisse réutiliser des textes copiés depuis votre wiki, vous devriez choisir <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipédia utilisait auparavant la Licence de Documentation Libre GNU (GFDL).\nC’est une licence valide, mais difficile à comprendre. \nIl est aussi difficile de réutiliser du contenu sous la licence GFDL.",
"config-email-settings": "Paramètres de courriel",
"config-enable-email": "Activer les courriels sortants",
- "config-enable-email-help": "Si vous souhaitez utiliser le courriel, vous devez avoir les [https://secure.php.net/manual/en/mail.configuration.php paramètres courriel de PHP] configurés correctement (texte en anglais).\nSi vous ne voulez pas du service de courriel, vous pouvez le désactiver ici.",
+ "config-enable-email-help": "Si vous souhaitez utiliser le courriel, vous devez avoir les [https://www.php.net/manual/en/mail.configuration.php paramètres courriel de PHP] configurés correctement (texte en anglais).\nSi vous ne voulez pas du service de courriel, vous pouvez le désactiver ici.",
"config-email-user": "Activer les courriers électroniques d'utilisateur à utilisateur",
"config-email-user-help": "Permet à tous les utilisateurs d'envoyer des courriels à d'autres utilisateurs si cela est activé dans leurs préférences.",
"config-email-usertalk": "Activer la notification des pages de discussion des utilisateurs",
"config-page-existingwiki": "Vouiqui ègzistent",
"config-env-php": "PHP $1 est enstalâ.",
"config-memory-raised": "Lo paramètre <code>memory_limit</code> de PHP ére a $1, portâ a $2.",
- "config-apc": "[https://secure.php.net/apc APC] est enstalâ",
+ "config-apc": "[https://www.php.net/apc APC] est enstalâ",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] est enstalâ",
"config-diff3-bad": "GNU diff3 entrovâblo.",
"config-db-type": "Tipo de bâsa de balyês :",
"config-pcre-no-utf8": "<strong>Erro fatal:</strong> Semella que o módulo PCRE do PHP foi compilado sen o soporte PCRE_UTF8.\nMediaWiki necesita soporte UTF-8 para funcionar correctamente.",
"config-memory-raised": "O parámetro <code>memory_limit</code> do PHP é $1. Aumentado a $2.",
"config-memory-bad": "<strong>Atención:<strong> O parámetro <code>memory_limit</code> do PHP é $1.\nProbablemente é un valor baixo de máis.\nA instalación pode fallar!",
- "config-apc": "[https://secure.php.net/apc APC] está instalado",
- "config-apcu": "[https://secure.php.net/apcu APCu] está instalado",
+ "config-apc": "[https://www.php.net/apc APC] está instalado",
+ "config-apcu": "[https://www.php.net/apcu APCu] está instalado",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] está instalado",
- "config-no-cache-apcu": "<strong>Advertencia:</strong> Non se puido atopar [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA caché de obxectos non está activada.",
+ "config-no-cache-apcu": "<strong>Advertencia:</strong> Non se puido atopar [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA caché de obxectos non está activada.",
"config-mod-security": "<strong>Atención:</strong> O seu servidor web ten o [https://modsecurity.org/ mod_security] activado. Se estivese mal configurado, pode causar problemas a MediaWiki ou calquera outro software que permita aos usuarios publicar contidos arbitrarios.\nOlle a [https://modsecurity.org/documentation/ documentación do mod_security] ou póñase en contacto co soporte do seu servidor se atopa erros aleatorios.",
"config-diff3-bad": "GNU diff3 non se atopou.",
"config-git": "Atopouse o software de control da versión de Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki soporta os seguintes sistemas de bases de datos:\n\n$1\n\nSe non ve listado a continuación o sistema de base de datos que intenta usar, siga as instrucións ligadas enriba para activar o soporte.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é o obxectivo principal para MediaWiki e é o que mellor soportado está. MediaWiki tamén funciona con [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que son compatibles con MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Como compilar PHP con compatibilidade MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] é un sistema de base de datos popular e de código aberto como alternativa a MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Como compilar PHP con compatibilidade PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] é un sistema de base de datos lixeiro moi ben soportado. ([https://secure.php.net/manual/en/pdo.installation.php Como compilar o PHP con soporte SQLite], emprega PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] é un sistema comercial de xestión de base de datos a nivel empresarial. ([https://secure.php.net/manual/en/oci8.installation.php Como compilar PHP con soporte OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] é un sistema comercial de xestión de base de datos de nivel empresarial para Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Como compilar o PHP con compatibilidade SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é o obxectivo principal para MediaWiki e é o que mellor soportado está. MediaWiki tamén funciona con [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que son compatibles con MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Como compilar PHP con compatibilidade MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] é un sistema de base de datos popular e de código aberto como alternativa a MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Como compilar PHP con compatibilidade PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] é un sistema de base de datos lixeiro moi ben soportado. ([https://www.php.net/manual/en/pdo.installation.php Como compilar o PHP con soporte SQLite], emprega PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] é un sistema comercial de xestión de base de datos a nivel empresarial. ([https://www.php.net/manual/en/oci8.installation.php Como compilar PHP con soporte OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] é un sistema comercial de xestión de base de datos de nivel empresarial para Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Como compilar o PHP con compatibilidade SQLSRV])",
"config-header-mysql": "Configuración MariaDB/MySQL",
"config-header-postgres": "Configuración do PostgreSQL",
"config-header-sqlite": "Configuración do SQLite",
"config-license-help": "Moitos wikis públicos liberan todas as súas contribucións baixo unha [https://freedomdefined.org/Definition/Gl licenza libre].\nIsto axuda a crear un sentido de propiedade comunitaria e anima a seguir contribuíndo durante moito tempo.\nXeralmente, non é necesario nos wikis privados ou de empresas.\n\nSe quere poder empregar textos da Wikipedia, así como que a Wikipedia poida aceptar textos copiados do seu wiki, escolla a licenza <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nA licenza de documentación libre de GNU era a licenza anterior da Wikipedia.\nMalia aínda ser unha licenza válida, é difícil de entender.\nTamén é difícil reusar contidos baixo esta licenza.",
"config-email-settings": "Configuración do correo electrónico",
"config-enable-email": "Activar os correos electrónicos de saída",
- "config-enable-email-help": "Se quere que o correo electrónico funcione, cómpre configurar os [https://secure.php.net/manual/en/mail.configuration.php parámetros PHP de correo] correctamente.\nSe non quere ningunha característica de correo electrónico, pode desactivalas aquí.",
+ "config-enable-email-help": "Se quere que o correo electrónico funcione, cómpre configurar os [https://www.php.net/manual/en/mail.configuration.php parámetros PHP de correo] correctamente.\nSe non quere ningunha característica de correo electrónico, pode desactivalas aquí.",
"config-email-user": "Activar o intercambio de correos electrónicos entre usuarios",
"config-email-user-help": "Permitir que todos os usuarios intercambien correos electrónicos, se o teñen activado nas súas preferencias.",
"config-email-usertalk": "Activar a notificación da páxina de conversa de usuario",
"config-pcre-no-utf8": "'''Fatale Fähler: S PHP-Modul PCRE isch schyns ohni PCRE_UTF8-Unterstitzig kompiliert wore.'''\nMediaWiki brucht d UTF-8-Unterstitzi zum fählerfrej lauffähig syy.",
"config-memory-raised": "Dr PHP-Parameter <code>memory_limit</code> lyt bi $1 un isch uf $2 uffegsetzt wore.",
"config-memory-bad": "'''Warnig:''' Dr PHP-Parameter <code>memory_limit</code> lyt bi $1.\nDää Wärt isch wahrschyns z nider.\nDr Inschtallationsvorgang chennt wäge däm fählschlaa!",
- "config-apc": "[https://secure.php.net/apc APC] isch inschtalliert",
+ "config-apc": "[https://www.php.net/apc APC] isch inschtalliert",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] isch inschtalliert",
"config-diff3-bad": "GNU diff3 isch nit gfunde wore.",
"config-imagemagick": "ImageMagick isch gfunde wore: <code>$1</code>.\nMiniaturaasichte vu Bilder sin megli, sobald s Uffelade vu Dateie aktiviert isch.",
"config-pcre-no-utf8": "<strong>שגיאה סופנית</strong>: נראה שמודול PCRE של PHP מהודר ללא תמיכה ב־PCRE_UTF8.\nמדיה־ויקי דורשת תמיכה ב־UTF-8 לפעילות נכונה.",
"config-memory-raised": "ערך האפשרות <code>memory_limit</code> של PHP הוא $1, הועלה ל־$2.",
"config-memory-bad": "'''אזהרה:''' ערך האפשרות <code>memory_limit</code> של PHP הוא $1.\nזה כנראה נמוך מדי.\nההתקנה עשויה להיכשל!",
- "config-apc": "[https://secure.php.net/apc APC] מותקן",
- "config-apcu": "[https://secure.php.net/apcu APCu] מותקן",
+ "config-apc": "[https://www.php.net/apc APC] מותקן",
+ "config-apcu": "[https://www.php.net/apcu APCu] מותקן",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] מותקן",
- "config-no-cache-apcu": "<strong>אזהרה:</strong> לא נמצא [https://secure.php.net/apcu APCu] או [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nמטמון עצמים לא מופעל.",
+ "config-no-cache-apcu": "<strong>אזהרה:</strong> לא נמצא [https://www.php.net/apcu APCu] או [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nמטמון עצמים לא מופעל.",
"config-mod-security": "'''אזהרה''': בשרת הווב שלך מופעל [https://modsecurity.org/ mod_security]. אם הוא לא מוגדר טוב, זה יכול לגרום לבעיות במדיה־ויקי ובתכנה אחרת שמאפשרת למשתמשים לשלוח תוכן שרירותי.\nיש לקרוא את [https://modsecurity.org/documentation/ התיעוד של mod_security] או ליצור קשר עם אנשי התמיכה של שירותי האירוח שלכם אם מופיעות לך שגיאות אקראיות.",
"config-diff3-bad": "GNU diff3 לא נמצא.",
"config-git": "נמצאה Git, תכנת בקרת התצורה: <code dir=\"ltr\">$1</code>.",
"config-type-mysql": "MariaDB, MySQL, או תואם",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "מדיה־ויקי תומכת במערכות מסדי הנתונים הבאות:\n\n$1\n\nאם אינך רואה את מסד הנתונים שלך ברשימה, יש לעקוב אחר ההוראות המקושרות לעיל כדי להפעיל את התמיכה.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] הוא היעד העיקרי עבור מדיה־ויקי ולו התמיכה הטובה ביותר. מדיה־ויקי עובדת גם עם [{{int:version-db-mysql-url}} MySQL] ועם [{{int:version-db-percona-url}} Percona Server], שתואמים ל־MariaDB. (ר׳ [https://secure.php.net/manual/en/mysql.installation.php how to compile PHP with MySQL support])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] הוא מסד נתונים נפוץ בקוד פתוח והוא נפוץ בתור חלופה ל־MySQL. (ר׳ [https://secure.php.net/manual/en/pgsql.installation.php how to compile PHP with PostgreSQL support]).",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] הוא מסד נתונים קליל עם תמיכה טובה מאוד. (ר׳ [https://secure.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], משתמש ב־PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] הוא מסד נתונים עסקי מסחרי. (ר׳ [https://secure.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] הוא מסד נתונים עסקי מסחרי לחלונות. ([https://secure.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] הוא היעד העיקרי עבור מדיה־ויקי ולו התמיכה הטובה ביותר. מדיה־ויקי עובדת גם עם [{{int:version-db-mysql-url}} MySQL] ועם [{{int:version-db-percona-url}} Percona Server], שתואמים ל־MariaDB. (ר׳ [https://www.php.net/manual/en/mysql.installation.php how to compile PHP with MySQL support])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] הוא מסד נתונים נפוץ בקוד פתוח והוא נפוץ בתור חלופה ל־MySQL. (ר׳ [https://www.php.net/manual/en/pgsql.installation.php how to compile PHP with PostgreSQL support]).",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] הוא מסד נתונים קליל עם תמיכה טובה מאוד. (ר׳ [https://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], משתמש ב־PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] הוא מסד נתונים עסקי מסחרי. (ר׳ [https://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] הוא מסד נתונים עסקי מסחרי לחלונות. ([https://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV support])",
"config-header-mysql": "הגדרות MariaDB/MySQL",
"config-header-postgres": "הגדרות PostgreSQL",
"config-header-sqlite": "הגדרות SQLite",
"config-license-help": "אתרי ויקי ציבוריים רבים מפרסמים את כל התרומות [https://freedomdefined.org/Definition ברישיון חופשי].\nזה עוזר ליצור תחושה של בעלות קהילתית ומעודד תרומה לאורך זמן.\nזה בדרך כלל לא נחוץ לאתר ויקי פרטי או אתר של חברה מסחרית.\n\nאם האפשרות להשתמש בטקסט מוויקיפדיה והאפשרות שוויקיפדיה תוכל תקבל עותקים של טקסטים מהוויקי שלך חשובות לך, כדאי לבחור ב<strong>{{int:config-license-cc-by-sa}}</strong>.\n\nויקיפדיה השתמשה בעבר ברישיון החופשי למסמכים של גנו (GNU FDL או GFDL).\nהוא עדיין רישיון תקין, אבל קשה להבנה.\nכמו־כן, קשה לעשות שימוש חוזר ביצירות שפורסמו לפי GFDL.",
"config-email-settings": "הגדרות דוא״ל",
"config-enable-email": "להפעיל דוא״ל יוצא",
- "config-enable-email-help": "אם אתם רוצים שדוא״ל יעבוד, [https://secure.php.net/manual/en/mail.configuration.php אפשרויות הדוא״ל של PHP] צריכות להיות מוגדרות נכון.\nאם אינכם רוצים להפעיל שום אפשרויות דוא״ל, כבו אותן כאן ועכשיו.",
+ "config-enable-email-help": "אם אתם רוצים שדוא״ל יעבוד, [https://www.php.net/manual/en/mail.configuration.php אפשרויות הדוא״ל של PHP] צריכות להיות מוגדרות נכון.\nאם אינכם רוצים להפעיל שום אפשרויות דוא״ל, כבו אותן כאן ועכשיו.",
"config-email-user": "להפעיל שליחת דוא״ל ממשתמש למשתמש",
"config-email-user-help": "לאפשר לכל המשתמשים לשלוח אחד לשני דוא״ל אם הם הפעילו את זה בהעדפות שלהם.",
"config-email-usertalk": "להפעיל הודעות על דף שיחת משתמש",
"config-env-php": "PHP $1 स्थापित किया गया है।",
"config-env-hhvm": "एचएचवीएम $1 स्थापित किया गया है।",
"config-memory-raised": "पीएचपी की <code>memory_limit</code> सीमा $1 है, जो $2 तक बढ़ गई है।",
- "config-apc": "[https://secure.php.net/apc एपीसी] स्थापित है।",
- "config-apcu": "[https://secure.php.net/apcu एपीसीयू] स्थापित है।",
+ "config-apc": "[https://www.php.net/apc एपीसी] स्थापित है।",
+ "config-apcu": "[https://www.php.net/apcu एपीसीयू] स्थापित है।",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension विनकैश] स्थापित है।",
"config-using-32bit": "<विशेष>चेतावनी:</विशेष> आपका सिस्टम 32-बिट पूर्णांक के साथ चल रहा है यह [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit विवेचित नहीं है]।",
"config-db-type": "डेटाबेस प्रकार:",
"config-pcre-no-utf8": "'''Fataler Fehler:''' Das PHP-Modul PCRE scheint ohne PCRE_UTF8-Unterstützung kompiliert worre sin.\nMediaWiki benöticht die UTF-8-Unnerstützung, um fehlerfrei looffähich zu sin.",
"config-memory-raised": "Der PHP-Parameter <code>memory_limit</code> betruch $1 und woard uff $2 erhöcht.",
"config-memory-bad": "'''Warnung:''' Der PHP-Parameter <code>memory_limit</code> beträcht $1.\nDer Weart ist wahrscheinlich zu niedrich.\nDer Installationsvoargang könnt doher scheitre!",
- "config-apc": "[https://secure.php.net/apc APC] ist installiert",
+ "config-apc": "[https://www.php.net/apc APC] ist installiert",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ist installiert",
"config-mod-security": "'''Warnung:''' Uff dem Webserver woard [https://modsecurity.org/ ModSecurity] aktiviert. Sofern falsch konfiguriert, kann das zu Probleme mit MediaWiki sowie annrer Software uff dem Server führe und es Benutzer ermöchliche beliebiche Inhalte im Wiki Renzustelle.\nFür weitre Informatione empfehle mir die [https://modsecurity.org/documentation/ Dokumentation zu ModSecurity] orrer den Kontakt zum Hoster, sofern Fehler ufftrete.",
"config-diff3-bad": "GNU diff3 woard net gefund.",
"config-type-mysql": "MySQL (orrer kompatible Datebanksysteme)",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki unnerstützt die follichenne Datebanksysteme:\n\n$1\n\nSoweit net das Datebanksystem oongezeicht weard, das verwennt werre soll, gebt das uwe en Link zu der Oonleitung mit Informatione, wie das aktiviert sin kann.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] ist das von MediaWiki primär unterstützte Datebanksystem. MediaWiki funktioniert ooch mit [{{int:version-db-mariadb-url}} MariaDB] und [{{int:version-db-percona-url}} Percona Server], die MySQL-kompatibel sind. ([https://secure.php.net/manual/en/mysqli.installation.php Oonleitung zur Kompilierung von PHP mit MySQL-Unnerstützung] [englisch Sproch])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ist en beliebtes Open-Source-Datebanksystem und ein Alternativ zu MySQL. Es gibt awer enche klenre Implementierungsfehler, so dass von der Nutzung in ener Produktivumgebung abgerat weard. ([https://secure.php.net/manual/en/pgsql.installation.php Oonnleitung zur Kompilierung von PHP mit PostgreSQL-Unterstützung])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] ist das von MediaWiki primär unterstützte Datebanksystem. MediaWiki funktioniert ooch mit [{{int:version-db-mariadb-url}} MariaDB] und [{{int:version-db-percona-url}} Percona Server], die MySQL-kompatibel sind. ([https://www.php.net/manual/en/mysqli.installation.php Oonleitung zur Kompilierung von PHP mit MySQL-Unnerstützung] [englisch Sproch])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ist en beliebtes Open-Source-Datebanksystem und ein Alternativ zu MySQL. Es gibt awer enche klenre Implementierungsfehler, so dass von der Nutzung in ener Produktivumgebung abgerat weard. ([https://www.php.net/manual/en/pgsql.installation.php Oonnleitung zur Kompilierung von PHP mit PostgreSQL-Unterstützung])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] ist en verschlanktes Datebanksystem, das ooch gut unnerstützt weard ([http://www.php.net/manual/de/pdo.installation.php Oonleitung zur Kompilierung von PHP mit SQLite-Unterstützung], verwennt PHP Data Objects (PDO))",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] ist en kommerzielle Unnernehmensdatebank ([http://www.php.net/manual/en/oci8.installation.php Oonleitung zur Kompilierung von PHP mit OCI8-Unnerstützung (en)])",
"config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ist en gewerbliche Unnernehmensdatebank für Windows. ([Config-dbsupport-oracle/manual/de/sqlsrv.installation.php Oonleitung zur Kompilierung von PHP mithilfe SQLSRV-Unnerstützung])",
"config-pcre-no-utf8": "'''Ćežki zmylk''': Zda so, zo PCRE-modul za PHP ma so bjez PCRE_UTF8-podpěry kompilować.\nMediaWiki trjeba UTF-8-podpěru, zo by korektnje fungował.",
"config-memory-raised": "PHP-parameter <code>memory_limit</code> je $1, je so na hódnotu $2 zwyšił.",
"config-memory-bad": "'''Warnowanje:''' PHP-parameter <code>memory_limit</code> ma hódnotu $1,\nTo je najskerje přeniske.\nInstalacija móhła so njeporadźić!",
- "config-apc": "[https://secure.php.net/apc APC] je instalowany",
+ "config-apc": "[https://www.php.net/apc APC] je instalowany",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je instalowany",
"config-diff3-bad": "GNU diff3 njenamakany.",
"config-no-uri": "'''Zmylk:''' Aktualny URI njeda so postajić.\nInstalacija bu přetorhnjena.",
"config-type-postgres": "PostgreSQL",
"config-type-sqlite": "SQLite",
"config-type-oracle": "Oracle",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] je primarny cil za MediaWiki a podpěruje so najlěpje. MediaWiki funguje tež z [{{int:version-db-mariadb-url}} MariaDB] a [{{int:version-db-percona-url}} Percona Server], kotrejž stej kompatibelnej z MySQL. ([https://secure.php.net/manual/en/mysqli.installation.php Nawod ke kompilowanju PHP z MySQL-podpěru])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularny system datoweje banki zjawneho žórła jako alternatiwa k MySQL. Móhło hišće někotre zmylki eksistować, a njeporuča so jón w produktiwnej wokolinje wužiwać. ([https://secure.php.net/manual/en/pgsql.installation.php Nawod za kompilowanje PHP z podpěru PostgreSQL])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] je primarny cil za MediaWiki a podpěruje so najlěpje. MediaWiki funguje tež z [{{int:version-db-mariadb-url}} MariaDB] a [{{int:version-db-percona-url}} Percona Server], kotrejž stej kompatibelnej z MySQL. ([https://www.php.net/manual/en/mysqli.installation.php Nawod ke kompilowanju PHP z MySQL-podpěru])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularny system datoweje banki zjawneho žórła jako alternatiwa k MySQL. Móhło hišće někotre zmylki eksistować, a njeporuča so jón w produktiwnej wokolinje wužiwać. ([https://www.php.net/manual/en/pgsql.installation.php Nawod za kompilowanje PHP z podpěru PostgreSQL])",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je komercielna předewzaćelska datowa banka. ([http://www.php.net/manual/en/oci8.installation.php Nawod za kompilowanje PHP z OCI8-podpěru])",
"config-header-mysql": "Nastajenja MySQL",
"config-header-postgres": "Nastajenja PostgreSQL",
"config-pcre-no-utf8": "<strong>Kritikus hiba:</strong> Úgy tűnik, hogy a PHP PRCE modulja PRCE_UTF8 támogatás nélkül lett fordítva.\nA MediaWikinek UTF-8-támogatásra van szüksége a helyes működéshez.",
"config-memory-raised": "A PHP <code>memory_limit</code> beállításának értéke: $1. Meg lett növelve a következő értékre: $2.",
"config-memory-bad": "<strong>Figyelmeztetés:</strong> A PHP <code>memory_limit</code> beállításának értéke $1.\nEz az érték valószínűleg túl kevés, a telepítés sikertelen lehet.",
- "config-apc": "Az [https://secure.php.net/apc APC] telepítve van",
- "config-apcu": "Az [https://secure.php.net/apcu APCu] telepítve van",
+ "config-apc": "Az [https://www.php.net/apc APC] telepítve van",
+ "config-apcu": "Az [https://www.php.net/apcu APCu] telepítve van",
"config-wincache": "A [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] telepítve van",
- "config-no-cache-apcu": "<strong>Figyelmeztetés:</strong> nem találhatók a következők: [https://secure.php.net/apcu APCu] vagy [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nAz objektum gyorsítótárazása nincs engedélyezve.",
+ "config-no-cache-apcu": "<strong>Figyelmeztetés:</strong> nem találhatók a következők: [https://www.php.net/apcu APCu] vagy [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nAz objektum gyorsítótárazása nincs engedélyezve.",
"config-diff3-bad": "GNU diff3 nem található.",
"config-git": "Megtaláltam a Git verziókezelő szoftvert: <code>$1</code>.",
"config-git-bad": "A Git verziókezelő rendszer nem található.",
"config-type-mysql": "MySQL (vagy kompatibilis)",
"config-type-mssql": "Microsoft SQL Szerver",
"config-support-info": "A MediaWiki a következő adatbázisrendszereket támogatja:\n\n$1\n\nHa az alábbi listán nem találod azt a rendszert, melyet használni szeretnél, a fenti linken található instrukciókat követve engedélyezheted a támogatását.",
- "config-dbsupport-mysql": "* A [{{int:version-db-mysql-url}} MySQL] a MediaWiki elsődleges célpontja, így a legjobban támogatott. A MediaWiki elfut [{{int:version-db-mariadb-url}} MariaDB-n] és [{{int:version-db-percona-url}} Percona Serveren] is, mivel ezek MySQL-kompatibilisek. ([https://secure.php.net/manual/en/mysql.installation.php Hogyan fordítható a PHP MySQL-támogatással])",
- "config-dbsupport-postgres": "* A [{{int:version-db-postgres-url}} PostgreSQL] népszerű, nyílt forráskódú adatbázisrendszer, a MySQL alternatívája. ([https://secure.php.net/manual/en/pgsql.installation.php Hogyan fordítható a PHP PostgreSQL-támogatással])",
+ "config-dbsupport-mysql": "* A [{{int:version-db-mysql-url}} MySQL] a MediaWiki elsődleges célpontja, így a legjobban támogatott. A MediaWiki elfut [{{int:version-db-mariadb-url}} MariaDB-n] és [{{int:version-db-percona-url}} Percona Serveren] is, mivel ezek MySQL-kompatibilisek. ([https://www.php.net/manual/en/mysql.installation.php Hogyan fordítható a PHP MySQL-támogatással])",
+ "config-dbsupport-postgres": "* A [{{int:version-db-postgres-url}} PostgreSQL] népszerű, nyílt forráskódú adatbázisrendszer, a MySQL alternatívája. ([https://www.php.net/manual/en/pgsql.installation.php Hogyan fordítható a PHP PostgreSQL-támogatással])",
"config-dbsupport-sqlite": "* Az [{{int:version-db-sqlite-url}} SQLite] egy könnyű, nagyon jól támogatott adatbázisrendszer. ([http://www.php.net/manual/en/pdo.installation.php Hogyan fordítható a PHP SQLite-támogatással], PDO-t használ)",
"config-dbsupport-oracle": "* Az [{{int:version-db-oracle-url}} Oracle] kereskedelmi, vállalati adatbázisrendszer. ([http://www.php.net/manual/en/oci8.installation.php Hogyan fordítható a PHP OCI8-támogatással])",
- "config-dbsupport-mssql": "* A [{{int:version-db-mssql-url}} Microsoft SQL Server] kereskedelmi, vállalati adatbázisrendszer. ([https://secure.php.net/manual/en/sqlsrv.installation.php Hogyan fordítható a PHP SQLSRV-támogatással])",
+ "config-dbsupport-mssql": "* A [{{int:version-db-mssql-url}} Microsoft SQL Server] kereskedelmi, vállalati adatbázisrendszer. ([https://www.php.net/manual/en/sqlsrv.installation.php Hogyan fordítható a PHP SQLSRV-támogatással])",
"config-header-mysql": "MySQL-beállítások",
"config-header-postgres": "PostgreSQL-beállítások",
"config-header-sqlite": "SQLite-beállítások",
"config-pcre-no-utf8": "'''Fatal''': Le modulo PCRE de PHP pare haber essite compilate sin supporto de PCRE_UTF8.\nMediaWiki require supporto de UTF-8 pro functionar correctemente.",
"config-memory-raised": "Le <code>memory_limit</code> de PHP es $1, elevate a $2.",
"config-memory-bad": "'''Aviso:''' Le <code>memory_limit</code> de PHP es $1.\nIsto es probabilemente troppo basse.\nLe installation pote faller!",
- "config-apc": "[https://secure.php.net/apc APC] es installate",
- "config-apcu": "[https://secure.php.net/apcu APCu] es installate",
+ "config-apc": "[https://www.php.net/apc APC] es installate",
+ "config-apcu": "[https://www.php.net/apcu APCu] es installate",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] es installate",
- "config-no-cache-apcu": "<strong>Attention:</strong> Impossibile trovar [https://secure.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLe cache de objectos non es activate.",
+ "config-no-cache-apcu": "<strong>Attention:</strong> Impossibile trovar [https://www.php.net/apcu APCu] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nLe cache de objectos non es activate.",
"config-mod-security": "<strong>Attention</strong>: [https://modsecurity.org/ mod_security]/mod_security2 es active in tu servitor web. Multe configurationes commun de isto causa problemas pro MediaWiki o altere software que permitte al usatores de publicar contento arbitrari. Si possibile, isto deberea esser disactivate.\nAlteremente, consulta le [https://modsecurity.org/documentation/ documentation de mod_security] o contacta le servicio de adjuta de tu servitor si tu incontra estranie errores.",
"config-diff3-bad": "Le utilitate de comparation de texto GNU diff3 non ha essite trovate. Es possibile ignorar isto pro le momento, ma tu poterea incontrar conflictos de modification plus frequentemente.",
"config-git": "Systema de controlo de version Git trovate: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki supporta le sequente systemas de base de datos:\n\n$1\n\nSi tu non vide hic infra le systema de base de datos que tu tenta usar, alora seque le instructiones ligate hic supra pro activar le supporto.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] es le systema primari pro MediaWiki e le melio supportate. MediaWiki functiona anque con [{{int:version-db-mysql-url}} MySQL] e con [{{int:version-db-percona-url}} Percona Server], le quales es compatibile con MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Como compilar PHP con supporto de MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] es un systema de base de datos popular e open source, alternativa a MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Como compilar PHP con supporto de PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] es un systema de base de datos legier que es multo ben supportate. ([https://secure.php.net/manual/en/pdo.installation.php Como compilar PHP con supporto de SQLite], usa PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] es un banca de datos commercial pro interprisas. ([https://secure.php.net/manual/en/oci8.installation.php Como compilar PHP con supporto de OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] es un base de datos de interprisa commercial pro Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Como compilar PHP con supporto de SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] es le systema primari pro MediaWiki e le melio supportate. MediaWiki functiona anque con [{{int:version-db-mysql-url}} MySQL] e con [{{int:version-db-percona-url}} Percona Server], le quales es compatibile con MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Como compilar PHP con supporto de MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] es un systema de base de datos popular e open source, alternativa a MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Como compilar PHP con supporto de PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] es un systema de base de datos legier que es multo ben supportate. ([https://www.php.net/manual/en/pdo.installation.php Como compilar PHP con supporto de SQLite], usa PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] es un banca de datos commercial pro interprisas. ([https://www.php.net/manual/en/oci8.installation.php Como compilar PHP con supporto de OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] es un base de datos de interprisa commercial pro Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Como compilar PHP con supporto de SQLSRV])",
"config-header-mysql": "Configuration de MariaDB/MySQL",
"config-header-postgres": "Configuration de PostgreSQL",
"config-header-sqlite": "Configuration de SQLite",
"config-pcre-no-utf8": "'''Fatal''': Modul PCRE PHP tampaknya dikompilasi tanpa dukungan PCRE_UTF8.\nMediaWiki memerlukan dukungan UTF-8 untuk berfungsi dengan benar.",
"config-memory-raised": "<code>memory_limit</code> PHP adalah $1, dinaikkan ke $2.",
"config-memory-bad": "'''Peringatan:''' <code>memory_limit</code> PHP adalah $1.\nIni terlalu rendah.\nInstalasi terancam gagal!",
- "config-apc": "[https://secure.php.net/apc APC] telah dipasang",
- "config-apcu": "[https://secure.php.net/apcu APCu] telah dipasang",
+ "config-apc": "[https://www.php.net/apc APC] telah dipasang",
+ "config-apcu": "[https://www.php.net/apcu APCu] telah dipasang",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] telah diinstal",
- "config-no-cache-apcu": "<strong>Peringatan:</strong> Tidak dapat menemukan [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] atau [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]. Singgahan obyek tidak diaktifkan.",
+ "config-no-cache-apcu": "<strong>Peringatan:</strong> Tidak dapat menemukan [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] atau [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]. Singgahan obyek tidak diaktifkan.",
"config-mod-security": "<strong>Peringatan:</strong> Server web Anda memiliki [https://modsecurity.org/ mod_security] yang diaktifkan. Jika salah dalam mengkonfigurasi, ini dapat menyebabkan masalah untuk MediaWiki atau perangkat lunak lain yang memungkinkan pengguna untuk mengirim sembarang konten.\nLihat [https://modsecurity.org/documentation/ dokumentasi mod_security] atau hubungi layanan host Anda jika Anda mengalami kesalahan acak.",
"config-diff3-bad": "GNU diff3 tidak ditemukan.",
"config-git": "Menemukan perangkat lunak kontrol versi Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki mendukung sistem basis data berikut:\n\n$1\n\nJika Anda tidak melihat sistem basis data yang Anda gunakan tercantum di bawah ini, ikuti petunjuk terkait di atas untuk mengaktifkan dukungan.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] adalah target utama MediaWiki dan memiliki dukungan terbaik. MediaWiki juga berjalan dengan [{{int:version-db-mariadb-url}} MariaDB] dan [{{int:version-db-percona-url}} Server Percona], yang kompatibel dengan MySQL. ([https://secure.php.net/manual/en/mysql.installation.php Cara mengompilasi PHP dengan dukungan MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] adalah sistem basis data sumber terbuka populer sebagai alternatif MySQL.([https://secure.php.net/manual/en/pgsql.installation.php Bagaimana mengompilasikan PHP dengan dukungan PostgreSQL])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] adalah target utama MediaWiki dan memiliki dukungan terbaik. MediaWiki juga berjalan dengan [{{int:version-db-mariadb-url}} MariaDB] dan [{{int:version-db-percona-url}} Server Percona], yang kompatibel dengan MySQL. ([https://www.php.net/manual/en/mysql.installation.php Cara mengompilasi PHP dengan dukungan MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] adalah sistem basis data sumber terbuka populer sebagai alternatif MySQL.([https://www.php.net/manual/en/pgsql.installation.php Bagaimana mengompilasikan PHP dengan dukungan PostgreSQL])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] adalah sistem basis data yang ringan yang sangat baik dukungannya. ([http://www.php.net/manual/en/pdo.installation.php cara mengompilasi PHP dengan dukungan SQLite], menggunakan PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] adalah basis data komersial untuk perusahaan. ([http://www.php.net/manual/en/oci8.installation.php cara mengompilasi PHP dengan dukungan OCI8])",
- "config-dbsupport-mssql": "[{{int:version-db-mssql-url}} Microsoft SQL Server] adalah database perusahaan komersial untuk Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Bagaimana cara mengkompilasi PHP dengan dukungan SQLSRV])",
+ "config-dbsupport-mssql": "[{{int:version-db-mssql-url}} Microsoft SQL Server] adalah database perusahaan komersial untuk Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Bagaimana cara mengkompilasi PHP dengan dukungan SQLSRV])",
"config-header-mysql": "Pengaturan MariaDB/MySQL",
"config-header-postgres": "Pengaturan PostgreSQL",
"config-header-sqlite": "Pengaturan SQLite",
"config-env-bad": "Omno verifikesis.\nVu NE POVAS intalar MediaWiki.",
"config-env-php": "PHP $1 instalesis.",
"config-env-hhvm": "HHVM $1 instalesis.",
- "config-apc": "[https://secure.php.net/apc APC] instalesis",
- "config-apcu": "[https://secure.php.net/apcu APCu] instalesis",
+ "config-apc": "[https://www.php.net/apc APC] instalesis",
+ "config-apcu": "[https://www.php.net/apcu APCu] instalesis",
"config-profile-private": "Privata wiki",
"config-license": "Autoroyuro e permiso:",
"config-license-cc-0": "Creative Commons Zero (Publika domeno)",
"config-copyright": "=== Höfundarréttur og skilmálar ===\n\n$1\n\nÞetta er frjáls hugbúnaður; þú mátt dreifa honum og/eða breyta samkvæmt skilmálum í almenna GNU GPL notkunarleyfinu eins og það er gefið út af Frjálsu hugbúnaðarstofnuninni; annaðhvort útgáfu 2 af GPL-leyfinu, eða (ef þér sýnist svo) einhverri nýrri útgáfu leyfisins.\n\nHugbúnaði þessum er dreift í þeirri von að hann geti verið gagnlegur, en <strong>ÁN ALLRAR ÁBYRGÐAR</strong>; einnig án þeirrar ábyrgðar sem gefin er í skyn með <strong>SELJANLEIKA</strong> eða <strong>EIGINLEIKUM TIL TILTEKINNA NOTA</strong>. Sjá almenna GNU GPL notkunarleyfið fyrir nánari upplýsingar.\n\nÞað ætti að hafa fylgt afrit af almenna <doclink href=Copying>GNU GPL notkunarleyfinu</doclink> með forritinu; ef ekki skrifið þá Fjálsu hugbúnarstofnuninni: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, eða [https://www.gnu.org/copyleft/gpl.html lestu það á netinu].",
"config-env-php": "PHP $1 er uppsett.",
"config-env-hhvm": "HHVM $1 er uppsett.",
- "config-apc": "[https://secure.php.net/apc APC] er uppsett",
+ "config-apc": "[https://www.php.net/apc APC] er uppsett",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] er uppsett",
"config-diff3-bad": "GNU diff3 textasamanburðartólið fannst ekki. Þú getur hunsað þetta núna, en þú gætir lent oftar í breytingaárekstrum.",
"config-using-server": "Nota \"<nowiki>$1</nowiki>\" sem heiti á þjóni.",
"config-pcre-no-utf8": "'''Errore''': Il modulo PCRE di PHP sembra essere stato compilato senza il supporto PCRE_UTF8, ma MediaWiki lo richiede per funzionare correttamente.",
"config-memory-raised": "Il valore <code>memory_limit</code> di PHP è $1, aumentato a $2.",
"config-memory-bad": "''Attenzione:''' Il valore di <code>memory_limit</code> di PHP è $1.\nProbabilmente è troppo basso.\nL'installazione potrebbe non riuscire!",
- "config-apc": "[https://secure.php.net/apc APC] è installato",
- "config-apcu": "[https://secure.php.net/apcu APCu] è installato",
+ "config-apc": "[https://www.php.net/apc APC] è installato",
+ "config-apcu": "[https://www.php.net/apcu APCu] è installato",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] è installato",
- "config-no-cache-apcu": "'''Attenzione:''' [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] non sono stati trovati.\nLa caching degli oggetti non è attivata.",
+ "config-no-cache-apcu": "'''Attenzione:''' [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] non sono stati trovati.\nLa caching degli oggetti non è attivata.",
"config-mod-security": "<strong>Attenzione:</strong> Il tuo server web ha il [https://modsecurity.org/ mod_security] abilitato. Se non correttamente configurato, può creare problemi a MediaWiki o ad altro software che permette agli utenti di pubblicare contenuto.\nFai riferimento alla [https://modsecurity.org/documentation/ documentazione sul mod_security] o contatta il supporto tecnico del tuo provider di hosting se si verificano errori.",
"config-diff3-bad": "Utilità di confronto del testo GNU diff3 non trovata. È possibile ignorare per ora, ma potresti incorrere in conflitti di modifica più frequentemente.",
"config-git": "Trovato software di controllo della versione Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL o compatibile",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki supporta i seguenti sistemi di database:\n\n$1\n\nSe fra quelli elencati qui sotto non vedi il sistema di database che vorresti utilizzare, seguire le istruzioni linkate sopra per abilitare il supporto.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] è la configurazione preferibile per MediaWiki ed è quella meglio supportata. MediaWiki funziona anche con [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], che sono compatibili con MariaDB.([https://secure.php.net/manual/en/mysqli.installation.php Come compilare PHP con supporto MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] è un popolare sistema di database open source come alternativa a MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Come compilare PHP con supporto PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] è un sistema di database leggero, che è supportato molto bene. ([https://secure.php.net/manual/en/pdo.installation.php Come compilare PHP con supporto SQLite], utilizza PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] è un database di un'impresa commerciale. ([https://secure.php.net/manual/en/oci8.installation.php Come compilare PHP con supporto OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] è un database di un'impresa commerciale per Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Come compilare PHP con supporto SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] è la configurazione preferibile per MediaWiki ed è quella meglio supportata. MediaWiki funziona anche con [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], che sono compatibili con MariaDB.([https://www.php.net/manual/en/mysqli.installation.php Come compilare PHP con supporto MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] è un popolare sistema di database open source come alternativa a MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Come compilare PHP con supporto PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] è un sistema di database leggero, che è supportato molto bene. ([https://www.php.net/manual/en/pdo.installation.php Come compilare PHP con supporto SQLite], utilizza PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] è un database di un'impresa commerciale. ([https://www.php.net/manual/en/oci8.installation.php Come compilare PHP con supporto OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] è un database di un'impresa commerciale per Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Come compilare PHP con supporto SQLSRV])",
"config-header-mysql": "Impostazioni MySQL",
"config-header-postgres": "Impostazioni PostgreSQL",
"config-header-sqlite": "Impostazioni SQLite",
"config-license-help": "Molti wiki pubblici rilasciano i loro contributi con una [https://freedomdefined.org/Definition licenza libera]. Questo aiuta a creare un senso di proprietà condivisa nella comunità e incoraggia a contribuire a lungo termine. Non è generalmente necessario per un wiki privato o aziendale.\n\nSe vuoi usare testi da Wikipedia, o desideri che Wikipedia possa essere in grado di accettare testi copiati dal tuo wiki, dovresti scegliere <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nIn precedenza Wikipedia ha utilizzato la GNU Free Documentation License. La GFDL è una licenza valida, ma è di difficile comprensione e complica il riutilizzo dei contenuti.",
"config-email-settings": "Impostazioni email",
"config-enable-email": "Abilita la posta elettronica in uscita",
- "config-enable-email-help": "Se vuoi che funzionino le email, le [https://secure.php.net/manual/en/mail.configuration.php impostazioni della posta] devono essere configurate correttamente.\nSe non si desidera alcuna funzionalità di posta elettronica, puoi disabilitarla qui.",
+ "config-enable-email-help": "Se vuoi che funzionino le email, le [https://www.php.net/manual/en/mail.configuration.php impostazioni della posta] devono essere configurate correttamente.\nSe non si desidera alcuna funzionalità di posta elettronica, puoi disabilitarla qui.",
"config-email-user": "Abilita invio email fra utenti",
"config-email-user-help": "Consente a tutti gli utenti di inviarsi a vicenda email, se lo hanno abilitato nelle loro preferenze.",
"config-email-usertalk": "Abilita le notifiche per le pagine di discussione utente",
"config-pcre-no-utf8": "<strong>致命的エラー:</strong> PHP の PCRE が PCRE_UTF8 対応なしでコンパイルされているようです。\nMediaWiki を正しく動作させるには、UTF-8 対応が必要です。",
"config-memory-raised": "PHPの<code>memory_limit</code>は$1で、$2に引き上げられました。",
"config-memory-bad": "<strong>警告:</strong> PHPの<code>memory_limit</code>に$1に設定されています。\nこの値はおそらく小さすぎます。\nインストールが失敗するおそれがあります!",
- "config-apc": "[https://secure.php.net/apc APC] がインストール済み",
- "config-apcu": "[https://secure.php.net/apcu APCu] がインストール済み",
+ "config-apc": "[https://www.php.net/apc APC] がインストール済み",
+ "config-apcu": "[https://www.php.net/apcu APCu] がインストール済み",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] がインストール済み",
- "config-no-cache-apcu": "<strong>警告:</strong> [https://secure.php.net/apcu APCu]、 [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] のいずれも見つかりませんでした。\nオブジェクトのキャッシュは有効化されません。",
+ "config-no-cache-apcu": "<strong>警告:</strong> [https://www.php.net/apcu APCu]、 [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] のいずれも見つかりませんでした。\nオブジェクトのキャッシュは有効化されません。",
"config-mod-security": "<strong>警告:</strong> あなたのウェブサーバーでは [https://modsecurity.org/ mod_security] が有効になっています。正しく構成されていない場合は、MediaWiki や利用者にコンテンツの投稿を許可するその他のソフトウェアに問題が発生する場合があります。\n[https://modsecurity.org/documentation/ mod_security の説明文書]を確認するか、ランダムなエラーが発生した場合はあなたのホストのサポートにお問い合わせください。",
"config-diff3-bad": "GNU diff3 が見つかりません。",
"config-git": "バージョン管理ソフトウェア Git が見つかりました: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "マイクロソフト SQL Server",
"config-support-info": "MediaWiki は以下のデータベース システムに対応しています:\n\n$1\n\n使用しようとしているデータベース システムが下記の一覧にない場合は、上記リンク先の手順に従ってインストールしてください。",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]はMediaWikiの主要な対象であり、最もよくサポートされています。MediaWikiはMariaDBと互換性のある[{{int:version-db-mysql-url}} MySQL]、[{{int:version-db-percona-url}} Percona Server]でも動きます。 ([https://secure.php.net/manual/ja/mysqli.installation.php PHPをMySQLサポート付きでコンパイルする方法])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] は、MySQLの代替として人気がある公開のデータベースシステムです。([https://secure.php.net/manual/en/pgsql.installation.php PHPをPostgreSQLサポート付きでコンパイルする方法])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]は、良くサポートされている、軽量データベースシステムです。([https://secure.php.net/manual/ja/pdo.installation.php SQLiteに対応したPHPをコンパイルする方法]、PDOを使用)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]は商業企業のデータベースです。([https://secure.php.net/manual/en/oci8.installation.php OCI8サポートなPHPをコンパイルする方法])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server]は商業企業のWindows用データベースです。([https://secure.php.net/manual/en/sqlsrv.installation.php SQLSRVサポートなPHPをコンパイルする方法])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]はMediaWikiの主要な対象であり、最もよくサポートされています。MediaWikiはMariaDBと互換性のある[{{int:version-db-mysql-url}} MySQL]、[{{int:version-db-percona-url}} Percona Server]でも動きます。 ([https://www.php.net/manual/ja/mysqli.installation.php PHPをMySQLサポート付きでコンパイルする方法])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] は、MySQLの代替として人気がある公開のデータベースシステムです。([https://www.php.net/manual/en/pgsql.installation.php PHPをPostgreSQLサポート付きでコンパイルする方法])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]は、良くサポートされている、軽量データベースシステムです。([https://www.php.net/manual/ja/pdo.installation.php SQLiteに対応したPHPをコンパイルする方法]、PDOを使用)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]は商業企業のデータベースです。([https://www.php.net/manual/en/oci8.installation.php OCI8サポートなPHPをコンパイルする方法])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server]は商業企業のWindows用データベースです。([https://www.php.net/manual/en/sqlsrv.installation.php SQLSRVサポートなPHPをコンパイルする方法])",
"config-header-mysql": "MariaDB/MySQL の設定",
"config-header-postgres": "PostgreSQL の設定",
"config-header-sqlite": "SQLite の設定",
"config-enable-email-help": "メールを使用したい場合は、[Config-dbsupport-oracle/manual/en/mail.configuration.php PHP のメール設定]が正しく設定されている必要があります。\nメールの機能を使用しない場合は、ここで無効にすることができます。",
"config-email-user": "利用者間のメールを有効にする",
"config-email-user-help": "設定で有効になっている場合、すべてのユーザーがお互いにメールのやりとりを行うことを許可する。",
- "config-email-usertalk": "ユーザーのトークページでの通知を有効にする",
- "config-email-usertalk-help": "設定で有効にしている場合は、ユーザーのトークページの変更の通知を受けることをユーザーに許可する。",
+ "config-email-usertalk": "利用者のトークページでの通知を有効にする",
+ "config-email-usertalk-help": "設定で有効にしている場合は、利用者のトークページの変更の通知を受けることを利用者に許可する。",
"config-email-watchlist": "ウォッチリストの通知を有効にする",
"config-email-watchlist-help": "利用者が設定で有効にしている場合、閲覧されたページに関する通知を受け取ることを許可する。",
"config-email-auth": "メールの認証を有効にする",
"config-sidebar": "* [https://www.mediawiki.org მედიავიკის ვებ-გვერდი]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/ka მომხმარებლების დახმარება]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/ka ადმინისტრატორების დახმარება]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ka FAQ]\n----\n* <doclink href=Readme>წამიკითხე</doclink>\n* <doclink href=ReleaseNotes>ინფორმაცია გამოშვებაზე</doclink>\n* <doclink href=Copying>ლიცენზია</doclink>\n* <doclink href=UpgradeDoc>განახლება</doclink>",
"config-env-php": "PHP $1 დაინსტალირებულია",
"config-env-hhvm": "HHVM $1 დაინსტალირებულია.",
- "config-apc": "[https://secure.php.net/apc APC] დაყენდა",
+ "config-apc": "[https://www.php.net/apc APC] დაყენდა",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] დაყენდა",
"config-diff3-bad": "GNU diff3 ვერ მოიძებნა.",
"config-db-type": "მონაცემთა ბაზის ტიპი:",
"config-pcre-no-utf8": "<strong>치명:</strong> PHP의 PCRE 모듈은 RCRE_UTF8 지원 없이 컴파일된 것 같습니다.\n미디어위키가 올바르게 작동하려면 UTF-8을 지원해야 합니다.",
"config-memory-raised": "PHP의 <code>memory_limit</code>는 $1이며 $2(으)로 늘렸습니다.",
"config-memory-bad": "<strong>경고:</strong> PHP의 <code>memory_limit</code>는 $1입니다.\n아마도 너무 낮은 것 같습니다.\n설치가 실패할 수 있습니다!",
- "config-apc": "[https://secure.php.net/apc APC]가 설치되었습니다",
- "config-apcu": "[https://secure.php.net/apcu APCu]가 설치되었습니다",
+ "config-apc": "[https://www.php.net/apc APC]가 설치되었습니다",
+ "config-apcu": "[https://www.php.net/apcu APCu]가 설치되었습니다",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]가 설치되었습니다",
- "config-no-cache-apcu": "<strong>경고:</strong> [https://secure.php.net/apcu APCu] 또는 [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]를 찾을 수 없습니다.",
+ "config-no-cache-apcu": "<strong>경고:</strong> [https://www.php.net/apcu APCu] 또는 [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]를 찾을 수 없습니다.",
"config-mod-security": "<strong>경고:</strong> 웹 서버에 [https://modsecurity.org/ mod_security]가 허용되었습니다. 잘못 설정된 경우 미디어위키나 사용자가 임의의 내용을 게시할 수 있는 다른 소프트웨어에 대한 문제를 일으킬 수 있습니다.\n[https://modsecurity.org/documentation/ mod_security] 문서를 참고하거나 임의의 오류가 발생할 경우 호스트의 지원 요청에 문의하십시오.",
"config-diff3-bad": "GNU diff3를 찾을 수 없습니다.",
"config-git": "Git 버전 관리 소프트웨어를 찾았습니다: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL 서버",
"config-support-info": "미디어위키는 다음의 데이터베이스 시스템을 지원합니다:\n\n$1\n\n데이터베이스 시스템이 표시되지 않을 때 아래에 나열된 다음 지원을 활성화하려면 위의 링크된 지시에 따라 설치해볼 수 있습니다.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]는 미디어위키의 기본 대상이며 가장 잘 지원됩니다. 미디어위키는 또한 MariaDB와 호환되는 [{{int:version-db-mysql-url}} MySQL]과 [{{int:version-db-percona-url}} Percona 서버]에서도 작동합니다. ([https://secure.php.net/manual/en/mysql.installation.php MySQL 지원으로 PHP를 컴파일하는 방법])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL]은 MySQL의 대안으로서 인기 있는 오픈 소스 데이터베이스 시스템입니다. ([https://secure.php.net/manual/en/pgsql.installation.php PostgreSQL 지원으로 PHP를 컴파일하는 방법])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]는 매우 잘 지원되고 가벼운 데이터베이스 시스템입니다. ([https://secure.php.net/manual/en/pdo.installation.php SQLite 지원으로 PHP를 컴파일하는 방법], PDO 사용)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]은 상용 기업 데이터베이스입니다. ([https://secure.php.net/manual/en/oci8.installation.php OCI8 지원으로 PHP를 컴파일하는 방법])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL 서버]는 Windows용 상용 기업 데이터베이스입니다. ([https://secure.php.net/manual/en/sqlsrv.installation.php SQLSRV 지원으로 PHP를 컴파일하는 방법])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]는 미디어위키의 기본 대상이며 가장 잘 지원됩니다. 미디어위키는 또한 MariaDB와 호환되는 [{{int:version-db-mysql-url}} MySQL]과 [{{int:version-db-percona-url}} Percona 서버]에서도 작동합니다. ([https://www.php.net/manual/en/mysql.installation.php MySQL 지원으로 PHP를 컴파일하는 방법])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL]은 MySQL의 대안으로서 인기 있는 오픈 소스 데이터베이스 시스템입니다. ([https://www.php.net/manual/en/pgsql.installation.php PostgreSQL 지원으로 PHP를 컴파일하는 방법])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]는 매우 잘 지원되고 가벼운 데이터베이스 시스템입니다. ([https://www.php.net/manual/en/pdo.installation.php SQLite 지원으로 PHP를 컴파일하는 방법], PDO 사용)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]은 상용 기업 데이터베이스입니다. ([https://www.php.net/manual/en/oci8.installation.php OCI8 지원으로 PHP를 컴파일하는 방법])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL 서버]는 Windows용 상용 기업 데이터베이스입니다. ([https://www.php.net/manual/en/sqlsrv.installation.php SQLSRV 지원으로 PHP를 컴파일하는 방법])",
"config-header-mysql": "MariaDB/MySQL 설정",
"config-header-postgres": "PostgreSQL 설정",
"config-header-sqlite": "SQLite 설정",
"config-pcre-no-utf8": "'''Dä:''' Et PHP-Modul <i lang=\"en\">PCRE</i> schingk ohne de <i lang=\"en\">PCRE_UTF8</i>-Aandeile övversaz ze sin.\nMediaWiki bruch dä UTF-8-Krohm ävver, öm ohne Fähler loufe ze künne.",
"config-memory-raised": "Der jrühzte zohjelasse Shpeisherbedarf vum PHP, et <code lang=\"en\">memory_limit</code>, shtund op $1 un es op $2 erop jesaz woode.",
"config-memory-bad": "'''Opjepaß:''' Dem PHP singe Parameeter <code lang=\"en\">memory_limit</code> es $1.\nDat es wall ze winnisch.\nEt Enreeschte kunnt doh draan kappott jon!",
- "config-apc": "Dä <code lang=\"en\">[https://secure.php.net/apc APC]</code> es ennjeresht.",
+ "config-apc": "Dä <code lang=\"en\">[https://www.php.net/apc APC]</code> es ennjeresht.",
"config-wincache": "Dä <code lang=\"en\">[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]</code> es ennjeresht.",
- "config-no-cache-apcu": "'''Opjepaß:''' Mer kunnte dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[https://secure.php.net/apcu APCu]</code>, dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[http://xcache.lighttpd.net/ XCache]</code> udder dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]</code> nit fenge.\nEt <i lang=\"en\" xml:lang=\"en\" dir=\"rtl\">object caching</i> es nit müjjelesch un es ußjeschalldt.",
+ "config-no-cache-apcu": "'''Opjepaß:''' Mer kunnte dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[https://www.php.net/apcu APCu]</code>, dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[http://xcache.lighttpd.net/ XCache]</code> udder dä <code lang=\"en\" xml:lang=\"en\" dir=\"rtl\">[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]</code> nit fenge.\nEt <i lang=\"en\" xml:lang=\"en\" dir=\"rtl\">object caching</i> es nit müjjelesch un es ußjeschalldt.",
"config-mod-security": "<strong>Opjepaß</strong>: Dinge Wäbßööver hät <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[https://modsecurity.org/ mod_security]</code> enjeschalldt. Jenohch schtandattmähßejje Enschtällonge heh em Wikki künne Problehme met MehdijaWikki un och met ander Projramme aanschtivvelle, di zohlohße, dat vun ußerhallef öhndsene Krohm op dä Webßööver jebraat wähde künnt.\nWann müjjelesch sullt mer dat affschallde. Söns beloor Der di Sigg <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[https://modsecurity.org/documentation/ mod_security documentation]</code> udder donn met dä Fachlück för Dinge Webßööver kalle, wann zohfälleje un koomijje Fähler bemärke deihß.",
"config-diff3-bad": "Mer han <i lang=\"en\">GNU</i> <code lang=\"en\">diff3</code> nit jefonge.",
"config-git": "Mer han de Väsjohn <code>$1</code> vun däm Väsjohnsverwalldongsprojamm <i lang=\"en\">Git</i> jefonge.",
"config-type-oracle": "<i lang=\"en\">Oracle</i>",
"config-type-mssql": "Dä <i lang=\"en\" xml:lang=\"en\">SQL</i>-ẞööver vun <i lang=\"en\" xml:lang=\"en\">Microsoft</i>",
"config-support-info": "MediaWiki kann met heh dä Daatebangk_Süßteeme zosamme jonn:\n\n$1\n\nWann dat Daatebangk_Süßteem, wat De nämme wells, onge nit dobei es, dann donn desch aan di Aanleidonge hallde, di bovve verlengk sen, öm et op Dingem ẞööver singem Süßteem müjjelesh ze maache, se aan et Loufe ze krijje.",
- "config-dbsupport-mysql": "* <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mysql-url}} MySQL]</i> es dat vum MediaWiki et eets un et bäß ongerschtöz Daatebangksüßtehm. Et leuf ävver och met <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mariadb-url}} MariaDB]</i> un <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-percona-url}} Percona Server]</i>. Di sin kumpatihbel mem <i lang=\"en\" xml:lang=\"en\">MySQL</i>. ([https://secure.php.net/manual/de/mysql.installation.php Aanleidung för et Övversäze un Enreeschte von <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"PHP Hypertext Preprocessor\">PHP</i> met <i lang=\"en\">MySQL</i> dobei, op Deutsch])",
- "config-dbsupport-postgres": "* <i lang=\"en\">[{{int:version-db-postgres-url}} PostgreSQL]</i> es e bikannt Daatebangksüßtehm met offe Quälltäxde, un ed es och en Wahl nävve <i lang=\"en\">MySQL</i>. Et sinn_er ävver paa klein Fählersche bekannt, um mer künne et em Momang för et reschtijje Werke nit ämfähle. ([https://secure.php.net/manual/de/pgsql.installation.php Aanleidung för et Övversäze un Enreeschte von PHP met <i lang=\"en\">PostgreSQL</i> dobei, op Deutsch])",
+ "config-dbsupport-mysql": "* <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mysql-url}} MySQL]</i> es dat vum MediaWiki et eets un et bäß ongerschtöz Daatebangksüßtehm. Et leuf ävver och met <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mariadb-url}} MariaDB]</i> un <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-percona-url}} Percona Server]</i>. Di sin kumpatihbel mem <i lang=\"en\" xml:lang=\"en\">MySQL</i>. ([https://www.php.net/manual/de/mysql.installation.php Aanleidung för et Övversäze un Enreeschte von <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"PHP Hypertext Preprocessor\">PHP</i> met <i lang=\"en\">MySQL</i> dobei, op Deutsch])",
+ "config-dbsupport-postgres": "* <i lang=\"en\">[{{int:version-db-postgres-url}} PostgreSQL]</i> es e bikannt Daatebangksüßtehm met offe Quälltäxde, un ed es och en Wahl nävve <i lang=\"en\">MySQL</i>. Et sinn_er ävver paa klein Fählersche bekannt, um mer künne et em Momang för et reschtijje Werke nit ämfähle. ([https://www.php.net/manual/de/pgsql.installation.php Aanleidung för et Övversäze un Enreeschte von PHP met <i lang=\"en\">PostgreSQL</i> dobei, op Deutsch])",
"config-dbsupport-sqlite": "* <i lang=\"en\">[{{int:version-db-sqlite-url}} SQLite]</i> es e eijfach Daatebangksüßtehm, wat joot en Schoß jehallde weed. ([http://www.php.net/manual/de/pdo.installation.php Aanleidong för et Övversäze un Enreeschte von PHP met <i lang=\"en\">SQLite</i> dobei, op Deutsch])",
"config-dbsupport-oracle": "* <i lang=\"en\">[{{int:version-db-oracle-url}} Oracle]</i> es e jeschäfflesch Daatebangksüßtehm för Ferme. ([http://www.php.net/manual/de/oci8.installation.php Aanleidong för et Övversäze un Enreeschte von PHP met <i lang=\"en\" xml:lang=\"en\">OCI8</i> dobei, op Deutsch])",
- "config-dbsupport-mssql": "* Dä <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mssql-url}} Microsoft SQL Server]</i> es e jeschäfflesch Dahtebangksüßtehm för Rääschner met <i lang=\"en\" xml:lang=\"en\">Windows</i>. ([https://secure.php.net/manual/en/sqlsrv.installation.php Aanleidong för et Övversäze un Enreeschte von <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"PHP Hypertext Preprocessor\">PHP</i> met <i lang=\"en\" xml:lang=\"en\">SQLSRV </i> dobei, op Deutsch])",
+ "config-dbsupport-mssql": "* Dä <i lang=\"en\" xml:lang=\"en\">[{{int:version-db-mssql-url}} Microsoft SQL Server]</i> es e jeschäfflesch Dahtebangksüßtehm för Rääschner met <i lang=\"en\" xml:lang=\"en\">Windows</i>. ([https://www.php.net/manual/en/sqlsrv.installation.php Aanleidong för et Övversäze un Enreeschte von <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"PHP Hypertext Preprocessor\">PHP</i> met <i lang=\"en\" xml:lang=\"en\">SQLSRV </i> dobei, op Deutsch])",
"config-header-mysql": "De Enshtällunge för de <i lang=\"en\">MySQL</i> Daatebangk",
"config-header-postgres": "De Enshtällunge för de <i lang=\"en\">PostgreSQL</i> Daatebangk",
"config-header-sqlite": "De Enshtällunge för de <i lang=\"en\">SQLite</i> Daatebangk",
"config-page-upgradedoc": "Bilindkirin",
"config-page-existingwiki": "Wîkiya heye",
"config-restart": "Erê, jinûve bide destpêkirin",
- "config-apc": "[https://secure.php.net/apc APC] hate avakirin",
+ "config-apc": "[https://www.php.net/apc APC] hate avakirin",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] hate avakirin",
"config-diff3-bad": "GNU diff3 nehate dîtin.",
"config-db-type": "Cureya danegehê:",
"config-no-db": "Et konnt kee passenden Datebank-Driver fonnt ginn! Dir musst een Datebank-Driver fir PHP installéieren.\n{{PLURAL:$2|Dësn Datebank-Typ gëtt|Dës Datebank-Type ginn}} ënnerstëtzt: $1.\n\nWann Dir PHP selwer compiléiert hutt, da rekonfiguréiert en mat dem ageschalten Datebank-Client, zum Beispill an deem Dir <code>./configure --with-mysqli</code> benotzt.\nWann Dir PHP vun engem Debian oder Ubuntu Package aus installéiert hutt, da musst Dir och den php5-mysql Modul installéieren.",
"config-outdated-sqlite": "'''Warnung:''' SQLite $1 ass installéiert. Allerdengs brauch MediaWiki SQLite $2 oder méi nei. SQLite ass dofir net disponibel.",
"config-memory-bad": "'''Opgepasst:''' De Parameter <code>memory_limit</code> vu PHP ass $1.\nDat ass wahrscheinlech ze niddreg.\nD'Installatioun kéint net funktionéieren.",
- "config-apc": "[https://secure.php.net/apc APC] ass installéiert",
- "config-apcu": "[https://secure.php.net/apcu APCu] ass installéiert.",
+ "config-apc": "[https://www.php.net/apc APC] ass installéiert",
+ "config-apcu": "[https://www.php.net/apcu APCu] ass installéiert.",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ass installéiert",
"config-diff3-bad": "GNU diff3 gouf net fonnt.",
"config-git": "D'Software Git fir d'Kontroll vu Versioune gouf fonnt: <code>$1</code>.",
"config-type-sqlite": "SQLite",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ass e beléiften Open-Source-Datebanksystem an eng Alternativ zu MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Uleedung fir d'Kompilatoun vu PHP mat PostgreSQL-Ënnerstëtzung])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] ass e beléiften Open-Source-Datebanksystem an eng Alternativ zu MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Uleedung fir d'Kompilatoun vu PHP mat PostgreSQL-Ënnerstëtzung])",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] ass eng kommerziell Datebank-Software. ([http://www.php.net/manual/en/oci8.installation.php How to compile PHP mat OCI8 Ënnerstëtzung])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ass eng kommerziell Datebank-Software fir Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Wéi PHP mat SQLSRV Ënnerstëtzung kompiléieren])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] ass eng kommerziell Datebank-Software fir Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Wéi PHP mat SQLSRV Ënnerstëtzung kompiléieren])",
"config-header-mysql": "MariaDB/MySQL-Astellungen",
"config-header-postgres": "PostgreSQL-Astellungen",
"config-header-sqlite": "SQLite-Astellungen",
"config-pcre-no-utf8": "'''Fatale''': o modulo PCRE de PHP pâ ch'o segge stæto compilou sença o supporto PCRE_UTF8. A MediaWiki a-o richiede pe fonçionâ corettamente.",
"config-memory-raised": "O valô <code>memory_limit</code> de PHP o l'è $1, aomentou a $2.",
"config-memory-bad": "''Atençion:''' O valô de <code>memory_limit</code> do PHP o l'è $1.\nFoscia o l'è troppo basso.\nL'installaçion a porriæ fallî!",
- "config-apc": "[https://secure.php.net/apc APC] o l'è installou",
- "config-apcu": "[https://secure.php.net/apc APC] o l'è installou",
+ "config-apc": "[https://www.php.net/apc APC] o l'è installou",
+ "config-apcu": "[https://www.php.net/apc APC] o l'è installou",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] o l'è installou",
- "config-no-cache-apcu": "'''Atençion:''' [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ò [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] no son stæti trovæ.\nA caching di ogetti a no l'è attivâ.",
+ "config-no-cache-apcu": "'''Atençion:''' [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ò [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] no son stæti trovæ.\nA caching di ogetti a no l'è attivâ.",
"config-mod-security": "<strong>Atençion:</strong> O to serviou web o g'ha o [https://modsecurity.org/ mod_security] abilitou. Gh'è tante configuaçioin che crean di problemi a-a MediaWiki ò a atro software ch'o permette a-i utenti de pubbricâ quâ-se-segge contegnuo. Se poscibbile o doviæ ese disabilitou.\nFanni rifeimento a-a [https://modsecurity.org/documentation/ documentaçion insce-o mod_security] ò contatta o supporto tecnico do to provider de hosting se se veifica di erroî.",
"config-diff3-bad": "GNU diff3 non trovou.",
"config-git": "Trovou software de controllo da verscion Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL ò compatibbile",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki o supporta i seguenti scistemi de database:\n\n$1\n\nSe fra quelli elencæ chì de sotta no ti veddi o scistema de database che ti voriesci doeuviâ, segui e instruçioin inganciæ de d'ato pe abilitâ o supporto.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] a l'è a primma scelta pe MediaWiki e a l'è quella megio suportâ. MediaWiki a fonçion-a ascì con [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], che son compatibbili con MySQL.([https://secure.php.net/manual/en/mysqli.installation.php Comme compilâ PHP con suporto MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] o l'è un popolare scistema de database open source comme alternativa a MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Comme compilâ PHP con suporto PostgreSQL])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] a l'è a primma scelta pe MediaWiki e a l'è quella megio suportâ. MediaWiki a fonçion-a ascì con [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], che son compatibbili con MySQL.([https://www.php.net/manual/en/mysqli.installation.php Comme compilâ PHP con suporto MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] o l'è un popolare scistema de database open source comme alternativa a MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Comme compilâ PHP con suporto PostgreSQL])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] o l'è un scistema de database leggio, ch'o l'è suportou molto ben. ([http://www.php.net/manual/en/pdo.installation.php Comme compilâ PHP con suporto SQLite], o l'utilizza PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] o l'è un database de un'impreiza comerciâ. ([http://www.php.net/manual/en/oci8.installation.php Comme compilâ PHP con suporto OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] o l'è un database de un'impreiza commerciâ per Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Comme compilâ PHP con supporto SQLSRV])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] o l'è un database de un'impreiza commerciâ per Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Comme compilâ PHP con supporto SQLSRV])",
"config-header-mysql": "Impostaçioin MySQL",
"config-header-postgres": "Impostaçioin PostgreSQL",
"config-header-sqlite": "Impostaçioin SQLite",
"config-env-hhvm": "HHVM $1 yra įdiegtas.",
"config-outdated-sqlite": "<strong>Įspėjimas:</strong> jūs turite SQLite $1, kuri yra mažesnė nei minimali reikalinga versija $2. SQLite nebus prieinama.",
"config-memory-raised": "PHP <code>memory_limit</code> yra $1, padidintas iki $2.",
- "config-apc": "[https://secure.php.net/apc APC] yra įdiegtas",
- "config-apcu": "[https://secure.php.net/apcu APCu] yra įdiegtas",
+ "config-apc": "[https://www.php.net/apc APC] yra įdiegtas",
+ "config-apcu": "[https://www.php.net/apcu APCu] yra įdiegtas",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] yra įdiegtas",
- "config-no-cache-apcu": "<strong>Įspėjimas:</strong> Nepavyko rasti [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ar [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObjekto spartinimas neįjungtas.",
+ "config-no-cache-apcu": "<strong>Įspėjimas:</strong> Nepavyko rasti [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ar [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObjekto spartinimas neįjungtas.",
"config-diff3-bad": "GNU diff3 nerastas.",
"config-git": "Rasta Git versijų kontrolės sistema: <code>$1</code>.",
"config-imagemagick": "Rastas „ImageMagick“: <code>$1</code>.\nPaveikslėlių miniatiūrizavimas bus įjungtas, jeigu įgalinsite vaizdų įkėlimą.",
"config-restart": "Jā, restartēt",
"config-env-php": "PHP $1 ir uzstādīts.",
"config-env-hhvm": "HHVM $1 ir uzstādīts.",
- "config-apcu": "[https://secure.php.net/apcu APCu] ir uzstādīts",
+ "config-apcu": "[https://www.php.net/apcu APCu] ir uzstādīts",
"config-diff3-bad": "GNU diff3 nav atrasts.",
"config-db-host-oracle": "Datubāzes TNS:",
"config-db-name": "Datubāzes nosaukums:",
"config-pcre-no-utf8": "<strong>Кобно</strong>: PCRE-модулот на PHP е срочен без поддршка за PCRE_UTF8.\nМедијаВики бара поддршка за UTF-8 за да може да работи правилно.",
"config-memory-raised": "<code>memory_limit</code> за PHP изнесува $1, зголемен на $2.",
"config-memory-bad": "<strong>Предупредување:</strong> <code>memory_limit</code> за PHP изнесува $1.\nОва е веројатно премалку.\nВоспоставката може да не успее!",
- "config-apc": "[https://secure.php.net/apc APC] е воспоставен",
- "config-apcu": "[https://secure.php.net/apcu APCu] е воспоставен",
+ "config-apc": "[https://www.php.net/apc APC] е воспоставен",
+ "config-apcu": "[https://www.php.net/apcu APCu] е воспоставен",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] е воспоставен",
- "config-no-cache-apcu": "<strong>Предупредување:</strong> Не можев да го најдам [https://secure.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].",
+ "config-no-cache-apcu": "<strong>Предупредување:</strong> Не можев да го најдам [https://www.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].",
"config-mod-security": "'''Предупредување''': на вашиот опслужувач има овозможено [https://modsecurity.org/ mod_security]. Ако не е поставено како што треба, ова може да предизвика проблеми кај МедијаВики и други програми што им овозможуваат на корисниците да објавуваат произволни содржини.\nПогледнете ја [https://modsecurity.org/documentation/ mod_security документацијата] или обратете се кај домаќинот ако наидете на случајни грешки.",
"config-diff3-bad": "Не го пронајдов споредникот за текст ГНУ разл3 (GNU diff3). Засега ова можете да го занемарите, но затоа пак спротиставеностите во уредувањата може да зачестат.",
"config-git": "Го пронајдов Git програмот за контрола на верзии: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "МедијаВики ги поддржува следниве системи на бази на податоци:\n\n$1\n\nАко системот што сакате да го користите не е наведен подолу, тогаш проследете ја горенаведената врска со инструкции за да овозможите поддршка за тој систем.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] е главната цел на МедијаВики и најдобро е поддржан. МедијаВики работи и со [{{int:version-db-mysql-url}} MySQL] и [{{int:version-db-percona-url}} Percona], кои се складни со MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Како да срочите PHP со поддршка за MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] е популарен систем на бази на податоци со отворен код кој претставува алтернатива на MySQL ([https://secure.php.net/manual/en/pgsql.installation.php како да составите PHP со поддршка за PostgreSQL]). ([https://secure.php.net/manual/en/pgsql.installation.php Како да срочите PHP со поддршка за PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] е лесен систем за бази на податоци кој е многу добро поддржан. ([https://secure.php.net/manual/en/pdo.installation.php Како да составите PHP со поддршка за SQLite], користи PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] е база на податоци на комерцијално претпријатие. ([https://secure.php.net/manual/en/oci8.installation.php Како да составите PHP со поддршка за OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] е база на податоци на комерцијално претпријатиe за Windows ([https://secure.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV поддршка])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] е главната цел на МедијаВики и најдобро е поддржан. МедијаВики работи и со [{{int:version-db-mysql-url}} MySQL] и [{{int:version-db-percona-url}} Percona], кои се складни со MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Како да срочите PHP со поддршка за MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] е популарен систем на бази на податоци со отворен код кој претставува алтернатива на MySQL ([https://www.php.net/manual/en/pgsql.installation.php како да составите PHP со поддршка за PostgreSQL]). ([https://www.php.net/manual/en/pgsql.installation.php Како да срочите PHP со поддршка за PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] е лесен систем за бази на податоци кој е многу добро поддржан. ([https://www.php.net/manual/en/pdo.installation.php Како да составите PHP со поддршка за SQLite], користи PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] е база на податоци на комерцијално претпријатие. ([https://www.php.net/manual/en/oci8.installation.php Како да составите PHP со поддршка за OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] е база на податоци на комерцијално претпријатиe за Windows ([https://www.php.net/manual/en/sqlsrv.installation.php How to compile PHP with SQLSRV поддршка])",
"config-header-mysql": "Нагодувања на MariaDB/MySQL",
"config-header-postgres": "Нагодувања на PostgreSQL",
"config-header-sqlite": "Нагодувања на SQLite",
"config-outdated-sqlite": "<strong>इशारा:</strong> आपणापाशी SQLite $1 आहे, जी किमान आवश्यक आवृत्ती $2 पेक्षा, निम्न आहे. SQLite अनुपलब्ध राहील.",
"config-memory-raised": "पीएचपीची <code>memory_limit</code> ही $1 आहे, त्यास $2 ला वाढविली.",
"config-memory-bad": "पीएचपीची <code>memory_limit</code> ही $1 आहे.\nही बरीच खालच्या स्तरावरची आहे.\nउभारणी अयशस्वी होऊ शकते!",
- "config-apc": "[https://secure.php.net/apc APC] उभारली आहे",
+ "config-apc": "[https://www.php.net/apc APC] उभारली आहे",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] उभारली आहे",
"config-diff3-bad": "GNU diff3 सापडली नाही.",
"config-git-bad": "गीट आवृत्ती नियमन संचेतन सापडली नाही.",
"config-no-fts3": "<strong>Amaran:</strong> SQLite disusun tanpa [//sqlite.org/fts3.html modil FTS3], maka ciri-ciri pencarian tidak akan disediakan pada backend ini.",
"config-pcre-old": "<strong>Amaran keras:</strong> PCRE $1 ke atas diperlukan.\nBinari PHP anda berpaut dengan PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Keterangan lanjut].",
"config-memory-bad": "<strong>Amaran:</strong> <code>memory_limit</code> (Had memori) PHP ialah $1.\nIni mungkin terlalu rendah.\nPemasangan mungkin akan gagal!",
- "config-apc": "[https://secure.php.net/apc APC] dipasang",
+ "config-apc": "[https://www.php.net/apc APC] dipasang",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] dipasang",
"config-mod-security": "<strong>Amaran:</strong> Pelayan web anda dihidupkan [https://modsecurity.org/ mod_security]/mod_security2. Kebanyakan konfigurasinya yang umum boleh menimbulkan kesulitan untuk MediaWiki dan perisian-perisian lain yang membolehkan pengguna untuk mengeposkan kandungan yang sewenang-wenang.\nJika boleh, ciri-ciri ini harus dimatikan. Jika tidak, rujuki [https://modsecurity.org/documentation/ dokumentasi mod_security] atau hubungi bantuan hos anda jika anda menghadapi ralat sembarangan.",
"config-diff3-bad": "GNU diff3 tidak dijumpai.",
"config-env-hhvm": "اچاچویام $1 نصب بیه.",
"config-unicode-using-intl": "عادی یونیکد وسه [https://pecl.php.net/intl افزونهٔ intl برای PECL] جه استفاده هاکن.",
"config-memory-raised": "PHP's <code>memory_limit</code>, نسخهٔ $1 هسته، ونه نسخهٔ $2 ره بَیری آپگریت هاکنی.",
- "config-apc": "[https://secure.php.net/apc APC] نصب بیه.",
- "config-apcu": "[https://secure.php.net/apcu APCu] نصب بیه.",
+ "config-apc": "[https://www.php.net/apc APC] نصب بیه.",
+ "config-apcu": "[https://www.php.net/apcu APCu] نصب بیه.",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] نصب بیه.",
"config-diff3-bad": "GNU diff3 پیدا نیه.",
"config-mssql-auth": "نوع تأیید:",
"config-pcre-no-utf8": "<strong>Fatale:</strong> 'E module PCRE d' 'o PHP pare ca se so' compilate senza PCRE_UTF8 supporto.\nA MediaWiki serve nu supporto UTF-8 pe' putè funziunà apposto.",
"config-memory-raised": "'O valore 'e PHP <code>memory_limit</code> è $1, aumentato a $2.",
"config-memory-bad": "<strong>Attenziò:</strong> 'o valore 'e PHP <code>memory_limit</code> è $1.\nProbabbilmente troppo basso.\n'A installazione se putesse scassà!",
- "config-apc": "[https://secure.php.net/apc APC] è installato",
- "config-apcu": "[https://secure.php.net/apcu APCu] è installato",
+ "config-apc": "[https://www.php.net/apc APC] è installato",
+ "config-apcu": "[https://www.php.net/apcu APCu] è installato",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] è installato",
- "config-no-cache-apcu": "<strong>Attenziò:</strong> [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] nun so' state truvate.\n'A funziona caching 'e ll'oggette non è apicciata.",
+ "config-no-cache-apcu": "<strong>Attenziò:</strong> [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] nun so' state truvate.\n'A funziona caching 'e ll'oggette non è apicciata.",
"config-mod-security": "<strong>Attenziò:</strong> 'O servitore web vuosto téne [https://modsecurity.org/ mod_security]/mod_security2 appicciato. Ce stanno tante mpustaziune commune ca 'o facessero causà prubbleme a MediaWiki e ll'ati software ca permettessero ll'utente 'e pubbrecà cuntenute.\nSi putite, stutate sta funziona. Sinò, riferite 'a [https://modsecurity.org/documentation/ documentaziona ncopp' 'o mod_security] o cuntattate 'o host vuosto pe' ve dà supporto quanno se scummogliasse cocch'errore.",
"config-diff3-bad": "GNU diff3 nun truvato.",
"config-git": "Truvato software 'e cuntrollo d' 'a verziona Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL o compatibbele",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki supporta 'e sisteme 'e database ccà abbascio:\n\n$1\n\nSi nfra chiste ccà nun vedite 'o sistema 'e database ca vulite ausà, allora avite liegge 'e instruziune ccà ncoppa pe' ne dà supporto.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] è 'a configurazione cchiù mmeglio p' 'o MediaWiki e è chilla meglio suppurtata. MediaWiki può faticà pure cu' [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], ca fossero MariaDB cumpatibbele. ([https://secure.php.net/manual/en/mysqli.installation.php Comme s'adda fà pe' cumpilà PHP cu suppuorto MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] è nu sistema canusciuto 'e database open source ca fosse n'alternativa a MySQL.\n([https://secure.php.net/manual/en/pgsql.installation.php Comme s'avess'a cumpilà PHP cu suppuorto PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] è nu sistema 'e database leggero, ca fosse assaje buono suppurtato.\n([https://secure.php.net/manual/en/pdo.installation.php Comme cumpilà PHP cu suppuorto SQLite], aùsa PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] è nu database 'e na fraveca commerciale. ([https://secure.php.net/manual/en/oci8.installation.php Comme cumpilà PHP cu suppuorto OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] è nu database 'e na fraveca commerciale p' 'o Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Comme cumpilà PHP cu suppuorto SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] è 'a configurazione cchiù mmeglio p' 'o MediaWiki e è chilla meglio suppurtata. MediaWiki può faticà pure cu' [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], ca fossero MariaDB cumpatibbele. ([https://www.php.net/manual/en/mysqli.installation.php Comme s'adda fà pe' cumpilà PHP cu suppuorto MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] è nu sistema canusciuto 'e database open source ca fosse n'alternativa a MySQL.\n([https://www.php.net/manual/en/pgsql.installation.php Comme s'avess'a cumpilà PHP cu suppuorto PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] è nu sistema 'e database leggero, ca fosse assaje buono suppurtato.\n([https://www.php.net/manual/en/pdo.installation.php Comme cumpilà PHP cu suppuorto SQLite], aùsa PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] è nu database 'e na fraveca commerciale. ([https://www.php.net/manual/en/oci8.installation.php Comme cumpilà PHP cu suppuorto OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] è nu database 'e na fraveca commerciale p' 'o Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Comme cumpilà PHP cu suppuorto SQLSRV])",
"config-header-mysql": "Mpustaziune MariaDB/MySQL",
"config-header-postgres": "Mpustaziune PostgreSQL",
"config-header-sqlite": "Mpustaziune SQLite",
"config-pcre-no-utf8": "'''Fatal''': PHPs PCRE modul ser ut til å være kompilert uten PCRE_UTF8-støtte.\nMediaWiki krever UTF-8-støtte for å fungere riktig.",
"config-memory-raised": "PHPs <code>memory_limit</code> er $1, økt til $2.",
"config-memory-bad": "'''Advarsel:''' PHPs <code>memory_limit</code> er $1.\nDette er sannsynligvis for lavt.\nInstallasjonen kan mislykkes!",
- "config-apc": "[https://secure.php.net/apc APC] er installert",
- "config-apcu": "[https://secure.php.net/apcu APCu] er installert",
+ "config-apc": "[https://www.php.net/apc APC] er installert",
+ "config-apcu": "[https://www.php.net/apcu APCu] er installert",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] er installert",
- "config-no-cache-apcu": "<strong>Advarsel:</strong> Kunne ikke finne [https://secure.php.net/apcu APCu] eller [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObjekthurtiglagring er ikke aktivert.",
+ "config-no-cache-apcu": "<strong>Advarsel:</strong> Kunne ikke finne [https://www.php.net/apcu APCu] eller [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObjekthurtiglagring er ikke aktivert.",
"config-mod-security": "'''Advarsel''': Din web-tjener har [https://modsecurity.org/ mod_security] påslått. Hvis denne er feilinnstilt, kan det gi problemer for MediaWiki eller annen programvare som tillater brukere å poste vilkårlig innhold.\nSjekk [https://modsecurity.org/documentation/ mod_security-dokumentasjonen] eller ta kontakt med din nettleverandør hvis du opplever tilfeldige feil.",
"config-diff3-bad": "GNU diff3 ikke funnet.",
"config-git": "Har funnet Git version control software: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQLServer",
"config-support-info": "MediaWiki støtter følgende databasesystem:\n\n$1\n\nHvis du ikke ser databasesystemet du prøver å bruke i listen nedenfor, følg instruksjonene det er lenket til over for å aktivere støtte.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] er den foretrukne databasetypen for MediaWiki og har best støtte. MediaWiki fungerer også med [{{int:version-db-mysql-url}} MySQL] og [{{int:version-db-percona-url}} Percona Server], som begge er MariaDB-kompatible. ([https://secure.php.net/manual/en/mysqli.installation.php Hvordan kompilere PHP med MySQL-støtte])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] er et populært åpen kildekode-databasesystem og et alternativ til MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Hvordan kompilere PHP med PostgreSQL-støtte])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] er den foretrukne databasetypen for MediaWiki og har best støtte. MediaWiki fungerer også med [{{int:version-db-mysql-url}} MySQL] og [{{int:version-db-percona-url}} Percona Server], som begge er MariaDB-kompatible. ([https://www.php.net/manual/en/mysqli.installation.php Hvordan kompilere PHP med MySQL-støtte])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] er et populært åpen kildekode-databasesystem og et alternativ til MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Hvordan kompilere PHP med PostgreSQL-støtte])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] er et lettvekts-databasesystem som har veldig god støtte. ([http://www.php.net/manual/en/pdo.installation.php Hvordan kompilere PHP med SQLite-støtte], bruker PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] er en kommersiell database for bedrifter. ([https://secure.php.net/manual/en/oci8.installation.php Hvordan kompilere PHP med OCI8-støtte])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] er et kommersielt databasesystem under Windows for bedrifter. ([https://secure.php.net/manual/en/sqlsrv.installation.php Hvordan kompilere PHP med SQLSRV-støtte])",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] er en kommersiell database for bedrifter. ([https://www.php.net/manual/en/oci8.installation.php Hvordan kompilere PHP med OCI8-støtte])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] er et kommersielt databasesystem under Windows for bedrifter. ([https://www.php.net/manual/en/sqlsrv.installation.php Hvordan kompilere PHP med SQLSRV-støtte])",
"config-header-mysql": "MariadB/MySQL-innstillinger",
"config-header-postgres": "PostgreSQL-innstillinger",
"config-header-sqlite": "SQLite-innstillinger",
"config-license-help": "Mange åpne wikier legger alle bidrag under en [https://freedomdefined.org/Definition gratislisens].\nDette gir en følelse av felleseie og stimulerer til langvarige bidrag.\nDette er normalt unødvendig for en privat eller virksomhetsbegrenset wiki.\n\nHvis du ønsker å kunne bruke tekst fra Wikipedia, og at Wikipedia skal kunne ta i mot tekst kopiert fra din wiki, bør du velge <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia brukte tidligere GNU Free Documentation License.\nGFDL er en grei lisens, med vanskelig å forstå.\nDet er også vanskelig å gjenbruke innhold lisensiert under GFDL.",
"config-email-settings": "E-postinnstillinger",
"config-enable-email": "Aktiver utgående e-post",
- "config-enable-email-help": "Hvis du vil at e-post skal virke, må [https://secure.php.net/manual/en/mail.configuration.php PHPs epostinnstillinger] konfigureres riktig.\nHvis du ikke ønsker noen epostfunksjoner, kan du deaktivere dem her.",
+ "config-enable-email-help": "Hvis du vil at e-post skal virke, må [https://www.php.net/manual/en/mail.configuration.php PHPs epostinnstillinger] konfigureres riktig.\nHvis du ikke ønsker noen epostfunksjoner, kan du deaktivere dem her.",
"config-email-user": "Aktiver e-post mellom brukere",
"config-email-user-help": "Tillat alle brukere å sende hverandre e-post hvis de har aktivert det i deres innstillinger.",
"config-email-usertalk": "Aktiver brukerdiskusjonssidevarsler",
"config-pcre-no-utf8": "<strong>Onherstelbare fout:</strong> de module PRCE van PHP lijkt te zijn gecompileerd zonder ondersteuning voor PCRE_UTF8.\nMediaWiki heeft ondersteuning voor UTF-8 nodig om correct te kunnen werken.",
"config-memory-raised": "PHP's <code>memory_limit</code> is $1 en is verhoogd tot $2.",
"config-memory-bad": "'''Waarschuwing:''' PHP's <code>memory_limit</code> is $1.\nDit is waarschijnlijk te laag.\nDe installatie kan mislukken!",
- "config-apc": "[https://secure.php.net/apc APC] is op dit moment geïnstalleerd",
- "config-apcu": "[https://secure.php.net/apcu APCu] is geïnstalleerd",
+ "config-apc": "[https://www.php.net/apc APC] is op dit moment geïnstalleerd",
+ "config-apcu": "[https://www.php.net/apcu APCu] is geïnstalleerd",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] is op dit moment geïnstalleerd",
- "config-no-cache-apcu": "<strong>Waarschuwing:</strong> [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] of [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] is niet aangetroffen.\nHet cachen van objecten is niet ingeschakeld.",
+ "config-no-cache-apcu": "<strong>Waarschuwing:</strong> [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] of [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] is niet aangetroffen.\nHet cachen van objecten is niet ingeschakeld.",
"config-mod-security": "<strong>Waarschuwing:</strong> Uw webserver heeft de module [https://modsecurity.org/ mod_security]/mod_security2 ingeschakeld. Veel standaard instellingen hiervan zorgen voor problemen in combinatie met MediaWiki en andere software die gebruikers in staat stelt willekeurige inhoud te posten.\nIndien mogelijk, zou deze moeten worden uitgeschakeld. Lees anders de [https://modsecurity.org/documentation/ documentatie over mod_security] of neem contact op met de helpdesk van uw provider als u tegen problemen aanloopt.",
"config-diff3-bad": "GNU diff3 niet aangetroffen. U kunt dit voorlopig negeren, maar bewerkingsconflicten kunnen vaker voorkomen.",
"config-git": "Versiecontrolesoftware git is aangetroffen: <code>$1</code>",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki ondersteunt de volgende databasesystemen:\n\n$1\n\nAls u het databasesysteem dat u wilt gebruiken niet in de lijst terugvindt, volg dan de handleiding waarnaar hierboven wordt verwezen om ondersteuning toe te voegen.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] is de primaire database voor MediaWiki en wordt het best ondersteund. MediaWiki werkt ook met [{{int:version-db-mysql-url}} MySQL] en [{{int:version-db-percona-url}} Percona Server], die MariaDB-compatibel zijn ([https://secure.php.net/manual/en/mysqli.installation.php hoe PHP te compileren met MySQL-ondersteuning]).",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is een populair open source databasesysteem als alternatief voor MySQL.([https://secure.php.net/manual/en/pgsql.installation.php Hoe u PHP kunt compileren met ondersteuning voor PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is een zeer goed ondersteund lichtgewicht databasesysteem ([https://secure.php.net/manual/en/pdo.installation.php hoe PHP gecompileerd moet zijn met ondersteuning voor SQLite]; gebruikt PDO).",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is een commerciële database voor grote bedrijven ([https://secure.php.net/manual/en/oci8.installation.php PHP compileren met ondersteuning voor OCI8]).",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is een commerciële enterprisedatabase voor Windows ([https://secure.php.net/manual/en/sqlsrv.installation.php PHP compileren met ondersteuning voor SQLSRV]).",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] is de primaire database voor MediaWiki en wordt het best ondersteund. MediaWiki werkt ook met [{{int:version-db-mysql-url}} MySQL] en [{{int:version-db-percona-url}} Percona Server], die MariaDB-compatibel zijn ([https://www.php.net/manual/en/mysqli.installation.php hoe PHP te compileren met MySQL-ondersteuning]).",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is een populair open source databasesysteem als alternatief voor MySQL.([https://www.php.net/manual/en/pgsql.installation.php Hoe u PHP kunt compileren met ondersteuning voor PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is een zeer goed ondersteund lichtgewicht databasesysteem ([https://www.php.net/manual/en/pdo.installation.php hoe PHP gecompileerd moet zijn met ondersteuning voor SQLite]; gebruikt PDO).",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is een commerciële database voor grote bedrijven ([https://www.php.net/manual/en/oci8.installation.php PHP compileren met ondersteuning voor OCI8]).",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is een commerciële enterprisedatabase voor Windows ([https://www.php.net/manual/en/sqlsrv.installation.php PHP compileren met ondersteuning voor SQLSRV]).",
"config-header-mysql": "MariaDB/MySQL-instellingen",
"config-header-postgres": "PostgreSQL-instellingen",
"config-header-sqlite": "SQLite-instellingen",
"config-license-help": "In veel openbare wiki's zijn alle bijdragen beschikbaar onder een [https://freedomdefined.org/Definition vrije licentie].\nDit helpt bij het creëren van een gevoel van gemeenschappelijk eigendom en stimuleert bijdragen op lange termijn.\nDit is over het algemeen niet nodig is voor een particuliere of zakelijke wiki.\n\nAls u teksten uit Wikipedia wilt kunnen gebruiken en u wilt het mogelijk maken teksten uit uw wiki naar Wikipedia te kopiëren, kies dan de licentie <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nDe GNU Free Documentation License is de oude licentie voor inhoud uit Wikipedia.\nDit is nog steeds een geldige licentie, maar deze licentie is lastig te begrijpen.\nHet is ook lastig inhoud te hergebruiken onder de GFDL.",
"config-email-settings": "E-mailinstellingen",
"config-enable-email": "Uitgaande e-mail inschakelen",
- "config-enable-email-help": "Als u wilt dat e-mailen mogelijk is, dan moeten de [https://secure.php.net/manual/en/mail.configuration.php e-mailinstellingen van PHP] correct zijn.\nAls u niet wilt dat e-mailen mogelijk is, dan kunt u de instellingen hier uitschakelen.",
+ "config-enable-email-help": "Als u wilt dat e-mailen mogelijk is, dan moeten de [https://www.php.net/manual/en/mail.configuration.php e-mailinstellingen van PHP] correct zijn.\nAls u niet wilt dat e-mailen mogelijk is, dan kunt u de instellingen hier uitschakelen.",
"config-email-user": "E-mail tussen gebruikers inschakelen",
"config-email-user-help": "Gebruikers toestaan e-mail aan elkaar te verzenden als dit in de voorkeuren is ingesteld.",
"config-email-usertalk": "Gebruikersoverlegmeldingen inschakelen",
"config-env-hhvm": "HHVM $1 es installat.",
"config-unicode-using-intl": "Utilizacion de [https://pecl.php.net/intl l'extension PECL intl] per la normalizacion Unicode.",
"config-memory-raised": "Lo paramètre <code>memory_limit</code> de PHP èra a $1, portat a $2.",
- "config-apc": "[https://secure.php.net/apc APC] es installat",
+ "config-apc": "[https://www.php.net/apc APC] es installat",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] es installat",
"config-diff3-bad": "GNU diff3 pas trobat.",
"config-git": "Logicial de contraròtle de version Git trobat : <code>$1</code>.",
"config-pcre-no-utf8": "'''Błąd krytyczny''' – wydaje się, że moduł PCRE w PHP został skompilowany bez wsparcia dla UTF‐8.\nMediaWiki wymaga wsparcia dla UTF‐8 do prawidłowego działania.",
"config-memory-raised": "PHP <code>memory_limit</code> było ustawione na $1, zostanie zwiększone do $2.",
"config-memory-bad": "'''Uwaga:''' PHP <code>memory_limit</code> jest ustawione na $1.\nTo jest prawdopodobnie zbyt mało.\nInstalacja może się nie udać!",
- "config-apc": "[https://secure.php.net/apc APC] jest zainstalowany",
- "config-apcu": "[https://secure.php.net/apcu APCu] jest zainstalowany",
+ "config-apc": "[https://www.php.net/apc APC] jest zainstalowany",
+ "config-apcu": "[https://www.php.net/apcu APCu] jest zainstalowany",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] jest zainstalowany",
- "config-no-cache-apcu": "<strong>Ostrzeżenie:</strong> Nie można było znaleźć [https://secure.php.net/apcu APCu] ani [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nPamięć podręczna obiektów nie została włączona.",
+ "config-no-cache-apcu": "<strong>Ostrzeżenie:</strong> Nie można było znaleźć [https://www.php.net/apcu APCu] ani [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nPamięć podręczna obiektów nie została włączona.",
"config-mod-security": "''' Ostrzeżenie ''': Serwer sieci web ma włączone [https://modsecurity.org/ mod_security]. Jeśli jest niepoprawnie skonfigurowane, może być przyczyną problemów MediaWiki lub innego oprogramowania, które pozwala użytkownikom na wysyłanie dowolnej zawartości.\nSprawdź w [https://modsecurity.org/documentation/ dokumentacji mod_security] lub skontaktuj się z obsługa hosta, jeśli wystąpią losowe błędy.",
"config-diff3-bad": "Nie znaleziono funkcjonalności porównywania tekstu GNU diff3. Możesz zignorować ten komunikat, jednak konflikty edycji będą wówczas częstsze.",
"config-git": "Znaleziono oprogramowanie kontroli wersji Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL lub kompatybilna",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki może współpracować z następującymi systemami baz danych:\n\n$1\n\nPoniżej wyświetlone są systemy baz danych gotowe do użycia. Jeżeli poniżej brakuje bazy danych, z której chcesz skorzystać, oznacza to, że brakuje odpowiedniego oprogramowania lub zostało ono niepoprawnie skonfigurowane. Powyżej znajdziesz odnośniki do dokumentacji, która pomoże w konfiguracji odpowiednich komponentów.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] jest bazą danych, na której rozwijane jest oprogramowanie MediaWiki. MediaWiki działa również z [{{int:version-db-mysql-url}} MySQL] i [{{int:version-db-percona-url}} Percona Server], które są zgodne z MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Zobacz, jak skompilować PHP ze wsparciem dla MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] jest popularnym, otawrtym systemem baz danych, często stosowanym jako alternatywa dla MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Zobacz, jak skompilować PHP z obsługą PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] jest niewielkim systemem bazy danych, z którym MediaWiki bardzo dobrze współpracuje. ([https://secure.php.net/manual/pl/pdo.installation.php Zobacz, jak skompilować PHP ze wsparciem dla SQLite], korzystając z PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] jest komercyjną profesjonalną bazą danych. ([https://secure.php.net/manual/pl/oci8.installation.php Jak skompilować PHP ze wsparciem dla OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] jest komercyjną profesjonalną bazą danych. ([https://secure.php.net/manual/pl/sqlsrv.installation.php Jak skompilować PHP ze wsparciem dla SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] jest bazą danych, na której rozwijane jest oprogramowanie MediaWiki. MediaWiki działa również z [{{int:version-db-mysql-url}} MySQL] i [{{int:version-db-percona-url}} Percona Server], które są zgodne z MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Zobacz, jak skompilować PHP ze wsparciem dla MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] jest popularnym, otawrtym systemem baz danych, często stosowanym jako alternatywa dla MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Zobacz, jak skompilować PHP z obsługą PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] jest niewielkim systemem bazy danych, z którym MediaWiki bardzo dobrze współpracuje. ([https://www.php.net/manual/pl/pdo.installation.php Zobacz, jak skompilować PHP ze wsparciem dla SQLite], korzystając z PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] jest komercyjną profesjonalną bazą danych. ([https://www.php.net/manual/pl/oci8.installation.php Jak skompilować PHP ze wsparciem dla OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] jest komercyjną profesjonalną bazą danych. ([https://www.php.net/manual/pl/sqlsrv.installation.php Jak skompilować PHP ze wsparciem dla SQLSRV])",
"config-header-mysql": "Ustawienia MariaDB/MySQL",
"config-header-postgres": "Ustawienia PostgreSQL",
"config-header-sqlite": "Ustawienia SQLite",
"config-license-help": "Wiele publicznych wiki umieszcza wszystkie dopisane treści na [https://freedomdefined.org/Definition wolnej licencji].\nPomaga to tworzyć poczucie wspólnoty i zachęca do długoterminowego wkładu.\nNie jest to zazwyczaj konieczne w prywatnych lub firmowych wiki.\n\nJeśli chcesz móc użyć tekstu z Wikipedii i chcesz Wikipedia mogła zaakceptować tekst skopiowany z twojej wiki, należy wybrać <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia używała poprzednio GNU Free Documentation License.\nGFDL jest poprawną licencję, ale trudno ją zrozumieć.\nTrudno także ponowne użyć zawartości na licencji GFDL.",
"config-email-settings": "Ustawienia e-maili",
"config-enable-email": "Włącz wychodzące wiadomości e–mail",
- "config-enable-email-help": "Jeśli chcesz, aby działał e-mail, [https://secure.php.net/manual/pl/mail.configuration.php Ustawienia poczty PHP] muszą być poprawnie wprowadzone.\nJeśli nie chcesz jakichś funkcji poczty e-mail, można je wyłączyć tutaj.",
+ "config-enable-email-help": "Jeśli chcesz, aby działał e-mail, [https://www.php.net/manual/pl/mail.configuration.php Ustawienia poczty PHP] muszą być poprawnie wprowadzone.\nJeśli nie chcesz jakichś funkcji poczty e-mail, można je wyłączyć tutaj.",
"config-email-user": "Włącz możliwość przesyłania e‐maili pomiędzy użytkownikami",
"config-email-user-help": "Zezwalaj użytkownikom na wysyłanie wzajemnie e‐maili, jeśli będą mieć włączoną tę funkcję w swoich preferencjach.",
"config-email-usertalk": "Włącz powiadamianie o zmianach na stronie dyskusji użytkownika",
"config-install-done": "<strong>'''Gratulacje!</strong>\nUdało Ci się zainstalować MediaWiki.\n\nInstalator wygenerował plik konfiguracyjny <code>LocalSettings.php</code>.\n\nMusisz go pobrać i umieścić w katalogu głównym Twojej instalacji wiki (tym samym katalogu co index.php). Pobieranie powinno zacząć się automatycznie.\n\nJeżeli pobieranie nie zostało zaproponowane lub jeśli użytkownik je anulował, można ponownie uruchomić pobranie klikając poniższe łącze:\n\n$3\n\n<strong>Uwaga</strong>: Jeśli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie już dostępny po zakończeniu instalacji.\n\nPo załadowaniu pliku konfiguracyjnego możesz <strong>[$2 wejść na wiki]</strong>.",
"config-install-done-path": "<strong>Gratulacje!</strong>\nZainstalowałeś właśnie MediaWiki.\n\nInstalator wygenerował plik <code>LocalSettings.php</code>.\nZawiera całą Twoją konfigurację.\n\nMusisz go pobrać i umieścić w <code>$4</code>. Pobieranie powinno rozpocząć się automatycznie.\n\nJeżeli nie pojawiła się informacja o pobieraniu lub jeżeli ja anulowałeś, kliknij poniższy link:\n\n$3\n\n<strong>Uwaga:</strong> Jeżeli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie potem dostępny, jeżeli wyjdziesz z instalacji bez jego pobrania.\n\nGdy to będzie zrobione, możesz <strong>[$2 wejść na swoją wiki]</strong>.",
"config-install-success": "MediaWiki została pomyślnie zainstalowana. Możesz teraz\nodwiedzić <$1$2>, aby zobaczyć swoją wiki.\nJeśli masz pytania, sprawdź naszą listę najczęściej zadawanych pytań:\n<https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> lub użyj jednej z\nform wsparcia odsyłanej z tej strony.",
+ "config-install-db-success": "Baza danych została pomyślnie utworzona",
"config-download-localsettings": "Pobierz <code>LocalSettings.php</code>",
"config-help": "pomoc",
"config-help-tooltip": "kliknij, aby rozwinąć",
"config-pcre-no-utf8": "'''Fatal''': ël mòdul PCRE ëd PHP a smija esse compilà sensa l'apògg PCRE_UTF8.\nMediaWiki a ciama l'apògg d'UTF8 për marcé për da bin.",
"config-memory-raised": "<code>memory_limit</code> ëd PHP a l'é $1, aussà a $2.",
"config-memory-bad": "'''Avis:''' <code>memory_limit</code> ëd PHP a l'é $1.\nSossì a l'é probabilment tròp bass.\nL'instalassion a peul falì!",
- "config-apc": "[https://secure.php.net/apc APC] a l'é instalà",
+ "config-apc": "[https://www.php.net/apc APC] a l'é instalà",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] a l'é instalà",
"config-mod-security": "'''Avis''': Sò servent për l'aragnà a l'ha [https://modsecurity.org/ mod_security] abilità. Se mal configurà, a peul causé dij problema për MediaWiki o d'àutri programa ch'a përmëtto a j'utent dë spedì un contnù qualsëssìa.\nCh'a fasa arferiment a la [https://modsecurity.org/documentation/ mod_security documentassion] o ch'a contata l'echip ëd sò servissi s'a-j rivo dj'eror casuaj.",
"config-diff3-bad": "GNU diff3 pa trovà.",
"config-oracle-def-ts": "Spassi dla tàula dë stàndard:",
"config-oracle-temp-ts": "Spassi dla tàula temporani:",
"config-support-info": "MediaWiki a manten ij sistema ëd base ëd dàit sì-dapress:\n\n$1\n\nS'a vëd pa listà sì-sota ël sistema ëd base ëd dàit ch'a preuva a dovré, antlora va andaré a j'istrussion dl'anliura sì-dzora për abilité ël manteniment.",
- "config-dbsupport-mysql": "* $1 e l'é l'obietiv primari për MediaWiki e a l'é mej mantnù ([https://secure.php.net/manual/en/mysql.installation.php com compilé PHP con ël manteniment MySQL])",
- "config-dbsupport-postgres": "* $1 e l'é un sistema ëd base ëd dàit popolar a sorgiss duverta com alternativa a MySQL ([https://secure.php.net/manual/en/pgsql.installation.php com compilé PHP con ël manteniment ëd PostgreSQL]). A peulo ess-ie chèich cit bigat, e a l'é nen arcomandà ëd dovrelo an n'ambient ëd produssion.",
+ "config-dbsupport-mysql": "* $1 e l'é l'obietiv primari për MediaWiki e a l'é mej mantnù ([https://www.php.net/manual/en/mysql.installation.php com compilé PHP con ël manteniment MySQL])",
+ "config-dbsupport-postgres": "* $1 e l'é un sistema ëd base ëd dàit popolar a sorgiss duverta com alternativa a MySQL ([https://www.php.net/manual/en/pgsql.installation.php com compilé PHP con ël manteniment ëd PostgreSQL]). A peulo ess-ie chèich cit bigat, e a l'é nen arcomandà ëd dovrelo an n'ambient ëd produssion.",
"config-dbsupport-sqlite": "* $1 e l'é un sistema ëd base ëd dàit leger che a l'é motobin bin mantnù ([http://www.php.net/manual/en/pdo.installation.php com compilé PHP con ël manteniment ëd SQLite], a deuvra PDO)",
"config-dbsupport-oracle": "* $1 a l'é na base ëd dàit comersial për j'amprèise. ([http://www.php.net/manual/en/oci8.installation.php Com compilé PHP con ël manteniment OCI8])",
"config-header-mysql": "Ampostassion MySQL",
"config-restart": "هو، سر له نوي يې پيل کړه",
"config-env-php": "د $1 PHP نصب شو.",
"config-env-hhvm": "HHVM $1 نصب شو.",
- "config-apc": "[https://secure.php.net/apc APC] نصب شو",
+ "config-apc": "[https://www.php.net/apc APC] نصب شو",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] نصب شو",
"config-diff3-bad": "جي ان يو ډيف3 و نه موندل شو.",
"config-using-server": "د پالنگر نوم \"<nowiki>$1</nowiki>\" کارېږي.",
"config-pcre-no-utf8": "<strong>Erro fatal:</strong> O módulo PCRE do PHP parece ser compilado sem suporte a PCRE_UTF8.\nO MediaWiki requer suporte a UTF-8 para funcionar corretamente.",
"config-memory-raised": "A configuração <code>memory_limit</code> do PHP era $1; foi aumentada para $2.",
"config-memory-bad": "<strong>Aviso:</strong> A configuração <code>memory_limit</code> do PHP é $1.\nIsso provavelmente é muito baixo.\nA instalação pode falhar!",
- "config-apc": "[https://secure.php.net/apc APC] está instalado",
- "config-apcu": "[https://secure.php.net/apcu APCu] está instalado",
+ "config-apc": "[https://www.php.net/apc APC] está instalado",
+ "config-apcu": "[https://www.php.net/apcu APCu] está instalado",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] está instalado",
- "config-no-cache-apcu": "<strong>Aviso:</strong> Não foram encontrados o [https://secure.php.net/apcu APCu], ou o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA cache de objetos não está ativa.",
+ "config-no-cache-apcu": "<strong>Aviso:</strong> Não foram encontrados o [https://www.php.net/manual/pt_BR/book.apcu.php APCu], ou o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA cache de objetos não está ativa.",
"config-mod-security": "<strong>Aviso:</strong> Seu servidor web tem [https://modsecurity.org/ mod_security2] habilitado. Muitas configurações comuns de módulo podem causar problemas para o MediaWiki ou outro software que permite aos usuários postar conteúdo arbitrário.\nSe possível, ele dever ser desativad. Consulte a [https://modsecurity.org/documentation/ documentação do mod_security] ou entre em contato com o suporte do seu host se você encontrar erros aleatórios.",
"config-diff3-bad": "Utilitário de comparação de texto do GNU diff3 não encontrado. Você pode ignorar isso por enquanto, mas pode se deparar com conflitos de edição com mais frequência.",
"config-git": "Foi encontrado o software de controle de versão Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "O MediaWiki suporta os sistemas de banco de dados a seguir:\n\n$1\n\nSe você não vê o sistema de banco de dados que você está tentando usar listados abaixo, siga as instruções relacionadas acima, para ativar o suporte.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é a base de dados preferida para o MediaWiki e a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MariaDB. ([https://secure.php.net/manual/pt_BR/mysqli.installation.php Como compilar PHP com suporte para MySQL].)",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] é um popular sistema de banco de dados de código aberto como uma alternativa para o MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Como compilar o PHP com suporte PostgreSQL])",
- "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([https://secure.php.net/manual/en/pdo.installation.php Como compilar PHP com suporte para SQLite], usa PDO.)",
- "config-dbsupport-oracle": "* A [{{int:version-db-oracle-url}} Oracle] é uma base de dados comercial para empresas. ([https://secure.php.net/manual/en/oci8.installation.php Como compilar PHP com suporte para OCI8].)",
- "config-dbsupport-mssql": "* O [{{int:version-db-mssql-url}} Microsoft SQL Server] é uma base de dados comercial do Windows para empresas. ([https://secure.php.net/manual/en/sqlsrv.installation.php Como compilar PHP com suporte para SQLSRV].)",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é a base de dados preferida para o MediaWiki e a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MariaDB. ([https://www.php.net/manual/pt_BR/mysqli.installation.php Como compilar PHP com suporte para MySQL].)",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] é um popular sistema de banco de dados de código aberto como uma alternativa para o MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Como compilar o PHP com suporte PostgreSQL])",
+ "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([https://www.php.net/manual/en/pdo.installation.php Como compilar PHP com suporte para SQLite], usa PDO.)",
+ "config-dbsupport-oracle": "* A [{{int:version-db-oracle-url}} Oracle] é uma base de dados comercial para empresas. ([https://www.php.net/manual/pt_BR/oci8.installation.php Como compilar PHP com suporte para OCI8].)",
+ "config-dbsupport-mssql": "* O [{{int:version-db-mssql-url}} Microsoft SQL Server] é uma base de dados comercial do Windows para empresas. ([https://www.php.net/manual/en/sqlsrv.installation.php Como compilar PHP com suporte para SQLSRV].)",
"config-header-mysql": "Definições MariaDB/MySQL",
"config-header-postgres": "Configurações PostgreSQL",
"config-header-sqlite": "Configurações SQLite",
"config-license-help": "Muitas wikis públicas colocam todas as contribuições sob uma [https://freedomdefined.org/Definition licença livre].\nIsso ajuda a criar um senso de propriedade da comunidade e incentiva a contribuição de longo prazo.\nGeralmente não é necessário para uma empresa privada ou wiki corporativa.\nSe você quiser poder usar o texto da Wikipédia e quiser que a Wikipédia possa aceitar o texto copiado da sua wiki, você deve escolher <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nA Wikipédia usou anteriormente a Licença de Documentação Livre GNU.\n A GFDL é uma licença válida, mas é difícil de entender.\nTambém é difícil reutilizar conteúdo licenciado sob o GFDL.",
"config-email-settings": "Configurações de e-mail",
"config-enable-email": "Ativar envio de e-mail",
- "config-enable-email-help": "Se você quer que o e-mail funcione, estas [Config-dbsupport-oracle/manual/en/mail.configuration.php configurações de e-mail PHP] precisam ser configuradas corretamente.\nSe você não quiser usar nenhuma das funcionalidades, você pode desabilitá-las aqui.",
+ "config-enable-email-help": "Se você quer que o e-mail funcione, estas [https://www.php.net/manual/pt_BR/mail.configuration.php configurações de e-mail PHP] precisam ser configuradas corretamente.\nSe você não quiser usar nenhuma das funcionalidades, você pode desabilitá-las aqui.",
"config-email-user": "Ativar e-mails entre usuários",
"config-email-user-help": "Permitir que todos os usuários enviem e-mail entre si se eles tiverem habilitado este recurso em suas preferências.",
"config-email-usertalk": "Ativar notificação de alterações em páginas de discussão de usuário",
"config-pcre-no-utf8": "'''Erro fatal''': O módulo PCRE do PHP parece ter sido compilado sem suporte PCRE_UTF8.\nO MediaWiki necessita do suporte UTF-8 para funcionar corretamente.",
"config-memory-raised": "A configuração <code>memory_limit</code> do PHP era $1; foi aumentada para $2.",
"config-memory-bad": "<strong>Aviso:</strong> A configuração <code>memory_limit</code> do PHP é $1.\nIsto é provavelmente demasiado baixo.\nA instalação poderá falhar!",
- "config-apc": "[https://secure.php.net/apc APC] instalada",
- "config-apcu": "[https://secure.php.net/apcu APCu] instalado",
+ "config-apc": "[https://www.php.net/apc APC] instalada",
+ "config-apcu": "[https://www.php.net/apcu APCu] instalado",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] instalada",
- "config-no-cache-apcu": "<strong>Aviso:</strong> Não foi encontrado o [https://secure.php.net/apcu APCu] nem o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA cache de objetos não está ativa.",
+ "config-no-cache-apcu": "<strong>Aviso:</strong> Não foi encontrado o [https://www.php.net/apcu APCu] nem o [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nA cache de objetos não está ativa.",
"config-mod-security": "<strong>Aviso:</strong> O seu servidor de Internet tem o [https://modsecurity.org/ mod_security]/mod_security2 ativado. Muitas das suas configurações normais podem causar problemas ao MediaWiki e a outros programas, permitindo que os utilizadores publiquem conteúdos arbitrários.\nSe possível, isto deve ser desativado. Se não, consulte a [https://modsecurity.org/documentation/ mod_security documentação] ou peça apoio ao fornecedor do alojamento do seu servidor se encontrar erros aleatórios.",
"config-diff3-bad": "O utilitário de comparação de texto GNU diff3 não foi encontrado. Pode ignorar esta situação por agora, mas poderá encontrar conflitos entre edições mais frequentemente.",
"config-git": "Foi encontrado o software de controlo de versões Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "O MediaWiki suporta as seguintes plataformas de base de dados:\n\n$1\n\nSe a plataforma que pretende usar não está listada abaixo, siga as instruções nas hiperligações acima para ativar o suporte.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é a base de dados preferida para o MediaWiki e a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MariaDB. ([https://secure.php.net/manual/pt_BR/mysqli.installation.php Como compilar PHP com suporte para MySQL].)",
- "config-dbsupport-postgres": "* O [{{int:version-db-postgres-url}} PostgreSQL] é uma plataforma popular de base de dados de código aberto, alternativa ao MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Como compilar PHP com suporte para PostgreSQL].)",
- "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([https://secure.php.net/manual/en/pdo.installation.php Como compilar PHP com suporte para SQLite], usa PDO.)",
- "config-dbsupport-oracle": "* A [{{int:version-db-oracle-url}} Oracle] é uma base de dados comercial para empresas. ([https://secure.php.net/manual/en/oci8.installation.php Como compilar PHP com suporte para OCI8].)",
- "config-dbsupport-mssql": "* O [{{int:version-db-mssql-url}} Microsoft SQL Server] é uma base de dados comercial do Windows para empresas. ([https://secure.php.net/manual/en/sqlsrv.installation.php Como compilar PHP com suporte para SQLSRV].)",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] é a base de dados preferida para o MediaWiki e a melhor suportada. O MediaWiki também trabalha com [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], que são compatíveis com MariaDB. ([https://www.php.net/manual/pt_BR/mysqli.installation.php Como compilar PHP com suporte para MySQL].)",
+ "config-dbsupport-postgres": "* O [{{int:version-db-postgres-url}} PostgreSQL] é uma plataforma popular de base de dados de código aberto, alternativa ao MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Como compilar PHP com suporte para PostgreSQL].)",
+ "config-dbsupport-sqlite": "* O [{{int:version-db-sqlite-url}} SQLite] é uma plataforma de base de dados ligeira muito bem suportada. ([https://www.php.net/manual/en/pdo.installation.php Como compilar PHP com suporte para SQLite], usa PDO.)",
+ "config-dbsupport-oracle": "* A [{{int:version-db-oracle-url}} Oracle] é uma base de dados comercial para empresas. ([https://www.php.net/manual/en/oci8.installation.php Como compilar PHP com suporte para OCI8].)",
+ "config-dbsupport-mssql": "* O [{{int:version-db-mssql-url}} Microsoft SQL Server] é uma base de dados comercial do Windows para empresas. ([https://www.php.net/manual/en/sqlsrv.installation.php Como compilar PHP com suporte para SQLSRV].)",
"config-header-mysql": "Definições MariaDB/MySQL",
"config-header-postgres": "Definições PostgreSQL",
"config-header-sqlite": "Definições SQLite",
"config-license-help": "Muitas wikis de acesso público licenciam todas as colaborações com uma [https://freedomdefined.org/Definition licença livre].\nIsto ajuda a criar um sentido de propriedade da comunidade e encoraja as colaborações a longo prazo.\nTal não é geralmente necessário nas wikis privadas ou corporativas.\n\nSe pretende que seja possível usar textos da Wikipédia na sua wiki e que seja possível a Wikipédia aceitar textos copiados da sua wiki, deve escolher a licença <strong>{{int:config-license-cc-by-sa}}</strong>..\n\nA licença anterior da Wikipédia era a licença GNU Free Documentation License.\nA GFDL é uma licença válida, mas de difícil compreensão.\nTambém é difícil reutilizar conteúdos licenciados com a GFDL.",
"config-email-settings": "Definições do correio eletrónico",
"config-enable-email": "Ativar mensagens eletrónicas de saída",
- "config-enable-email-help": "Se quer que o correio eletrónico funcione, as [https://secure.php.net/manual/en/mail.configuration.php definições de correio eletrónico do PHP] têm de estar configuradas corretamente.\nSe não pretende viabilizar qualquer funcionalidade de correio eletrónico, pode desativá-lo aqui.",
+ "config-enable-email-help": "Se quer que o correio eletrónico funcione, as [https://www.php.net/manual/en/mail.configuration.php definições de correio eletrónico do PHP] têm de estar configuradas corretamente.\nSe não pretende viabilizar qualquer funcionalidade de correio eletrónico, pode desativá-lo aqui.",
"config-email-user": "Ativar mensagens eletrónicas entre utilizadores",
"config-email-user-help": "Permitir que todos os utilizadores troquem entre si mensagens de correio eletrónico, se tiverem ativado esta funcionalidade nas suas preferências.",
"config-email-usertalk": "Ativar notificações de alterações à página de discussão dos utilizadores",
"config-pcre-no-utf8": "<strong>Fatal:</strong> Modul PCRE al PHP pare să fie compilat fără suport PCRE_UTF8.\nMediaWiki necesită ca suportul UTF-8 să funcționeze corect.",
"config-memory-raised": "PHP<code>memory_limit</code> este $1, mărit cu $2.",
"config-memory-bad": "<strong>Atenție:</strong> PHP<code>memory_limit</code> este $1.\nAcesta este probabil mai jos.\nAceastă instalare eșuează!",
- "config-apc": "[https://secure.php.net/apc APC] este instalat",
- "config-apcu": "[https://secure.php.net/apcu APCu] este instalat",
+ "config-apc": "[https://www.php.net/apc APC] este instalat",
+ "config-apcu": "[https://www.php.net/apcu APCu] este instalat",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] este instalat",
- "config-no-cache-apcu": "<strong>Atenție:</strong> Nu am putut găsi [https://secure.php.net/apcu APCu] sau [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObiect cache nu este activat.",
+ "config-no-cache-apcu": "<strong>Atenție:</strong> Nu am putut găsi [https://www.php.net/apcu APCu] sau [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObiect cache nu este activat.",
"config-mod-security": "<strong>Avertisment:</strong> Serverul dvs. web are activat [https://modsecurity.org/ mod_security] / mod_security2. Multe configurații comune vor provoca probleme pentru MediaWiki și alte programe software care permit utilizatorilor să posteze conținuturi arbitrare.\nDacă este posibil, aceasta ar trebui dezactivată. În caz contrar, consultați documentația [https://modsecurity.org/documentation/ mod_security] sau contactați asistența gazdei dvs. dacă întâmpinați erori aleatorii.",
"config-diff3-bad": "Utilitarul de comparare a textului GNU diff3 nu a fost găsit. Puteți ignora acest lucru pentru moment, dar ar putea apărea mai frecvent conflicte de editare.",
"config-git": "Am găsit versiunea software de control Git:<code>$1</code>.",
"config-pcre-no-utf8": "'''Фатальная ошибка'''. Модуль PCRE для PHP, похоже, собран без поддержки PCRE_UTF8.\nMediaWiki требует поддержки UTF-8 для корректной работы.",
"config-memory-raised": "Ограничение на доступную PHP память (<code>memory_limit</code>) поднято с $1 до $2.",
"config-memory-bad": "'''Внимание:''' размер PHP <code>memory_limit</code> составляет $1.\nВероятно, этого слишком мало.\nУстановка может потерпеть неудачу!",
- "config-apc": "[https://secure.php.net/apc APC] установлен",
- "config-apcu": "[https://secure.php.net/apcu APCu] установлен",
+ "config-apc": "[https://www.php.net/apc APC] установлен",
+ "config-apcu": "[https://www.php.net/apcu APCu] установлен",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] установлен",
- "config-no-cache-apcu": "<strong>Внимание:</strong> Не найдены [https://secure.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКэширование объектов будет отключено.",
+ "config-no-cache-apcu": "<strong>Внимание:</strong> Не найдены [https://www.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКэширование объектов будет отключено.",
"config-mod-security": "<strong>Внимание</strong>: На вашем веб-сервере включён [https://modsecurity.org/ mod_security]/mod_security2. Многие его стандартные настройки могут вызывать проблемы для MediaWiki или другого ПО, позволяющего пользователям отправлять на сервер произвольный контент.\nПо возможности он должен быть отключён. Обратитесь к [https://modsecurity.org/documentation/ документации mod_security] или в службу поддержки вашего хостинг-провайдера, если вы сталкиваетесь со случайными ошибками.",
"config-diff3-bad": "Утилита для сравнения GNU diff3 не найдена. Вы можете пока это проигнорировать, но, скорее всего, вы будете видеть эту ошибку вновь и вновь при конфликтах редактирования.",
"config-git": "Найдена система контроля версий Git: <code>$1</code>.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki поддерживает следующие СУБД:\n\n$1\n\nЕсли вы не видите своей системы хранения данных в этом списке, следуйте инструкциям, на которые есть ссылка выше, чтобы получить поддержку.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] — основная база данных для MediaWiki, которая поддерживается лучше всего. MediaWiki также работает с [{{int:version-db-mariadb-url}} MariaDB] и [{{int:version-db-percona-url}} Percona Server], которые являются MariaDB-совместимыми. (См.[https://secure.php.net/manual/ru/mysql.installation.php Как собрать PHP с поддержкой MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — популярная СУБД с открытым исходным кодом, альтернатива MySQL. ([https://secure.php.net/manual/ru/pgsql.installation.php Как собрать PHP с поддержкой PostgreSQL]).",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — это легковесная система баз данных, имеющая очень хорошую поддержку. ([https://secure.php.net/manual/ru/pdo.installation.php Как собрать PHP с поддержкой SQLite], работающей посредством PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] — это коммерческая корпоративная база данных. ([https://secure.php.net/manual/ru/oci8.installation.php Как собрать PHP с поддержкой OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — это коммерческая корпоративная база данных для Windows. ([https://secure.php.net/manual/ru/sqlsrv.installation.php Как собрать PHP с поддержкой SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] — основная база данных для MediaWiki, которая поддерживается лучше всего. MediaWiki также работает с [{{int:version-db-mariadb-url}} MariaDB] и [{{int:version-db-percona-url}} Percona Server], которые являются MariaDB-совместимыми. (См.[https://www.php.net/manual/ru/mysql.installation.php Как собрать PHP с поддержкой MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — популярная СУБД с открытым исходным кодом, альтернатива MySQL. ([https://www.php.net/manual/ru/pgsql.installation.php Как собрать PHP с поддержкой PostgreSQL]).",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — это легковесная система баз данных, имеющая очень хорошую поддержку. ([https://www.php.net/manual/ru/pdo.installation.php Как собрать PHP с поддержкой SQLite], работающей посредством PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] — это коммерческая корпоративная база данных. ([https://www.php.net/manual/ru/oci8.installation.php Как собрать PHP с поддержкой OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — это коммерческая корпоративная база данных для Windows. ([https://www.php.net/manual/ru/sqlsrv.installation.php Как собрать PHP с поддержкой SQLSRV])",
"config-header-mysql": "Настройки MariaDB/MySQL",
"config-header-postgres": "Настройки PostgreSQL",
"config-header-sqlite": "Настройки SQLite",
"config-license-help": "Многие общедоступные вики разрешают использовать свои материалы на условиях [https://freedomdefined.org/Definition/Ru свободных лицензий].\nЭто помогает созданию чувства общности, стимулирует долгосрочное участие.\nНо в этом нет необходимости для частных или корпоративных вики.\n\nЕсли вы хотите использовать тексты из Википедии или хотите, что в Википедию можно было копировать тексты из вашей вики, вам следует выбрать <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nВикипедия ранее использовала лицензию GNU Free Documentation License.\nGFDL может быть использована, но она сложна для понимания и осложняет повторное использование материалов.",
"config-email-settings": "Настройки электронной почты",
"config-enable-email": "Включить исходящие e-mail",
- "config-enable-email-help": "Если вы хотите, чтобы электронная почта работала, необходимо правильно настроить [https://secure.php.net/manual/ru/mail.configuration.php работу почты в PHP].\nЕсли вы не хотите использовать возможности электронной почты, вы можете её здесь отключить.",
+ "config-enable-email-help": "Если вы хотите, чтобы электронная почта работала, необходимо правильно настроить [https://www.php.net/manual/ru/mail.configuration.php работу почты в PHP].\nЕсли вы не хотите использовать возможности электронной почты, вы можете её здесь отключить.",
"config-email-user": "Включить электронную почту от участника к участнику",
"config-email-user-help": "Разрешить всем пользователям отправлять друг другу электронные письма, если выставлена соответствующая настройка в профиле.",
"config-email-usertalk": "Включить уведомления пользователей о сообщениях на их странице обсуждения",
"config-pcre-no-utf8": "<strong>Fatal:</strong> PHP's PCRE module seems tae be compiled wioot PCRE_UTF8 support.\nMediaWiki requires UTF-8 support tae function correctly.",
"config-memory-raised": "PHP's <code>memerie_limit</code> is $1, raised til $2.",
"config-memory-bad": "<strong>Warnishment:</strong> PHP's <code>memerie_limit</code> is $1.\nThis is proably ower low.\nThe installation micht fail!",
- "config-apc": "[https://secure.php.net/apc APC] is installed.",
+ "config-apc": "[https://www.php.net/apc APC] is installed.",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] is instawed.",
- "config-no-cache-apcu": "<strong>Wairnin:</strong> Could nae find [https://secure.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] or [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObject cachin isna enabled.",
+ "config-no-cache-apcu": "<strong>Wairnin:</strong> Could nae find [https://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] or [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nObject cachin isna enabled.",
"config-mod-security": "<strong>Warnishment:</strong> Yer wab server haes [https://modsecurity.org/ mod_security] enabled. Gif misconfeegured, it can cause problems fer MediaWiki or ither saffware that allous uisers tae post arbitrie content.\nRefer til [https://modsecurity.org/documentation/ mod_security documentation] or contact yer host's support gif ye encounter random mistaks.",
"config-diff3-bad": "GNU diff3 naw foond.",
"config-git": "Foond the Git version control saffware: <code>$1</code>.",
"config-type-mysql": "MaSQL (or compâtible)",
"config-type-mssql": "Micræsaff SQL Server",
"config-support-info": "MediaWiki supports the follaein database systems:\n\n$1\n\nGif ye dinna see the database system ye'r tryin tae uise listed ablow, than follae the instructions linked abuin tae enable support.",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] is the primarie tairget fer MediaWiki n is best supported. MediaWiki warks forby wi [{{int:version-db-mariadb-url}} MariaDB] n [{{int:version-db-percona-url}} Percona Server], thir ar MySQL compatible. ([https://secure.php.net/manual/en/mysqli.installation.php Hou tae compile PHP wi MySQL support])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is ae popular apen soorce database system aes aen alternative til MySQL. Thaur micht be some wee bugs still hingin roond, n it's na recommendit fer uiss in ae production environment. ([https://secure.php.net/manual/en/pgsql.installation.php Hou tae compile PHP wi PostgreSQL support])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] is the primarie tairget fer MediaWiki n is best supported. MediaWiki warks forby wi [{{int:version-db-mariadb-url}} MariaDB] n [{{int:version-db-percona-url}} Percona Server], thir ar MySQL compatible. ([https://www.php.net/manual/en/mysqli.installation.php Hou tae compile PHP wi MySQL support])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] is ae popular apen soorce database system aes aen alternative til MySQL. Thaur micht be some wee bugs still hingin roond, n it's na recommendit fer uiss in ae production environment. ([https://www.php.net/manual/en/pgsql.installation.php Hou tae compile PHP wi PostgreSQL support])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is ae lichtweicht database system that is ver weel supportit. ([http://www.php.net/manual/en/pdo.installation.php Hou tae compile PHP wi SQLite support], uises PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is ae commercial enterprise database. ([http://www.php.net/manual/en/oci8.installation.php Hou tae compile PHP wi OCI8 support])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is ae commercial enterprise database fer Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Hou tae compile PHP wi SQLSRV support])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is ae commercial enterprise database fer Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Hou tae compile PHP wi SQLSRV support])",
"config-header-mysql": "MaSQL settins",
"config-header-postgres": "PostgreSQL settins",
"config-header-sqlite": "SQLite settins",
"config-pcre-no-utf8": "<strong>Kobno</strong>: PCRE modul PHP-a je hitan bez podrške za PCRE_UTF8.\nMediaWiki zahtijeva podršku za UTF-8 kako bi ispravno funkcionirao.",
"config-memory-raised": "<code>memory_limit</code> za PHP iznosi $1, povišen na $2.",
"config-memory-bad": "<strong>Upozorenje:</strong> <code>memory_limit</code> za PHP iznosi $1.\nOvo je vjerovatno premalo.\nUspostava možda neće uspjeti!",
- "config-apc": "[https://secure.php.net/apc APC] je uspostavljen",
- "config-apcu": "[https://secure.php.net/apcu APCu] je uspostavljen",
+ "config-apc": "[https://www.php.net/apc APC] je uspostavljen",
+ "config-apcu": "[https://www.php.net/apcu APCu] je uspostavljen",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je uspostavljen",
- "config-no-cache-apcu": "<strong>Upozorenje:</strong> Nisam mogao naći [https://secure.php.net/apcu APCu] ili [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].",
+ "config-no-cache-apcu": "<strong>Upozorenje:</strong> Nisam mogao naći [https://www.php.net/apcu APCu] ili [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].",
"config-mod-security": "'''Upozorenje''': na vašem poslužitelju ima omogućeno [https://modsecurity.org/ mod_security]. Ako nije postavljeno kako treba, to može uzrokovati probleme s programom MediaWikija i drugim programima koji korisnicima omogućuju objavljivanje proizvoljnog sadržaja.\nPogledajte [https://modsecurity.org/documentation/ mod_security dokumentaciju] ili kontaktirajte domaćin ako naiđete na slučajne greške.",
"config-diff3-bad": "Nisam pronašao usporednik za tekst GNU razl3 (GNU diff3). Za sada ovo možete zanemariti, ali kontradikcije u uređivanju mogu postati češće.",
"config-git": "Pronašao sam Git program za kontrolu verzija: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL ili kompatibilan",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki podržava sljedeće sustave baza podataka:\n\n$1\n\nAko sustav koji želite koristiti nije naveden u nastavku, slijedite vezu gore navedenih uputa kako biste omogućili podršku za taj sustav.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je glavna meta MediaWikija i najbolje je podržan. MediaWiki također radi sa [{{int:version-db-mysql-url}} MySQL-om] i [{{int:version-db-percona-url}} Percona], koji su skladni sa MariaDB-om. ([https://secure.php.net/manual/en/mysqli.installation.php Kako kompajlirati PHP sa podrškom MySQL-a])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularan sistem baza podataka otvorenog koda koji predstavlja alternativu MySQL-u. ([https://secure.php.net/manual/en/pgsql.installation.php Kako kompajlirati PHP sa podrškom PostgreSQL-a])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je lagan sistem baze podataka koji je veoma dobro podržan. ([https://secure.php.net/manual/en/pdo.installation.php Kako kompajlirati PHP sa podrškom SQLite-a], koristi PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je baza podataka komercijalnih preduzeća. ([https://secure.php.net/manual/en/oci8.installation.php Kako kompajlirati PHP sa podrškom OCI8-a])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je baza podataka komercijalnih preduzeća za Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Kako kompajlirati PHP sa podrškom SQLSRV-a])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je glavna meta MediaWikija i najbolje je podržan. MediaWiki također radi sa [{{int:version-db-mysql-url}} MySQL-om] i [{{int:version-db-percona-url}} Percona], koji su skladni sa MariaDB-om. ([https://www.php.net/manual/en/mysqli.installation.php Kako kompajlirati PHP sa podrškom MySQL-a])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularan sistem baza podataka otvorenog koda koji predstavlja alternativu MySQL-u. ([https://www.php.net/manual/en/pgsql.installation.php Kako kompajlirati PHP sa podrškom PostgreSQL-a])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je lagan sistem baze podataka koji je veoma dobro podržan. ([https://www.php.net/manual/en/pdo.installation.php Kako kompajlirati PHP sa podrškom SQLite-a], koristi PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je baza podataka komercijalnih preduzeća. ([https://www.php.net/manual/en/oci8.installation.php Kako kompajlirati PHP sa podrškom OCI8-a])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je baza podataka komercijalnih preduzeća za Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Kako kompajlirati PHP sa podrškom SQLSRV-a])",
"config-header-mysql": "Podešavanja MariaDB/MySQL-a",
"config-header-postgres": "Podešavanja PostgreSQL-a",
"config-header-sqlite": "Podešavanja SQLite-a",
"config-env-hhvm": "HHVM $1 je nameščen.",
"config-unicode-using-intl": "Uporaba [https://pecl.php.net/intl razširitve PECL intl] za normalizacijo unikoda.",
"config-memory-raised": "PHP-jev <code>memory_limit</code> je $1, dvignjen na $2.",
- "config-apc": "[https://secure.php.net/apc APC] je nameščen",
+ "config-apc": "[https://www.php.net/apc APC] je nameščen",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je nameščen",
"config-diff3-bad": "Pripomočka za primerjavo besedila GNU diff3 nismo našli. To lahko zaenkrat prezrete, ampak morda boste pogosteje naleteli na spore pri urejanju.",
"config-using-server": "Uporabljam ime strežnika \"<nowiki>$1</nowiki>\".",
"config-pcre-no-utf8": "<strong>Неотклоњива грешка:</strong> Изгледа да је PCRE модул PHP-а компајлиран без PCRE_UTF8 подршке.\nMediaWiki захтева UTF-8 подршку за исправно функционисање.",
"config-memory-raised": "Вредност параметра <code>memory_limit</code> PHP-а износи $1, подигнута на $2.",
"config-memory-bad": "<strong>Упозорење:</strong> Вредност параметра <code>memory_limit</code> PHP-а износи $1.\nОво је вероватно прениско.\nИнсталација можда неће успети!",
- "config-apc": "[https://secure.php.net/apc APC] је инсталиран",
- "config-apcu": "[https://secure.php.net/apcu APCu] је инсталиран",
+ "config-apc": "[https://www.php.net/apc APC] је инсталиран",
+ "config-apcu": "[https://www.php.net/apcu APCu] је инсталиран",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] је инсталиран",
- "config-no-cache-apcu": "<strong>Упозорење:</strong> Није могуће пронаћи [https://secure.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКеширање објеката није омогућено.",
+ "config-no-cache-apcu": "<strong>Упозорење:</strong> Није могуће пронаћи [https://www.php.net/apcu APCu] или [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКеширање објеката није омогућено.",
"config-diff3-bad": "GNU diff3 није пронађен.",
"config-git": "Пронађен је Git софтвер за контролу верзија: <code>$1</code>",
"config-git-bad": "Није пронађен Git софтвер за контролу верзија.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki подржава следеће системе база података:\n\n$1\n\nАко не видите систем који покушавате да користите на листи испод, онда пратите повезана упутства изнад како бисте омогућили подршку.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] је примарна мета за MediaWiki и најбоље је подржана. MediaWiki такође ради са [{{int:version-db-mysql-url}} MySQL-ом] и [{{int:version-db-percona-url}} Percona Server-ом], који су компатибилни са MariaDB-ом. ([https://secure.php.net/manual/en/mysqli.installation.php Како компајлирати PHP са подршком MySQL-а])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] је популаран систем база података отвореног кода кaо алтернатива MySQL-у. ([https://secure.php.net/manual/en/pgsql.installation.php Како компајлирати PHP са подршком PostgreSQL-а])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] је лаган систем базе података који је веома добро подржан. ([https://secure.php.net/manual/en/pdo.installation.php Како компајлирати PHP са подршком SQLite-а], користи PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] је база података комерцијалних предузећа. ([https://secure.php.net/manual/en/oci8.installation.php Како компајлирати PHP са подршком OCI8-а])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] је база података комерцијалних предузећа за Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Како компајлирати PHP са подршком SQLSRV-а])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] је примарна мета за MediaWiki и најбоље је подржана. MediaWiki такође ради са [{{int:version-db-mysql-url}} MySQL-ом] и [{{int:version-db-percona-url}} Percona Server-ом], који су компатибилни са MariaDB-ом. ([https://www.php.net/manual/en/mysqli.installation.php Како компајлирати PHP са подршком MySQL-а])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] је популаран систем база података отвореног кода кaо алтернатива MySQL-у. ([https://www.php.net/manual/en/pgsql.installation.php Како компајлирати PHP са подршком PostgreSQL-а])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] је лаган систем базе података који је веома добро подржан. ([https://www.php.net/manual/en/pdo.installation.php Како компајлирати PHP са подршком SQLite-а], користи PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] је база података комерцијалних предузећа. ([https://www.php.net/manual/en/oci8.installation.php Како компајлирати PHP са подршком OCI8-а])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] је база података комерцијалних предузећа за Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Како компајлирати PHP са подршком SQLSRV-а])",
"config-header-mysql": "Подешавања MariaDB/MySQL-а",
"config-header-postgres": "Подешавања PostgreSQL-а",
"config-header-sqlite": "Подешавања SQLite-а",
"config-pcre-no-utf8": "<strong>Neotklonjiva greška:</strong> Izgleda da je PCRE modul PHP-a kompajliran bez PCRE_UTF8 podrške.\nMediaWiki zahteva UTF-8 podršku za ispravno funkcionisanje.",
"config-memory-raised": "Vrednost parametra <code>memory_limit</code> PHP-a iznosi $1, podignuta na $2.",
"config-memory-bad": "<strong>Upozorenje:</strong> Vrednost parametra <code>memory_limit</code> PHP-a iznosi $1.\nOvo je verovatno prenisko.\nInstalacija možda neće uspeti!",
- "config-apc": "[https://secure.php.net/apc APC] je instaliran",
- "config-apcu": "[https://secure.php.net/apcu APCu] je instaliran",
+ "config-apc": "[https://www.php.net/apc APC] je instaliran",
+ "config-apcu": "[https://www.php.net/apcu APCu] je instaliran",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] je instaliran",
- "config-no-cache-apcu": "<strong>Upozorenje:</strong> Nije moguće pronaći [https://secure.php.net/apcu APCu] ili [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nKeširanje objekata nije omogućeno.",
+ "config-no-cache-apcu": "<strong>Upozorenje:</strong> Nije moguće pronaći [https://www.php.net/apcu APCu] ili [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nKeširanje objekata nije omogućeno.",
"config-diff3-bad": "GNU diff3 nije pronađen.",
"config-git": "Pronađen je Git softver za kontrolu verzija: <code>$1</code>",
"config-git-bad": "Nije pronađen Git softver za kontrolu verzija.",
"config-type-oracle": "Oracle",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki podržava sledeće sisteme baza podataka:\n\n$1\n\nAko ne vidite sistem koji pokušavate da koristite na listi ispod, onda pratite povezana uputstva iznad kako biste omogućili podršku.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je primarna meta za MediaWiki i najbolje je podržana. MediaWiki takođe radi sa [{{int:version-db-mysql-url}} MySQL-om] i [{{int:version-db-percona-url}} Percona Server-om], koji su kompatibilni sa MariaDB-om. ([https://secure.php.net/manual/en/mysqli.installation.php Kako kompajlirati PHP sa podrškom MySQL-a])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularan sistem baza podataka otvorenog koda kao alternativa MySQL-u. ([https://secure.php.net/manual/en/pgsql.installation.php Kako kompajlirati PHP sa podrškom PostgreSQL-a])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je lagan sistem baze podataka koji je veoma dobro podržan. ([https://secure.php.net/manual/en/pdo.installation.php Kako kompajlirati PHP sa podrškom SQLite-a], koristi PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je baza podataka komercijalnih preduzeća. ([https://secure.php.net/manual/en/oci8.installation.php Kako kompajlirati PHP sa podrškom OCI8-a])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je baza podataka komercijalnih preduzeća za Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Kako kompajlirati PHP sa podrškom SQLSRV-a])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] je primarna meta za MediaWiki i najbolje je podržana. MediaWiki takođe radi sa [{{int:version-db-mysql-url}} MySQL-om] i [{{int:version-db-percona-url}} Percona Server-om], koji su kompatibilni sa MariaDB-om. ([https://www.php.net/manual/en/mysqli.installation.php Kako kompajlirati PHP sa podrškom MySQL-a])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] je popularan sistem baza podataka otvorenog koda kao alternativa MySQL-u. ([https://www.php.net/manual/en/pgsql.installation.php Kako kompajlirati PHP sa podrškom PostgreSQL-a])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] je lagan sistem baze podataka koji je veoma dobro podržan. ([https://www.php.net/manual/en/pdo.installation.php Kako kompajlirati PHP sa podrškom SQLite-a], koristi PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] je baza podataka komercijalnih preduzeća. ([https://www.php.net/manual/en/oci8.installation.php Kako kompajlirati PHP sa podrškom OCI8-a])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] je baza podataka komercijalnih preduzeća za Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Kako kompajlirati PHP sa podrškom SQLSRV-a])",
"config-header-mysql": "Podešavanja MariaDB/MySQL-a",
"config-header-postgres": "Podešavanja PostgreSQL-a",
"config-header-sqlite": "Podešavanja SQLite-a",
"config-pcre-no-utf8": "'''Kritiskt:''' PHP:s PCRE-modul verkar vara kompilerat utan PCRE_UTF8-stöd.\nMediaWiki kräver stöd för UTF-8 för att fungera korrekt.",
"config-memory-raised": "PHPs <code>memory_limit</code> är $1, ökad till $2.",
"config-memory-bad": "''' Varning:''' PHP:s <code>memory_limit</code> är $1.\nDetta är förmodligen för lågt.\nInstallationen kan misslyckas!",
- "config-apc": "[https://secure.php.net/apc APC] är installerat",
- "config-apcu": "[https://secure.php.net/apcu APCu] är installerat",
+ "config-apc": "[https://www.php.net/apc APC] är installerat",
+ "config-apcu": "[https://www.php.net/apcu APCu] är installerat",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] är installerat",
- "config-no-cache-apcu": "<strong>Varning:</strong> Kunde inte hitta [https://secure.php.net/apcu APCu] eller [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nCachelagring av objekt är inte aktiverat.",
+ "config-no-cache-apcu": "<strong>Varning:</strong> Kunde inte hitta [https://www.php.net/apcu APCu] eller [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nCachelagring av objekt är inte aktiverat.",
"config-mod-security": "'''Varning:''' Din webbserver har [https://modsecurity.org/ mod_security] aktiverat. Om felaktigt konfigurerat kan den skapa problem för MediaWiki eller annan programvara som tillåter användaren att posta godtyckligt innehåll.\nTitta på [https://modsecurity.org/documentation/ mod_security-dokumentationen] eller kontakta din värd om du påträffar slumpmässiga fel.",
"config-diff3-bad": "Inget verktyg för att jämföra GNU diff3-text hittades. Du kan ignorera detta för tillfället, men då kan du stöta på redigeringskonflikter mer frekvent.",
"config-git": "Hittade Git-mjukvara för versionskontroll: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL eller kompatibelt",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki stöder följande databassystem:\n\n$1\n\nOm du inte ser det databassystem som du försöker använda nedanstående, följ då instruktionerna länkade ovan för aktivera stöd för det.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] är det primära målet för MediaWiki och stöds bäst. MediaWiki fungerar även med [{{int:version-db-mysql-url}} MySQL] och [{{int:version-db-percona-url}} Percona Server], som är kompatibla med MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Hur man kompilerar PHP med stöd för MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] är ett populärt databassystem med öppen källkod som ett alternativ till MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Hur man kompilerar PHP med PostgreSQL-stöd])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] är en lättviktsdatabassystem med väldigt bra stöd. ([https://secure.php.net/manual/en/pdo.installation.php Hur man kompilerar PHP med SQLite stöd], använder PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] är en kommersiellt databas för företag. ([https://secure.php.net/manual/en/oci8.installation.php Hur man kompilerar PHP med OCI8 stöd])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] är en kommersiellt databas för företag för Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Hur man kompilerar PHP med SQLSRV stöd])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] är det primära målet för MediaWiki och stöds bäst. MediaWiki fungerar även med [{{int:version-db-mysql-url}} MySQL] och [{{int:version-db-percona-url}} Percona Server], som är kompatibla med MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Hur man kompilerar PHP med stöd för MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] är ett populärt databassystem med öppen källkod som ett alternativ till MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Hur man kompilerar PHP med PostgreSQL-stöd])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] är en lättviktsdatabassystem med väldigt bra stöd. ([https://www.php.net/manual/en/pdo.installation.php Hur man kompilerar PHP med SQLite stöd], använder PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] är en kommersiellt databas för företag. ([https://www.php.net/manual/en/oci8.installation.php Hur man kompilerar PHP med OCI8 stöd])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] är en kommersiellt databas för företag för Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Hur man kompilerar PHP med SQLSRV stöd])",
"config-header-mysql": "MariaDB/MySQL-inställningar",
"config-header-postgres": "PostgreSQL-inställningar",
"config-header-sqlite": "SQLite-inställningar",
"config-outdated-sqlite": "<strong>హెచ్చరిక:</strong> మీ వద్ద SQLite $1 ఉంది. అదికావలసిన వెర్షను $2 కంటే దిగువది. SQLite అందుబాటులో ఉండదు.",
"config-memory-raised": "PHP యొక్క <code>memory_limit</code> $1, దాన్ని $2 కి పెంచాం.",
"config-memory-bad": "<strong>హెచ్చరిక:</strong> PHP యొక్క <code>memory_limit</code> $1.\nబహుశా ఇది మరీ తక్కువ.\nస్థాపన విఫలం కావచ్చు!",
- "config-apc": "[https://secure.php.net/apc APC] స్థాపించబడింది",
+ "config-apc": "[https://www.php.net/apc APC] స్థాపించబడింది",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] స్థాపించబడింది",
"config-diff3-bad": "GNU diff3 కనబడలేదు.",
"config-no-uri": "<strong>లోపం:</strong> ప్రస్తుత URI ఏమిటో నిర్ధారించలేకపోయాం.\nస్థాపన ఆగిపోయింది.",
"config-type-mysql": "MySQL (లేదా సరిపోయేది)",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki కింది డేటాబేసు వ్యవస్థలకు అనుకూలిస్తుంది:\n\n$1\n\nమీరు వాడదలచిన డేటాబేసు వ్యవస్ కింది జాబితాలో లేకపోతే, పైన లింకు ద్వారా ఇచ్చిన సూచనలను పాటించి, అనుకూలతలను సాధించండి.",
- "config-dbsupport-postgres": "* MySQL కు ప్రత్యామ్నాయంగా [{{int:version-db-postgres-url}} PostgreSQL] ప్రజామోదం పొందిన ఓపెన్సోర్సు డేటాబేసు వ్యవస్థ. దానిలో చిన్న చితకా లోపాలుండే అవకాశం ఉంది. అందుచేత దాన్ని ఉత్పాదక రంగంలో వాడవచ్చని చెప్పలేం. ([https://secure.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
+ "config-dbsupport-postgres": "* MySQL కు ప్రత్యామ్నాయంగా [{{int:version-db-postgres-url}} PostgreSQL] ప్రజామోదం పొందిన ఓపెన్సోర్సు డేటాబేసు వ్యవస్థ. దానిలో చిన్న చితకా లోపాలుండే అవకాశం ఉంది. అందుచేత దాన్ని ఉత్పాదక రంగంలో వాడవచ్చని చెప్పలేం. ([https://www.php.net/manual/en/pgsql.installation.php How to compile PHP with PostgreSQL support])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] ఓ తేలికైన డేటాబేసు వ్యవస్థ. దానికి చక్కటి అనుకూలతలున్నాయి. ([http://www.php.net/manual/en/pdo.installation.php How to compile PHP with SQLite support], uses PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] ఒక వాణిజ్యపరంగా సంస్థాగతంగా వాడదగ్గ డేటాబేసు. ([http://www.php.net/manual/en/oci8.installation.php How to compile PHP with OCI8 support])",
"config-header-mysql": "MySQL అమరికలు",
"config-pcre-no-utf8": "<strong>ข้อผิดพลาดร้ายแรง:</strong> โมดูล PCRE ของ PHP ดูเหมือนจะถูกคอมไพล์โดยไม่มีการรองรับ PCRE_UTF8\nMediaWiki ต้องการการรองรับ UTF-8 เพื่อให้ทำงานได้อย่างถูกต้อง",
"config-memory-raised": "<code>memory_limit</code> ของ PHP คือ $1 ได้เพิ่มเป็น $2",
"config-memory-bad": "<strong>คำเตือน:</strong> <code>memory_limit</code> ของ PHP คือ $1.\nเป็นไปได้ว่ามันอาจต่ำเกินไป\nการติดตั้งอาจล้มเหลวได้!",
- "config-apc": "มี [https://secure.php.net/apc APC] ติดตั้งอยู่",
- "config-apcu": "มี [https://secure.php.net/apcu APCu] ติดตั้งอยู่",
+ "config-apc": "มี [https://www.php.net/apc APC] ติดตั้งอยู่",
+ "config-apcu": "มี [https://www.php.net/apcu APCu] ติดตั้งอยู่",
"config-wincache": "มี [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] ติดตั้งอยู่",
- "config-no-cache-apcu": "<strong>คำเตือน:</strong> ไม่พบ [https://secure.php.net/apcu APCu] [http://xcache.lighttpd.net/ XCache] หรือ [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]\nการแคชวัตถุไม่ได้ถูกเปิดใช้งาน",
+ "config-no-cache-apcu": "<strong>คำเตือน:</strong> ไม่พบ [https://www.php.net/apcu APCu] [http://xcache.lighttpd.net/ XCache] หรือ [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]\nการแคชวัตถุไม่ได้ถูกเปิดใช้งาน",
"config-mod-security": "<strong>คำเตือน:</strong> เว็บเซิร์ฟเวอร์ของคุณมี [https://modsecurity.org/ mod_security]/mod_security2 เปิดใช้งานอยู่ การตั้งค่าทั่วไปหลายอย่างของสิ่งนี้จะก่อให้เกิดปัญหาสำหรับ MediaWiki และซอฟต์แวร์อื่นที่อนุญาตให้ผู้ใช้สามารถโพสต์เนื้อหาได้ตามที่ผู้ใช้\nหากเป็นไปได้ ควรปิดใช้งานคุณลักษณะนี้ หรือมิฉะนั้นก็ อ้างไปยัง[https://modsecurity.org/documentation/ เอกสารกำกับการใช้งาน mod_security] หรือติดต่อการสนับสนุนจากโฮสต์ของคุณ ถ้าคุณพบข้อผิดพลาดโดยสุ่ม",
"config-diff3-bad": "ไม่พบโปรแกรมเปรียบเทียบข้อความ GNU diff3 คุณสามารถละเว้นสิ่งนี้ได้ในตอนนี้ แต่อาจพบข้อขัดแย้งในการแก้ไขบ่อยครั้งกว่า",
"config-git": "พบซอฟต์แวร์ควบคุมรุ่น Git: <code>$1</code>",
"config-type-mysql": "MySQL (หรือที่เข้ากันได้)",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki สนับสนุนระบบฐานข้อมูลต่อไปนี้:\n\n$1\n\nถ้าคุณไม่พบระบบฐานข้อมูลที่คุณกำลังพยายามใช้ในรายการด้านล่างนี้ ให้ทำตามคำแนะนำที่เชื่อมโยงด้านบนเพื่อเปิดใช้งานการสนับสนุน",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] คือเป้าหมายหลักสำหรับ MediaWiki และได้รับการสนับสนุนดีที่สุด MediaWiki ยังคงสามารถใช้ได้ร่วมกับ [{{int:version-db-mariadb-url}} MariaDB] และ [{{int:version-db-percona-url}} Percona Server] ซึ่งเข้ากันได้กับ MySQL ([https://secure.php.net/manual/en/mysqli.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] คือระบบฐานข้อมูลแบบโอเพนซอร์สที่ได้รับความนิยมสูงที่สามารถใช้แทน MySQL ได้ ([https://secure.php.net/manual/en/pgsql.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน PostgreSQL])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] คือเป้าหมายหลักสำหรับ MediaWiki และได้รับการสนับสนุนดีที่สุด MediaWiki ยังคงสามารถใช้ได้ร่วมกับ [{{int:version-db-mariadb-url}} MariaDB] และ [{{int:version-db-percona-url}} Percona Server] ซึ่งเข้ากันได้กับ MySQL ([https://www.php.net/manual/en/mysqli.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] คือระบบฐานข้อมูลแบบโอเพนซอร์สที่ได้รับความนิยมสูงที่สามารถใช้แทน MySQL ได้ ([https://www.php.net/manual/en/pgsql.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน PostgreSQL])",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] คือระบบฐานข้อมูลขนาดเล็กที่ได้รับการสนับสนุนดีมาก ([http://www.php.net/manual/en/pdo.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน SQLite], ใช้ PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] คือฐานข้อมูลสำหรับองค์กรพาณิชย์ ([http://www.php.net/manual/en/oci8.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] คือฐานข้อมูลสำหรับองค์กรพาณิชย์สำหรับ Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน SQLSRV])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] คือฐานข้อมูลสำหรับองค์กรพาณิชย์สำหรับ Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php วิธีการคอมไพล์ PHP ด้วยการสนับสนุน SQLSRV])",
"config-header-mysql": "การตั้งค่า MySQL",
"config-header-postgres": "การตั้งค่า PostgreSQL",
"config-header-sqlite": "การตั้งค่า SQLite",
"config-pcre-no-utf8": "'''Malubha''': Tila tinipon ang modyul na PCRE ng PHP na wala ang suporta ng PCRE_UTF8.\nNangangailangan ang MediaWiki ng suporta ng UTF-8 upang maging tama ang pag-andar.",
"config-memory-raised": "Ang <code>hangganan_ng_alaala</code> ng PHP ay $1, itinaas sa $2.",
"config-memory-bad": "'''Babala:''' Ang <code>hangganan_ng_alaala</code> ng PHP ay $1.\nIto ay maaaring napakababa.\nMaaaring mabigo ang pagluluklok!",
- "config-apc": "Ininstala na ang [https://secure.php.net/apc APC]",
+ "config-apc": "Ininstala na ang [https://www.php.net/apc APC]",
"config-wincache": "Ininstala na ang [https://www.iis.net/downloads/microsoft/wincache-extension WinCache]",
"config-mod-security": "'''Babala''': Ang tagapaghain mo ng sangkasaputan ay pinagana na mayroong [https://modsecurity.org/ mod_security]. Kung mali ang kaayusan, makapagdurulot ito ng mga suliranin para sa MediaWiki o ibang mga sopwer na nagpapahintulot sa mga tagagamit na magpaskil ng hindi makatwirang nilalaman.\nSumangguni sa [https://modsecurity.org/documentation/ mod_security kasulatan] o makipag-ugnayan sa suporta ng iyong tagapagpasinaya kapag nakatagpo ng alin mang mga kamalian.",
"config-diff3-bad": "Hindi natagpuan ang GNU diff3.",
"config-type-sqlite": "SQLite",
"config-type-oracle": "Oracle",
"config-support-info": "Sinusuportahan ng MediaWiki ang sumusunod na mga sistema ng kalipunan ng dato:\n\n$1\n\nKung hindi mo makita ang sistema ng kalipunan ng dato na sinusubukan mong gamitin na nakatala sa ibaba, kung gayon ay sundi ang mga tagubilin na nakakawing sa itaas upang mapagana ang suporta,",
- "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] ang pangunahing puntirya para sa MediaWiki at ang pinaka sinusuportahan. Gumagana rin ang MediaWiki [{{int:version-db-mariadb-url}} MariaDB] at sa [{{int:version-db-percona-url}} Percona Server], na tugma sa MySQL. ([https://secure.php.net/manual/en/mysql.installation.php Paano magtipon ng PHP na mayroong suporta ng MySQL])",
- "config-dbsupport-postgres": "* Ang [{{int:version-db-postgres-url}} PostgreSQL] ay isang bantog na sistema ng kalipunan ng dato na bukas ang pinagmulan na panghalili sa MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Paano magtipon ng PHP na mayroong suporta ng PostgreSQL]).",
+ "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] ang pangunahing puntirya para sa MediaWiki at ang pinaka sinusuportahan. Gumagana rin ang MediaWiki [{{int:version-db-mariadb-url}} MariaDB] at sa [{{int:version-db-percona-url}} Percona Server], na tugma sa MySQL. ([https://www.php.net/manual/en/mysql.installation.php Paano magtipon ng PHP na mayroong suporta ng MySQL])",
+ "config-dbsupport-postgres": "* Ang [{{int:version-db-postgres-url}} PostgreSQL] ay isang bantog na sistema ng kalipunan ng dato na bukas ang pinagmulan na panghalili sa MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Paano magtipon ng PHP na mayroong suporta ng PostgreSQL]).",
"config-dbsupport-sqlite": "* Ang [{{int:version-db-sqlite-url}} SQLite] ay isang magaan ang timbang na sistema ng kalipunan ng dato na sinusuportahan nang napaka mainam. ([http://www.php.net/manual/en/pdo.installation.php Paano magtipon ng PHP na mayroong suporta ng SQLite], gumagamit ng PDO)",
"config-dbsupport-oracle": "* Ang [{{int:version-db-oracle-url}} Oracle] ay isang kalipunan ng dato ng kasigasigang pangkalakal. ([http://www.php.net/manual/en/oci8.installation.php Paano magtipunan ng PHP na mayroong suporta ng OCI8])",
"config-header-mysql": "Mga katakdaan ng MariaDB/MySQL",
"config-pcre-no-utf8": "<strong>Önemli hata:</strong> PHP'nin PCRE modülü PCRE_UTF8 desteği olmadan derlenmiş gözüküyor.\nMediaWiki'nin doğru çalışabilmesi için UTF-8 desteği gereklidir.",
"config-memory-raised": "PHP'nin <code>memory_limit</code> (hafıza sınırı) değeri $1, $2'ye yükseltildi.",
"config-memory-bad": "<strong>Uyarı:</strong> PHP'nin <code>memory_limit</code> (hafıza sınırı) değeri $1.\nBu büyük ihtimalle çok düşük.\nKurulum başarısız olabilir!",
- "config-apc": "[https://secure.php.net/apc APC] kuruldu",
- "config-apcu": "[https://secure.php.net/apcu APCu] kuruldu",
+ "config-apc": "[https://www.php.net/apc APC] kuruldu",
+ "config-apcu": "[https://www.php.net/apcu APCu] kuruldu",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kuruldu",
- "config-no-cache-apcu": "<strong>Uyarı:</strong> [https://secure.php.net/apcu APCu] ya da [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kurulumu bulunamadı.\nNesne önbellekleme etkin değil.",
+ "config-no-cache-apcu": "<strong>Uyarı:</strong> [https://www.php.net/apcu APCu] ya da [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kurulumu bulunamadı.\nNesne önbellekleme etkin değil.",
"config-mod-security": "<strong>'''Uyarı:'''</strong> Web sunucunuz [https://modsecurity.org/mod_security2 mod_security] etkin. Bunun birçok yaygın yapılandırması bulunur ve eğer yanlış yapılandırılmış ise, bu MediaWiki ve kullanıcılara isteğe bağlı içerik göndermesine izin veren diğer yazılımlar için sorun oluşturabilir.\nMümkünse bu devre dışı bırakılmalıdır. Aksi takdirde rastgele hatalar alırsanız [https://modsecurity.org/documentation/ mod_security belgelemesine] bakın ya da sunucunuzun desteğine başvurun.",
"config-diff3-bad": "GNU diff3 bulunamadı.",
"config-git": "Sürüm kontrol yazılımı Git bulundu: <code>$1</code>.",
"config-restart": "Әйе, яңадан башларга",
"config-env-php": "PHP $1 куелды.",
"config-env-hhvm": "HHVM $1 куелды.",
- "config-apc": "[https://secure.php.net/apc APC] куелды",
+ "config-apc": "[https://www.php.net/apc APC] куелды",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] куелды",
"config-diff3-bad": "GNU diff3 табылмады.",
"config-git": "Git юрамалар идарә итү системасы табылды: <code>$1</code>.",
"config-pcre-no-utf8": "'''Помилка''': PCRE-модуть PHP, вочевидь, було зібрано без підтримки PCRE_UTF8.\nMediaWiki вимагає підтримку UTF-8 для коректної роботи.",
"config-memory-raised": "Обмеження пам'яті PHP (<code>memory_limit</code>) $1, піднято до $2.",
"config-memory-bad": "'''Увага:''' Розмір пам'яті PHP (<code>memory_limit</code>) становить $1.\nІмовірно, це замало.\nВстановлення може не вдатись!",
- "config-apc": "[https://secure.php.net/apc APC] встановлено",
- "config-apcu": "[https://secure.php.net/apcu APCu] встановлено",
+ "config-apc": "[https://www.php.net/apc APC] встановлено",
+ "config-apcu": "[https://www.php.net/apcu APCu] встановлено",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] встановлено",
- "config-no-cache-apcu": "<strong>Увага:</strong> Не вдалося знайти [https://secure.php.net/apcu APCu] чи [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКешування об'єктів не ввімкнено.",
+ "config-no-cache-apcu": "<strong>Увага:</strong> Не вдалося знайти [https://www.php.net/apcu APCu] чи [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nКешування об'єктів не ввімкнено.",
"config-mod-security": "'''Увага''': на Вашому веб-сервері увімкнено [https://modsecurity.org/ mod_security]. У разі неправильних налаштувать, він може викликати проблеми MediaWiki або іншого ПЗ, яке дозволяє користувачам надсилати довільний вміст.\nЗверніться до [https://modsecurity.org/documentation/ документації mod_security] або підтримки Вашого хостера, якщо під час роботи виникають незрозумілі помилки.",
"config-diff3-bad": "УтилітуGNU diff3, призначену для порівняння текстів, не знайдено. Можете поки що це проігнорувати, але згодом, ймовірно, будете частіше натрапляти на конфлікти редагувань.",
"config-git": "Знайшов програму управління версіями Git: <code>$1</code>.",
"config-type-mysql": "\nMariaDB, MySQL (або сумісні)",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki підтримує такі системи баз даних:\n\n$1\n\nЯкщо Ви не бачите серед перерахованих систему баз даних, яку використовуєте, виконайте вказівки, вказані вище, щоб увімкнути підтримку.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] є основною ціллю для MediaWiki і найкраще підтримується. MediaWiki також працює з [{{int:version-db-mysql-url}} MySQL] та [{{int:version-db-percona-url}} Percona Server], які сумісні з MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Як зібрати PHP з підтримкою MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — популярна відкрита СУБД, альтернатива MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php як зібрати PHP з допомогою PostgreSQL]).",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] є основною ціллю для MediaWiki і найкраще підтримується. MediaWiki також працює з [{{int:version-db-mysql-url}} MySQL] та [{{int:version-db-percona-url}} Percona Server], які сумісні з MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Як зібрати PHP з підтримкою MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] — популярна відкрита СУБД, альтернатива MySQL. ([https://www.php.net/manual/en/pgsql.installation.php як зібрати PHP з допомогою PostgreSQL]).",
"config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] — легка система баз даних, яка дуже добре підтримується. ([http://www.php.net/manual/en/pdo.installation.php Як зібрати PHP з допомогою SQLite], що використовує PDO)",
"config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] — комерційна база даних масштабу підприємства. ([http://www.php.net/manual/en/oci8.installation.php Як зібрати PHP з підтримкою OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — це комерційна база даних для Windows масштабу підприємства. ([https://secure.php.net/manual/en/sqlsrv.installation.php Як зібрати PHP з підтримкою SQLSRV])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] — це комерційна база даних для Windows масштабу підприємства. ([https://www.php.net/manual/en/sqlsrv.installation.php Як зібрати PHP з підтримкою SQLSRV])",
"config-header-mysql": "Налаштування MariaDB/MySQL",
"config-header-postgres": "Налаштування PostgreSQL",
"config-header-sqlite": "Налаштування SQLite",
"config-pcre-no-utf8": "<strong>Lỗi chí tử:</strong> Mô đun PCRE của PHP dường như được biên dịch mà không có hỗ trợ PCRE_UTF8.\nMediaWiki yêu cầu phải có hỗ trợ UTF-8 để hoạt động chính xác.",
"config-memory-raised": "<code>memory_limit</code> của PHP là $1, tăng lên $2.",
"config-memory-bad": "<strong>Cảnh báo:</strong> <code>memory_limit</code> của PHP là $1.\nGiá trị này có lẽ quá thấp.\nCài đặt có thể bị thất bại!",
- "config-apc": "[https://secure.php.net/apc APC] đã được cài đặt",
- "config-apcu": "[https://secure.php.net/apcu APCu] đã được cài đặt",
+ "config-apc": "[https://www.php.net/apc APC] đã được cài đặt",
+ "config-apcu": "[https://www.php.net/apcu APCu] đã được cài đặt",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] đã được cài đặt",
- "config-no-cache-apcu": "<strong>Cảnh báo:</strong> Không tìm thấy [https://secure.php.net/apcu APCu] hoặc [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nVùng nhớ đệm đối tượng không được kích hoạt.",
+ "config-no-cache-apcu": "<strong>Cảnh báo:</strong> Không tìm thấy [https://www.php.net/apcu APCu] hoặc [https://www.iis.net/downloads/microsoft/wincache-extension WinCache].\nVùng nhớ đệm đối tượng không được kích hoạt.",
"config-mod-security": "<strong>Cảnh báo:</strong> [https://modsecurity.org/ mod_security]/mod_security2 đã được kích hoạt trên máy chủ Web của bạn. Nhiều cấu hình phổ biến của phần mềm này sẽ gây vấn đề cho MediaWiki và những phần mềm khác cho phép người dùng đăng các nội dung tùy tiện.\nNếu có thể, bạn nên vô hiệu nó. Còn không, tra cứu [https://modsecurity.org/documentation/ tài liệu mod_security] hoặc liên hệ với nhà cung cấp hỗ trợ cho máy chủ nếu bạn gặp những lỗi ngẫu nhiên nào đó.",
"config-diff3-bad": "Không tìm thấy GNU diff3.",
"config-git": "Đã tìm thấy phần mềm điều khiển phiên bản Git: <code>$1</code>.",
"config-type-mysql": "MariaDB, MySQL, hoặc tương hợp",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki hỗ trợ các hệ thống cơ sở dữ liệu sau đây:\n\n$1\n\nNếu bạn không thấy hệ thống cơ sở dữ liệu mà bạn đang muốn sử dụng được liệt kê dưới đây, thì hãy theo chỉ dẫn được liên kết ở trên để kích hoạt tính năng hỗ trợ.",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] là mục tiêu chính cho MediaWiki và được hỗ trợ tốt nhất. MediaWiki cũng làm việc với [{{int:version-db-mysql-url}} MySQL] và [{{int:version-db-percona-url}} Percona Server], là những cơ sở dữ liệu tương thích với MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của MySQL])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] là một hệ thống cơ sở dữ liệu mã nguồn mở phổ biến như là một thay thế cho MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của PostgreSQL])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] là một hệ thống cơ sở dữ liệu dung lượng nhẹ được hỗ trợ rất tốt. ([https://secure.php.net/manual/en/pdo.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLite], sử dụng PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] là một cơ sở dữ liệu doanh nghiệp thương mại. ([https://secure.php.net/manual/en/oci8.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của OCI8])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] là một cơ sở dữ liệu doanh nghiệp thương mại cho Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLSRV])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] là mục tiêu chính cho MediaWiki và được hỗ trợ tốt nhất. MediaWiki cũng làm việc với [{{int:version-db-mysql-url}} MySQL] và [{{int:version-db-percona-url}} Percona Server], là những cơ sở dữ liệu tương thích với MariaDB. ([https://www.php.net/manual/en/mysqli.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của MySQL])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] là một hệ thống cơ sở dữ liệu mã nguồn mở phổ biến như là một thay thế cho MySQL. ([https://www.php.net/manual/en/pgsql.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của PostgreSQL])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] là một hệ thống cơ sở dữ liệu dung lượng nhẹ được hỗ trợ rất tốt. ([https://www.php.net/manual/en/pdo.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLite], sử dụng PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] là một cơ sở dữ liệu doanh nghiệp thương mại. ([https://www.php.net/manual/en/oci8.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của OCI8])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] là một cơ sở dữ liệu doanh nghiệp thương mại cho Windows. ([https://www.php.net/manual/en/sqlsrv.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLSRV])",
"config-header-mysql": "Thiết lập MariaDB/MySQL",
"config-header-postgres": "Thiết lập PostgreSQL",
"config-header-sqlite": "Thiết lập SQLite",
"config-license-help": "Nhiều wiki công khai phát hành tất cả các đóng góp theo một [https://freedomdefined.org/Definition/Vi?uselang=vi giấy phép tự do].\nĐiều này giúp tạo nên thái độ cộng đồng sở hữu và ủng hộ sự đóng góp lâu dài.\nNói chung, một wiki riêng tư hoặc của công ty không nhất thiết phải sử dụng một giấy phép tự do.\n\nNếu bạn muốn được phép sử dụng văn bản từ Wikipedia và muốn Wikipedia nhận được những văn bản được sao chép từ wiki của bạn, bạn nên chọn <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia từng sử dụng Giấy phép Tài liệu Tự do GNU.\nGFDL là một giấy phép hợp lệ nhưng khó hiểu trên thực tế.\nNội dung được phát hành theo GFDL cũng khó tái sử dụng.",
"config-email-settings": "Thiết lập thư điện tử",
"config-enable-email": "Cho phép gửi thư điện tử đi",
- "config-enable-email-help": "Nếu bạn muốn khả năng gửi thư điện tử, [https://secure.php.net/manual/en/mail.configuration.php thiết lập mail của PHP] cần phải được cấu hình đúng.\nNếu bạn không muốn sử dụng bất kỳ tính năng thư điện tử nào, bạn có thể vô hiệu chúng ở đây.",
+ "config-enable-email-help": "Nếu bạn muốn khả năng gửi thư điện tử, [https://www.php.net/manual/en/mail.configuration.php thiết lập mail của PHP] cần phải được cấu hình đúng.\nNếu bạn không muốn sử dụng bất kỳ tính năng thư điện tử nào, bạn có thể vô hiệu chúng ở đây.",
"config-email-user": "Cho phép người dùng gửi thư điện tử cho người dùng khác",
"config-email-user-help": "Cho phép tất cả người dùng gửi thư điện tử cho nhau, nếu họ đã kích hoạt nó trong cài đặt tùy chọn của họ.",
"config-email-usertalk": "Gửi thư thông báo về tin nhắn mới",
"config-env-bad": "מ'האט קאנטראלירט די סביבה.\nאיר קענט נישט אינסטאלירן מעדיעוויקי.",
"config-env-php": "PHP $1 איז אינצטאלירט.",
"config-env-hhvm": "HHVM $1 איז אינסטאלירט.",
- "config-apc": "[https://secure.php.net/apc APC] איז אינסטאַלירט",
+ "config-apc": "[https://www.php.net/apc APC] איז אינסטאַלירט",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] איז אינסטאַלירט",
"config-diff3-bad": "GNU diff3 נישט געטראפן.",
"config-using-server": "באניצן סארווער־נאמען \"<nowiki>$1</nowiki>\".",
"config-pcre-no-utf8": "<strong>致命错误:</strong>PHP的PCRE模块在编译时可能没有包含PCRE_UTF8支持。\nMediaWiki需要UTF-8支持才能正常工作。",
"config-memory-raised": "PHP的内存使用上限<code>memory_limit</code>为$1,自动提升到$2。",
"config-memory-bad": "<strong>警告:</strong>PHP的内存使用上限<code>memory_limit</code>为$1。\n该设定可能过低,并导致安装失败!",
- "config-apc": "[https://secure.php.net/apc APC]已安装",
- "config-apcu": "已安装[https://secure.php.net/apcu APCu]",
+ "config-apc": "[https://www.php.net/apc APC]已安装",
+ "config-apcu": "已安装[https://www.php.net/apcu APCu]",
"config-wincache": "已安装[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]",
- "config-no-cache-apcu": "<strong>警告:</strong>找不到[https://secure.php.net/apcu APCu]或[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]。对象缓存未启用。",
+ "config-no-cache-apcu": "<strong>警告:</strong>找不到[https://www.php.net/apcu APCu]或[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]。对象缓存未启用。",
"config-mod-security": "<strong>警告:</strong>您的web服务器已启用[https://modsecurity.org/ mod_security]/mod_security2。它的很多常见配置可能导致MediaWiki及其他软件允许用户发布任意内容的问题。如果可能,这应当被禁用。否则,当您遭遇随机错误时,请参考[https://modsecurity.org/documentation/ mod_security 文档]或联络您的主机支持。",
"config-diff3-bad": "找不到GNU diff3文字对比工具程序,您可以暂时忽略它,但可能会更频繁遇到编辑冲突。",
"config-git": "发现Git版本控制软件:<code>$1</code>",
"config-type-mysql": "MariaDB、MySQL或兼容程序",
"config-type-mssql": "微软SQL服务器",
"config-support-info": "MediaWiki支持以下数据库系统:\n\n$1\n\n如果您在下面列出的数据库系统中没有找到您希望使用的系统,请根据上方链向的指引启用支持。",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]是用于MediaWiki的主要数据库,对它的支持最为完备。MediaWiki也可以在[{{int:version-db-mysql-url}} MySQL]和[{{int:version-db-percona-url}} Percona Server]下工作,它们与MariaDB兼容。([https://secure.php.net/manual/en/mysqli.installation.php 如何将对MySQL的支持编译进PHP中])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL]是一种流行的开源数据库系统,可作为MySQL的替代。([https://secure.php.net/manual/en/pgsql.installation.php 如何将对PostgreSQL的支持编译进PHP中])",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]是一种轻量级的数据库系统,能被良好地支持。([https://secure.php.net/manual/en/pdo.installation.php 如何将对SQLite的支持编译进PHP中],须使用PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]是一种商用企业级的数据库。([https://secure.php.net/manual/en/oci8.installation.php 如何将对OCI8的支持编译进PHP中])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server]是一个适用于Windows的商业性企业数据库。([https://secure.php.net/manual/en/sqlsrv.installation.php 如何编译带有SQLSRV支持的PHP])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB]是用于MediaWiki的主要数据库,对它的支持最为完备。MediaWiki也可以在[{{int:version-db-mysql-url}} MySQL]和[{{int:version-db-percona-url}} Percona Server]下工作,它们与MariaDB兼容。([https://www.php.net/manual/en/mysqli.installation.php 如何将对MySQL的支持编译进PHP中])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL]是一种流行的开源数据库系统,可作为MySQL的替代。([https://www.php.net/manual/en/pgsql.installation.php 如何将对PostgreSQL的支持编译进PHP中])",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]是一种轻量级的数据库系统,能被良好地支持。([https://www.php.net/manual/en/pdo.installation.php 如何将对SQLite的支持编译进PHP中],须使用PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle]是一种商用企业级的数据库。([https://www.php.net/manual/en/oci8.installation.php 如何将对OCI8的支持编译进PHP中])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server]是一个适用于Windows的商业性企业数据库。([https://www.php.net/manual/en/sqlsrv.installation.php 如何编译带有SQLSRV支持的PHP])",
"config-header-mysql": "MariaDB/MySQL设置",
"config-header-postgres": "PostgreSQL设置",
"config-header-sqlite": "SQLite设置",
"config-license-help": "许多公共wiki将所有用户贡献置于[https://freedomdefined.org/Definition 自由许可证]之下。这有助于构建社区的主人翁意识,并鼓励长期贡献。对于非公共wiki或公司wiki,这并非必要条件。\n\n如果您希望使用来自维基百科的内容,并希望维基百科能接受复制自您的wiki的内容,您应当选择<strong>{{int:config-license-cc-by-sa}}</strong>。\n\nGNU自由文档许可证是维基百科曾经使用过的许可证,并迄今有效。然而,该许可证难以理解,并会增加重用内容的难度。",
"config-email-settings": "电子邮件设置",
"config-enable-email": "启用出站电子邮件",
- "config-enable-email-help": "如果您希望使用电子邮件功能,请正确配置[https://secure.php.net/manual/en/mail.configuration.php PHP的邮件设定]。如果您不需要任何电子邮件功能,请在此处禁用它。",
+ "config-enable-email-help": "如果您希望使用电子邮件功能,请正确配置[https://www.php.net/manual/en/mail.configuration.php PHP的邮件设定]。如果您不需要任何电子邮件功能,请在此处禁用它。",
"config-email-user": "启用用户到用户的电子邮件",
"config-email-user-help": "允许所有用户互发邮件,假若他们启用了该功能。",
"config-email-usertalk": "启用用户讨论页面通知",
"config-pcre-no-utf8": "<strong>嚴重:</strong> PHP 的 PCRE 模組在編譯時未包含 PCRE_UTF8 支援。\nMediaWiki 需要支援 UTF-8 才可正常運作。",
"config-memory-raised": "PHP 的記憶體使用上限 <code>memory_limit</code> 目前為 $1,自動提高到 $2。",
"config-memory-bad": "<strong>警告:</strong>PHP 的記憶體使用上限 <code>memory_limit</code> 為 $1。\n該設定值可能過低。\n這可能導致後續的安裝失敗!",
- "config-apc": "[https://secure.php.net/apc APC] 已安裝",
- "config-apcu": "已安裝[https://secure.php.net/apcu APCu]",
+ "config-apc": "[https://www.php.net/apc APC] 已安裝",
+ "config-apcu": "已安裝[https://www.php.net/apcu APCu]",
"config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] 已安裝",
- "config-no-cache-apcu": "<strong>警告:</strong>找不到[https://secure.php.net/apcu APCu]或[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]。未開啟物件快取。",
+ "config-no-cache-apcu": "<strong>警告:</strong>找不到[https://www.php.net/apcu APCu]或[https://www.iis.net/downloads/microsoft/wincache-extension WinCache]。未開啟物件快取。",
"config-mod-security": "<strong>警告:</strong>您的網頁伺服器已開啟 [https://modsecurity.org/ mod_security] 模組,如果設定不恰當會導致使用者可在 MediaWiki 或其他應用程式發佈任意的內容。\n若您遇到任何問題,請參考 [https://modsecurity.org/documentation/ mod_security 文件] 或聯繫您的伺服器技術支援人員。",
"config-diff3-bad": "找不到 GNU diff3 文字比對工具程式,而您現在可忽略它,但可能會更頻繁地遇到編輯衝突。",
"config-git": "找到 Git 版本控制軟體:<code>$1</code>。",
"config-type-mysql": "MariaDB、MySQL、或與其相容的套件",
"config-type-mssql": "Microsoft SQL Server",
"config-support-info": "MediaWiki 支援以下資料庫系統:\n\n$1\n\n如果您下方沒有看到您要使用的資料庫系統,請根據上方連結指示開啟資料庫的支援。",
- "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] 是 MediaWiki 主要支援的資料庫系統。MediaWiki 也同時可運作與於 [{{int:version-db-mysql-url}} MySQL] 和 [{{int:version-db-percona-url}} Percona 伺服器],上述這些與 MariaDB 相容的資料庫系統。([https://secure.php.net/manual/en/mysqli.installation.php 如何編譯支援 MySQL 的 PHP])",
- "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] 是一套受歡迎的開源資料庫系統,可用來替代 MySQL。([https://secure.php.net/manual/en/pgsql.installation.php 如何編譯支援 PostgreSQL 的 PHP])。",
- "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] 是一套輕量級的資料庫系統,MediaWiki 可在此資料庫系統上良好的運作。([https://secure.php.net/manual/en/pdo.installation.php 如何編譯支援 SQLite 的 PHP],須透過 PDO)",
- "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] 是一套商用企業級的資料庫。([https://secure.php.net/manual/en/oci8.installation.php 如何編譯支援 OCI8 的 PHP])",
- "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] 是一套 Windows 專用的商用企業級的資料庫。 ([https://secure.php.net/manual/en/sqlsrv.installation.php 如何編譯支援 SQLSRV 的 PHP])",
+ "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] 是 MediaWiki 主要支援的資料庫系統。MediaWiki 也同時可運作與於 [{{int:version-db-mysql-url}} MySQL] 和 [{{int:version-db-percona-url}} Percona 伺服器],上述這些與 MariaDB 相容的資料庫系統。([https://www.php.net/manual/en/mysqli.installation.php 如何編譯支援 MySQL 的 PHP])",
+ "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] 是一套受歡迎的開源資料庫系統,可用來替代 MySQL。([https://www.php.net/manual/en/pgsql.installation.php 如何編譯支援 PostgreSQL 的 PHP])。",
+ "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] 是一套輕量級的資料庫系統,MediaWiki 可在此資料庫系統上良好的運作。([https://www.php.net/manual/en/pdo.installation.php 如何編譯支援 SQLite 的 PHP],須透過 PDO)",
+ "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] 是一套商用企業級的資料庫。([https://www.php.net/manual/en/oci8.installation.php 如何編譯支援 OCI8 的 PHP])",
+ "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] 是一套 Windows 專用的商用企業級的資料庫。 ([https://www.php.net/manual/en/sqlsrv.installation.php 如何編譯支援 SQLSRV 的 PHP])",
"config-header-mysql": "MariaDB/MySQL 設定",
"config-header-postgres": "PostgreSQL 設定",
"config-header-sqlite": "SQLite 設定",
"config-license-help": "許多開放式 Wiki 會以 [https://freedomdefined.org/Definition 自由授權條款] 的方式釋放出編者的所有貢獻,這有助於構建社群的所有權,並且能鼓勵長期貢獻。對於封閉式的 Wiki 或公司 Wiki 則是非必要的。\n\n如果您希望使用來自維基百科(Wikipedia)的內容,並希望維基百科能接受您的 Wiki 內容,請應選擇 <strong>{{int:config-license-cc-by-sa}}</strong> 授權條款。\n\n維基百科(Wikipedia)先前是使用 GNU 自由文件授權條款,\n但該授權條款的內容較難理解,因此較難再利用在該條款底下的內容。",
"config-email-settings": "E-mail 設定",
"config-enable-email": "開啟外寄電子郵件",
- "config-enable-email-help": "如果您要使用電子郵件功能,請正確設定 [https://secure.php.net/manual/en/mail.configuration.php PHP 的郵件設定]。\n如果您不需要使用電子郵件功能,請在此處關閉。",
+ "config-enable-email-help": "如果您要使用電子郵件功能,請正確設定 [https://www.php.net/manual/en/mail.configuration.php PHP 的郵件設定]。\n如果您不需要使用電子郵件功能,請在此處關閉。",
"config-email-user": "開啟使用者對使用者間的電子郵件互通",
"config-email-user-help": "若使用者在個人偏好設定開啟了此功能,則可允許使用者間相互傳送郵件。",
"config-email-usertalk": "開啟使用者討論頁面通知",
* @since 1.23
*
* @param array $array1 The array to compare from
- * @param array $array2,... More arrays to compare against
+ * @param array ...$arrays More arrays to compare against
* @return array An array containing all the values from array1
* that are not present in any of the other arrays.
*/
- public static function arrayDiffAssocRecursive( $array1 ) {
- $arrays = func_get_args();
- array_shift( $arrays );
+ public static function arrayDiffAssocRecursive( $array1, ...$arrays ) {
$ret = [];
foreach ( $array1 as $key => $value ) {
// FIXME: Simplify now we only support PHP 7.0.0+
// Note: PCRE doesn't support multiple capture groups with the same name by default.
// - PCRE 6.7 introduced the "J" modifier (PCRE_INFO_JCHANGED for PCRE_DUPNAMES).
- // https://secure.php.net/manual/en/reference.pcre.pattern.modifiers.php
+ // https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
// However this isn't useful since it just ignores all but the first one.
// Also, while the modifier was introduced in PCRE 6.7 (PHP 5.2+) it was
// not exposed to public preg_* functions until PHP 5.6.0.
// - PCRE 8.36 fixed this to work as expected (e.g. merge conceptually to
// only return the one matched in the part that actually matched).
// However MediaWiki supports 5.5.9, which has PCRE 8.32
- // Per https://secure.php.net/manual/en/pcre.installation.php:
+ // Per https://www.php.net/manual/en/pcre.installation.php:
// - PCRE 8.32 (PHP 5.5.0)
// - PCRE 8.34 (PHP 5.5.10, PHP 5.6.0)
// - PCRE 8.37 (PHP 5.5.26, PHP 5.6.9, PHP 7.0.0)
/**
* @param callable $callback Callback that gets called by __toString
- * @param mixed $param,... Parameters to the callback
+ * @param mixed ...$params Parameters to the callback
*/
- public function __construct( $callback /*...*/ ) {
- $this->params = func_get_args();
- array_shift( $this->params );
+ public function __construct( $callback, ...$params ) {
$this->callback = $callback;
+ $this->params = $params;
}
/**
*
* Like MemoizedCallable::invokeArgs(), but variadic.
*
- * @param mixed $params,... Parameters for memoized function or method.
+ * @param mixed ...$params Parameters for memoized function or method.
* @return mixed The memoized callable's return value.
*/
- public function invoke() {
- return $this->invokeArgs( func_get_args() );
+ public function invoke( ...$params ) {
+ return $this->invokeArgs( $params );
}
/**
<?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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
/**
* File system based backend.
*
public function getFeatures() {
if ( $this->isWindows && version_compare( PHP_VERSION, '7.1', 'lt' ) ) {
// PHP before 7.1 used 8-bit code page for filesystem paths on Windows;
- // See https://secure.php.net/manual/en/migration71.windows-support.php
+ // See https://www.php.net/manual/en/migration71.windows-support.php
return 0;
} else {
return FileBackend::ATTR_UNICODE_PATHS;
return true; // suppress from PHP handler
}
}
-
-/**
- * @see FileBackendStoreOpHandle
- */
-class FSFileOpHandle extends FileBackendStoreOpHandle {
- public $cmd; // string; shell command
- public $chmodPath; // string; file to chmod
-
- /**
- * @param FSFileBackend $backend
- * @param array $params
- * @param callable $call
- * @param string $cmd
- * @param int|null $chmodPath
- */
- public function __construct(
- FSFileBackend $backend, array $params, $call, $cmd, $chmodPath = null
- ) {
- $this->backend = $backend;
- $this->params = $params;
- $this->call = $call;
- $this->cmd = $cmd;
- $this->chmodPath = $chmodPath;
- }
-}
-
-/**
- * Wrapper around RecursiveDirectoryIterator/DirectoryIterator that
- * catches exception or does any custom behavoir that we may want.
- * Do not use this class from places outside FSFileBackend.
- *
- * @ingroup FileBackend
- */
-abstract class FSFileBackendList implements Iterator {
- /** @var Iterator */
- protected $iter;
-
- /** @var int */
- protected $suffixStart;
-
- /** @var int */
- protected $pos = 0;
-
- /** @var array */
- protected $params = [];
-
- /**
- * @param string $dir File system directory
- * @param array $params
- */
- public function __construct( $dir, array $params ) {
- $path = realpath( $dir ); // normalize
- if ( $path === false ) {
- $path = $dir;
- }
- $this->suffixStart = strlen( $path ) + 1; // size of "path/to/dir/"
- $this->params = $params;
-
- try {
- $this->iter = $this->initIterator( $path );
- } catch ( UnexpectedValueException $e ) {
- $this->iter = null; // bad permissions? deleted?
- }
- }
-
- /**
- * Return an appropriate iterator object to wrap
- *
- * @param string $dir File system directory
- * @return Iterator
- */
- protected function initIterator( $dir ) {
- if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
- # Get an iterator that will get direct sub-nodes
- return new DirectoryIterator( $dir );
- } else { // recursive
- # Get an iterator that will return leaf nodes (non-directories)
- # RecursiveDirectoryIterator extends FilesystemIterator.
- # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
- $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
-
- return new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator( $dir, $flags ),
- RecursiveIteratorIterator::CHILD_FIRST // include dirs
- );
- }
- }
-
- /**
- * @see Iterator::key()
- * @return int
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::current()
- * @return string|bool String or false
- */
- public function current() {
- return $this->getRelPath( $this->iter->current()->getPathname() );
- }
-
- /**
- * @see Iterator::next()
- * @throws FileBackendError
- */
- public function next() {
- try {
- $this->iter->next();
- $this->filterViaNext();
- } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
- throw new FileBackendError( "File iterator gave UnexpectedValueException." );
- }
- ++$this->pos;
- }
-
- /**
- * @see Iterator::rewind()
- * @throws FileBackendError
- */
- public function rewind() {
- $this->pos = 0;
- try {
- $this->iter->rewind();
- $this->filterViaNext();
- } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
- throw new FileBackendError( "File iterator gave UnexpectedValueException." );
- }
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- return $this->iter && $this->iter->valid();
- }
-
- /**
- * Filter out items by advancing to the next ones
- */
- protected function filterViaNext() {
- }
-
- /**
- * Return only the relative path and normalize slashes to FileBackend-style.
- * Uses the "real path" since the suffix is based upon that.
- *
- * @param string $dir
- * @return string
- */
- protected function getRelPath( $dir ) {
- $path = realpath( $dir );
- if ( $path === false ) {
- $path = $dir;
- }
-
- return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
- }
-}
-
-class FSFileBackendDirList extends FSFileBackendList {
- protected function filterViaNext() {
- while ( $this->iter->valid() ) {
- if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
- $this->iter->next(); // skip non-directories and dot files
- } else {
- break;
- }
- }
- }
-}
-
-class FSFileBackendFileList extends FSFileBackendList {
- protected function filterViaNext() {
- while ( $this->iter->valid() ) {
- if ( !$this->iter->current()->isFile() ) {
- $this->iter->next(); // skip non-files and dot files
- } else {
- break;
- }
- }
- }
-}
* - StatusValue::newGood() if this method is called without parameters
* - StatusValue::newFatal() with all parameters to this method if passed in
*
- * @param string $args,...
+ * @param string ...$args
* @return StatusValue
*/
- final protected function newStatus() {
- $args = func_get_args();
+ final protected function newStatus( ...$args ) {
if ( count( $args ) ) {
$sv = StatusValue::newFatal( ...$args );
} else {
+++ /dev/null
-<?php
-/**
- * File backend exception for checked exceptions (e.g. I/O errors)
- *
- * @ingroup FileBackend
- * @since 1.22
- */
-class FileBackendError extends Exception {
-}
return $mime ?: 'unknown/unknown';
}
}
-
-/**
- * FileBackendStore helper class for performing asynchronous file operations.
- *
- * For example, calling FileBackendStore::createInternal() with the "async"
- * param flag may result in a StatusValue that contains this object as a value.
- * This class is largely backend-specific and is mostly just "magic" to be
- * passed to FileBackendStore::executeOpHandlesInternal().
- */
-abstract class FileBackendStoreOpHandle {
- /** @var array */
- public $params = []; // params to caller functions
- /** @var FileBackendStore */
- public $backend;
- /** @var array */
- public $resourcesToClose = [];
-
- public $call; // string; name that identifies the function called
-
- /**
- * Close all open file handles
- */
- public function closeResources() {
- array_map( 'fclose', $this->resourcesToClose );
- }
-}
-
-/**
- * FileBackendStore helper function to handle listings that span container shards.
- * Do not use this class from places outside of FileBackendStore.
- *
- * @ingroup FileBackend
- */
-abstract class FileBackendStoreShardListIterator extends FilterIterator {
- /** @var FileBackendStore */
- protected $backend;
-
- /** @var array */
- protected $params;
-
- /** @var string Full container name */
- protected $container;
-
- /** @var string Resolved relative path */
- protected $directory;
-
- /** @var array */
- protected $multiShardPaths = []; // (rel path => 1)
-
- /**
- * @param FileBackendStore $backend
- * @param string $container Full storage container name
- * @param string $dir Storage directory relative to container
- * @param array $suffixes List of container shard suffixes
- * @param array $params
- */
- public function __construct(
- FileBackendStore $backend, $container, $dir, array $suffixes, array $params
- ) {
- $this->backend = $backend;
- $this->container = $container;
- $this->directory = $dir;
- $this->params = $params;
-
- $iter = new AppendIterator();
- foreach ( $suffixes as $suffix ) {
- $iter->append( $this->listFromShard( $this->container . $suffix ) );
- }
-
- parent::__construct( $iter );
- }
-
- public function accept() {
- $rel = $this->getInnerIterator()->current(); // path relative to given directory
- $path = $this->params['dir'] . "/{$rel}"; // full storage path
- if ( $this->backend->isSingleShardPathInternal( $path ) ) {
- return true; // path is only on one shard; no issue with duplicates
- } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
- // Don't keep listing paths that are on multiple shards
- return false;
- } else {
- $this->multiShardPaths[$rel] = 1;
-
- return true;
- }
- }
-
- public function rewind() {
- parent::rewind();
- $this->multiShardPaths = [];
- }
-
- /**
- * Get the list for a given container shard
- *
- * @param string $container Resolved container name
- * @return Iterator
- */
- abstract protected function listFromShard( $container );
-}
-
-/**
- * Iterator for listing directories
- */
-class FileBackendStoreShardDirIterator extends FileBackendStoreShardListIterator {
- protected function listFromShard( $container ) {
- $list = $this->backend->getDirectoryListInternal(
- $container, $this->directory, $this->params );
- if ( $list === null ) {
- return new ArrayIterator( [] );
- } else {
- return is_array( $list ) ? new ArrayIterator( $list ) : $list;
- }
- }
-}
-
-/**
- * Iterator for listing regular files
- */
-class FileBackendStoreShardFileIterator extends FileBackendStoreShardListIterator {
- protected function listFromShard( $container ) {
- $list = $this->backend->getFileListInternal(
- $container, $this->directory, $this->params );
- if ( $list === null ) {
- return new ArrayIterator( [] );
- } else {
- return is_array( $list ) ? new ArrayIterator( $list ) : $list;
- }
- }
-}
$this->logger->error( $msg, $msgParams );
}
}
-
-/**
- * @see FileBackendStoreOpHandle
- */
-class SwiftFileOpHandle extends FileBackendStoreOpHandle {
- /** @var array List of Requests for MultiHttpClient */
- public $httpOp;
- /** @var Closure */
- public $callback;
-
- /**
- * @param SwiftFileBackend $backend
- * @param Closure $callback Function that takes (HTTP request array, status)
- * @param array $httpOp MultiHttpClient op
- */
- public function __construct( SwiftFileBackend $backend, Closure $callback, array $httpOp ) {
- $this->backend = $backend;
- $this->callback = $callback;
- $this->httpOp = $httpOp;
- }
-}
-
-/**
- * SwiftFileBackend helper class to page through listings.
- * Swift also has a listing limit of 10,000 objects for sanity.
- * Do not use this class from places outside SwiftFileBackend.
- *
- * @ingroup FileBackend
- */
-abstract class SwiftFileBackendList implements Iterator {
- /** @var array List of path or (path,stat array) entries */
- protected $bufferIter = [];
-
- /** @var string List items *after* this path */
- protected $bufferAfter = null;
-
- /** @var int */
- protected $pos = 0;
-
- /** @var array */
- protected $params = [];
-
- /** @var SwiftFileBackend */
- protected $backend;
-
- /** @var string Container name */
- protected $container;
-
- /** @var string Storage directory */
- protected $dir;
-
- /** @var int */
- protected $suffixStart;
-
- const PAGE_SIZE = 9000; // file listing buffer size
-
- /**
- * @param SwiftFileBackend $backend
- * @param string $fullCont Resolved container name
- * @param string $dir Resolved directory relative to container
- * @param array $params
- */
- public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) {
- $this->backend = $backend;
- $this->container = $fullCont;
- $this->dir = $dir;
- if ( substr( $this->dir, -1 ) === '/' ) {
- $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
- }
- if ( $this->dir == '' ) { // whole container
- $this->suffixStart = 0;
- } else { // dir within container
- $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/"
- }
- $this->params = $params;
- }
-
- /**
- * @see Iterator::key()
- * @return int
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::next()
- */
- public function next() {
- // Advance to the next file in the page
- next( $this->bufferIter );
- ++$this->pos;
- // Check if there are no files left in this page and
- // advance to the next page if this page was not empty.
- if ( !$this->valid() && count( $this->bufferIter ) ) {
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
- }
-
- /**
- * @see Iterator::rewind()
- */
- public function rewind() {
- $this->pos = 0;
- $this->bufferAfter = null;
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- if ( $this->bufferIter === null ) {
- return false; // some failure?
- } else {
- return ( current( $this->bufferIter ) !== false ); // no paths can have this value
- }
- }
-
- /**
- * Get the given list portion (page)
- *
- * @param string $container Resolved container name
- * @param string $dir Resolved path relative to container
- * @param string &$after
- * @param int $limit
- * @param array $params
- * @return Traversable|array
- */
- abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params );
-}
-
-/**
- * Iterator for listing directories
- */
-class SwiftFileBackendDirList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- return substr( current( $this->bufferIter ), $this->suffixStart, -1 );
- }
-
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}
-
-/**
- * Iterator for listing regular files
- */
-class SwiftFileBackendFileList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- list( $path, $stat ) = current( $this->bufferIter );
- $relPath = substr( $path, $this->suffixStart );
- if ( is_array( $stat ) ) {
- $storageDir = rtrim( $this->params['dir'], '/' );
- $this->backend->loadListingStatInternal( "$storageDir/$relPath", $stat );
- }
-
- return $relPath;
- }
-
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}
--- /dev/null
+<?php
+/**
+ * File backend exception for checked exceptions (e.g. I/O errors)
+ *
+ * @ingroup FileBackend
+ * @since 1.22
+ */
+class FileBackendError extends Exception {
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+class FSFileBackendDirList extends FSFileBackendList {
+ protected function filterViaNext() {
+ while ( $this->iter->valid() ) {
+ if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
+ $this->iter->next(); // skip non-directories and dot files
+ } else {
+ break;
+ }
+ }
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+class FSFileBackendFileList extends FSFileBackendList {
+ protected function filterViaNext() {
+ while ( $this->iter->valid() ) {
+ if ( !$this->iter->current()->isFile() ) {
+ $this->iter->next(); // skip non-files and dot files
+ } else {
+ break;
+ }
+ }
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * Wrapper around RecursiveDirectoryIterator/DirectoryIterator that
+ * catches exception or does any custom behavoir that we may want.
+ * Do not use this class from places outside FSFileBackend.
+ *
+ * @ingroup FileBackend
+ */
+abstract class FSFileBackendList implements Iterator {
+ /** @var Iterator */
+ protected $iter;
+
+ /** @var int */
+ protected $suffixStart;
+
+ /** @var int */
+ protected $pos = 0;
+
+ /** @var array */
+ protected $params = [];
+
+ /**
+ * @param string $dir File system directory
+ * @param array $params
+ */
+ public function __construct( $dir, array $params ) {
+ $path = realpath( $dir ); // normalize
+ if ( $path === false ) {
+ $path = $dir;
+ }
+ $this->suffixStart = strlen( $path ) + 1; // size of "path/to/dir/"
+ $this->params = $params;
+
+ try {
+ $this->iter = $this->initIterator( $path );
+ } catch ( UnexpectedValueException $e ) {
+ $this->iter = null; // bad permissions? deleted?
+ }
+ }
+
+ /**
+ * Return an appropriate iterator object to wrap
+ *
+ * @param string $dir File system directory
+ * @return Iterator
+ */
+ protected function initIterator( $dir ) {
+ if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
+ # Get an iterator that will get direct sub-nodes
+ return new DirectoryIterator( $dir );
+ } else { // recursive
+ # Get an iterator that will return leaf nodes (non-directories)
+ # RecursiveDirectoryIterator extends FilesystemIterator.
+ # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
+ $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
+
+ return new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator( $dir, $flags ),
+ RecursiveIteratorIterator::CHILD_FIRST // include dirs
+ );
+ }
+ }
+
+ /**
+ * @see Iterator::key()
+ * @return int
+ */
+ public function key() {
+ return $this->pos;
+ }
+
+ /**
+ * @see Iterator::current()
+ * @return string|bool String or false
+ */
+ public function current() {
+ return $this->getRelPath( $this->iter->current()->getPathname() );
+ }
+
+ /**
+ * @see Iterator::next()
+ * @throws FileBackendError
+ */
+ public function next() {
+ try {
+ $this->iter->next();
+ $this->filterViaNext();
+ } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
+ throw new FileBackendError( "File iterator gave UnexpectedValueException." );
+ }
+ ++$this->pos;
+ }
+
+ /**
+ * @see Iterator::rewind()
+ * @throws FileBackendError
+ */
+ public function rewind() {
+ $this->pos = 0;
+ try {
+ $this->iter->rewind();
+ $this->filterViaNext();
+ } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
+ throw new FileBackendError( "File iterator gave UnexpectedValueException." );
+ }
+ }
+
+ /**
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ return $this->iter && $this->iter->valid();
+ }
+
+ /**
+ * Filter out items by advancing to the next ones
+ */
+ protected function filterViaNext() {
+ }
+
+ /**
+ * Return only the relative path and normalize slashes to FileBackend-style.
+ * Uses the "real path" since the suffix is based upon that.
+ *
+ * @param string $dir
+ * @return string
+ */
+ protected function getRelPath( $dir ) {
+ $path = realpath( $dir );
+ if ( $path === false ) {
+ $path = $dir;
+ }
+
+ return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * Iterator for listing directories
+ */
+class FileBackendStoreShardDirIterator extends FileBackendStoreShardListIterator {
+ protected function listFromShard( $container ) {
+ $list = $this->backend->getDirectoryListInternal(
+ $container, $this->directory, $this->params );
+ if ( $list === null ) {
+ return new ArrayIterator( [] );
+ } else {
+ return is_array( $list ) ? new ArrayIterator( $list ) : $list;
+ }
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * Iterator for listing regular files
+ */
+class FileBackendStoreShardFileIterator extends FileBackendStoreShardListIterator {
+ protected function listFromShard( $container ) {
+ $list = $this->backend->getFileListInternal(
+ $container, $this->directory, $this->params );
+ if ( $list === null ) {
+ return new ArrayIterator( [] );
+ } else {
+ return is_array( $list ) ? new ArrayIterator( $list ) : $list;
+ }
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * FileBackendStore helper function to handle listings that span container shards.
+ * Do not use this class from places outside of FileBackendStore.
+ *
+ * @ingroup FileBackend
+ */
+abstract class FileBackendStoreShardListIterator extends FilterIterator {
+ /** @var FileBackendStore */
+ protected $backend;
+
+ /** @var array */
+ protected $params;
+
+ /** @var string Full container name */
+ protected $container;
+
+ /** @var string Resolved relative path */
+ protected $directory;
+
+ /** @var array */
+ protected $multiShardPaths = []; // (rel path => 1)
+
+ /**
+ * @param FileBackendStore $backend
+ * @param string $container Full storage container name
+ * @param string $dir Storage directory relative to container
+ * @param array $suffixes List of container shard suffixes
+ * @param array $params
+ */
+ public function __construct(
+ FileBackendStore $backend, $container, $dir, array $suffixes, array $params
+ ) {
+ $this->backend = $backend;
+ $this->container = $container;
+ $this->directory = $dir;
+ $this->params = $params;
+
+ $iter = new AppendIterator();
+ foreach ( $suffixes as $suffix ) {
+ $iter->append( $this->listFromShard( $this->container . $suffix ) );
+ }
+
+ parent::__construct( $iter );
+ }
+
+ public function accept() {
+ $rel = $this->getInnerIterator()->current(); // path relative to given directory
+ $path = $this->params['dir'] . "/{$rel}"; // full storage path
+ if ( $this->backend->isSingleShardPathInternal( $path ) ) {
+ return true; // path is only on one shard; no issue with duplicates
+ } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
+ // Don't keep listing paths that are on multiple shards
+ return false;
+ } else {
+ $this->multiShardPaths[$rel] = 1;
+
+ return true;
+ }
+ }
+
+ public function rewind() {
+ parent::rewind();
+ $this->multiShardPaths = [];
+ }
+
+ /**
+ * Get the list for a given container shard
+ *
+ * @param string $container Resolved container name
+ * @return Iterator
+ */
+ abstract protected function listFromShard( $container );
+}
--- /dev/null
+<?php
+/**
+ * OpenStack Swift based file backend.
+ *
+ * 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 FileBackend
+ * @author Russ Nelson
+ */
+
+/**
+ * Iterator for listing directories
+ */
+class SwiftFileBackendDirList extends SwiftFileBackendList {
+ /**
+ * @see Iterator::current()
+ * @return string|bool String (relative path) or false
+ */
+ public function current() {
+ return substr( current( $this->bufferIter ), $this->suffixStart, -1 );
+ }
+
+ protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
+ return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params );
+ }
+}
--- /dev/null
+<?php
+/**
+ * OpenStack Swift based file backend.
+ *
+ * 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 FileBackend
+ * @author Russ Nelson
+ */
+
+/**
+ * Iterator for listing regular files
+ */
+class SwiftFileBackendFileList extends SwiftFileBackendList {
+ /**
+ * @see Iterator::current()
+ * @return string|bool String (relative path) or false
+ */
+ public function current() {
+ list( $path, $stat ) = current( $this->bufferIter );
+ $relPath = substr( $path, $this->suffixStart );
+ if ( is_array( $stat ) ) {
+ $storageDir = rtrim( $this->params['dir'], '/' );
+ $this->backend->loadListingStatInternal( "$storageDir/$relPath", $stat );
+ }
+
+ return $relPath;
+ }
+
+ protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
+ return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params );
+ }
+}
--- /dev/null
+<?php
+/**
+ * OpenStack Swift based file backend.
+ *
+ * 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 FileBackend
+ * @author Russ Nelson
+ */
+
+/**
+ * SwiftFileBackend helper class to page through listings.
+ * Swift also has a listing limit of 10,000 objects for sanity.
+ * Do not use this class from places outside SwiftFileBackend.
+ *
+ * @ingroup FileBackend
+ */
+abstract class SwiftFileBackendList implements Iterator {
+ /** @var array List of path or (path,stat array) entries */
+ protected $bufferIter = [];
+
+ /** @var string List items *after* this path */
+ protected $bufferAfter = null;
+
+ /** @var int */
+ protected $pos = 0;
+
+ /** @var array */
+ protected $params = [];
+
+ /** @var SwiftFileBackend */
+ protected $backend;
+
+ /** @var string Container name */
+ protected $container;
+
+ /** @var string Storage directory */
+ protected $dir;
+
+ /** @var int */
+ protected $suffixStart;
+
+ const PAGE_SIZE = 9000; // file listing buffer size
+
+ /**
+ * @param SwiftFileBackend $backend
+ * @param string $fullCont Resolved container name
+ * @param string $dir Resolved directory relative to container
+ * @param array $params
+ */
+ public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) {
+ $this->backend = $backend;
+ $this->container = $fullCont;
+ $this->dir = $dir;
+ if ( substr( $this->dir, -1 ) === '/' ) {
+ $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
+ }
+ if ( $this->dir == '' ) { // whole container
+ $this->suffixStart = 0;
+ } else { // dir within container
+ $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/"
+ }
+ $this->params = $params;
+ }
+
+ /**
+ * @see Iterator::key()
+ * @return int
+ */
+ public function key() {
+ return $this->pos;
+ }
+
+ /**
+ * @see Iterator::next()
+ */
+ public function next() {
+ // Advance to the next file in the page
+ next( $this->bufferIter );
+ ++$this->pos;
+ // Check if there are no files left in this page and
+ // advance to the next page if this page was not empty.
+ if ( !$this->valid() && count( $this->bufferIter ) ) {
+ $this->bufferIter = $this->pageFromList(
+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
+ ); // updates $this->bufferAfter
+ }
+ }
+
+ /**
+ * @see Iterator::rewind()
+ */
+ public function rewind() {
+ $this->pos = 0;
+ $this->bufferAfter = null;
+ $this->bufferIter = $this->pageFromList(
+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
+ ); // updates $this->bufferAfter
+ }
+
+ /**
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ if ( $this->bufferIter === null ) {
+ return false; // some failure?
+ } else {
+ return ( current( $this->bufferIter ) !== false ); // no paths can have this value
+ }
+ }
+
+ /**
+ * Get the given list portion (page)
+ *
+ * @param string $container Resolved container name
+ * @param string $dir Resolved path relative to container
+ * @param string &$after
+ * @param int $limit
+ * @param array $params
+ * @return Traversable|array
+ */
+ abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params );
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * @see FileBackendStoreOpHandle
+ */
+class FSFileOpHandle extends FileBackendStoreOpHandle {
+ public $cmd; // string; shell command
+ public $chmodPath; // string; file to chmod
+
+ /**
+ * @param FSFileBackend $backend
+ * @param array $params
+ * @param callable $call
+ * @param string $cmd
+ * @param int|null $chmodPath
+ */
+ public function __construct(
+ FSFileBackend $backend, array $params, $call, $cmd, $chmodPath = null
+ ) {
+ $this->backend = $backend;
+ $this->params = $params;
+ $this->call = $call;
+ $this->cmd = $cmd;
+ $this->chmodPath = $chmodPath;
+ }
+}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * FileBackendStore helper class for performing asynchronous file operations.
+ *
+ * For example, calling FileBackendStore::createInternal() with the "async"
+ * param flag may result in a StatusValue that contains this object as a value.
+ * This class is largely backend-specific and is mostly just "magic" to be
+ * passed to FileBackendStore::executeOpHandlesInternal().
+ */
+abstract class FileBackendStoreOpHandle {
+ /** @var array */
+ public $params = []; // params to caller functions
+ /** @var FileBackendStore */
+ public $backend;
+ /** @var array */
+ public $resourcesToClose = [];
+
+ public $call; // string; name that identifies the function called
+
+ /**
+ * Close all open file handles
+ */
+ public function closeResources() {
+ array_map( 'fclose', $this->resourcesToClose );
+ }
+}
--- /dev/null
+<?php
+/**
+ * OpenStack Swift based file backend.
+ *
+ * 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 FileBackend
+ * @author Russ Nelson
+ */
+
+/**
+ * @see FileBackendStoreOpHandle
+ */
+class SwiftFileOpHandle extends FileBackendStoreOpHandle {
+ /** @var array List of Requests for MultiHttpClient */
+ public $httpOp;
+ /** @var Closure */
+ public $callback;
+
+ /**
+ * @param SwiftFileBackend $backend
+ * @param Closure $callback Function that takes (HTTP request array, status)
+ * @param array $httpOp MultiHttpClient op
+ */
+ public function __construct( SwiftFileBackend $backend, Closure $callback, array $httpOp ) {
+ $this->backend = $backend;
+ $this->callback = $callback;
+ $this->httpOp = $httpOp;
+ }
+}
public $funDecls = array();
public $varDecls = array();
- public function __construct($t, $type=0)
+ public function __construct($t, $type=0, ...$nodes)
{
if ($token = $t->currentToken())
{
$this->lineno = $t->lineno;
}
- if (($numargs = func_num_args()) > 2)
+ foreach($nodes as $node)
{
- $args = func_get_args();
- for ($i = 2; $i < $numargs; $i++)
- $this->addNode($args[$i]);
+ $this->addNode($node);
}
}
if ( $eocdrPos !== false ) {
$this->logger->info( __METHOD__ . ": ZIP signature present in $file\n" );
// Check if it really is a ZIP file, make sure the EOCDR is at the end (T40432)
- // FIXME: unpack()'s third argument was added in PHP 7.1
- // @phan-suppress-next-line PhanParamTooManyInternal
- $commentLength = unpack( "n", $tail, $eocdrPos + 20 )[0];
+ $commentLength = unpack( "n", substr( $tail, $eocdrPos + 20 ) )[0];
if ( $eocdrPos + 22 + $commentLength !== strlen( $tail ) ) {
$this->logger->info( __METHOD__ . ": ZIP EOCDR not at end. Not a ZIP file." );
} else {
}
public function set( $key, $value, $exptime = 0, $flags = 0 ) {
- apcu_store(
+ return apcu_store(
$key . self::KEY_SUFFIX,
$this->serialize( $value ),
$exptime
);
-
- return true;
}
public function add( $key, $value, $exptime = 0, $flags = 0 ) {
/**
* Posted by cc[plus]php[at]c2se[dot]com on 25-Mar-2009 09:12
- * to https://secure.php.net/manual/en/ref.pgsql.php
+ * to https://www.php.net/manual/en/ref.pgsql.php
*
* Parsing a postgres array can be a tricky problem, he's my
* take on this, it handles multi-dimensional arrays plus
/**
* Get the number of fields in a result object
- * @see https://secure.php.net/mysql_num_fields
+ * @see https://www.php.net/mysql_num_fields
*
* @param mixed $res A SQL result
* @return int
/**
* Get a field name in a result object
- * @see https://secure.php.net/mysql_field_name
+ * @see https://www.php.net/mysql_field_name
*
* @param mixed $res A SQL result
* @param int $n
/**
* Change the position of the cursor in a result object
- * @see https://secure.php.net/mysql_data_seek
+ * @see https://www.php.net/mysql_data_seek
*
* @param mixed $res A SQL result
* @param int $row
/**
* Get the last error number
- * @see https://secure.php.net/mysql_errno
+ * @see https://www.php.net/mysql_errno
*
* @return int
*/
/**
* Get a description of the last error
- * @see https://secure.php.net/mysql_error
+ * @see https://www.php.net/mysql_error
*
* @return string
*/
/**
* Get the number of rows affected by the last write query
- * @see https://secure.php.net/mysql_affected_rows
+ * @see https://www.php.net/mysql_affected_rows
*
* @return int
*/
--- /dev/null
+<?php
+/**
+ * Contains a class for dealing with database log entries
+ *
+ * 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
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @since 1.19
+ */
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * A value class to process existing log entries. In other words, this class caches a log
+ * entry from the database and provides an immutable object-oriented representation of it.
+ *
+ * @since 1.19
+ */
+class DatabaseLogEntry extends LogEntryBase {
+
+ /**
+ * Returns array of information that is needed for querying
+ * log entries. Array contains the following keys:
+ * tables, fields, conds, options and join_conds
+ *
+ * @return array
+ */
+ public static function getSelectQueryData() {
+ $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+ $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
+
+ $tables = array_merge(
+ [ 'logging' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ]
+ );
+ $fields = [
+ 'log_id', 'log_type', 'log_action', 'log_timestamp',
+ 'log_namespace', 'log_title', // unused log_page
+ 'log_params', 'log_deleted',
+ 'user_id', 'user_name', 'user_editcount',
+ ] + $commentQuery['fields'] + $actorQuery['fields'];
+
+ $joins = [
+ // IPs don't have an entry in user table
+ 'user' => [ 'LEFT JOIN', 'user_id=' . $actorQuery['fields']['log_user'] ],
+ ] + $commentQuery['joins'] + $actorQuery['joins'];
+
+ return [
+ 'tables' => $tables,
+ 'fields' => $fields,
+ 'conds' => [],
+ 'options' => [],
+ 'join_conds' => $joins,
+ ];
+ }
+
+ /**
+ * Constructs new LogEntry from database result row.
+ * Supports rows from both logging and recentchanges table.
+ *
+ * @param stdClass|array $row
+ * @return DatabaseLogEntry
+ */
+ public static function newFromRow( $row ) {
+ $row = (object)$row;
+ if ( isset( $row->rc_logid ) ) {
+ return new RCDatabaseLogEntry( $row );
+ } else {
+ return new self( $row );
+ }
+ }
+
+ /**
+ * Loads a LogEntry with the given id from database
+ *
+ * @param int $id
+ * @param IDatabase $db
+ * @return DatabaseLogEntry|null
+ */
+ public static function newFromId( $id, IDatabase $db ) {
+ $queryInfo = self::getSelectQueryData();
+ $queryInfo['conds'] += [ 'log_id' => $id ];
+ $row = $db->selectRow(
+ $queryInfo['tables'],
+ $queryInfo['fields'],
+ $queryInfo['conds'],
+ __METHOD__,
+ $queryInfo['options'],
+ $queryInfo['join_conds']
+ );
+ if ( !$row ) {
+ return null;
+ }
+ return self::newFromRow( $row );
+ }
+
+ /** @var stdClass Database result row. */
+ protected $row;
+
+ /** @var User */
+ protected $performer;
+
+ /** @var array Parameters for log entry */
+ protected $params;
+
+ /** @var int A rev id associated to the log entry */
+ protected $revId = null;
+
+ /** @var bool Whether the parameters for this log entry are stored in new or old format. */
+ protected $legacy;
+
+ protected function __construct( $row ) {
+ $this->row = $row;
+ }
+
+ /**
+ * Returns the unique database id.
+ *
+ * @return int
+ */
+ public function getId() {
+ return (int)$this->row->log_id;
+ }
+
+ /**
+ * Returns whatever is stored in the database field.
+ *
+ * @return string
+ */
+ protected function getRawParameters() {
+ return $this->row->log_params;
+ }
+
+ public function isLegacy() {
+ // This extracts the property
+ $this->getParameters();
+ return $this->legacy;
+ }
+
+ public function getType() {
+ return $this->row->log_type;
+ }
+
+ public function getSubtype() {
+ return $this->row->log_action;
+ }
+
+ public function getParameters() {
+ if ( !isset( $this->params ) ) {
+ $blob = $this->getRawParameters();
+ Wikimedia\suppressWarnings();
+ $params = LogEntryBase::extractParams( $blob );
+ Wikimedia\restoreWarnings();
+ if ( $params !== false ) {
+ $this->params = $params;
+ $this->legacy = false;
+ } else {
+ $this->params = LogPage::extractParams( $blob );
+ $this->legacy = true;
+ }
+
+ if ( isset( $this->params['associated_rev_id'] ) ) {
+ $this->revId = $this->params['associated_rev_id'];
+ unset( $this->params['associated_rev_id'] );
+ }
+ }
+
+ return $this->params;
+ }
+
+ public function getAssociatedRevId() {
+ // This extracts the property
+ $this->getParameters();
+ return $this->revId;
+ }
+
+ public function getPerformer() {
+ if ( !$this->performer ) {
+ $actorId = isset( $this->row->log_actor ) ? (int)$this->row->log_actor : 0;
+ $userId = (int)$this->row->log_user;
+ if ( $userId !== 0 || $actorId !== 0 ) {
+ // logged-in users
+ if ( isset( $this->row->user_name ) ) {
+ $this->performer = User::newFromRow( $this->row );
+ } elseif ( $actorId !== 0 ) {
+ $this->performer = User::newFromActorId( $actorId );
+ } else {
+ $this->performer = User::newFromId( $userId );
+ }
+ } else {
+ // IP users
+ $userText = $this->row->log_user_text;
+ $this->performer = User::newFromName( $userText, false );
+ }
+ }
+
+ return $this->performer;
+ }
+
+ public function getTarget() {
+ $namespace = $this->row->log_namespace;
+ $page = $this->row->log_title;
+ return Title::makeTitle( $namespace, $page );
+ }
+
+ public function getTimestamp() {
+ return wfTimestamp( TS_MW, $this->row->log_timestamp );
+ }
+
+ public function getComment() {
+ return CommentStore::getStore()->getComment( 'log_comment', $this->row )->text;
+ }
+
+ public function getDeleted() {
+ return $this->row->log_deleted;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Contains a class for formatting log legacy entries
+ *
+ * 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
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @since 1.19
+ */
+
+/**
+ * This class formats all log entries for log types
+ * which have not been converted to the new system.
+ * This is not about old log entries which store
+ * parameters in a different format - the new
+ * LogFormatter classes have code to support formatting
+ * those too.
+ * @since 1.19
+ */
+class LegacyLogFormatter extends LogFormatter {
+ /**
+ * Backward compatibility for extension changing the comment from
+ * the LogLine hook. This will be set by the first call on getComment(),
+ * then it might be modified by the hook when calling getActionLinks(),
+ * so that the modified value will be returned when calling getComment()
+ * a second time.
+ *
+ * @var string|null
+ */
+ private $comment = null;
+
+ /**
+ * Cache for the result of getActionLinks() so that it does not need to
+ * run multiple times depending on the order that getComment() and
+ * getActionLinks() are called.
+ *
+ * @var string|null
+ */
+ private $revert = null;
+
+ public function getComment() {
+ if ( $this->comment === null ) {
+ $this->comment = parent::getComment();
+ }
+
+ // Make sure we execute the LogLine hook so that we immediately return
+ // the correct value.
+ if ( $this->revert === null ) {
+ $this->getActionLinks();
+ }
+
+ return $this->comment;
+ }
+
+ /**
+ * @return string
+ * @return-taint onlysafefor_html
+ */
+ protected function getActionMessage() {
+ $entry = $this->entry;
+ $action = LogPage::actionText(
+ $entry->getType(),
+ $entry->getSubtype(),
+ $entry->getTarget(),
+ $this->plaintext ? null : $this->context->getSkin(),
+ (array)$entry->getParameters(),
+ !$this->plaintext // whether to filter [[]] links
+ );
+
+ $performer = $this->getPerformerElement();
+ if ( !$this->irctext ) {
+ $sep = $this->msg( 'word-separator' );
+ $sep = $this->plaintext ? $sep->text() : $sep->escaped();
+ $action = $performer . $sep . $action;
+ }
+
+ return $action;
+ }
+
+ public function getActionLinks() {
+ if ( $this->revert !== null ) {
+ return $this->revert;
+ }
+
+ if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
+ $this->revert = '';
+ return $this->revert;
+ }
+
+ $title = $this->entry->getTarget();
+ $type = $this->entry->getType();
+ $subtype = $this->entry->getSubtype();
+
+ // Do nothing. The implementation is handled by the hook modifiying the
+ // passed-by-ref parameters. This also changes the default value so that
+ // getComment() and getActionLinks() do not call them indefinitely.
+ $this->revert = '';
+
+ // This is to populate the $comment member of this instance so that it
+ // can be modified when calling the hook just below.
+ if ( $this->comment === null ) {
+ $this->getComment();
+ }
+
+ $params = $this->entry->getParameters();
+
+ Hooks::run( 'LogLine', [ $type, $subtype, $title, $params,
+ &$this->comment, &$this->revert, $this->entry->getTimestamp() ] );
+
+ return $this->revert;
+ }
+}
<?php
/**
- * Contain classes for dealing with individual log entries
- *
- * This is how I see the log system history:
- * - appending to plain wiki pages
- * - formatting log entries based on database fields
- * - user is now part of the action message
+ * Contains a class for dealing with individual log entries
*
* 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
* @since 1.19
*/
-use MediaWiki\ChangeTags\Taggable;
-use MediaWiki\Linker\LinkTarget;
-use MediaWiki\User\UserIdentity;
-use Wikimedia\Rdbms\IDatabase;
-use Wikimedia\Assert\Assert;
-
/**
* Interface for log entries. Every log entry has these methods.
*
*/
public function isDeleted( $field );
}
-
-/**
- * Extends the LogEntryInterface with some basic functionality
- *
- * @since 1.19
- */
-abstract class LogEntryBase implements LogEntry {
-
- public function getFullType() {
- return $this->getType() . '/' . $this->getSubtype();
- }
-
- public function isDeleted( $field ) {
- return ( $this->getDeleted() & $field ) === $field;
- }
-
- /**
- * Whether the parameters for this log are stored in new or
- * old format.
- *
- * @return bool
- */
- public function isLegacy() {
- return false;
- }
-
- /**
- * Create a blob from a parameter array
- *
- * @since 1.26
- * @param array $params
- * @return string
- */
- public static function makeParamBlob( $params ) {
- return serialize( (array)$params );
- }
-
- /**
- * Extract a parameter array from a blob
- *
- * @since 1.26
- * @param string $blob
- * @return array
- */
- public static function extractParams( $blob ) {
- return unserialize( $blob );
- }
-}
-
-/**
- * A value class to process existing log entries. In other words, this class caches a log
- * entry from the database and provides an immutable object-oriented representation of it.
- *
- * @since 1.19
- */
-class DatabaseLogEntry extends LogEntryBase {
-
- /**
- * Returns array of information that is needed for querying
- * log entries. Array contains the following keys:
- * tables, fields, conds, options and join_conds
- *
- * @return array
- */
- public static function getSelectQueryData() {
- $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
- $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
-
- $tables = array_merge(
- [ 'logging' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ]
- );
- $fields = [
- 'log_id', 'log_type', 'log_action', 'log_timestamp',
- 'log_namespace', 'log_title', // unused log_page
- 'log_params', 'log_deleted',
- 'user_id', 'user_name', 'user_editcount',
- ] + $commentQuery['fields'] + $actorQuery['fields'];
-
- $joins = [
- // IPs don't have an entry in user table
- 'user' => [ 'LEFT JOIN', 'user_id=' . $actorQuery['fields']['log_user'] ],
- ] + $commentQuery['joins'] + $actorQuery['joins'];
-
- return [
- 'tables' => $tables,
- 'fields' => $fields,
- 'conds' => [],
- 'options' => [],
- 'join_conds' => $joins,
- ];
- }
-
- /**
- * Constructs new LogEntry from database result row.
- * Supports rows from both logging and recentchanges table.
- *
- * @param stdClass|array $row
- * @return DatabaseLogEntry
- */
- public static function newFromRow( $row ) {
- $row = (object)$row;
- if ( isset( $row->rc_logid ) ) {
- return new RCDatabaseLogEntry( $row );
- } else {
- return new self( $row );
- }
- }
-
- /**
- * Loads a LogEntry with the given id from database
- *
- * @param int $id
- * @param IDatabase $db
- * @return DatabaseLogEntry|null
- */
- public static function newFromId( $id, IDatabase $db ) {
- $queryInfo = self::getSelectQueryData();
- $queryInfo['conds'] += [ 'log_id' => $id ];
- $row = $db->selectRow(
- $queryInfo['tables'],
- $queryInfo['fields'],
- $queryInfo['conds'],
- __METHOD__,
- $queryInfo['options'],
- $queryInfo['join_conds']
- );
- if ( !$row ) {
- return null;
- }
- return self::newFromRow( $row );
- }
-
- /** @var stdClass Database result row. */
- protected $row;
-
- /** @var User */
- protected $performer;
-
- /** @var array Parameters for log entry */
- protected $params;
-
- /** @var int A rev id associated to the log entry */
- protected $revId = null;
-
- /** @var bool Whether the parameters for this log entry are stored in new or old format. */
- protected $legacy;
-
- protected function __construct( $row ) {
- $this->row = $row;
- }
-
- /**
- * Returns the unique database id.
- *
- * @return int
- */
- public function getId() {
- return (int)$this->row->log_id;
- }
-
- /**
- * Returns whatever is stored in the database field.
- *
- * @return string
- */
- protected function getRawParameters() {
- return $this->row->log_params;
- }
-
- public function isLegacy() {
- // This extracts the property
- $this->getParameters();
- return $this->legacy;
- }
-
- public function getType() {
- return $this->row->log_type;
- }
-
- public function getSubtype() {
- return $this->row->log_action;
- }
-
- public function getParameters() {
- if ( !isset( $this->params ) ) {
- $blob = $this->getRawParameters();
- Wikimedia\suppressWarnings();
- $params = LogEntryBase::extractParams( $blob );
- Wikimedia\restoreWarnings();
- if ( $params !== false ) {
- $this->params = $params;
- $this->legacy = false;
- } else {
- $this->params = LogPage::extractParams( $blob );
- $this->legacy = true;
- }
-
- if ( isset( $this->params['associated_rev_id'] ) ) {
- $this->revId = $this->params['associated_rev_id'];
- unset( $this->params['associated_rev_id'] );
- }
- }
-
- return $this->params;
- }
-
- public function getAssociatedRevId() {
- // This extracts the property
- $this->getParameters();
- return $this->revId;
- }
-
- public function getPerformer() {
- if ( !$this->performer ) {
- $actorId = isset( $this->row->log_actor ) ? (int)$this->row->log_actor : 0;
- $userId = (int)$this->row->log_user;
- if ( $userId !== 0 || $actorId !== 0 ) {
- // logged-in users
- if ( isset( $this->row->user_name ) ) {
- $this->performer = User::newFromRow( $this->row );
- } elseif ( $actorId !== 0 ) {
- $this->performer = User::newFromActorId( $actorId );
- } else {
- $this->performer = User::newFromId( $userId );
- }
- } else {
- // IP users
- $userText = $this->row->log_user_text;
- $this->performer = User::newFromName( $userText, false );
- }
- }
-
- return $this->performer;
- }
-
- public function getTarget() {
- $namespace = $this->row->log_namespace;
- $page = $this->row->log_title;
- $title = Title::makeTitle( $namespace, $page );
-
- return $title;
- }
-
- public function getTimestamp() {
- return wfTimestamp( TS_MW, $this->row->log_timestamp );
- }
-
- public function getComment() {
- return CommentStore::getStore()->getComment( 'log_comment', $this->row )->text;
- }
-
- public function getDeleted() {
- return $this->row->log_deleted;
- }
-}
-
-/**
- * A subclass of DatabaseLogEntry for objects constructed from entries in the
- * recentchanges table (rather than the logging table).
- */
-class RCDatabaseLogEntry extends DatabaseLogEntry {
-
- public function getId() {
- return $this->row->rc_logid;
- }
-
- protected function getRawParameters() {
- return $this->row->rc_params;
- }
-
- public function getAssociatedRevId() {
- return $this->row->rc_this_oldid;
- }
-
- public function getType() {
- return $this->row->rc_log_type;
- }
-
- public function getSubtype() {
- return $this->row->rc_log_action;
- }
-
- public function getPerformer() {
- if ( !$this->performer ) {
- $actorId = isset( $this->row->rc_actor ) ? (int)$this->row->rc_actor : 0;
- $userId = (int)$this->row->rc_user;
- if ( $actorId !== 0 ) {
- $this->performer = User::newFromActorId( $actorId );
- } elseif ( $userId !== 0 ) {
- $this->performer = User::newFromId( $userId );
- } else {
- $userText = $this->row->rc_user_text;
- // Might be an IP, don't validate the username
- $this->performer = User::newFromName( $userText, false );
- }
- }
-
- return $this->performer;
- }
-
- public function getTarget() {
- $namespace = $this->row->rc_namespace;
- $page = $this->row->rc_title;
- $title = Title::makeTitle( $namespace, $page );
-
- return $title;
- }
-
- public function getTimestamp() {
- return wfTimestamp( TS_MW, $this->row->rc_timestamp );
- }
-
- public function getComment() {
- return CommentStore::getStore()
- // Legacy because the row may have used RecentChange::selectFields()
- ->getCommentLegacy( wfGetDB( DB_REPLICA ), 'rc_comment', $this->row )->text;
- }
-
- public function getDeleted() {
- return $this->row->rc_deleted;
- }
-}
-
-/**
- * Class for creating new log entries and inserting them into the database.
- *
- * @since 1.19
- */
-class ManualLogEntry extends LogEntryBase implements Taggable {
- /** @var string Type of log entry */
- protected $type;
-
- /** @var string Sub type of log entry */
- protected $subtype;
-
- /** @var array Parameters for log entry */
- protected $parameters = [];
-
- /** @var array */
- protected $relations = [];
-
- /** @var User Performer of the action for the log entry */
- protected $performer;
-
- /** @var Title Target title for the log entry */
- protected $target;
-
- /** @var string Timestamp of creation of the log entry */
- protected $timestamp;
-
- /** @var string Comment for the log entry */
- protected $comment = '';
-
- /** @var int A rev id associated to the log entry */
- protected $revId = 0;
-
- /** @var string[] Change tags add to the log entry */
- protected $tags = [];
-
- /** @var int Deletion state of the log entry */
- protected $deleted;
-
- /** @var int ID of the log entry */
- protected $id;
-
- /** @var bool Can this log entry be patrolled? */
- protected $isPatrollable = false;
-
- /** @var bool Whether this is a legacy log entry */
- protected $legacy = false;
-
- /**
- * @since 1.19
- * @param string $type
- * @param string $subtype
- */
- public function __construct( $type, $subtype ) {
- $this->type = $type;
- $this->subtype = $subtype;
- }
-
- /**
- * Set extra log parameters.
- *
- * You can pass params to the log action message by prefixing the keys with
- * a number and optional type, using colons to separate the fields. The
- * numbering should start with number 4, the first three parameters are
- * hardcoded for every message.
- *
- * If you want to store stuff that should not be available in messages, don't
- * prefix the array key with a number and don't use the colons.
- *
- * Example:
- * $entry->setParameters(
- * '4::color' => 'blue',
- * '5:number:count' => 3000,
- * 'animal' => 'dog'
- * );
- *
- * @since 1.19
- * @param array $parameters Associative array
- */
- public function setParameters( $parameters ) {
- $this->parameters = $parameters;
- }
-
- /**
- * Declare arbitrary tag/value relations to this log entry.
- * These can be used to filter log entries later on.
- *
- * @param array $relations Map of (tag => (list of values|value))
- * @since 1.22
- */
- public function setRelations( array $relations ) {
- $this->relations = $relations;
- }
-
- /**
- * Set the user that performed the action being logged.
- *
- * @since 1.19
- * @param UserIdentity $performer
- */
- public function setPerformer( UserIdentity $performer ) {
- $this->performer = User::newFromIdentity( $performer );
- }
-
- /**
- * Set the title of the object changed.
- *
- * @since 1.19
- * @param LinkTarget $target
- */
- public function setTarget( LinkTarget $target ) {
- $this->target = Title::newFromLinkTarget( $target );
- }
-
- /**
- * Set the timestamp of when the logged action took place.
- *
- * @since 1.19
- * @param string $timestamp
- */
- public function setTimestamp( $timestamp ) {
- $this->timestamp = $timestamp;
- }
-
- /**
- * Set a comment associated with the action being logged.
- *
- * @since 1.19
- * @param string $comment
- */
- public function setComment( $comment ) {
- $this->comment = $comment;
- }
-
- /**
- * Set an associated revision id.
- *
- * For example, the ID of the revision that was inserted to mark a page move
- * or protection, file upload, etc.
- *
- * @since 1.27
- * @param int $revId
- */
- public function setAssociatedRevId( $revId ) {
- $this->revId = $revId;
- }
-
- /**
- * Set change tags for the log entry.
- *
- * Passing `null` means the same as empty array,
- * for compatibility with WikiPage::doUpdateRestrictions().
- *
- * @since 1.27
- * @param string|string[]|null $tags
- * @deprecated since 1.33 Please use addTags() instead
- */
- public function setTags( $tags ) {
- if ( $this->tags ) {
- wfDebug( 'Overwriting existing ManualLogEntry tags' );
- }
- $this->tags = [];
- if ( $tags !== null ) {
- $this->addTags( $tags );
- }
- }
-
- /**
- * Add change tags for the log entry
- *
- * @since 1.33
- * @param string|string[] $tags Tags to apply
- */
- public function addTags( $tags ) {
- if ( is_string( $tags ) ) {
- $tags = [ $tags ];
- }
- Assert::parameterElementType( 'string', $tags, 'tags' );
- $this->tags = array_unique( array_merge( $this->tags, $tags ) );
- }
-
- /**
- * Set whether this log entry should be made patrollable
- * This shouldn't depend on config, only on whether there is full support
- * in the software for patrolling this log entry.
- * False by default
- *
- * @since 1.27
- * @param bool $patrollable
- */
- public function setIsPatrollable( $patrollable ) {
- $this->isPatrollable = (bool)$patrollable;
- }
-
- /**
- * Set the 'legacy' flag
- *
- * @since 1.25
- * @param bool $legacy
- */
- public function setLegacy( $legacy ) {
- $this->legacy = $legacy;
- }
-
- /**
- * Set the 'deleted' flag.
- *
- * @since 1.19
- * @param int $deleted One of LogPage::DELETED_* bitfield constants
- */
- public function setDeleted( $deleted ) {
- $this->deleted = $deleted;
- }
-
- /**
- * Insert the entry into the `logging` table.
- *
- * @param IDatabase|null $dbw
- * @return int ID of the log entry
- * @throws MWException
- */
- public function insert( IDatabase $dbw = null ) {
- global $wgActorTableSchemaMigrationStage;
-
- $dbw = $dbw ?: wfGetDB( DB_MASTER );
-
- if ( $this->timestamp === null ) {
- $this->timestamp = wfTimestampNow();
- }
-
- // Trim spaces on user supplied text
- $comment = trim( $this->getComment() );
-
- $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 ) {
- $params['associated_rev_id'] = $revId;
- $relations['associated_rev_id'] = $revId;
- }
-
- $data = [
- 'log_type' => $this->getType(),
- 'log_action' => $this->getSubtype(),
- 'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
- 'log_namespace' => $this->getTarget()->getNamespace(),
- 'log_title' => $this->getTarget()->getDBkey(),
- 'log_page' => $this->getTarget()->getArticleID(),
- 'log_params' => LogEntryBase::makeParamBlob( $params ),
- ];
- if ( isset( $this->deleted ) ) {
- $data['log_deleted'] = $this->deleted;
- }
- $data += CommentStore::getStore()->insert( $dbw, 'log_comment', $comment );
- $data += ActorMigration::newMigration()
- ->getInsertValues( $dbw, 'log_user', $this->getPerformer() );
-
- $dbw->insert( 'logging', $data, __METHOD__ );
- $this->id = $dbw->insertId();
-
- $rows = [];
- foreach ( $relations as $tag => $values ) {
- if ( !strlen( $tag ) ) {
- throw new MWException( "Got empty log search tag." );
- }
-
- if ( !is_array( $values ) ) {
- $values = [ $values ];
- }
-
- foreach ( $values as $value ) {
- $rows[] = [
- 'ls_field' => $tag,
- 'ls_value' => $value,
- 'ls_log_id' => $this->id
- ];
- }
- }
- if ( count( $rows ) ) {
- $dbw->insert( 'log_search', $rows, __METHOD__, 'IGNORE' );
- }
-
- return $this->id;
- }
-
- /**
- * Get a RecentChanges object for the log entry
- *
- * @param int $newId
- * @return RecentChange
- * @since 1.23
- */
- public function getRecentChange( $newId = 0 ) {
- $formatter = LogFormatter::newFromEntry( $this );
- $context = RequestContext::newExtraneousContext( $this->getTarget() );
- $formatter->setContext( $context );
-
- $logpage = SpecialPage::getTitleFor( 'Log', $this->getType() );
- $user = $this->getPerformer();
- $ip = "";
- if ( $user->isAnon() ) {
- // "MediaWiki default" and friends may have
- // no IP address in their name
- if ( IP::isIPAddress( $user->getName() ) ) {
- $ip = $user->getName();
- }
- }
-
- return RecentChange::newLogEntry(
- $this->getTimestamp(),
- $logpage,
- $user,
- $formatter->getPlainActionText(),
- $ip,
- $this->getType(),
- $this->getSubtype(),
- $this->getTarget(),
- $this->getComment(),
- LogEntryBase::makeParamBlob( $this->getParameters() ),
- $newId,
- $formatter->getIRCActionComment(), // Used for IRC feeds
- $this->getAssociatedRevId(), // Used for e.g. moves and uploads
- $this->getIsPatrollable()
- );
- }
-
- /**
- * Publish the log entry.
- *
- * @param int $newId Id of the log entry.
- * @param string $to One of: rcandudp (default), rc, udp
- */
- public function publish( $newId, $to = 'rcandudp' ) {
- $canAddTags = true;
- // FIXME: this code should be removed once all callers properly call publish()
- if ( $to === 'udp' && !$newId && !$this->getAssociatedRevId() ) {
- \MediaWiki\Logger\LoggerFactory::getInstance( 'logging' )->warning(
- 'newId and/or revId must be set when calling ManualLogEntry::publish()',
- [
- 'newId' => $newId,
- 'to' => $to,
- 'revId' => $this->getAssociatedRevId(),
- // pass a new exception to register the stack trace
- 'exception' => new RuntimeException()
- ]
- );
- $canAddTags = false;
- }
-
- DeferredUpdates::addCallableUpdate(
- function () use ( $newId, $to, $canAddTags ) {
- $log = new LogPage( $this->getType() );
- if ( !$log->isRestricted() ) {
- Hooks::runWithoutAbort( 'ManualLogEntryBeforePublish', [ $this ] );
- $rc = $this->getRecentChange( $newId );
-
- if ( $to === 'rc' || $to === 'rcandudp' ) {
- // save RC, passing tags so they are applied there
- $rc->addTags( $this->getTags() );
- $rc->save( $rc::SEND_NONE );
- } else {
- $tags = $this->getTags();
- if ( $tags && $canAddTags ) {
- $revId = $this->getAssociatedRevId();
- ChangeTags::addTags(
- $tags,
- null,
- $revId > 0 ? $revId : null,
- $newId > 0 ? $newId : null
- );
- }
- }
-
- if ( $to === 'udp' || $to === 'rcandudp' ) {
- $rc->notifyRCFeeds();
- }
- }
- },
- DeferredUpdates::POSTSEND,
- wfGetDB( DB_MASTER )
- );
- }
-
- public function getType() {
- return $this->type;
- }
-
- public function getSubtype() {
- return $this->subtype;
- }
-
- public function getParameters() {
- return $this->parameters;
- }
-
- /**
- * @return User
- */
- public function getPerformer() {
- return $this->performer;
- }
-
- /**
- * @return Title
- */
- public function getTarget() {
- return $this->target;
- }
-
- public function getTimestamp() {
- $ts = $this->timestamp ?? wfTimestampNow();
-
- return wfTimestamp( TS_MW, $ts );
- }
-
- public function getComment() {
- return $this->comment;
- }
-
- /**
- * @since 1.27
- * @return int
- */
- public function getAssociatedRevId() {
- return $this->revId;
- }
-
- /**
- * @since 1.27
- * @return string[]
- */
- public function getTags() {
- return $this->tags;
- }
-
- /**
- * Whether this log entry is patrollable
- *
- * @since 1.27
- * @return bool
- */
- public function getIsPatrollable() {
- return $this->isPatrollable;
- }
-
- /**
- * @since 1.25
- * @return bool
- */
- public function isLegacy() {
- return $this->legacy;
- }
-
- public function getDeleted() {
- return (int)$this->deleted;
- }
-}
--- /dev/null
+<?php
+/**
+ * Contains a class for dealing with individual log entries
+ *
+ * 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
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @since 1.19
+ */
+
+/**
+ * Extends the LogEntry Interface with some basic functionality
+ *
+ * @since 1.19
+ */
+abstract class LogEntryBase implements LogEntry {
+
+ public function getFullType() {
+ return $this->getType() . '/' . $this->getSubtype();
+ }
+
+ public function isDeleted( $field ) {
+ return ( $this->getDeleted() & $field ) === $field;
+ }
+
+ /**
+ * Whether the parameters for this log are stored in new or
+ * old format.
+ *
+ * @return bool
+ */
+ public function isLegacy() {
+ return false;
+ }
+
+ /**
+ * Create a blob from a parameter array
+ *
+ * @since 1.26
+ * @param array $params
+ * @return string
+ */
+ public static function makeParamBlob( $params ) {
+ return serialize( (array)$params );
+ }
+
+ /**
+ * Extract a parameter array from a blob
+ *
+ * @since 1.26
+ * @param string $blob
+ * @return array
+ */
+ public static function extractParams( $blob ) {
+ return unserialize( $blob );
+ }
+}
<?php
/**
- * Contains classes for formatting log entries
+ * Contains a class for formatting log entries
*
* 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
return [ $name => $value ];
}
}
-
-/**
- * This class formats all log entries for log types
- * which have not been converted to the new system.
- * This is not about old log entries which store
- * parameters in a different format - the new
- * LogFormatter classes have code to support formatting
- * those too.
- * @since 1.19
- */
-class LegacyLogFormatter extends LogFormatter {
- /**
- * Backward compatibility for extension changing the comment from
- * the LogLine hook. This will be set by the first call on getComment(),
- * then it might be modified by the hook when calling getActionLinks(),
- * so that the modified value will be returned when calling getComment()
- * a second time.
- *
- * @var string|null
- */
- private $comment = null;
-
- /**
- * Cache for the result of getActionLinks() so that it does not need to
- * run multiple times depending on the order that getComment() and
- * getActionLinks() are called.
- *
- * @var string|null
- */
- private $revert = null;
-
- public function getComment() {
- if ( $this->comment === null ) {
- $this->comment = parent::getComment();
- }
-
- // Make sure we execute the LogLine hook so that we immediately return
- // the correct value.
- if ( $this->revert === null ) {
- $this->getActionLinks();
- }
-
- return $this->comment;
- }
-
- /**
- * @return string
- * @return-taint onlysafefor_html
- */
- protected function getActionMessage() {
- $entry = $this->entry;
- $action = LogPage::actionText(
- $entry->getType(),
- $entry->getSubtype(),
- $entry->getTarget(),
- $this->plaintext ? null : $this->context->getSkin(),
- (array)$entry->getParameters(),
- !$this->plaintext // whether to filter [[]] links
- );
-
- $performer = $this->getPerformerElement();
- if ( !$this->irctext ) {
- $sep = $this->msg( 'word-separator' );
- $sep = $this->plaintext ? $sep->text() : $sep->escaped();
- $action = $performer . $sep . $action;
- }
-
- return $action;
- }
-
- public function getActionLinks() {
- if ( $this->revert !== null ) {
- return $this->revert;
- }
-
- if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
- $this->revert = '';
- return $this->revert;
- }
-
- $title = $this->entry->getTarget();
- $type = $this->entry->getType();
- $subtype = $this->entry->getSubtype();
-
- // Do nothing. The implementation is handled by the hook modifiying the
- // passed-by-ref parameters. This also changes the default value so that
- // getComment() and getActionLinks() do not call them indefinitely.
- $this->revert = '';
-
- // This is to populate the $comment member of this instance so that it
- // can be modified when calling the hook just below.
- if ( $this->comment === null ) {
- $this->getComment();
- }
-
- $params = $this->entry->getParameters();
-
- Hooks::run( 'LogLine', [ $type, $subtype, $title, $params,
- &$this->comment, &$this->revert, $this->entry->getTimestamp() ] );
-
- return $this->revert;
- }
-}
'log_page' => $this->target->getArticleID(),
'log_params' => $this->params
];
- $data += CommentStore::getStore()->insert( $dbw, 'log_comment', $this->comment );
+ $data += MediaWikiServices::getInstance()->getCommentStore()->insert(
+ $dbw,
+ 'log_comment',
+ $this->comment
+ );
$data += ActorMigration::newMigration()->getInsertValues( $dbw, 'log_user', $this->doer );
$dbw->insert( 'logging', $data, __METHOD__ );
$newId = $dbw->insertId();
--- /dev/null
+<?php
+/**
+ * Contains a class for dealing with manual log entries
+ *
+ * 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
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @since 1.19
+ */
+
+use MediaWiki\ChangeTags\Taggable;
+use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
+use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Assert\Assert;
+
+/**
+ * Class for creating new log entries and inserting them into the database.
+ *
+ * @since 1.19
+ */
+class ManualLogEntry extends LogEntryBase implements Taggable {
+ /** @var string Type of log entry */
+ protected $type;
+
+ /** @var string Sub type of log entry */
+ protected $subtype;
+
+ /** @var array Parameters for log entry */
+ protected $parameters = [];
+
+ /** @var array */
+ protected $relations = [];
+
+ /** @var User Performer of the action for the log entry */
+ protected $performer;
+
+ /** @var Title Target title for the log entry */
+ protected $target;
+
+ /** @var string Timestamp of creation of the log entry */
+ protected $timestamp;
+
+ /** @var string Comment for the log entry */
+ protected $comment = '';
+
+ /** @var int A rev id associated to the log entry */
+ protected $revId = 0;
+
+ /** @var string[] Change tags add to the log entry */
+ protected $tags = [];
+
+ /** @var int Deletion state of the log entry */
+ protected $deleted;
+
+ /** @var int ID of the log entry */
+ protected $id;
+
+ /** @var bool Can this log entry be patrolled? */
+ protected $isPatrollable = false;
+
+ /** @var bool Whether this is a legacy log entry */
+ protected $legacy = false;
+
+ /**
+ * @since 1.19
+ * @param string $type
+ * @param string $subtype
+ */
+ public function __construct( $type, $subtype ) {
+ $this->type = $type;
+ $this->subtype = $subtype;
+ }
+
+ /**
+ * Set extra log parameters.
+ *
+ * You can pass params to the log action message by prefixing the keys with
+ * a number and optional type, using colons to separate the fields. The
+ * numbering should start with number 4, the first three parameters are
+ * hardcoded for every message.
+ *
+ * If you want to store stuff that should not be available in messages, don't
+ * prefix the array key with a number and don't use the colons.
+ *
+ * Example:
+ * $entry->setParameters(
+ * '4::color' => 'blue',
+ * '5:number:count' => 3000,
+ * 'animal' => 'dog'
+ * );
+ *
+ * @since 1.19
+ * @param array $parameters Associative array
+ */
+ public function setParameters( $parameters ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Declare arbitrary tag/value relations to this log entry.
+ * These can be used to filter log entries later on.
+ *
+ * @param array $relations Map of (tag => (list of values|value))
+ * @since 1.22
+ */
+ public function setRelations( array $relations ) {
+ $this->relations = $relations;
+ }
+
+ /**
+ * Set the user that performed the action being logged.
+ *
+ * @since 1.19
+ * @param UserIdentity $performer
+ */
+ public function setPerformer( UserIdentity $performer ) {
+ $this->performer = User::newFromIdentity( $performer );
+ }
+
+ /**
+ * Set the title of the object changed.
+ *
+ * @since 1.19
+ * @param LinkTarget $target
+ */
+ public function setTarget( LinkTarget $target ) {
+ $this->target = Title::newFromLinkTarget( $target );
+ }
+
+ /**
+ * Set the timestamp of when the logged action took place.
+ *
+ * @since 1.19
+ * @param string $timestamp
+ */
+ public function setTimestamp( $timestamp ) {
+ $this->timestamp = $timestamp;
+ }
+
+ /**
+ * Set a comment associated with the action being logged.
+ *
+ * @since 1.19
+ * @param string $comment
+ */
+ public function setComment( $comment ) {
+ $this->comment = $comment;
+ }
+
+ /**
+ * Set an associated revision id.
+ *
+ * For example, the ID of the revision that was inserted to mark a page move
+ * or protection, file upload, etc.
+ *
+ * @since 1.27
+ * @param int $revId
+ */
+ public function setAssociatedRevId( $revId ) {
+ $this->revId = $revId;
+ }
+
+ /**
+ * Set change tags for the log entry.
+ *
+ * Passing `null` means the same as empty array,
+ * for compatibility with WikiPage::doUpdateRestrictions().
+ *
+ * @since 1.27
+ * @param string|string[]|null $tags
+ * @deprecated since 1.33 Please use addTags() instead
+ */
+ public function setTags( $tags ) {
+ if ( $this->tags ) {
+ wfDebug( 'Overwriting existing ManualLogEntry tags' );
+ }
+ $this->tags = [];
+ if ( $tags !== null ) {
+ $this->addTags( $tags );
+ }
+ }
+
+ /**
+ * Add change tags for the log entry
+ *
+ * @since 1.33
+ * @param string|string[] $tags Tags to apply
+ */
+ public function addTags( $tags ) {
+ if ( is_string( $tags ) ) {
+ $tags = [ $tags ];
+ }
+ Assert::parameterElementType( 'string', $tags, 'tags' );
+ $this->tags = array_unique( array_merge( $this->tags, $tags ) );
+ }
+
+ /**
+ * Set whether this log entry should be made patrollable
+ * This shouldn't depend on config, only on whether there is full support
+ * in the software for patrolling this log entry.
+ * False by default
+ *
+ * @since 1.27
+ * @param bool $patrollable
+ */
+ public function setIsPatrollable( $patrollable ) {
+ $this->isPatrollable = (bool)$patrollable;
+ }
+
+ /**
+ * Set the 'legacy' flag
+ *
+ * @since 1.25
+ * @param bool $legacy
+ */
+ public function setLegacy( $legacy ) {
+ $this->legacy = $legacy;
+ }
+
+ /**
+ * Set the 'deleted' flag.
+ *
+ * @since 1.19
+ * @param int $deleted One of LogPage::DELETED_* bitfield constants
+ */
+ public function setDeleted( $deleted ) {
+ $this->deleted = $deleted;
+ }
+
+ /**
+ * Insert the entry into the `logging` table.
+ *
+ * @param IDatabase|null $dbw
+ * @return int ID of the log entry
+ * @throws MWException
+ */
+ public function insert( IDatabase $dbw = null ) {
+ global $wgActorTableSchemaMigrationStage;
+
+ $dbw = $dbw ?: wfGetDB( DB_MASTER );
+
+ if ( $this->timestamp === null ) {
+ $this->timestamp = wfTimestampNow();
+ }
+
+ // Trim spaces on user supplied text
+ $comment = trim( $this->getComment() );
+
+ $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 ) {
+ $params['associated_rev_id'] = $revId;
+ $relations['associated_rev_id'] = $revId;
+ }
+
+ $data = [
+ 'log_type' => $this->getType(),
+ 'log_action' => $this->getSubtype(),
+ 'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
+ 'log_namespace' => $this->getTarget()->getNamespace(),
+ 'log_title' => $this->getTarget()->getDBkey(),
+ 'log_page' => $this->getTarget()->getArticleID(),
+ 'log_params' => LogEntryBase::makeParamBlob( $params ),
+ ];
+ if ( isset( $this->deleted ) ) {
+ $data['log_deleted'] = $this->deleted;
+ }
+ $data += CommentStore::getStore()->insert( $dbw, 'log_comment', $comment );
+ $data += ActorMigration::newMigration()
+ ->getInsertValues( $dbw, 'log_user', $this->getPerformer() );
+
+ $dbw->insert( 'logging', $data, __METHOD__ );
+ $this->id = $dbw->insertId();
+
+ $rows = [];
+ foreach ( $relations as $tag => $values ) {
+ if ( !strlen( $tag ) ) {
+ throw new MWException( "Got empty log search tag." );
+ }
+
+ if ( !is_array( $values ) ) {
+ $values = [ $values ];
+ }
+
+ foreach ( $values as $value ) {
+ $rows[] = [
+ 'ls_field' => $tag,
+ 'ls_value' => $value,
+ 'ls_log_id' => $this->id
+ ];
+ }
+ }
+ if ( count( $rows ) ) {
+ $dbw->insert( 'log_search', $rows, __METHOD__, 'IGNORE' );
+ }
+
+ return $this->id;
+ }
+
+ /**
+ * Get a RecentChanges object for the log entry
+ *
+ * @param int $newId
+ * @return RecentChange
+ * @since 1.23
+ */
+ public function getRecentChange( $newId = 0 ) {
+ $formatter = LogFormatter::newFromEntry( $this );
+ $context = RequestContext::newExtraneousContext( $this->getTarget() );
+ $formatter->setContext( $context );
+
+ $logpage = SpecialPage::getTitleFor( 'Log', $this->getType() );
+ $user = $this->getPerformer();
+ $ip = "";
+ if ( $user->isAnon() ) {
+ // "MediaWiki default" and friends may have
+ // no IP address in their name
+ if ( IP::isIPAddress( $user->getName() ) ) {
+ $ip = $user->getName();
+ }
+ }
+
+ return RecentChange::newLogEntry(
+ $this->getTimestamp(),
+ $logpage,
+ $user,
+ $formatter->getPlainActionText(),
+ $ip,
+ $this->getType(),
+ $this->getSubtype(),
+ $this->getTarget(),
+ $this->getComment(),
+ LogEntryBase::makeParamBlob( $this->getParameters() ),
+ $newId,
+ $formatter->getIRCActionComment(), // Used for IRC feeds
+ $this->getAssociatedRevId(), // Used for e.g. moves and uploads
+ $this->getIsPatrollable()
+ );
+ }
+
+ /**
+ * Publish the log entry.
+ *
+ * @param int $newId Id of the log entry.
+ * @param string $to One of: rcandudp (default), rc, udp
+ */
+ public function publish( $newId, $to = 'rcandudp' ) {
+ $canAddTags = true;
+ // FIXME: this code should be removed once all callers properly call publish()
+ if ( $to === 'udp' && !$newId && !$this->getAssociatedRevId() ) {
+ \MediaWiki\Logger\LoggerFactory::getInstance( 'logging' )->warning(
+ 'newId and/or revId must be set when calling ManualLogEntry::publish()',
+ [
+ 'newId' => $newId,
+ 'to' => $to,
+ 'revId' => $this->getAssociatedRevId(),
+ // pass a new exception to register the stack trace
+ 'exception' => new RuntimeException()
+ ]
+ );
+ $canAddTags = false;
+ }
+
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $newId, $to, $canAddTags ) {
+ $log = new LogPage( $this->getType() );
+ if ( !$log->isRestricted() ) {
+ Hooks::runWithoutAbort( 'ManualLogEntryBeforePublish', [ $this ] );
+ $rc = $this->getRecentChange( $newId );
+
+ if ( $to === 'rc' || $to === 'rcandudp' ) {
+ // save RC, passing tags so they are applied there
+ $rc->addTags( $this->getTags() );
+ $rc->save( $rc::SEND_NONE );
+ } else {
+ $tags = $this->getTags();
+ if ( $tags && $canAddTags ) {
+ $revId = $this->getAssociatedRevId();
+ ChangeTags::addTags(
+ $tags,
+ null,
+ $revId > 0 ? $revId : null,
+ $newId > 0 ? $newId : null
+ );
+ }
+ }
+
+ if ( $to === 'udp' || $to === 'rcandudp' ) {
+ $rc->notifyRCFeeds();
+ }
+ }
+ },
+ DeferredUpdates::POSTSEND,
+ wfGetDB( DB_MASTER )
+ );
+ }
+
+ public function getType() {
+ return $this->type;
+ }
+
+ public function getSubtype() {
+ return $this->subtype;
+ }
+
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * @return User
+ */
+ public function getPerformer() {
+ return $this->performer;
+ }
+
+ /**
+ * @return Title
+ */
+ public function getTarget() {
+ return $this->target;
+ }
+
+ public function getTimestamp() {
+ $ts = $this->timestamp ?? wfTimestampNow();
+
+ return wfTimestamp( TS_MW, $ts );
+ }
+
+ public function getComment() {
+ return $this->comment;
+ }
+
+ /**
+ * @since 1.27
+ * @return int
+ */
+ public function getAssociatedRevId() {
+ return $this->revId;
+ }
+
+ /**
+ * @since 1.27
+ * @return string[]
+ */
+ public function getTags() {
+ return $this->tags;
+ }
+
+ /**
+ * Whether this log entry is patrollable
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function getIsPatrollable() {
+ return $this->isPatrollable;
+ }
+
+ /**
+ * @since 1.25
+ * @return bool
+ */
+ public function isLegacy() {
+ return $this->legacy;
+ }
+
+ public function getDeleted() {
+ return (int)$this->deleted;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Contains a class for dealing with recent changes database log entries
+ *
+ * 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
+ * @author Niklas Laxström
+ * @license GPL-2.0-or-later
+ * @since 1.19
+ */
+
+/**
+ * A subclass of DatabaseLogEntry for objects constructed from entries in the
+ * recentchanges table (rather than the logging table).
+ */
+class RCDatabaseLogEntry extends DatabaseLogEntry {
+
+ public function getId() {
+ return $this->row->rc_logid;
+ }
+
+ protected function getRawParameters() {
+ return $this->row->rc_params;
+ }
+
+ public function getAssociatedRevId() {
+ return $this->row->rc_this_oldid;
+ }
+
+ public function getType() {
+ return $this->row->rc_log_type;
+ }
+
+ public function getSubtype() {
+ return $this->row->rc_log_action;
+ }
+
+ public function getPerformer() {
+ if ( !$this->performer ) {
+ $actorId = isset( $this->row->rc_actor ) ? (int)$this->row->rc_actor : 0;
+ $userId = (int)$this->row->rc_user;
+ if ( $actorId !== 0 ) {
+ $this->performer = User::newFromActorId( $actorId );
+ } elseif ( $userId !== 0 ) {
+ $this->performer = User::newFromId( $userId );
+ } else {
+ $userText = $this->row->rc_user_text;
+ // Might be an IP, don't validate the username
+ $this->performer = User::newFromName( $userText, false );
+ }
+ }
+
+ return $this->performer;
+ }
+
+ public function getTarget() {
+ $namespace = $this->row->rc_namespace;
+ $page = $this->row->rc_title;
+ return Title::makeTitle( $namespace, $page );
+ }
+
+ public function getTimestamp() {
+ return wfTimestamp( TS_MW, $this->row->rc_timestamp );
+ }
+
+ public function getComment() {
+ return CommentStore::getStore()
+ // Legacy because the row may have used RecentChange::selectFields()
+ ->getCommentLegacy( wfGetDB( DB_REPLICA ), 'rc_comment', $this->row )->text;
+ }
+
+ public function getDeleted() {
+ return $this->row->rc_deleted;
+ }
+}
* @ingroup Media
*/
class DjVuImage {
+
/**
- * @const DJVUTXT_MEMORY_LIMIT Memory limit for the DjVu description software
+ * Memory limit for the DjVu description software
*/
const DJVUTXT_MEMORY_LIMIT = 300000;
* Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!)
* @param string $path The filename
* @return array|bool Follow the format of PHP getimagesize() internal function.
- * See https://secure.php.net/getimagesize. MediaWiki will only ever use the
+ * See https://www.php.net/getimagesize. MediaWiki will only ever use the
* first two array keys (the width and height), and the 'bits' associative
* key. All other array keys are ignored. Returning a 'bits' key is optional
* as not all formats have a notion of "bitdepth". Returns false on failure.
/** @var Message */
private $msg;
- function __construct( $msg, $width, $height /*, ... */ ) {
- $args = array_slice( func_get_args(), 3 );
+ function __construct( $msg, $width, $height, ...$args ) {
$this->msg = wfMessage( $msg )->params( $args );
$this->width = intval( $width );
$this->height = intval( $height );
return $svg->getMetadata();
}
}
-
-/**
- * @ingroup Media
- */
-class SVGReader {
- const DEFAULT_WIDTH = 512;
- const DEFAULT_HEIGHT = 512;
- const NS_SVG = 'http://www.w3.org/2000/svg';
- const LANG_PREFIX_MATCH = 1;
- const LANG_FULL_MATCH = 2;
-
- /** @var null|XMLReader */
- private $reader = null;
-
- /** @var bool */
- private $mDebug = false;
-
- /** @var array */
- private $metadata = [];
- private $languages = [];
- private $languagePrefixes = [];
-
- /**
- * Creates an SVGReader drawing from the source provided
- * @param string $source URI from which to read
- * @throws MWException|Exception
- */
- function __construct( $source ) {
- global $wgSVGMetadataCutoff;
- $this->reader = new XMLReader();
-
- // Don't use $file->getSize() since file object passed to SVGHandler::getMetadata is bogus.
- $size = filesize( $source );
- if ( $size === false ) {
- throw new MWException( "Error getting filesize of SVG." );
- }
-
- if ( $size > $wgSVGMetadataCutoff ) {
- $this->debug( "SVG is $size bytes, which is bigger than $wgSVGMetadataCutoff. Truncating." );
- $contents = file_get_contents( $source, false, null, 0, $wgSVGMetadataCutoff );
- if ( $contents === false ) {
- throw new MWException( 'Error reading SVG file.' );
- }
- $this->reader->XML( $contents, null, LIBXML_NOERROR | LIBXML_NOWARNING );
- } else {
- $this->reader->open( $source, null, LIBXML_NOERROR | LIBXML_NOWARNING );
- }
-
- // Expand entities, since Adobe Illustrator uses them for xmlns
- // attributes (T33719). Note that libxml2 has some protection
- // against large recursive entity expansions so this is not as
- // insecure as it might appear to be. However, it is still extremely
- // insecure. It's necessary to wrap any read() calls with
- // libxml_disable_entity_loader() to avoid arbitrary local file
- // inclusion, or even arbitrary code execution if the expect
- // extension is installed (T48859).
- $oldDisable = libxml_disable_entity_loader( true );
- $this->reader->setParserProperty( XMLReader::SUBST_ENTITIES, true );
-
- $this->metadata['width'] = self::DEFAULT_WIDTH;
- $this->metadata['height'] = self::DEFAULT_HEIGHT;
-
- // The size in the units specified by the SVG file
- // (for the metadata box)
- // Per the SVG spec, if unspecified, default to '100%'
- $this->metadata['originalWidth'] = '100%';
- $this->metadata['originalHeight'] = '100%';
-
- // Because we cut off the end of the svg making an invalid one. Complicated
- // try catch thing to make sure warnings get restored. Seems like there should
- // be a better way.
- Wikimedia\suppressWarnings();
- try {
- $this->read();
- } catch ( Exception $e ) {
- // Note, if this happens, the width/height will be taken to be 0x0.
- // Should we consider it the default 512x512 instead?
- Wikimedia\restoreWarnings();
- libxml_disable_entity_loader( $oldDisable );
- throw $e;
- }
- Wikimedia\restoreWarnings();
- libxml_disable_entity_loader( $oldDisable );
- }
-
- /**
- * @return array Array with the known metadata
- */
- public function getMetadata() {
- return $this->metadata;
- }
-
- /**
- * Read the SVG
- * @throws MWException
- * @return bool
- */
- protected function read() {
- $keepReading = $this->reader->read();
-
- /* Skip until first element */
- while ( $keepReading && $this->reader->nodeType != XMLReader::ELEMENT ) {
- $keepReading = $this->reader->read();
- }
-
- if ( $this->reader->localName != 'svg' || $this->reader->namespaceURI != self::NS_SVG ) {
- throw new MWException( "Expected <svg> tag, got " .
- $this->reader->localName . " in NS " . $this->reader->namespaceURI );
- }
- $this->debug( "<svg> tag is correct." );
- $this->handleSVGAttribs();
-
- $exitDepth = $this->reader->depth;
- $keepReading = $this->reader->read();
- while ( $keepReading ) {
- $tag = $this->reader->localName;
- $type = $this->reader->nodeType;
- $isSVG = ( $this->reader->namespaceURI == self::NS_SVG );
-
- $this->debug( "$tag" );
-
- if ( $isSVG && $tag == 'svg' && $type == XMLReader::END_ELEMENT
- && $this->reader->depth <= $exitDepth
- ) {
- break;
- } elseif ( $isSVG && $tag == 'title' ) {
- $this->readField( $tag, 'title' );
- } elseif ( $isSVG && $tag == 'desc' ) {
- $this->readField( $tag, 'description' );
- } elseif ( $isSVG && $tag == 'metadata' && $type == XMLReader::ELEMENT ) {
- $this->readXml( 'metadata' );
- } elseif ( $isSVG && $tag == 'script' ) {
- // We normally do not allow scripted svgs.
- // However its possible to configure MW to let them
- // in, and such files should be considered animated.
- $this->metadata['animated'] = true;
- } elseif ( $tag !== '#text' ) {
- $this->debug( "Unhandled top-level XML tag $tag" );
-
- // Recurse into children of current tag, looking for animation and languages.
- $this->animateFilterAndLang( $tag );
- }
-
- // Goto next element, which is sibling of current (Skip children).
- $keepReading = $this->reader->next();
- }
-
- $this->reader->close();
-
- $this->metadata['translations'] = $this->languages + $this->languagePrefixes;
-
- return true;
- }
-
- /**
- * Read a textelement from an element
- *
- * @param string $name Name of the element that we are reading from
- * @param string $metafield Field that we will fill with the result
- */
- private function readField( $name, $metafield = null ) {
- $this->debug( "Read field $metafield" );
- if ( !$metafield || $this->reader->nodeType != XMLReader::ELEMENT ) {
- return;
- }
- $keepReading = $this->reader->read();
- while ( $keepReading ) {
- if ( $this->reader->localName == $name
- && $this->reader->namespaceURI == self::NS_SVG
- && $this->reader->nodeType == XMLReader::END_ELEMENT
- ) {
- break;
- } elseif ( $this->reader->nodeType == XMLReader::TEXT ) {
- $this->metadata[$metafield] = trim( $this->reader->value );
- }
- $keepReading = $this->reader->read();
- }
- }
-
- /**
- * Read an XML snippet from an element
- *
- * @param string $metafield Field that we will fill with the result
- * @throws MWException
- */
- private function readXml( $metafield = null ) {
- $this->debug( "Read top level metadata" );
- if ( !$metafield || $this->reader->nodeType != XMLReader::ELEMENT ) {
- return;
- }
- // @todo Find and store type of xml snippet. metadata['metadataType'] = "rdf"
- $this->metadata[$metafield] = trim( $this->reader->readInnerXml() );
-
- $this->reader->next();
- }
-
- /**
- * Filter all children, looking for animated elements.
- * Also get a list of languages that can be targeted.
- *
- * @param string $name Name of the element that we are reading from
- */
- private function animateFilterAndLang( $name ) {
- $this->debug( "animate filter for tag $name" );
- if ( $this->reader->nodeType != XMLReader::ELEMENT ) {
- return;
- }
- if ( $this->reader->isEmptyElement ) {
- return;
- }
- $exitDepth = $this->reader->depth;
- $keepReading = $this->reader->read();
- while ( $keepReading ) {
- if ( $this->reader->localName == $name && $this->reader->depth <= $exitDepth
- && $this->reader->nodeType == XMLReader::END_ELEMENT
- ) {
- break;
- } elseif ( $this->reader->namespaceURI == self::NS_SVG
- && $this->reader->nodeType == XMLReader::ELEMENT
- ) {
- $sysLang = $this->reader->getAttribute( 'systemLanguage' );
- if ( !is_null( $sysLang ) && $sysLang !== '' ) {
- // See https://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
- $langList = explode( ',', $sysLang );
- foreach ( $langList as $langItem ) {
- $langItem = trim( $langItem );
- if ( Language::isWellFormedLanguageTag( $langItem ) ) {
- $this->languages[$langItem] = self::LANG_FULL_MATCH;
- }
- // Note, the standard says that any prefix should work,
- // here we do only the initial prefix, since that will catch
- // 99% of cases, and we are going to compare against fallbacks.
- // This differs mildly from how the spec says languages should be
- // handled, however it matches better how the MediaWiki language
- // preference is generally handled.
- $dash = strpos( $langItem, '-' );
- // Intentionally checking both !false and > 0 at the same time.
- if ( $dash ) {
- $itemPrefix = substr( $langItem, 0, $dash );
- if ( Language::isWellFormedLanguageTag( $itemPrefix ) ) {
- $this->languagePrefixes[$itemPrefix] = self::LANG_PREFIX_MATCH;
- }
- }
- }
- }
- switch ( $this->reader->localName ) {
- case 'script':
- // Normally we disallow files with
- // <script>, but its possible
- // to configure MW to disable
- // such checks.
- case 'animate':
- case 'set':
- case 'animateMotion':
- case 'animateColor':
- case 'animateTransform':
- $this->debug( "HOUSTON WE HAVE ANIMATION" );
- $this->metadata['animated'] = true;
- break;
- }
- }
- $keepReading = $this->reader->read();
- }
- }
-
- private function debug( $data ) {
- if ( $this->mDebug ) {
- wfDebug( "SVGReader: $data\n" );
- }
- }
-
- /**
- * Parse the attributes of an SVG element
- *
- * The parser has to be in the start element of "<svg>"
- */
- private function handleSVGAttribs() {
- $defaultWidth = self::DEFAULT_WIDTH;
- $defaultHeight = self::DEFAULT_HEIGHT;
- $aspect = 1.0;
- $width = null;
- $height = null;
-
- if ( $this->reader->getAttribute( 'viewBox' ) ) {
- // min-x min-y width height
- $viewBox = preg_split( '/\s*[\s,]\s*/', trim( $this->reader->getAttribute( 'viewBox' ) ) );
- if ( count( $viewBox ) == 4 ) {
- $viewWidth = $this->scaleSVGUnit( $viewBox[2] );
- $viewHeight = $this->scaleSVGUnit( $viewBox[3] );
- if ( $viewWidth > 0 && $viewHeight > 0 ) {
- $aspect = $viewWidth / $viewHeight;
- $defaultHeight = $defaultWidth / $aspect;
- }
- }
- }
- if ( $this->reader->getAttribute( 'width' ) ) {
- $width = $this->scaleSVGUnit( $this->reader->getAttribute( 'width' ), $defaultWidth );
- $this->metadata['originalWidth'] = $this->reader->getAttribute( 'width' );
- }
- if ( $this->reader->getAttribute( 'height' ) ) {
- $height = $this->scaleSVGUnit( $this->reader->getAttribute( 'height' ), $defaultHeight );
- $this->metadata['originalHeight'] = $this->reader->getAttribute( 'height' );
- }
-
- if ( !isset( $width ) && !isset( $height ) ) {
- $width = $defaultWidth;
- $height = $width / $aspect;
- } elseif ( isset( $width ) && !isset( $height ) ) {
- $height = $width / $aspect;
- } elseif ( isset( $height ) && !isset( $width ) ) {
- $width = $height * $aspect;
- }
-
- if ( $width > 0 && $height > 0 ) {
- $this->metadata['width'] = intval( round( $width ) );
- $this->metadata['height'] = intval( round( $height ) );
- }
- }
-
- /**
- * Return a rounded pixel equivalent for a labeled CSS/SVG length.
- * https://www.w3.org/TR/SVG11/coords.html#Units
- *
- * @param string $length CSS/SVG length.
- * @param float|int $viewportSize Optional scale for percentage units...
- * @return float Length in pixels
- */
- static function scaleSVGUnit( $length, $viewportSize = 512 ) {
- static $unitLength = [
- 'px' => 1.0,
- 'pt' => 1.25,
- 'pc' => 15.0,
- 'mm' => 3.543307,
- 'cm' => 35.43307,
- 'in' => 90.0,
- 'em' => 16.0, // fake it?
- 'ex' => 12.0, // fake it?
- '' => 1.0, // "User units" pixels by default
- ];
- $matches = [];
- if ( preg_match(
- '/^\s*([-+]?\d*(?:\.\d+|\d+)(?:[Ee][-+]?\d+)?)\s*(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/',
- $length,
- $matches
- ) ) {
- $length = floatval( $matches[1] );
- $unit = $matches[2];
- if ( $unit == '%' ) {
- return $length * 0.01 * $viewportSize;
- } else {
- return $length * $unitLength[$unit];
- }
- } else {
- // Assume pixels
- return floatval( $length );
- }
- }
-}
--- /dev/null
+<?php
+/**
+ * Extraction of SVG image metadata.
+ *
+ * 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 Media
+ * @author "Derk-Jan Hartman <hartman _at_ videolan d0t org>"
+ * @author Brion Vibber
+ * @copyright Copyright © 2010-2010 Brion Vibber, Derk-Jan Hartman
+ * @license GPL-2.0-or-later
+ */
+
+/**
+ * @ingroup Media
+ */
+class SVGReader {
+ const DEFAULT_WIDTH = 512;
+ const DEFAULT_HEIGHT = 512;
+ const NS_SVG = 'http://www.w3.org/2000/svg';
+ const LANG_PREFIX_MATCH = 1;
+ const LANG_FULL_MATCH = 2;
+
+ /** @var null|XMLReader */
+ private $reader = null;
+
+ /** @var bool */
+ private $mDebug = false;
+
+ /** @var array */
+ private $metadata = [];
+ private $languages = [];
+ private $languagePrefixes = [];
+
+ /**
+ * Creates an SVGReader drawing from the source provided
+ * @param string $source URI from which to read
+ * @throws MWException|Exception
+ */
+ function __construct( $source ) {
+ global $wgSVGMetadataCutoff;
+ $this->reader = new XMLReader();
+
+ // Don't use $file->getSize() since file object passed to SVGHandler::getMetadata is bogus.
+ $size = filesize( $source );
+ if ( $size === false ) {
+ throw new MWException( "Error getting filesize of SVG." );
+ }
+
+ if ( $size > $wgSVGMetadataCutoff ) {
+ $this->debug( "SVG is $size bytes, which is bigger than $wgSVGMetadataCutoff. Truncating." );
+ $contents = file_get_contents( $source, false, null, 0, $wgSVGMetadataCutoff );
+ if ( $contents === false ) {
+ throw new MWException( 'Error reading SVG file.' );
+ }
+ $this->reader->XML( $contents, null, LIBXML_NOERROR | LIBXML_NOWARNING );
+ } else {
+ $this->reader->open( $source, null, LIBXML_NOERROR | LIBXML_NOWARNING );
+ }
+
+ // Expand entities, since Adobe Illustrator uses them for xmlns
+ // attributes (T33719). Note that libxml2 has some protection
+ // against large recursive entity expansions so this is not as
+ // insecure as it might appear to be. However, it is still extremely
+ // insecure. It's necessary to wrap any read() calls with
+ // libxml_disable_entity_loader() to avoid arbitrary local file
+ // inclusion, or even arbitrary code execution if the expect
+ // extension is installed (T48859).
+ $oldDisable = libxml_disable_entity_loader( true );
+ $this->reader->setParserProperty( XMLReader::SUBST_ENTITIES, true );
+
+ $this->metadata['width'] = self::DEFAULT_WIDTH;
+ $this->metadata['height'] = self::DEFAULT_HEIGHT;
+
+ // The size in the units specified by the SVG file
+ // (for the metadata box)
+ // Per the SVG spec, if unspecified, default to '100%'
+ $this->metadata['originalWidth'] = '100%';
+ $this->metadata['originalHeight'] = '100%';
+
+ // Because we cut off the end of the svg making an invalid one. Complicated
+ // try catch thing to make sure warnings get restored. Seems like there should
+ // be a better way.
+ Wikimedia\suppressWarnings();
+ try {
+ $this->read();
+ } catch ( Exception $e ) {
+ // Note, if this happens, the width/height will be taken to be 0x0.
+ // Should we consider it the default 512x512 instead?
+ Wikimedia\restoreWarnings();
+ libxml_disable_entity_loader( $oldDisable );
+ throw $e;
+ }
+ Wikimedia\restoreWarnings();
+ libxml_disable_entity_loader( $oldDisable );
+ }
+
+ /**
+ * @return array Array with the known metadata
+ */
+ public function getMetadata() {
+ return $this->metadata;
+ }
+
+ /**
+ * Read the SVG
+ * @throws MWException
+ * @return bool
+ */
+ protected function read() {
+ $keepReading = $this->reader->read();
+
+ /* Skip until first element */
+ while ( $keepReading && $this->reader->nodeType != XMLReader::ELEMENT ) {
+ $keepReading = $this->reader->read();
+ }
+
+ if ( $this->reader->localName != 'svg' || $this->reader->namespaceURI != self::NS_SVG ) {
+ throw new MWException( "Expected <svg> tag, got " .
+ $this->reader->localName . " in NS " . $this->reader->namespaceURI );
+ }
+ $this->debug( "<svg> tag is correct." );
+ $this->handleSVGAttribs();
+
+ $exitDepth = $this->reader->depth;
+ $keepReading = $this->reader->read();
+ while ( $keepReading ) {
+ $tag = $this->reader->localName;
+ $type = $this->reader->nodeType;
+ $isSVG = ( $this->reader->namespaceURI == self::NS_SVG );
+
+ $this->debug( "$tag" );
+
+ if ( $isSVG && $tag == 'svg' && $type == XMLReader::END_ELEMENT
+ && $this->reader->depth <= $exitDepth
+ ) {
+ break;
+ } elseif ( $isSVG && $tag == 'title' ) {
+ $this->readField( $tag, 'title' );
+ } elseif ( $isSVG && $tag == 'desc' ) {
+ $this->readField( $tag, 'description' );
+ } elseif ( $isSVG && $tag == 'metadata' && $type == XMLReader::ELEMENT ) {
+ $this->readXml( 'metadata' );
+ } elseif ( $isSVG && $tag == 'script' ) {
+ // We normally do not allow scripted svgs.
+ // However its possible to configure MW to let them
+ // in, and such files should be considered animated.
+ $this->metadata['animated'] = true;
+ } elseif ( $tag !== '#text' ) {
+ $this->debug( "Unhandled top-level XML tag $tag" );
+
+ // Recurse into children of current tag, looking for animation and languages.
+ $this->animateFilterAndLang( $tag );
+ }
+
+ // Goto next element, which is sibling of current (Skip children).
+ $keepReading = $this->reader->next();
+ }
+
+ $this->reader->close();
+
+ $this->metadata['translations'] = $this->languages + $this->languagePrefixes;
+
+ return true;
+ }
+
+ /**
+ * Read a textelement from an element
+ *
+ * @param string $name Name of the element that we are reading from
+ * @param string $metafield Field that we will fill with the result
+ */
+ private function readField( $name, $metafield = null ) {
+ $this->debug( "Read field $metafield" );
+ if ( !$metafield || $this->reader->nodeType != XMLReader::ELEMENT ) {
+ return;
+ }
+ $keepReading = $this->reader->read();
+ while ( $keepReading ) {
+ if ( $this->reader->localName == $name
+ && $this->reader->namespaceURI == self::NS_SVG
+ && $this->reader->nodeType == XMLReader::END_ELEMENT
+ ) {
+ break;
+ } elseif ( $this->reader->nodeType == XMLReader::TEXT ) {
+ $this->metadata[$metafield] = trim( $this->reader->value );
+ }
+ $keepReading = $this->reader->read();
+ }
+ }
+
+ /**
+ * Read an XML snippet from an element
+ *
+ * @param string $metafield Field that we will fill with the result
+ * @throws MWException
+ */
+ private function readXml( $metafield = null ) {
+ $this->debug( "Read top level metadata" );
+ if ( !$metafield || $this->reader->nodeType != XMLReader::ELEMENT ) {
+ return;
+ }
+ // @todo Find and store type of xml snippet. metadata['metadataType'] = "rdf"
+ $this->metadata[$metafield] = trim( $this->reader->readInnerXml() );
+
+ $this->reader->next();
+ }
+
+ /**
+ * Filter all children, looking for animated elements.
+ * Also get a list of languages that can be targeted.
+ *
+ * @param string $name Name of the element that we are reading from
+ */
+ private function animateFilterAndLang( $name ) {
+ $this->debug( "animate filter for tag $name" );
+ if ( $this->reader->nodeType != XMLReader::ELEMENT ) {
+ return;
+ }
+ if ( $this->reader->isEmptyElement ) {
+ return;
+ }
+ $exitDepth = $this->reader->depth;
+ $keepReading = $this->reader->read();
+ while ( $keepReading ) {
+ if ( $this->reader->localName == $name && $this->reader->depth <= $exitDepth
+ && $this->reader->nodeType == XMLReader::END_ELEMENT
+ ) {
+ break;
+ } elseif ( $this->reader->namespaceURI == self::NS_SVG
+ && $this->reader->nodeType == XMLReader::ELEMENT
+ ) {
+ $sysLang = $this->reader->getAttribute( 'systemLanguage' );
+ if ( !is_null( $sysLang ) && $sysLang !== '' ) {
+ // See https://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
+ $langList = explode( ',', $sysLang );
+ foreach ( $langList as $langItem ) {
+ $langItem = trim( $langItem );
+ if ( Language::isWellFormedLanguageTag( $langItem ) ) {
+ $this->languages[$langItem] = self::LANG_FULL_MATCH;
+ }
+ // Note, the standard says that any prefix should work,
+ // here we do only the initial prefix, since that will catch
+ // 99% of cases, and we are going to compare against fallbacks.
+ // This differs mildly from how the spec says languages should be
+ // handled, however it matches better how the MediaWiki language
+ // preference is generally handled.
+ $dash = strpos( $langItem, '-' );
+ // Intentionally checking both !false and > 0 at the same time.
+ if ( $dash ) {
+ $itemPrefix = substr( $langItem, 0, $dash );
+ if ( Language::isWellFormedLanguageTag( $itemPrefix ) ) {
+ $this->languagePrefixes[$itemPrefix] = self::LANG_PREFIX_MATCH;
+ }
+ }
+ }
+ }
+ switch ( $this->reader->localName ) {
+ case 'script':
+ // Normally we disallow files with
+ // <script>, but its possible
+ // to configure MW to disable
+ // such checks.
+ case 'animate':
+ case 'set':
+ case 'animateMotion':
+ case 'animateColor':
+ case 'animateTransform':
+ $this->debug( "HOUSTON WE HAVE ANIMATION" );
+ $this->metadata['animated'] = true;
+ break;
+ }
+ }
+ $keepReading = $this->reader->read();
+ }
+ }
+
+ private function debug( $data ) {
+ if ( $this->mDebug ) {
+ wfDebug( "SVGReader: $data\n" );
+ }
+ }
+
+ /**
+ * Parse the attributes of an SVG element
+ *
+ * The parser has to be in the start element of "<svg>"
+ */
+ private function handleSVGAttribs() {
+ $defaultWidth = self::DEFAULT_WIDTH;
+ $defaultHeight = self::DEFAULT_HEIGHT;
+ $aspect = 1.0;
+ $width = null;
+ $height = null;
+
+ if ( $this->reader->getAttribute( 'viewBox' ) ) {
+ // min-x min-y width height
+ $viewBox = preg_split( '/\s*[\s,]\s*/', trim( $this->reader->getAttribute( 'viewBox' ) ) );
+ if ( count( $viewBox ) == 4 ) {
+ $viewWidth = $this->scaleSVGUnit( $viewBox[2] );
+ $viewHeight = $this->scaleSVGUnit( $viewBox[3] );
+ if ( $viewWidth > 0 && $viewHeight > 0 ) {
+ $aspect = $viewWidth / $viewHeight;
+ $defaultHeight = $defaultWidth / $aspect;
+ }
+ }
+ }
+ if ( $this->reader->getAttribute( 'width' ) ) {
+ $width = $this->scaleSVGUnit( $this->reader->getAttribute( 'width' ), $defaultWidth );
+ $this->metadata['originalWidth'] = $this->reader->getAttribute( 'width' );
+ }
+ if ( $this->reader->getAttribute( 'height' ) ) {
+ $height = $this->scaleSVGUnit( $this->reader->getAttribute( 'height' ), $defaultHeight );
+ $this->metadata['originalHeight'] = $this->reader->getAttribute( 'height' );
+ }
+
+ if ( !isset( $width ) && !isset( $height ) ) {
+ $width = $defaultWidth;
+ $height = $width / $aspect;
+ } elseif ( isset( $width ) && !isset( $height ) ) {
+ $height = $width / $aspect;
+ } elseif ( isset( $height ) && !isset( $width ) ) {
+ $width = $height * $aspect;
+ }
+
+ if ( $width > 0 && $height > 0 ) {
+ $this->metadata['width'] = intval( round( $width ) );
+ $this->metadata['height'] = intval( round( $height ) );
+ }
+ }
+
+ /**
+ * Return a rounded pixel equivalent for a labeled CSS/SVG length.
+ * https://www.w3.org/TR/SVG11/coords.html#Units
+ *
+ * @param string $length CSS/SVG length.
+ * @param float|int $viewportSize Optional scale for percentage units...
+ * @return float Length in pixels
+ */
+ static function scaleSVGUnit( $length, $viewportSize = 512 ) {
+ static $unitLength = [
+ 'px' => 1.0,
+ 'pt' => 1.25,
+ 'pc' => 15.0,
+ 'mm' => 3.543307,
+ 'cm' => 35.43307,
+ 'in' => 90.0,
+ 'em' => 16.0, // fake it?
+ 'ex' => 12.0, // fake it?
+ '' => 1.0, // "User units" pixels by default
+ ];
+ $matches = [];
+ if ( preg_match(
+ '/^\s*([-+]?\d*(?:\.\d+|\d+)(?:[Ee][-+]?\d+)?)\s*(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/',
+ $length,
+ $matches
+ ) ) {
+ $length = floatval( $matches[1] );
+ $unit = $matches[2];
+ if ( $unit == '%' ) {
+ return $length * 0.01 * $viewportSize;
+ } else {
+ return $length * $unitLength[$unit];
+ }
+ } else {
+ // Assume pixels
+ return floatval( $length );
+ }
+ }
+}
function toHtml( $options = [] ) {
global $wgPriorityHints, $wgElementTiming;
- if ( count( func_get_args() ) == 2 ) {
+ if ( func_num_args() == 2 ) {
throw new MWException( __METHOD__ . ' called in the old style' );
}
}
# Forge a return array containing metadata information just like getimagesize()
- # See PHP documentation at: https://secure.php.net/getimagesize
+ # See PHP documentation at: https://www.php.net/getimagesize
return [
0 => $header['width'],
1 => $header['height'],
$out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
/**
- * @var $file File
+ * @var File $file
*/
foreach ( $dupes as $file ) {
$fromSrc = '';
/**
* @param Parser $parser
- * @param string $part1
+ * @param string $part1 Message key
+ * @param mixed ...$params To pass to wfMessage()
* @return array
*/
- public static function intFunction( $parser, $part1 = '' /*, ... */ ) {
+ public static function intFunction( $parser, $part1 = '', ...$params ) {
if ( strval( $part1 ) !== '' ) {
- $args = array_slice( func_get_args(), 2 );
- $message = wfMessage( $part1, $args )
+ $message = wfMessage( $part1, $params )
->inLanguage( $parser->getOptions()->getUserLangObj() );
return [ $message->plain(), 'noparse' => false ];
} else {
/**
* @param Parser $parser
* @param string $username
+ * @param string ...$forms What to output for each gender
* @return string
*/
- public static function gender( $parser, $username ) {
- $forms = array_slice( func_get_args(), 2 );
-
+ public static function gender( $parser, $username, ...$forms ) {
// Some shortcuts to avoid loading user data unnecessarily
if ( count( $forms ) === 0 ) {
return '';
/**
* @param Parser $parser
* @param string $text
+ * @param string ...$forms What to output for each number (singular, dual, plural, etc.)
* @return string
*/
- public static function plural( $parser, $text = '' ) {
- $forms = array_slice( func_get_args(), 2 );
+ public static function plural( $parser, $text = '', ...$forms ) {
$text = $parser->getFunctionLang()->parseFormattedNumber( $text );
settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
return $parser->getFunctionLang()->convertPlural( $text, $forms );
// @TODO: disallow this word on all namespaces
$this->nsInfo->isContent( $this->mTitle->getNamespace() )
) {
- return $this->mRevisionId ? '-' : '';
+ if ( $this->mRevisionId ) {
+ return '-';
+ } else {
+ $this->mOutput->setFlag( 'vary-revision-exists' );
+
+ return '';
+ }
};
$pageLang = $this->getFunctionLang();
*/
/**
- * Fetch an option, generically
+ * Fetch an option and track that is was accessed
* @since 1.30
* @param string $name Option name
* @return mixed
throw new InvalidArgumentException( "Unknown parser option $name" );
}
- if ( isset( self::$lazyOptions[$name] ) && $this->options[$name] === null ) {
- $this->options[$name] = call_user_func( self::$lazyOptions[$name], $this, $name );
- }
+ $this->lazyLoadOption( $name );
if ( !empty( self::$inCacheKey[$name] ) ) {
$this->optionUsed( $name );
}
return $this->options[$name];
}
+ /**
+ * @param string $name Lazy load option without tracking usage
+ */
+ private function lazyLoadOption( $name ) {
+ if ( isset( self::$lazyOptions[$name] ) && $this->options[$name] === null ) {
+ $this->options[$name] = call_user_func( self::$lazyOptions[$name], $this, $name );
+ }
+ }
+
/**
* Set an option, generically
* @since 1.30
* @since 1.25
*/
public function matches( ParserOptions $other ) {
- // Populate lazy options
- foreach ( self::$lazyOptions as $name => $callback ) {
- if ( $this->options[$name] === null ) {
- $this->options[$name] = call_user_func( $callback, $this, $name );
- }
- if ( $other->options[$name] === null ) {
- $other->options[$name] = call_user_func( $callback, $other, $name );
- }
- }
-
// Compare most options
$options = array_keys( $this->options );
$options = array_diff( $options, [
'enableLimitReport', // only affects HTML comments
] );
foreach ( $options as $option ) {
+ // Resolve any lazy options
+ $this->lazyLoadOption( $option );
+ $other->lazyLoadOption( $option );
+
$o1 = $this->optionToString( $this->options[$option] );
$o2 = $this->optionToString( $other->options[$option] );
if ( $o1 !== $o2 ) {
return true;
}
+ /**
+ * @param ParserOptions $other
+ * @return bool Whether the cache key relevant options match those of $other
+ * @since 1.33
+ */
+ public function matchesForCacheKey( ParserOptions $other ) {
+ foreach ( self::allCacheVaryingOptions() as $option ) {
+ // Populate any lazy options
+ $this->lazyLoadOption( $option );
+ $other->lazyLoadOption( $option );
+
+ $o1 = $this->optionToString( $this->options[$option] );
+ $o2 = $this->optionToString( $other->options[$option] );
+ if ( $o1 !== $o2 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Registers a callback for tracking which ParserOptions which are used.
* This is a private API with the parser.
$inCacheKey = self::allCacheVaryingOptions();
// Resolve any lazy options
- foreach ( array_intersect( $forOptions, $inCacheKey, array_keys( self::$lazyOptions ) ) as $k ) {
- if ( $this->options[$k] === null ) {
- $this->options[$k] = call_user_func( self::$lazyOptions[$k], $this, $k );
- }
+ $lazyOpts = array_intersect( $forOptions, $inCacheKey, array_keys( self::$lazyOptions ) );
+ foreach ( $lazyOpts as $k ) {
+ $this->lazyLoadOption( $k );
}
$options = $this->options;
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* @ingroup Parser
return;
}
- $cache = ObjectCache::getLocalClusterInstance();
+ $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
$key = $cache->makeKey(
defined( 'static::CACHE_PREFIX' ) ? static::CACHE_PREFIX : static::class,
- md5( $text ), $flags );
+ md5( $text ),
+ $flags
+ );
$value = sprintf( "%08d", static::CACHE_VERSION ) . $tree;
$cache->set( $key, $value, 86400 );
return false;
}
- $cache = ObjectCache::getLocalClusterInstance();
+ $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
$key = $cache->makeKey(
defined( 'static::CACHE_PREFIX' ) ? static::CACHE_PREFIX : static::class,
- md5( $text ), $flags );
+ md5( $text ),
+ $flags
+ );
$value = $cache->get( $key );
if ( !$value ) {
/**
* @param string $sep
* @param int $flags
- * @param string|PPNode_DOM|DOMDocument $args,...
+ * @param string|PPNode_DOM|DOMDocument ...$args
* @return string
*/
- public function implodeWithFlags( $sep, $flags /*, ... */ ) {
- $args = array_slice( func_get_args(), 2 );
-
+ public function implodeWithFlags( $sep, $flags, ...$args ) {
$first = true;
$s = '';
foreach ( $args as $root ) {
* This previously called implodeWithFlags but has now been inlined to reduce stack depth
*
* @param string $sep
- * @param string|PPNode_DOM|DOMDocument $args,...
+ * @param string|PPNode_DOM|DOMDocument ...$args
* @return string
*/
- public function implode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
-
+ public function implode( $sep, ...$args ) {
$first = true;
$s = '';
foreach ( $args as $root ) {
* with implode()
*
* @param string $sep
- * @param string|PPNode_DOM|DOMDocument $args,...
+ * @param string|PPNode_DOM|DOMDocument ...$args
* @return array
*/
- public function virtualImplode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
+ public function virtualImplode( $sep, ...$args ) {
$out = [];
$first = true;
* @param string $start
* @param string $sep
* @param string $end
- * @param string|PPNode_DOM|DOMDocument $args,...
+ * @param string|PPNode_DOM|DOMDocument ...$args
* @return array
*/
- public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
- $args = array_slice( func_get_args(), 3 );
+ public function virtualBracketedImplode( $start, $sep, $end, ...$args ) {
$out = [ $start ];
$first = true;
/**
* @param string $sep
* @param int $flags
- * @param string|PPNode $args,...
+ * @param string|PPNode ...$args
* @return string
*/
- public function implodeWithFlags( $sep, $flags /*, ... */ ) {
- $args = array_slice( func_get_args(), 2 );
-
+ public function implodeWithFlags( $sep, $flags, ...$args ) {
$first = true;
$s = '';
foreach ( $args as $root ) {
* Implode with no flags specified
* This previously called implodeWithFlags but has now been inlined to reduce stack depth
* @param string $sep
- * @param string|PPNode $args,...
+ * @param string|PPNode ...$args
* @return string
*/
- public function implode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
-
+ public function implode( $sep, ...$args ) {
$first = true;
$s = '';
foreach ( $args as $root ) {
* with implode()
*
* @param string $sep
- * @param string|PPNode $args,...
+ * @param string|PPNode ...$args
* @return PPNode_Hash_Array
*/
- public function virtualImplode( $sep /*, ... */ ) {
- $args = array_slice( func_get_args(), 1 );
+ public function virtualImplode( $sep, ...$args ) {
$out = [];
$first = true;
* @param string $start
* @param string $sep
* @param string $end
- * @param string|PPNode $args,...
+ * @param string|PPNode ...$args
* @return PPNode_Hash_Array
*/
- public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
- $args = array_slice( func_get_args(), 3 );
+ public function virtualBracketedImplode( $start, $sep, $end, ...$args ) {
$out = [ $start ];
$first = true;
*
* @param array $store
* @param int $index
- * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text
+ * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|false
+ * @throws MWException
*/
public static function factory( array $store, $index ) {
if ( !isset( $store[$index] ) ) {
/**
* Convert a node to XML, for debugging
+ * @return string
*/
public function __toString() {
$inner = '';
use OutputPage;
use Parser;
use ParserOptions;
-use PreferencesFormLegacy;
+use PreferencesFormOOUI;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
use Skin;
public function getForm(
User $user,
IContextSource $context,
- $formClass = PreferencesFormLegacy::class,
+ $formClass = PreferencesFormOOUI::class,
array $remove = []
) {
// We use ButtonWidgets in some of the getPreferences() functions
}
/**
- * @var $htmlForm HTMLForm
+ * @var HTMLForm $htmlForm
*/
$htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
use HTMLForm;
use IContextSource;
+use PreferencesFormOOUI;
use User;
/**
* A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a
* given user. These definitions are in the form of an HTMLForm descriptor.
*
- * PreferencesFormLegacy (a subclass of HTMLForm) is used to generate the Preferences form, and
+ * PreferencesFormOOUI (a subclass of HTMLForm) is used to generate the Preferences form, and
* handles generic submission, CSRF protection, layout and other logic in a reusable manner.
*
* In order to generate the form, the HTMLForm object needs an array structure detailing the
public function getForm(
User $user,
IContextSource $contextSource,
- $formClass = \PreferencesFormLegacy::class,
+ $formClass = PreferencesFormOOUI::class,
array $remove = []
);
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Profiler
+ */
+use Wikimedia\ScopedCallback;
+
+/**
+ * Subclass ScopedCallback to avoid call_user_func_array(), which is slow.
+ *
+ * @internal For use by SectionProfiler
+ * @since 1.25
+ */
+class SectionProfileCallback extends ScopedCallback {
+ /** @var SectionProfiler */
+ protected $profiler;
+ /** @var string */
+ protected $section;
+
+ /**
+ * @param SectionProfiler $profiler
+ * @param string $section
+ */
+ public function __construct( SectionProfiler $profiler, $section ) {
+ parent::__construct( null );
+ $this->profiler = $profiler;
+ $this->section = $section;
+ }
+
+ function __destruct() {
+ $this->profiler->profileOutInternal( $this->section );
+ }
+}
}
}
}
-
-/**
- * Subclass ScopedCallback to avoid call_user_func_array(), which is slow
- *
- * This class should not be used outside of SectionProfiler
- */
-class SectionProfileCallback extends ScopedCallback {
- /** @var SectionProfiler */
- protected $profiler;
- /** @var string */
- protected $section;
-
- /**
- * @param SectionProfiler $profiler
- * @param string $section
- */
- public function __construct( SectionProfiler $profiler, $section ) {
- parent::__construct( null );
- $this->profiler = $profiler;
- $this->section = $section;
- }
-
- function __destruct() {
- $this->profiler->profileOutInternal( $this->section );
- }
-}
*/
public $missingPhpExtensions = [];
+ /**
+ * @var string[]
+ */
+ public $missingAbilities = [];
+
/**
* @param array $errors Each error has a 'msg' and 'type' key at minimum
*/
case 'missing-phpExtension':
$this->missingPhpExtensions[] = $info['missing'];
break;
+ case 'missing-ability':
+ $this->missingAbilities[] = $info['missing'];
+ break;
case 'missing-skins':
$this->missingSkins[] = $info['missing'];
break;
use Composer\Semver\Semver;
use Wikimedia\ScopedCallback;
+use MediaWiki\Shell\Shell;
+use MediaWiki\ShellDisabledError;
/**
* ExtensionRegistry class
// A few more things to vary the cache on
$versions = [
'registration' => self::CACHE_VERSION,
- 'mediawiki' => $wgVersion
+ 'mediawiki' => $wgVersion,
+ 'abilities' => $this->getAbilities(),
];
// We use a try/catch because we don't want to fail here
$this->finished = true;
}
+ /**
+ * Get the list of abilities and their values
+ * @return bool[]
+ */
+ private function getAbilities() {
+ return [
+ 'shell' => !Shell::isDisabled(),
+ ];
+ }
+
+ /**
+ * Queries information about the software environment and constructs an appropiate version checker
+ *
+ * @return VersionChecker
+ */
+ private function buildVersionChecker() {
+ global $wgVersion;
+ // array to optionally specify more verbose error messages for
+ // missing abilities
+ $abilityErrors = [
+ 'shell' => ( new ShellDisabledError() )->getMessage(),
+ ];
+
+ return new VersionChecker(
+ $wgVersion,
+ PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION,
+ get_loaded_extensions(),
+ $this->getAbilities(),
+ $abilityErrors
+ );
+ }
+
/**
* Process a queue of extensions and return their extracted data
*
* @throws ExtensionDependencyError
*/
public function readFromQueue( array $queue ) {
- global $wgVersion;
$autoloadClasses = [];
$autoloadNamespaces = [];
$autoloaderPaths = [];
$processor = new ExtensionProcessor();
- $versionChecker = new VersionChecker(
- $wgVersion,
- PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION,
- get_loaded_extensions()
- );
+ $versionChecker = $this->buildVersionChecker();
$extDependencies = [];
$incompatible = [];
$warnings = false;
*/
private $phpExtensions = [];
+ /**
+ * @var bool[] List of provided abilities
+ */
+ private $abilities = [];
+
+ /**
+ * @var string[] List of provided ability errors
+ */
+ private $abilityErrors = [];
+
/**
* @var array Loaded extensions
*/
* @param string $coreVersion Current version of core
* @param string $phpVersion Current PHP version
* @param string[] $phpExtensions List of installed PHP extensions
+ * @param bool[] $abilities List of provided abilities
+ * @param string[] $abilityErrors Error messages for the abilities
*/
- public function __construct( $coreVersion, $phpVersion, array $phpExtensions ) {
+ public function __construct(
+ $coreVersion, $phpVersion, array $phpExtensions,
+ array $abilities = [], array $abilityErrors = []
+ ) {
$this->versionParser = new VersionParser();
$this->setCoreVersion( $coreVersion );
$this->setPhpVersion( $phpVersion );
$this->phpExtensions = $phpExtensions;
+ $this->abilities = $abilities;
+ $this->abilityErrors = $abilityErrors;
}
/**
* 'MediaWiki' => '>= 1.25.0',
* 'platform': {
* 'php': '>= 7.0.0',
- * 'ext-foo': '*'
+ * 'ext-foo': '*',
+ * 'ability-bar': true
* },
* 'extensions' => {
* 'FooBaz' => '>= 1.25.0'
'missing' => $phpExtension,
];
}
+ } elseif ( substr( $dependency, 0, 8 ) === 'ability-' ) {
+ // Other abilities the environment might provide.
+ $ability = substr( $dependency, 8 );
+ if ( !isset( $this->abilities[$ability] ) ) {
+ throw new UnexpectedValueException( 'Dependency type '
+ . $dependency . ' unknown in ' . $extension );
+ }
+ if ( !is_bool( $constraint ) ) {
+ throw new UnexpectedValueException( 'Only booleans are '
+ . 'allowed to to indicate the presence of abilities '
+ . 'in ' . $extension );
+ }
+
+ if ( $constraint === true &&
+ $this->abilities[$ability] !== true
+ ) {
+ // add custom error message for missing ability if specified
+ $customMessage = '';
+ if ( isset( $this->abilityErrors[$ability] ) ) {
+ $customMessage = ': ' . $this->abilityErrors[$ability];
+ }
+
+ $errors[] = [
+ 'msg' =>
+ "{$extension} requires \"{$ability}\" ability"
+ . $customMessage
+ ,
+ 'type' => 'missing-ability',
+ 'missing' => $ability,
+ ];
+ }
} else {
// add other platform dependencies here
throw new UnexpectedValueException( 'Dependency type ' . $dependency .
if ( !$context->getDebug() ) {
$strContent = self::filter( $filter, $strContent );
+ } else {
+ // In debug mode, separate each response by a new line.
+ // For example, between 'mw.loader.implement();' statements.
+ $strContent = $this->ensureNewline( $strContent );
}
if ( $context->getOnly() === 'scripts' ) {
private function getConfigSettings( $context ) {
$conf = $this->getConfig();
- // We can't use Title::newMainPage() if 'mainpage' is in
- // $wgForceUIMsgAsContentMsg because that will try to use the session
- // user's language and we have no session user. This does the
- // equivalent but falling back to our ResourceLoaderContext language
- // instead.
- $mainPage = Title::newFromText( $context->msg( 'mainpage' )->inContentLanguage()->text() );
- if ( !$mainPage ) {
- $mainPage = Title::newFromText( 'Main Page' );
- }
+ // Passing a context is important as Title::newMainPage() may otherwise
+ // try to intialise a session, which is not allowed on load.php requests.
+ $mainPage = Title::newMainPage( $context );
/**
* Namespace related preparation
/** @var array Feature values */
protected $features = [];
- /** @const string profile type for completionSearch */
+ /** Profile type for completionSearch */
const COMPLETION_PROFILE_TYPE = 'completionSearchProfile';
- /** @const string profile type for query independent ranking features */
+ /** Profile type for query independent ranking features */
const FT_QUERY_INDEP_PROFILE_TYPE = 'fulltextQueryIndepProfile';
- /** @const int flag for legalSearchChars: includes all chars allowed in a search query */
+ /** Integer flag for legalSearchChars: includes all chars allowed in a search query */
const CHARS_ALL = 1;
- /** @const int flag for legalSearchChars: includes all chars allowed in a search term */
+ /** Integer flag for legalSearchChars: includes all chars allowed in a search term */
const CHARS_NO_SYNTAX = 2;
/**
* @ingroup Search
*/
class SearchResultSet implements Countable, IteratorAggregate {
+
/**
- * Types of interwiki results
- */
- /**
- * Results that are displayed only together with existing main wiki results
- * @var int
+ * Identifier for interwiki results that are displayed only together with existing main wiki
+ * results.
*/
const SECONDARY_RESULTS = 0;
+
/**
- * Results that can displayed even if no existing main wiki results exist
- * @var int
+ * Identifier for interwiki results that can be displayed even if no existing main wiki results
+ * exist.
*/
const INLINE_RESULTS = 1;
* Adds parameters to the command. All parameters are sanitized via Shell::escape().
* Null values are ignored.
*
- * @param string|string[] $args,...
+ * @param string|string[] ...$args
* @return $this
*/
- public function params( /* ... */ ) {
- $args = func_get_args();
+ public function params( ...$args ) {
if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
// If only one argument has been passed, and that argument is an array,
// treat it as a list of arguments
* Adds unsafe parameters to the command. These parameters are NOT sanitized in any way.
* Null values are ignored.
*
- * @param string|string[] $args,...
+ * @param string|string[] ...$args
* @return $this
*/
- public function unsafeParams( /* ... */ ) {
- $args = func_get_args();
+ public function unsafeParams( ...$args ) {
if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
// If only one argument has been passed, and that argument is an array,
// treat it as a list of arguments
}
// clear get_last_error without actually raising an error
- // from https://secure.php.net/manual/en/function.error-get-last.php#113518
+ // from https://www.php.net/manual/en/function.error-get-last.php#113518
// TODO replace with clear_last_error when requirements are bumped to PHP7
set_error_handler( function () {
}, 0 );
--- /dev/null
+<?php
+/**
+ * Implements Special:Activeusers
+ *
+ * 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
+ */
+
+/**
+ * Implements Special:Activeusers
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialActiveUsers extends SpecialPage {
+
+ public function __construct() {
+ parent::__construct( 'Activeusers' );
+ }
+
+ /**
+ * @param string|null $par Parameter passed to the page or null
+ */
+ public function execute( $par ) {
+ $out = $this->getOutput();
+
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $opts = new FormOptions();
+
+ $opts->add( 'username', '' );
+ $opts->add( 'groups', [] );
+ $opts->add( 'excludegroups', [] );
+ // Backwards-compatibility with old URLs
+ $opts->add( 'hidebots', false, FormOptions::BOOL );
+ $opts->add( 'hidesysops', false, FormOptions::BOOL );
+
+ $opts->fetchValuesFromRequest( $this->getRequest() );
+
+ if ( $par !== null ) {
+ $opts->setValue( 'username', $par );
+ }
+
+ $pager = new ActiveUsersPager( $this->getContext(), $opts );
+ $usersBody = $pager->getBody();
+
+ $this->buildForm();
+
+ if ( $usersBody ) {
+ $out->addHTML(
+ $pager->getNavigationBar() .
+ Html::rawElement( 'ul', [], $usersBody ) .
+ $pager->getNavigationBar()
+ );
+ } else {
+ $out->addWikiMsg( 'activeusers-noresult' );
+ }
+ }
+
+ /**
+ * Generate and output the form
+ */
+ protected function buildForm() {
+ $groups = User::getAllGroups();
+
+ $options = [];
+ foreach ( $groups as $group ) {
+ $msg = htmlspecialchars( UserGroupMembership::getGroupName( $group ) );
+ $options[$msg] = $group;
+ }
+ asort( $options );
+
+ // Backwards-compatibility with old URLs
+ $req = $this->getRequest();
+ $excludeDefault = [];
+ if ( $req->getCheck( 'hidebots' ) ) {
+ $excludeDefault[] = 'bot';
+ }
+ if ( $req->getCheck( 'hidesysops' ) ) {
+ $excludeDefault[] = 'sysop';
+ }
+
+ $formDescriptor = [
+ 'username' => [
+ 'type' => 'user',
+ 'name' => 'username',
+ 'label-message' => 'activeusers-from',
+ ],
+ 'groups' => [
+ 'type' => 'multiselect',
+ 'dropdown' => true,
+ 'flatlist' => true,
+ 'name' => 'groups',
+ 'label-message' => 'activeusers-groups',
+ 'options' => $options,
+ ],
+ 'excludegroups' => [
+ 'type' => 'multiselect',
+ 'dropdown' => true,
+ 'flatlist' => true,
+ 'name' => 'excludegroups',
+ 'label-message' => 'activeusers-excludegroups',
+ 'options' => $options,
+ 'default' => $excludeDefault,
+ ],
+ ];
+
+ HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+ // For the 'multiselect' field values to be preserved on submit
+ ->setFormIdentifier( 'specialactiveusers' )
+ ->setIntro( $this->getIntroText() )
+ ->setWrapperLegendMsg( 'activeusers' )
+ ->setSubmitTextMsg( 'activeusers-submit' )
+ // prevent setting subpage and 'username' parameter at the same time
+ ->setAction( $this->getPageTitle()->getLocalURL() )
+ ->setMethod( 'get' )
+ ->prepareForm()
+ ->displayForm( false );
+ }
+
+ /**
+ * Return introductory message.
+ * @return string
+ */
+ protected function getIntroText() {
+ $days = $this->getConfig()->get( 'ActiveUserDays' );
+
+ $intro = $this->msg( 'activeusers-intro' )->numParams( $days )->parse();
+
+ // Mention the level of cache staleness...
+ $dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
+ $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
+ if ( $rcMax ) {
+ $cTime = $dbr->selectField( 'querycache_info',
+ 'qci_timestamp',
+ [ 'qci_type' => 'activeusers' ],
+ __METHOD__
+ );
+ if ( $cTime ) {
+ $secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime );
+ } else {
+ $rcMin = $dbr->selectField( 'recentchanges', 'MIN(rc_timestamp)' );
+ $secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin );
+ }
+ if ( $secondsOld > 0 ) {
+ $intro .= $this->msg( 'cachedspecial-viewing-cached-ttl' )
+ ->durationParams( $secondsOld )->parseAsBlock();
+ }
+ }
+
+ return $intro;
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Activeusers
- *
- * 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
- */
-
-/**
- * Implements Special:Activeusers
- *
- * @ingroup SpecialPage
- */
-class SpecialActiveUsers extends SpecialPage {
-
- public function __construct() {
- parent::__construct( 'Activeusers' );
- }
-
- /**
- * @param string|null $par Parameter passed to the page or null
- */
- public function execute( $par ) {
- $out = $this->getOutput();
-
- $this->setHeaders();
- $this->outputHeader();
-
- $opts = new FormOptions();
-
- $opts->add( 'username', '' );
- $opts->add( 'groups', [] );
- $opts->add( 'excludegroups', [] );
- // Backwards-compatibility with old URLs
- $opts->add( 'hidebots', false, FormOptions::BOOL );
- $opts->add( 'hidesysops', false, FormOptions::BOOL );
-
- $opts->fetchValuesFromRequest( $this->getRequest() );
-
- if ( $par !== null ) {
- $opts->setValue( 'username', $par );
- }
-
- $pager = new ActiveUsersPager( $this->getContext(), $opts );
- $usersBody = $pager->getBody();
-
- $this->buildForm();
-
- if ( $usersBody ) {
- $out->addHTML(
- $pager->getNavigationBar() .
- Html::rawElement( 'ul', [], $usersBody ) .
- $pager->getNavigationBar()
- );
- } else {
- $out->addWikiMsg( 'activeusers-noresult' );
- }
- }
-
- /**
- * Generate and output the form
- */
- protected function buildForm() {
- $groups = User::getAllGroups();
-
- $options = [];
- foreach ( $groups as $group ) {
- $msg = htmlspecialchars( UserGroupMembership::getGroupName( $group ) );
- $options[$msg] = $group;
- }
- asort( $options );
-
- // Backwards-compatibility with old URLs
- $req = $this->getRequest();
- $excludeDefault = [];
- if ( $req->getCheck( 'hidebots' ) ) {
- $excludeDefault[] = 'bot';
- }
- if ( $req->getCheck( 'hidesysops' ) ) {
- $excludeDefault[] = 'sysop';
- }
-
- $formDescriptor = [
- 'username' => [
- 'type' => 'user',
- 'name' => 'username',
- 'label-message' => 'activeusers-from',
- ],
- 'groups' => [
- 'type' => 'multiselect',
- 'dropdown' => true,
- 'flatlist' => true,
- 'name' => 'groups',
- 'label-message' => 'activeusers-groups',
- 'options' => $options,
- ],
- 'excludegroups' => [
- 'type' => 'multiselect',
- 'dropdown' => true,
- 'flatlist' => true,
- 'name' => 'excludegroups',
- 'label-message' => 'activeusers-excludegroups',
- 'options' => $options,
- 'default' => $excludeDefault,
- ],
- ];
-
- HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
- // For the 'multiselect' field values to be preserved on submit
- ->setFormIdentifier( 'specialactiveusers' )
- ->setIntro( $this->getIntroText() )
- ->setWrapperLegendMsg( 'activeusers' )
- ->setSubmitTextMsg( 'activeusers-submit' )
- // prevent setting subpage and 'username' parameter at the same time
- ->setAction( $this->getPageTitle()->getLocalURL() )
- ->setMethod( 'get' )
- ->prepareForm()
- ->displayForm( false );
- }
-
- /**
- * Return introductory message.
- * @return string
- */
- protected function getIntroText() {
- $days = $this->getConfig()->get( 'ActiveUserDays' );
-
- $intro = $this->msg( 'activeusers-intro' )->numParams( $days )->parse();
-
- // Mention the level of cache staleness...
- $dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
- $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
- if ( $rcMax ) {
- $cTime = $dbr->selectField( 'querycache_info',
- 'qci_timestamp',
- [ 'qci_type' => 'activeusers' ],
- __METHOD__
- );
- if ( $cTime ) {
- $secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime );
- } else {
- $rcMin = $dbr->selectField( 'recentchanges', 'MIN(rc_timestamp)' );
- $secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin );
- }
- if ( $secondsOld > 0 ) {
- $intro .= $this->msg( 'cachedspecial-viewing-cached-ttl' )
- ->durationParams( $secondsOld )->parseAsBlock();
- }
- }
-
- return $intro;
- }
-
- protected function getGroupName() {
- return 'users';
- }
-}
*
* @todo strtotime() only accepts English strings. This means the expiry input
* can only be specified in English.
- * @see https://secure.php.net/manual/en/function.strtotime.php
+ * @see https://www.php.net/manual/en/function.strtotime.php
*
* @param string $expiry Whatever was typed into the form
* @return string|bool Timestamp or 'infinity' or false on error.
--- /dev/null
+<?php
+/**
+ * Implements Special:Booksources
+ *
+ * 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 outputs information on sourcing a book with a particular ISBN
+ * The parser creates links to this page when dealing with ISBNs in wikitext
+ *
+ * @author Rob Church <robchur@gmail.com>
+ * @ingroup SpecialPage
+ */
+class SpecialBookSources extends SpecialPage {
+ public function __construct() {
+ parent::__construct( 'Booksources' );
+ }
+
+ /**
+ * @param string|null $isbn ISBN passed as a subpage parameter
+ */
+ public function execute( $isbn ) {
+ $out = $this->getOutput();
+
+ $this->setHeaders();
+ $this->outputHeader();
+
+ // User provided ISBN
+ $isbn = $isbn ?: $this->getRequest()->getText( 'isbn' );
+ $isbn = trim( $isbn );
+
+ $this->buildForm( $isbn );
+
+ if ( $isbn !== '' ) {
+ if ( !self::isValidISBN( $isbn ) ) {
+ $out->wrapWikiMsg(
+ "<div class=\"error\">\n$1\n</div>",
+ 'booksources-invalid-isbn'
+ );
+ }
+
+ $this->showList( $isbn );
+ }
+ }
+
+ /**
+ * Return whether a given ISBN (10 or 13) is valid.
+ *
+ * @param string $isbn ISBN passed for check
+ * @return bool
+ */
+ public static function isValidISBN( $isbn ) {
+ $isbn = self::cleanIsbn( $isbn );
+ $sum = 0;
+ if ( strlen( $isbn ) == 13 ) {
+ for ( $i = 0; $i < 12; $i++ ) {
+ if ( $isbn[$i] === 'X' ) {
+ return false;
+ } elseif ( $i % 2 == 0 ) {
+ $sum += $isbn[$i];
+ } else {
+ $sum += 3 * $isbn[$i];
+ }
+ }
+
+ $check = ( 10 - ( $sum % 10 ) ) % 10;
+ if ( (string)$check === $isbn[12] ) {
+ return true;
+ }
+ } elseif ( strlen( $isbn ) == 10 ) {
+ for ( $i = 0; $i < 9; $i++ ) {
+ if ( $isbn[$i] === 'X' ) {
+ return false;
+ }
+ $sum += $isbn[$i] * ( $i + 1 );
+ }
+
+ $check = $sum % 11;
+ if ( $check == 10 ) {
+ $check = "X";
+ }
+ if ( (string)$check === $isbn[9] ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Trim ISBN and remove characters which aren't required
+ *
+ * @param string $isbn Unclean ISBN
+ * @return string
+ */
+ private static function cleanIsbn( $isbn ) {
+ return trim( preg_replace( '![^0-9X]!', '', $isbn ) );
+ }
+
+ /**
+ * Generate a form to allow users to enter an ISBN
+ *
+ * @param string $isbn
+ */
+ private function buildForm( $isbn ) {
+ $formDescriptor = [
+ 'isbn' => [
+ 'type' => 'text',
+ 'name' => 'isbn',
+ 'label-message' => 'booksources-isbn',
+ 'default' => $isbn,
+ 'autofocus' => true,
+ 'required' => true,
+ ],
+ ];
+
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getPageTitle() );
+ HTMLForm::factory( 'ooui', $formDescriptor, $context )
+ ->setWrapperLegendMsg( 'booksources-search-legend' )
+ ->setSubmitTextMsg( 'booksources-search' )
+ ->setMethod( 'get' )
+ ->prepareForm()
+ ->displayForm( false );
+ }
+
+ /**
+ * Determine where to get the list of book sources from,
+ * format and output them
+ *
+ * @param string $isbn
+ * @throws MWException
+ * @return bool
+ */
+ private function showList( $isbn ) {
+ $out = $this->getOutput();
+
+ $isbn = self::cleanIsbn( $isbn );
+ # Hook to allow extensions to insert additional HTML,
+ # e.g. for API-interacting plugins and so on
+ Hooks::run( 'BookInformation', [ $isbn, $out ] );
+
+ # Check for a local page such as Project:Book_sources and use that if available
+ $page = $this->msg( 'booksources' )->inContentLanguage()->text();
+ $title = Title::makeTitleSafe( NS_PROJECT, $page ); # Show list in content language
+ if ( is_object( $title ) && $title->exists() ) {
+ $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
+ $content = $rev->getContent();
+
+ if ( $content instanceof TextContent ) {
+ // XXX: in the future, this could be stored as structured data, defining a list of book sources
+
+ $text = $content->getText();
+ $out->addWikiTextAsInterface( str_replace( 'MAGICNUMBER', $isbn, $text ) );
+
+ return true;
+ } else {
+ throw new MWException( "Unexpected content type for book sources: " . $content->getModel() );
+ }
+ }
+
+ # Fall back to the defaults given in the language file
+ $out->addWikiMsg( 'booksources-text' );
+ $out->addHTML( '<ul>' );
+ $items = MediaWikiServices::getInstance()->getContentLanguage()->getBookstoreList();
+ foreach ( $items as $label => $url ) {
+ $out->addHTML( $this->makeListItem( $isbn, $label, $url ) );
+ }
+ $out->addHTML( '</ul>' );
+
+ return true;
+ }
+
+ /**
+ * Format a book source list item
+ *
+ * @param string $isbn
+ * @param string $label Book source label
+ * @param string $url Book source URL
+ * @return string
+ */
+ private function makeListItem( $isbn, $label, $url ) {
+ $url = str_replace( '$1', $isbn, $url );
+
+ return Html::rawElement( 'li', [],
+ Html::element( 'a', [ 'href' => $url, 'class' => 'external' ], $label )
+ );
+ }
+
+ protected function getGroupName() {
+ return 'wiki';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Booksources
- *
- * 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 outputs information on sourcing a book with a particular ISBN
- * The parser creates links to this page when dealing with ISBNs in wikitext
- *
- * @author Rob Church <robchur@gmail.com>
- * @ingroup SpecialPage
- */
-class SpecialBookSources extends SpecialPage {
- public function __construct() {
- parent::__construct( 'Booksources' );
- }
-
- /**
- * @param string|null $isbn ISBN passed as a subpage parameter
- */
- public function execute( $isbn ) {
- $out = $this->getOutput();
-
- $this->setHeaders();
- $this->outputHeader();
-
- // User provided ISBN
- $isbn = $isbn ?: $this->getRequest()->getText( 'isbn' );
- $isbn = trim( $isbn );
-
- $this->buildForm( $isbn );
-
- if ( $isbn !== '' ) {
- if ( !self::isValidISBN( $isbn ) ) {
- $out->wrapWikiMsg(
- "<div class=\"error\">\n$1\n</div>",
- 'booksources-invalid-isbn'
- );
- }
-
- $this->showList( $isbn );
- }
- }
-
- /**
- * Return whether a given ISBN (10 or 13) is valid.
- *
- * @param string $isbn ISBN passed for check
- * @return bool
- */
- public static function isValidISBN( $isbn ) {
- $isbn = self::cleanIsbn( $isbn );
- $sum = 0;
- if ( strlen( $isbn ) == 13 ) {
- for ( $i = 0; $i < 12; $i++ ) {
- if ( $isbn[$i] === 'X' ) {
- return false;
- } elseif ( $i % 2 == 0 ) {
- $sum += $isbn[$i];
- } else {
- $sum += 3 * $isbn[$i];
- }
- }
-
- $check = ( 10 - ( $sum % 10 ) ) % 10;
- if ( (string)$check === $isbn[12] ) {
- return true;
- }
- } elseif ( strlen( $isbn ) == 10 ) {
- for ( $i = 0; $i < 9; $i++ ) {
- if ( $isbn[$i] === 'X' ) {
- return false;
- }
- $sum += $isbn[$i] * ( $i + 1 );
- }
-
- $check = $sum % 11;
- if ( $check == 10 ) {
- $check = "X";
- }
- if ( (string)$check === $isbn[9] ) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Trim ISBN and remove characters which aren't required
- *
- * @param string $isbn Unclean ISBN
- * @return string
- */
- private static function cleanIsbn( $isbn ) {
- return trim( preg_replace( '![^0-9X]!', '', $isbn ) );
- }
-
- /**
- * Generate a form to allow users to enter an ISBN
- *
- * @param string $isbn
- */
- private function buildForm( $isbn ) {
- $formDescriptor = [
- 'isbn' => [
- 'type' => 'text',
- 'name' => 'isbn',
- 'label-message' => 'booksources-isbn',
- 'default' => $isbn,
- 'autofocus' => true,
- 'required' => true,
- ],
- ];
-
- $context = new DerivativeContext( $this->getContext() );
- $context->setTitle( $this->getPageTitle() );
- HTMLForm::factory( 'ooui', $formDescriptor, $context )
- ->setWrapperLegendMsg( 'booksources-search-legend' )
- ->setSubmitTextMsg( 'booksources-search' )
- ->setMethod( 'get' )
- ->prepareForm()
- ->displayForm( false );
- }
-
- /**
- * Determine where to get the list of book sources from,
- * format and output them
- *
- * @param string $isbn
- * @throws MWException
- * @return bool
- */
- private function showList( $isbn ) {
- $out = $this->getOutput();
-
- $isbn = self::cleanIsbn( $isbn );
- # Hook to allow extensions to insert additional HTML,
- # e.g. for API-interacting plugins and so on
- Hooks::run( 'BookInformation', [ $isbn, $out ] );
-
- # Check for a local page such as Project:Book_sources and use that if available
- $page = $this->msg( 'booksources' )->inContentLanguage()->text();
- $title = Title::makeTitleSafe( NS_PROJECT, $page ); # Show list in content language
- if ( is_object( $title ) && $title->exists() ) {
- $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
- $content = $rev->getContent();
-
- if ( $content instanceof TextContent ) {
- // XXX: in the future, this could be stored as structured data, defining a list of book sources
-
- $text = $content->getText();
- $out->addWikiTextAsInterface( str_replace( 'MAGICNUMBER', $isbn, $text ) );
-
- return true;
- } else {
- throw new MWException( "Unexpected content type for book sources: " . $content->getModel() );
- }
- }
-
- # Fall back to the defaults given in the language file
- $out->addWikiMsg( 'booksources-text' );
- $out->addHTML( '<ul>' );
- $items = MediaWikiServices::getInstance()->getContentLanguage()->getBookstoreList();
- foreach ( $items as $label => $url ) {
- $out->addHTML( $this->makeListItem( $isbn, $label, $url ) );
- }
- $out->addHTML( '</ul>' );
-
- return true;
- }
-
- /**
- * Format a book source list item
- *
- * @param string $isbn
- * @param string $label Book source label
- * @param string $url Book source URL
- * @return string
- */
- private function makeListItem( $isbn, $label, $url ) {
- $url = str_replace( '$1', $isbn, $url );
-
- return Html::rawElement( 'li', [],
- Html::element( 'a', [ 'href' => $url, 'class' => 'external' ], $label )
- );
- }
-
- protected function getGroupName() {
- return 'wiki';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Emailuser
+ *
+ * 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 MediaWiki\Preferences\MultiUsernameFilter;
+
+/**
+ * A special page that allows users to send e-mails to other users
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialEmailUser extends UnlistedSpecialPage {
+ protected $mTarget;
+
+ /**
+ * @var User|string $mTargetObj
+ */
+ protected $mTargetObj;
+
+ public function __construct() {
+ parent::__construct( 'Emailuser' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function getDescription() {
+ $target = self::getTarget( $this->mTarget, $this->getUser() );
+ if ( !$target instanceof User ) {
+ return $this->msg( 'emailuser-title-notarget' )->text();
+ }
+
+ return $this->msg( 'emailuser-title-target', $target->getName() )->text();
+ }
+
+ protected function getFormFields() {
+ $linkRenderer = $this->getLinkRenderer();
+ return [
+ 'From' => [
+ 'type' => 'info',
+ 'raw' => 1,
+ 'default' => $linkRenderer->makeLink(
+ $this->getUser()->getUserPage(),
+ $this->getUser()->getName()
+ ),
+ 'label-message' => 'emailfrom',
+ 'id' => 'mw-emailuser-sender',
+ ],
+ 'To' => [
+ 'type' => 'info',
+ 'raw' => 1,
+ 'default' => $linkRenderer->makeLink(
+ $this->mTargetObj->getUserPage(),
+ $this->mTargetObj->getName()
+ ),
+ 'label-message' => 'emailto',
+ 'id' => 'mw-emailuser-recipient',
+ ],
+ 'Target' => [
+ 'type' => 'hidden',
+ 'default' => $this->mTargetObj->getName(),
+ ],
+ 'Subject' => [
+ 'type' => 'text',
+ 'default' => $this->msg( 'defemailsubject',
+ $this->getUser()->getName() )->inContentLanguage()->text(),
+ 'label-message' => 'emailsubject',
+ 'maxlength' => 200,
+ 'size' => 60,
+ 'required' => true,
+ ],
+ 'Text' => [
+ 'type' => 'textarea',
+ 'rows' => 20,
+ 'label-message' => 'emailmessage',
+ 'required' => true,
+ ],
+ 'CCMe' => [
+ 'type' => 'check',
+ 'label-message' => 'emailccme',
+ 'default' => $this->getUser()->getBoolOption( 'ccmeonemails' ),
+ ],
+ ];
+ }
+
+ public function execute( $par ) {
+ $out = $this->getOutput();
+ $request = $this->getRequest();
+ $out->addModuleStyles( 'mediawiki.special' );
+
+ $this->mTarget = $par ?? $request->getVal( 'wpTarget', $request->getVal( 'target', '' ) );
+
+ // Make sure, that HTMLForm uses the correct target.
+ $request->setVal( 'wpTarget', $this->mTarget );
+
+ // This needs to be below assignment of $this->mTarget because
+ // getDescription() needs it to determine the correct page title.
+ $this->setHeaders();
+ $this->outputHeader();
+
+ // error out if sending user cannot do this
+ $error = self::getPermissionsError(
+ $this->getUser(),
+ $this->getRequest()->getVal( 'wpEditToken' ),
+ $this->getConfig()
+ );
+
+ switch ( $error ) {
+ case null:
+ # Wahey!
+ break;
+ case 'badaccess':
+ throw new PermissionsError( 'sendemail' );
+ case 'blockedemailuser':
+ throw $this->getBlockedEmailError();
+ case 'actionthrottledtext':
+ throw new ThrottledError;
+ case 'mailnologin':
+ case 'usermaildisabled':
+ throw new ErrorPageError( $error, "{$error}text" );
+ default:
+ # It's a hook error
+ list( $title, $msg, $params ) = $error;
+ throw new ErrorPageError( $title, $msg, $params );
+ }
+
+ // Make sure, that a submitted form isn't submitted to a subpage (which could be
+ // a non-existing username)
+ $context = new DerivativeContext( $this->getContext() );
+ $context->setTitle( $this->getPageTitle() ); // Remove subpage
+ $this->setContext( $context );
+
+ // A little hack: HTMLForm will check $this->mTarget only, if the form was posted, not
+ // if the user opens Special:EmailUser/Florian (e.g.). So check, if the user did that
+ // and show the "Send email to user" form directly, if so. Show the "enter username"
+ // form, otherwise.
+ $this->mTargetObj = self::getTarget( $this->mTarget, $this->getUser() );
+ if ( !$this->mTargetObj instanceof User ) {
+ $this->userForm( $this->mTarget );
+ } else {
+ $this->sendEmailForm();
+ }
+ }
+
+ /**
+ * Validate target User
+ *
+ * @param string $target Target user name
+ * @param User|null $sender User sending the email
+ * @return User|string User object on success or a string on error
+ */
+ public static function getTarget( $target, User $sender = null ) {
+ if ( $sender === null ) {
+ wfDeprecated( __METHOD__ . ' without specifying the sending user', '1.30' );
+ }
+
+ if ( $target == '' ) {
+ wfDebug( "Target is empty.\n" );
+
+ return 'notarget';
+ }
+
+ $nu = User::newFromName( $target );
+ $error = self::validateTarget( $nu, $sender );
+
+ return $error ?: $nu;
+ }
+
+ /**
+ * Validate target User
+ *
+ * @param User $target Target user
+ * @param User|null $sender User sending the email
+ * @return string Error message or empty string if valid.
+ * @since 1.30
+ */
+ public static function validateTarget( $target, User $sender = null ) {
+ if ( $sender === null ) {
+ wfDeprecated( __METHOD__ . ' without specifying the sending user', '1.30' );
+ }
+
+ if ( !$target instanceof User || !$target->getId() ) {
+ wfDebug( "Target is invalid user.\n" );
+
+ return 'notarget';
+ }
+
+ if ( !$target->isEmailConfirmed() ) {
+ wfDebug( "User has no valid email.\n" );
+
+ return 'noemail';
+ }
+
+ if ( !$target->canReceiveEmail() ) {
+ wfDebug( "User does not allow user emails.\n" );
+
+ return 'nowikiemail';
+ }
+
+ if ( $sender !== null && !$target->getOption( 'email-allow-new-users' ) &&
+ $sender->isNewbie()
+ ) {
+ wfDebug( "User does not allow user emails from new users.\n" );
+
+ return 'nowikiemail';
+ }
+
+ if ( $sender !== null ) {
+ $blacklist = $target->getOption( 'email-blacklist', '' );
+ if ( $blacklist ) {
+ $blacklist = MultiUsernameFilter::splitIds( $blacklist );
+ $lookup = CentralIdLookup::factory();
+ $senderId = $lookup->centralIdFromLocalUser( $sender );
+ if ( $senderId !== 0 && in_array( $senderId, $blacklist ) ) {
+ wfDebug( "User does not allow user emails from this user.\n" );
+
+ return 'nowikiemail';
+ }
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Check whether a user is allowed to send email
+ *
+ * @param User $user
+ * @param string $editToken Edit token
+ * @param Config|null $config optional for backwards compatibility
+ * @return null|string|array Null on success, string on error, or array on
+ * hook error
+ */
+ public static function getPermissionsError( $user, $editToken, Config $config = null ) {
+ if ( $config === null ) {
+ wfDebug( __METHOD__ . ' called without a Config instance passed to it' );
+ $config = MediaWikiServices::getInstance()->getMainConfig();
+ }
+ if ( !$config->get( 'EnableEmail' ) || !$config->get( 'EnableUserEmail' ) ) {
+ return 'usermaildisabled';
+ }
+
+ // Run this before $user->isAllowed, to show appropriate message to anons (T160309)
+ if ( !$user->isEmailConfirmed() ) {
+ return 'mailnologin';
+ }
+
+ if ( !$user->isAllowed( 'sendemail' ) ) {
+ return 'badaccess';
+ }
+
+ if ( $user->isBlockedFromEmailuser() ) {
+ wfDebug( "User is blocked from sending e-mail.\n" );
+
+ return "blockedemailuser";
+ }
+
+ // Check the ping limiter without incrementing it - we'll check it
+ // again later and increment it on a successful send
+ if ( $user->pingLimiter( 'emailuser', 0 ) ) {
+ wfDebug( "Ping limiter triggered.\n" );
+
+ return 'actionthrottledtext';
+ }
+
+ $hookErr = false;
+
+ Hooks::run( 'UserCanSendEmail', [ &$user, &$hookErr ] );
+ Hooks::run( 'EmailUserPermissionsErrors', [ $user, $editToken, &$hookErr ] );
+
+ if ( $hookErr ) {
+ return $hookErr;
+ }
+
+ return null;
+ }
+
+ /**
+ * Form to ask for target user name.
+ *
+ * @param string $name User name submitted.
+ */
+ protected function userForm( $name ) {
+ $htmlForm = HTMLForm::factory( 'ooui', [
+ 'Target' => [
+ 'type' => 'user',
+ 'exists' => true,
+ 'label' => $this->msg( 'emailusername' )->text(),
+ 'id' => 'emailusertarget',
+ 'autofocus' => true,
+ 'value' => $name,
+ ]
+ ], $this->getContext() );
+
+ $htmlForm
+ ->setMethod( 'post' )
+ ->setSubmitCallback( [ $this, 'sendEmailForm' ] )
+ ->setFormIdentifier( 'userForm' )
+ ->setId( 'askusername' )
+ ->setWrapperLegendMsg( 'emailtarget' )
+ ->setSubmitTextMsg( 'emailusernamesubmit' )
+ ->show();
+ }
+
+ public function sendEmailForm() {
+ $out = $this->getOutput();
+
+ $ret = $this->mTargetObj;
+ if ( !$ret instanceof User ) {
+ if ( $this->mTarget != '' ) {
+ // Messages used here: notargettext, noemailtext, nowikiemailtext
+ $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
+ return Status::newFatal( $ret );
+ }
+ return false;
+ }
+
+ $htmlForm = HTMLForm::factory( 'ooui', $this->getFormFields(), $this->getContext() );
+ // By now we are supposed to be sure that $this->mTarget is a user name
+ $htmlForm
+ ->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() )
+ ->setSubmitTextMsg( 'emailsend' )
+ ->setSubmitCallback( [ __CLASS__, 'submit' ] )
+ ->setFormIdentifier( 'sendEmailForm' )
+ ->setWrapperLegendMsg( 'email-legend' )
+ ->loadData();
+
+ if ( !Hooks::run( 'EmailUserForm', [ &$htmlForm ] ) ) {
+ return false;
+ }
+
+ $result = $htmlForm->show();
+
+ if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
+ $out->setPageTitle( $this->msg( 'emailsent' ) );
+ $out->addWikiMsg( 'emailsenttext', $this->mTarget );
+ $out->returnToMain( false, $ret->getUserPage() );
+ }
+ return true;
+ }
+
+ /**
+ * Really send a mail. Permissions should have been checked using
+ * getPermissionsError(). It is probably also a good
+ * idea to check the edit token and ping limiter in advance.
+ *
+ * @param array $data
+ * @param IContextSource $context
+ * @return Status|bool
+ */
+ public static function submit( array $data, IContextSource $context ) {
+ $config = $context->getConfig();
+
+ $target = self::getTarget( $data['Target'], $context->getUser() );
+ if ( !$target instanceof User ) {
+ // Messages used here: notargettext, noemailtext, nowikiemailtext
+ return Status::newFatal( $target . 'text' );
+ }
+
+ $to = MailAddress::newFromUser( $target );
+ $from = MailAddress::newFromUser( $context->getUser() );
+ $subject = $data['Subject'];
+ $text = $data['Text'];
+
+ // Add a standard footer and trim up trailing newlines
+ $text = rtrim( $text ) . "\n\n-- \n";
+ $text .= $context->msg( 'emailuserfooter',
+ $from->name, $to->name )->inContentLanguage()->text();
+
+ // Check and increment the rate limits
+ if ( $context->getUser()->pingLimiter( 'emailuser' ) ) {
+ throw new ThrottledError();
+ }
+
+ $error = false;
+ if ( !Hooks::run( 'EmailUser', [ &$to, &$from, &$subject, &$text, &$error ] ) ) {
+ if ( $error instanceof Status ) {
+ return $error;
+ } elseif ( $error === false || $error === '' || $error === [] ) {
+ // Possibly to tell HTMLForm to pretend there was no submission?
+ return false;
+ } elseif ( $error === true ) {
+ // Hook sent the mail itself and indicates success?
+ return Status::newGood();
+ } elseif ( is_array( $error ) ) {
+ $status = Status::newGood();
+ foreach ( $error as $e ) {
+ $status->fatal( $e );
+ }
+ return $status;
+ } elseif ( $error instanceof MessageSpecifier ) {
+ return Status::newFatal( $error );
+ } else {
+ // Ugh. Either a raw HTML string, or something that's supposed
+ // to be treated like one.
+ $type = is_object( $error ) ? get_class( $error ) : gettype( $error );
+ wfDeprecated( "EmailUser hook returning a $type as \$error", '1.29' );
+ return Status::newFatal( new ApiRawMessage(
+ [ '$1', Message::rawParam( (string)$error ) ], 'hookaborted'
+ ) );
+ }
+ }
+
+ if ( $config->get( 'UserEmailUseReplyTo' ) ) {
+ /**
+ * Put the generic wiki autogenerated address in the From:
+ * header and reserve the user for Reply-To.
+ *
+ * This is a bit ugly, but will serve to differentiate
+ * wiki-borne mails from direct mails and protects against
+ * SPF and bounce problems with some mailers (see below).
+ */
+ $mailFrom = new MailAddress( $config->get( 'PasswordSender' ),
+ $context->msg( 'emailsender' )->inContentLanguage()->text() );
+ $replyTo = $from;
+ } else {
+ /**
+ * Put the sending user's e-mail address in the From: header.
+ *
+ * This is clean-looking and convenient, but has issues.
+ * One is that it doesn't as clearly differentiate the wiki mail
+ * from "directly" sent mails.
+ *
+ * Another is that some mailers (like sSMTP) will use the From
+ * address as the envelope sender as well. For open sites this
+ * can cause mails to be flunked for SPF violations (since the
+ * wiki server isn't an authorized sender for various users'
+ * domains) as well as creating a privacy issue as bounces
+ * containing the recipient's e-mail address may get sent to
+ * the sending user.
+ */
+ $mailFrom = $from;
+ $replyTo = null;
+ }
+
+ $status = UserMailer::send( $to, $mailFrom, $subject, $text, [
+ 'replyTo' => $replyTo,
+ ] );
+
+ if ( !$status->isGood() ) {
+ return $status;
+ } else {
+ // if the user requested a copy of this mail, do this now,
+ // unless they are emailing themselves, in which case one
+ // copy of the message is sufficient.
+ if ( $data['CCMe'] && $to != $from ) {
+ $ccTo = $from;
+ $ccFrom = $from;
+ $ccSubject = $context->msg( 'emailccsubject' )->plaintextParams(
+ $target->getName(), $subject )->text();
+ $ccText = $text;
+
+ Hooks::run( 'EmailUserCC', [ &$ccTo, &$ccFrom, &$ccSubject, &$ccText ] );
+
+ if ( $config->get( 'UserEmailUseReplyTo' ) ) {
+ $mailFrom = new MailAddress(
+ $config->get( 'PasswordSender' ),
+ $context->msg( 'emailsender' )->inContentLanguage()->text()
+ );
+ $replyTo = $ccFrom;
+ } else {
+ $mailFrom = $ccFrom;
+ $replyTo = null;
+ }
+
+ $ccStatus = UserMailer::send(
+ $ccTo, $mailFrom, $ccSubject, $ccText, [
+ 'replyTo' => $replyTo,
+ ] );
+ $status->merge( $ccStatus );
+ }
+
+ Hooks::run( 'EmailUserComplete', [ $to, $from, $subject, $text ] );
+
+ return $status;
+ }
+ }
+
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ $user = User::newFromName( $search );
+ if ( !$user ) {
+ // No prefix suggestion for invalid user
+ return [];
+ }
+ // Autocomplete subpage as user list - public to allow caching
+ return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+
+ /**
+ * Builds an error message based on the block params
+ *
+ * @return ErrorPageError
+ */
+ private function getBlockedEmailError() {
+ $block = $this->getUser()->mBlock;
+ $params = $block->getBlockErrorParams( $this->getContext() );
+
+ $msg = $block->isSitewide() ? 'blockedtext' : 'blocked-email-user';
+ return new ErrorPageError( 'blockedtitle', $msg, $params );
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Emailuser
- *
- * 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 MediaWiki\Preferences\MultiUsernameFilter;
-
-/**
- * A special page that allows users to send e-mails to other users
- *
- * @ingroup SpecialPage
- */
-class SpecialEmailUser extends UnlistedSpecialPage {
- protected $mTarget;
-
- /**
- * @var User|string $mTargetObj
- */
- protected $mTargetObj;
-
- public function __construct() {
- parent::__construct( 'Emailuser' );
- }
-
- public function doesWrites() {
- return true;
- }
-
- public function getDescription() {
- $target = self::getTarget( $this->mTarget, $this->getUser() );
- if ( !$target instanceof User ) {
- return $this->msg( 'emailuser-title-notarget' )->text();
- }
-
- return $this->msg( 'emailuser-title-target', $target->getName() )->text();
- }
-
- protected function getFormFields() {
- $linkRenderer = $this->getLinkRenderer();
- return [
- 'From' => [
- 'type' => 'info',
- 'raw' => 1,
- 'default' => $linkRenderer->makeLink(
- $this->getUser()->getUserPage(),
- $this->getUser()->getName()
- ),
- 'label-message' => 'emailfrom',
- 'id' => 'mw-emailuser-sender',
- ],
- 'To' => [
- 'type' => 'info',
- 'raw' => 1,
- 'default' => $linkRenderer->makeLink(
- $this->mTargetObj->getUserPage(),
- $this->mTargetObj->getName()
- ),
- 'label-message' => 'emailto',
- 'id' => 'mw-emailuser-recipient',
- ],
- 'Target' => [
- 'type' => 'hidden',
- 'default' => $this->mTargetObj->getName(),
- ],
- 'Subject' => [
- 'type' => 'text',
- 'default' => $this->msg( 'defemailsubject',
- $this->getUser()->getName() )->inContentLanguage()->text(),
- 'label-message' => 'emailsubject',
- 'maxlength' => 200,
- 'size' => 60,
- 'required' => true,
- ],
- 'Text' => [
- 'type' => 'textarea',
- 'rows' => 20,
- 'label-message' => 'emailmessage',
- 'required' => true,
- ],
- 'CCMe' => [
- 'type' => 'check',
- 'label-message' => 'emailccme',
- 'default' => $this->getUser()->getBoolOption( 'ccmeonemails' ),
- ],
- ];
- }
-
- public function execute( $par ) {
- $out = $this->getOutput();
- $request = $this->getRequest();
- $out->addModuleStyles( 'mediawiki.special' );
-
- $this->mTarget = $par ?? $request->getVal( 'wpTarget', $request->getVal( 'target', '' ) );
-
- // Make sure, that HTMLForm uses the correct target.
- $request->setVal( 'wpTarget', $this->mTarget );
-
- // This needs to be below assignment of $this->mTarget because
- // getDescription() needs it to determine the correct page title.
- $this->setHeaders();
- $this->outputHeader();
-
- // error out if sending user cannot do this
- $error = self::getPermissionsError(
- $this->getUser(),
- $this->getRequest()->getVal( 'wpEditToken' ),
- $this->getConfig()
- );
-
- switch ( $error ) {
- case null:
- # Wahey!
- break;
- case 'badaccess':
- throw new PermissionsError( 'sendemail' );
- case 'blockedemailuser':
- throw $this->getBlockedEmailError();
- case 'actionthrottledtext':
- throw new ThrottledError;
- case 'mailnologin':
- case 'usermaildisabled':
- throw new ErrorPageError( $error, "{$error}text" );
- default:
- # It's a hook error
- list( $title, $msg, $params ) = $error;
- throw new ErrorPageError( $title, $msg, $params );
- }
-
- // Make sure, that a submitted form isn't submitted to a subpage (which could be
- // a non-existing username)
- $context = new DerivativeContext( $this->getContext() );
- $context->setTitle( $this->getPageTitle() ); // Remove subpage
- $this->setContext( $context );
-
- // A little hack: HTMLForm will check $this->mTarget only, if the form was posted, not
- // if the user opens Special:EmailUser/Florian (e.g.). So check, if the user did that
- // and show the "Send email to user" form directly, if so. Show the "enter username"
- // form, otherwise.
- $this->mTargetObj = self::getTarget( $this->mTarget, $this->getUser() );
- if ( !$this->mTargetObj instanceof User ) {
- $this->userForm( $this->mTarget );
- } else {
- $this->sendEmailForm();
- }
- }
-
- /**
- * Validate target User
- *
- * @param string $target Target user name
- * @param User|null $sender User sending the email
- * @return User|string User object on success or a string on error
- */
- public static function getTarget( $target, User $sender = null ) {
- if ( $sender === null ) {
- wfDeprecated( __METHOD__ . ' without specifying the sending user', '1.30' );
- }
-
- if ( $target == '' ) {
- wfDebug( "Target is empty.\n" );
-
- return 'notarget';
- }
-
- $nu = User::newFromName( $target );
- $error = self::validateTarget( $nu, $sender );
-
- return $error ?: $nu;
- }
-
- /**
- * Validate target User
- *
- * @param User $target Target user
- * @param User|null $sender User sending the email
- * @return string Error message or empty string if valid.
- * @since 1.30
- */
- public static function validateTarget( $target, User $sender = null ) {
- if ( $sender === null ) {
- wfDeprecated( __METHOD__ . ' without specifying the sending user', '1.30' );
- }
-
- if ( !$target instanceof User || !$target->getId() ) {
- wfDebug( "Target is invalid user.\n" );
-
- return 'notarget';
- }
-
- if ( !$target->isEmailConfirmed() ) {
- wfDebug( "User has no valid email.\n" );
-
- return 'noemail';
- }
-
- if ( !$target->canReceiveEmail() ) {
- wfDebug( "User does not allow user emails.\n" );
-
- return 'nowikiemail';
- }
-
- if ( $sender !== null && !$target->getOption( 'email-allow-new-users' ) &&
- $sender->isNewbie()
- ) {
- wfDebug( "User does not allow user emails from new users.\n" );
-
- return 'nowikiemail';
- }
-
- if ( $sender !== null ) {
- $blacklist = $target->getOption( 'email-blacklist', '' );
- if ( $blacklist ) {
- $blacklist = MultiUsernameFilter::splitIds( $blacklist );
- $lookup = CentralIdLookup::factory();
- $senderId = $lookup->centralIdFromLocalUser( $sender );
- if ( $senderId !== 0 && in_array( $senderId, $blacklist ) ) {
- wfDebug( "User does not allow user emails from this user.\n" );
-
- return 'nowikiemail';
- }
- }
- }
-
- return '';
- }
-
- /**
- * Check whether a user is allowed to send email
- *
- * @param User $user
- * @param string $editToken Edit token
- * @param Config|null $config optional for backwards compatibility
- * @return null|string|array Null on success, string on error, or array on
- * hook error
- */
- public static function getPermissionsError( $user, $editToken, Config $config = null ) {
- if ( $config === null ) {
- wfDebug( __METHOD__ . ' called without a Config instance passed to it' );
- $config = MediaWikiServices::getInstance()->getMainConfig();
- }
- if ( !$config->get( 'EnableEmail' ) || !$config->get( 'EnableUserEmail' ) ) {
- return 'usermaildisabled';
- }
-
- // Run this before $user->isAllowed, to show appropriate message to anons (T160309)
- if ( !$user->isEmailConfirmed() ) {
- return 'mailnologin';
- }
-
- if ( !$user->isAllowed( 'sendemail' ) ) {
- return 'badaccess';
- }
-
- if ( $user->isBlockedFromEmailuser() ) {
- wfDebug( "User is blocked from sending e-mail.\n" );
-
- return "blockedemailuser";
- }
-
- // Check the ping limiter without incrementing it - we'll check it
- // again later and increment it on a successful send
- if ( $user->pingLimiter( 'emailuser', 0 ) ) {
- wfDebug( "Ping limiter triggered.\n" );
-
- return 'actionthrottledtext';
- }
-
- $hookErr = false;
-
- Hooks::run( 'UserCanSendEmail', [ &$user, &$hookErr ] );
- Hooks::run( 'EmailUserPermissionsErrors', [ $user, $editToken, &$hookErr ] );
-
- if ( $hookErr ) {
- return $hookErr;
- }
-
- return null;
- }
-
- /**
- * Form to ask for target user name.
- *
- * @param string $name User name submitted.
- */
- protected function userForm( $name ) {
- $htmlForm = HTMLForm::factory( 'ooui', [
- 'Target' => [
- 'type' => 'user',
- 'exists' => true,
- 'label' => $this->msg( 'emailusername' )->text(),
- 'id' => 'emailusertarget',
- 'autofocus' => true,
- 'value' => $name,
- ]
- ], $this->getContext() );
-
- $htmlForm
- ->setMethod( 'post' )
- ->setSubmitCallback( [ $this, 'sendEmailForm' ] )
- ->setFormIdentifier( 'userForm' )
- ->setId( 'askusername' )
- ->setWrapperLegendMsg( 'emailtarget' )
- ->setSubmitTextMsg( 'emailusernamesubmit' )
- ->show();
- }
-
- public function sendEmailForm() {
- $out = $this->getOutput();
-
- $ret = $this->mTargetObj;
- if ( !$ret instanceof User ) {
- if ( $this->mTarget != '' ) {
- // Messages used here: notargettext, noemailtext, nowikiemailtext
- $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
- return Status::newFatal( $ret );
- }
- return false;
- }
-
- $htmlForm = HTMLForm::factory( 'ooui', $this->getFormFields(), $this->getContext() );
- // By now we are supposed to be sure that $this->mTarget is a user name
- $htmlForm
- ->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() )
- ->setSubmitTextMsg( 'emailsend' )
- ->setSubmitCallback( [ __CLASS__, 'submit' ] )
- ->setFormIdentifier( 'sendEmailForm' )
- ->setWrapperLegendMsg( 'email-legend' )
- ->loadData();
-
- if ( !Hooks::run( 'EmailUserForm', [ &$htmlForm ] ) ) {
- return false;
- }
-
- $result = $htmlForm->show();
-
- if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
- $out->setPageTitle( $this->msg( 'emailsent' ) );
- $out->addWikiMsg( 'emailsenttext', $this->mTarget );
- $out->returnToMain( false, $ret->getUserPage() );
- }
- return true;
- }
-
- /**
- * Really send a mail. Permissions should have been checked using
- * getPermissionsError(). It is probably also a good
- * idea to check the edit token and ping limiter in advance.
- *
- * @param array $data
- * @param IContextSource $context
- * @return Status|bool
- */
- public static function submit( array $data, IContextSource $context ) {
- $config = $context->getConfig();
-
- $target = self::getTarget( $data['Target'], $context->getUser() );
- if ( !$target instanceof User ) {
- // Messages used here: notargettext, noemailtext, nowikiemailtext
- return Status::newFatal( $target . 'text' );
- }
-
- $to = MailAddress::newFromUser( $target );
- $from = MailAddress::newFromUser( $context->getUser() );
- $subject = $data['Subject'];
- $text = $data['Text'];
-
- // Add a standard footer and trim up trailing newlines
- $text = rtrim( $text ) . "\n\n-- \n";
- $text .= $context->msg( 'emailuserfooter',
- $from->name, $to->name )->inContentLanguage()->text();
-
- // Check and increment the rate limits
- if ( $context->getUser()->pingLimiter( 'emailuser' ) ) {
- throw new ThrottledError();
- }
-
- $error = false;
- if ( !Hooks::run( 'EmailUser', [ &$to, &$from, &$subject, &$text, &$error ] ) ) {
- if ( $error instanceof Status ) {
- return $error;
- } elseif ( $error === false || $error === '' || $error === [] ) {
- // Possibly to tell HTMLForm to pretend there was no submission?
- return false;
- } elseif ( $error === true ) {
- // Hook sent the mail itself and indicates success?
- return Status::newGood();
- } elseif ( is_array( $error ) ) {
- $status = Status::newGood();
- foreach ( $error as $e ) {
- $status->fatal( $e );
- }
- return $status;
- } elseif ( $error instanceof MessageSpecifier ) {
- return Status::newFatal( $error );
- } else {
- // Ugh. Either a raw HTML string, or something that's supposed
- // to be treated like one.
- $type = is_object( $error ) ? get_class( $error ) : gettype( $error );
- wfDeprecated( "EmailUser hook returning a $type as \$error", '1.29' );
- return Status::newFatal( new ApiRawMessage(
- [ '$1', Message::rawParam( (string)$error ) ], 'hookaborted'
- ) );
- }
- }
-
- if ( $config->get( 'UserEmailUseReplyTo' ) ) {
- /**
- * Put the generic wiki autogenerated address in the From:
- * header and reserve the user for Reply-To.
- *
- * This is a bit ugly, but will serve to differentiate
- * wiki-borne mails from direct mails and protects against
- * SPF and bounce problems with some mailers (see below).
- */
- $mailFrom = new MailAddress( $config->get( 'PasswordSender' ),
- $context->msg( 'emailsender' )->inContentLanguage()->text() );
- $replyTo = $from;
- } else {
- /**
- * Put the sending user's e-mail address in the From: header.
- *
- * This is clean-looking and convenient, but has issues.
- * One is that it doesn't as clearly differentiate the wiki mail
- * from "directly" sent mails.
- *
- * Another is that some mailers (like sSMTP) will use the From
- * address as the envelope sender as well. For open sites this
- * can cause mails to be flunked for SPF violations (since the
- * wiki server isn't an authorized sender for various users'
- * domains) as well as creating a privacy issue as bounces
- * containing the recipient's e-mail address may get sent to
- * the sending user.
- */
- $mailFrom = $from;
- $replyTo = null;
- }
-
- $status = UserMailer::send( $to, $mailFrom, $subject, $text, [
- 'replyTo' => $replyTo,
- ] );
-
- if ( !$status->isGood() ) {
- return $status;
- } else {
- // if the user requested a copy of this mail, do this now,
- // unless they are emailing themselves, in which case one
- // copy of the message is sufficient.
- if ( $data['CCMe'] && $to != $from ) {
- $ccTo = $from;
- $ccFrom = $from;
- $ccSubject = $context->msg( 'emailccsubject' )->plaintextParams(
- $target->getName(), $subject )->text();
- $ccText = $text;
-
- Hooks::run( 'EmailUserCC', [ &$ccTo, &$ccFrom, &$ccSubject, &$ccText ] );
-
- if ( $config->get( 'UserEmailUseReplyTo' ) ) {
- $mailFrom = new MailAddress(
- $config->get( 'PasswordSender' ),
- $context->msg( 'emailsender' )->inContentLanguage()->text()
- );
- $replyTo = $ccFrom;
- } else {
- $mailFrom = $ccFrom;
- $replyTo = null;
- }
-
- $ccStatus = UserMailer::send(
- $ccTo, $mailFrom, $ccSubject, $ccText, [
- 'replyTo' => $replyTo,
- ] );
- $status->merge( $ccStatus );
- }
-
- Hooks::run( 'EmailUserComplete', [ $to, $from, $subject, $text ] );
-
- return $status;
- }
- }
-
- /**
- * Return an array of subpages beginning with $search that this special page will accept.
- *
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return (usually 10)
- * @param int $offset Number of results to skip (usually 0)
- * @return string[] Matching subpages
- */
- public function prefixSearchSubpages( $search, $limit, $offset ) {
- $user = User::newFromName( $search );
- if ( !$user ) {
- // No prefix suggestion for invalid user
- return [];
- }
- // Autocomplete subpage as user list - public to allow caching
- return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
- }
-
- protected function getGroupName() {
- return 'users';
- }
-
- /**
- * Builds an error message based on the block params
- *
- * @return ErrorPageError
- */
- private function getBlockedEmailError() {
- $block = $this->getUser()->mBlock;
- $params = $block->getBlockErrorParams( $this->getContext() );
-
- $msg = $block->isSitewide() ? 'blockedtext' : 'blocked-email-user';
- return new ErrorPageError( 'blockedtitle', $msg, $params );
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Listfiles
+ *
+ * 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
+ */
+
+class SpecialListFiles extends IncludableSpecialPage {
+ public function __construct() {
+ parent::__construct( 'Listfiles' );
+ }
+
+ public function execute( $par ) {
+ $this->setHeaders();
+ $this->outputHeader();
+
+ if ( $this->including() ) {
+ $userName = $par;
+ $search = '';
+ $showAll = false;
+ } else {
+ $userName = $this->getRequest()->getText( 'user', $par );
+ $search = $this->getRequest()->getText( 'ilsearch', '' );
+ $showAll = $this->getRequest()->getBool( 'ilshowall', false );
+ }
+
+ $pager = new ImageListPager(
+ $this->getContext(),
+ $userName,
+ $search,
+ $this->including(),
+ $showAll
+ );
+
+ $out = $this->getOutput();
+ if ( $this->including() ) {
+ $out->addParserOutputContent( $pager->getBodyOutput() );
+ } else {
+ $user = $pager->getRelevantUser();
+ $this->getSkin()->setRelevantUser( $user );
+ $pager->getForm();
+ $out->addParserOutputContent( $pager->getFullOutput() );
+ }
+ }
+
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ $user = User::newFromName( $search );
+ if ( !$user ) {
+ // No prefix suggestion for invalid user
+ return [];
+ }
+ // Autocomplete subpage as user list - public to allow caching
+ return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+ }
+
+ protected function getGroupName() {
+ return 'media';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Listgrants
+ *
+ * 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
+ */
+
+/**
+ * This special page lists all defined rights grants and the associated rights.
+ * See also @ref $wgGrantPermissions and @ref $wgGrantPermissionGroups.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialListGrants extends SpecialPage {
+ function __construct() {
+ parent::__construct( 'Listgrants' );
+ }
+
+ /**
+ * Show the special page
+ * @param string|null $par
+ */
+ public function execute( $par ) {
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $out = $this->getOutput();
+ $out->addModuleStyles( 'mediawiki.special' );
+
+ $out->addHTML(
+ \Html::openElement( 'table',
+ [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
+ '<tr>' .
+ \Html::element( 'th', null, $this->msg( 'listgrants-grant' )->text() ) .
+ \Html::element( 'th', null, $this->msg( 'listgrants-rights' )->text() ) .
+ '</tr>'
+ );
+
+ foreach ( $this->getConfig()->get( 'GrantPermissions' ) as $grant => $rights ) {
+ $descs = [];
+ $rights = array_filter( $rights ); // remove ones with 'false'
+ foreach ( $rights as $permission => $granted ) {
+ $descs[] = $this->msg(
+ 'listgrouprights-right-display',
+ \User::getRightDescription( $permission ),
+ '<span class="mw-listgrants-right-name">' . $permission . '</span>'
+ )->parse();
+ }
+ if ( $descs === [] ) {
+ $grantCellHtml = '';
+ } else {
+ sort( $descs );
+ $grantCellHtml = '<ul><li>' . implode( "</li>\n<li>", $descs ) . '</li></ul>';
+ }
+
+ $id = Sanitizer::escapeIdForAttribute( $grant );
+ $out->addHTML( \Html::rawElement( 'tr', [ 'id' => $id ],
+ "<td>" .
+ $this->msg(
+ "listgrants-grant-display",
+ \User::getGrantName( $grant ),
+ "<span class='mw-listgrants-grant-name'>" . $id . "</span>"
+ )->parse() .
+ "</td>" .
+ "<td>" . $grantCellHtml . "</td>"
+ ) );
+ }
+
+ $out->addHTML( \Html::closeElement( 'table' ) );
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Listgrouprights
+ *
+ * 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;
+
+/**
+ * This special page lists all defined user groups and the associated rights.
+ * See also @ref $wgGroupPermissions.
+ *
+ * @ingroup SpecialPage
+ * @author Petr Kadlec <mormegil@centrum.cz>
+ */
+class SpecialListGroupRights extends SpecialPage {
+ public function __construct() {
+ parent::__construct( 'Listgrouprights' );
+ }
+
+ /**
+ * Show the special page
+ * @param string|null $par
+ */
+ public function execute( $par ) {
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $out = $this->getOutput();
+ $out->addModuleStyles( 'mediawiki.special' );
+
+ $out->wrapWikiMsg( "<div class=\"mw-listgrouprights-key\">\n$1\n</div>", 'listgrouprights-key' );
+
+ $out->addHTML(
+ Xml::openElement( 'table', [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
+ '<tr>' .
+ Xml::element( 'th', null, $this->msg( 'listgrouprights-group' )->text() ) .
+ Xml::element( 'th', null, $this->msg( 'listgrouprights-rights' )->text() ) .
+ '</tr>'
+ );
+
+ $config = $this->getConfig();
+ $groupPermissions = $config->get( 'GroupPermissions' );
+ $revokePermissions = $config->get( 'RevokePermissions' );
+ $addGroups = $config->get( 'AddGroups' );
+ $removeGroups = $config->get( 'RemoveGroups' );
+ $groupsAddToSelf = $config->get( 'GroupsAddToSelf' );
+ $groupsRemoveFromSelf = $config->get( 'GroupsRemoveFromSelf' );
+ $allGroups = array_unique( array_merge(
+ array_keys( $groupPermissions ),
+ array_keys( $revokePermissions ),
+ array_keys( $addGroups ),
+ array_keys( $removeGroups ),
+ array_keys( $groupsAddToSelf ),
+ array_keys( $groupsRemoveFromSelf )
+ ) );
+ asort( $allGroups );
+
+ $linkRenderer = $this->getLinkRenderer();
+
+ foreach ( $allGroups as $group ) {
+ $permissions = $groupPermissions[$group] ?? [];
+ $groupname = ( $group == '*' ) // Replace * with a more descriptive groupname
+ ? 'all'
+ : $group;
+
+ $groupnameLocalized = UserGroupMembership::getGroupName( $groupname );
+
+ $grouppageLocalizedTitle = UserGroupMembership::getGroupPage( $groupname )
+ ?: Title::newFromText( MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname );
+
+ if ( $group == '*' || !$grouppageLocalizedTitle ) {
+ // Do not make a link for the generic * group or group with invalid group page
+ $grouppage = htmlspecialchars( $groupnameLocalized );
+ } else {
+ $grouppage = $linkRenderer->makeLink(
+ $grouppageLocalizedTitle,
+ $groupnameLocalized
+ );
+ }
+
+ if ( $group === 'user' ) {
+ // Link to Special:listusers for implicit group 'user'
+ $grouplink = '<br />' . $linkRenderer->makeKnownLink(
+ SpecialPage::getTitleFor( 'Listusers' ),
+ $this->msg( 'listgrouprights-members' )->text()
+ );
+ } elseif ( !in_array( $group, $config->get( 'ImplicitGroups' ) ) ) {
+ $grouplink = '<br />' . $linkRenderer->makeKnownLink(
+ SpecialPage::getTitleFor( 'Listusers' ),
+ $this->msg( 'listgrouprights-members' )->text(),
+ [],
+ [ 'group' => $group ]
+ );
+ } else {
+ // No link to Special:listusers for other implicit groups as they are unlistable
+ $grouplink = '';
+ }
+
+ $revoke = $revokePermissions[$group] ?? [];
+ $addgroups = $addGroups[$group] ?? [];
+ $removegroups = $removeGroups[$group] ?? [];
+ $addgroupsSelf = $groupsAddToSelf[$group] ?? [];
+ $removegroupsSelf = $groupsRemoveFromSelf[$group] ?? [];
+
+ $id = $group == '*' ? false : Sanitizer::escapeIdForAttribute( $group );
+ $out->addHTML( Html::rawElement( 'tr', [ 'id' => $id ], "
+ <td>$grouppage$grouplink</td>
+ <td>" .
+ $this->formatPermissions( $permissions, $revoke, $addgroups, $removegroups,
+ $addgroupsSelf, $removegroupsSelf ) .
+ '</td>
+ '
+ ) );
+ }
+ $out->addHTML( Xml::closeElement( 'table' ) );
+ $this->outputNamespaceProtectionInfo();
+ }
+
+ private function outputNamespaceProtectionInfo() {
+ $out = $this->getOutput();
+ $namespaceProtection = $this->getConfig()->get( 'NamespaceProtection' );
+
+ if ( count( $namespaceProtection ) == 0 ) {
+ return;
+ }
+
+ $header = $this->msg( 'listgrouprights-namespaceprotection-header' )->text();
+ $out->addHTML(
+ Html::rawElement( 'h2', [], Html::element( 'span', [
+ 'class' => 'mw-headline',
+ 'id' => substr( Parser::guessSectionNameFromStrippedText( $header ), 1 )
+ ], $header ) ) .
+ Xml::openElement( 'table', [ 'class' => 'wikitable' ] ) .
+ Html::element(
+ 'th',
+ [],
+ $this->msg( 'listgrouprights-namespaceprotection-namespace' )->text()
+ ) .
+ Html::element(
+ 'th',
+ [],
+ $this->msg( 'listgrouprights-namespaceprotection-restrictedto' )->text()
+ )
+ );
+ $linkRenderer = $this->getLinkRenderer();
+ ksort( $namespaceProtection );
+ $validNamespaces = MWNamespace::getValidNamespaces();
+ $contLang = MediaWikiServices::getInstance()->getContentLanguage();
+ foreach ( $namespaceProtection as $namespace => $rights ) {
+ if ( !in_array( $namespace, $validNamespaces ) ) {
+ continue;
+ }
+
+ if ( $namespace == NS_MAIN ) {
+ $namespaceText = $this->msg( 'blanknamespace' )->text();
+ } else {
+ $namespaceText = $contLang->convertNamespace( $namespace );
+ }
+
+ $out->addHTML(
+ Xml::openElement( 'tr' ) .
+ Html::rawElement(
+ 'td',
+ [],
+ $linkRenderer->makeLink(
+ SpecialPage::getTitleFor( 'Allpages' ),
+ $namespaceText,
+ [],
+ [ 'namespace' => $namespace ]
+ )
+ ) .
+ Xml::openElement( 'td' ) . Xml::openElement( 'ul' )
+ );
+
+ if ( !is_array( $rights ) ) {
+ $rights = [ $rights ];
+ }
+
+ foreach ( $rights as $right ) {
+ $out->addHTML(
+ Html::rawElement( 'li', [], $this->msg(
+ 'listgrouprights-right-display',
+ User::getRightDescription( $right ),
+ Html::element(
+ 'span',
+ [ 'class' => 'mw-listgrouprights-right-name' ],
+ $right
+ )
+ )->parse() )
+ );
+ }
+
+ $out->addHTML(
+ Xml::closeElement( 'ul' ) .
+ Xml::closeElement( 'td' ) .
+ Xml::closeElement( 'tr' )
+ );
+ }
+ $out->addHTML( Xml::closeElement( 'table' ) );
+ }
+
+ /**
+ * Create a user-readable list of permissions from the given array.
+ *
+ * @param array $permissions Array of permission => bool (from $wgGroupPermissions items)
+ * @param array $revoke Array of permission => bool (from $wgRevokePermissions items)
+ * @param array $add Array of groups this group is allowed to add or true
+ * @param array $remove Array of groups this group is allowed to remove or true
+ * @param array $addSelf Array of groups this group is allowed to add to self or true
+ * @param array $removeSelf Array of group this group is allowed to remove from self or true
+ * @return string HTML list of all granted permissions
+ */
+ private function formatPermissions( $permissions, $revoke, $add, $remove, $addSelf, $removeSelf ) {
+ $r = [];
+ foreach ( $permissions as $permission => $granted ) {
+ // show as granted only if it isn't revoked to prevent duplicate display of permissions
+ if ( $granted && ( !isset( $revoke[$permission] ) || !$revoke[$permission] ) ) {
+ $r[] = $this->msg( 'listgrouprights-right-display',
+ User::getRightDescription( $permission ),
+ '<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
+ )->parse();
+ }
+ }
+ foreach ( $revoke as $permission => $revoked ) {
+ if ( $revoked ) {
+ $r[] = $this->msg( 'listgrouprights-right-revoked',
+ User::getRightDescription( $permission ),
+ '<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
+ )->parse();
+ }
+ }
+
+ sort( $r );
+
+ $lang = $this->getLanguage();
+ $allGroups = User::getAllGroups();
+
+ $changeGroups = [
+ 'addgroup' => $add,
+ 'removegroup' => $remove,
+ 'addgroup-self' => $addSelf,
+ 'removegroup-self' => $removeSelf
+ ];
+
+ foreach ( $changeGroups as $messageKey => $changeGroup ) {
+ if ( $changeGroup === true ) {
+ // For grep: listgrouprights-addgroup-all, listgrouprights-removegroup-all,
+ // listgrouprights-addgroup-self-all, listgrouprights-removegroup-self-all
+ $r[] = $this->msg( 'listgrouprights-' . $messageKey . '-all' )->escaped();
+ } elseif ( is_array( $changeGroup ) ) {
+ $changeGroup = array_intersect( array_values( array_unique( $changeGroup ) ), $allGroups );
+ if ( count( $changeGroup ) ) {
+ $groupLinks = [];
+ foreach ( $changeGroup as $group ) {
+ $groupLinks[] = UserGroupMembership::getLink( $group, $this->getContext(), 'wiki' );
+ }
+ // For grep: listgrouprights-addgroup, listgrouprights-removegroup,
+ // listgrouprights-addgroup-self, listgrouprights-removegroup-self
+ $r[] = $this->msg( 'listgrouprights-' . $messageKey,
+ $lang->listToText( $groupLinks ), count( $changeGroup ) )->parse();
+ }
+ }
+ }
+
+ if ( empty( $r ) ) {
+ return '';
+ } else {
+ return '<ul><li>' . implode( "</li>\n<li>", $r ) . '</li></ul>';
+ }
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Listusers
+ *
+ * Copyright © 2004 Brion Vibber, lcrocker, Tim Starling,
+ * Domas Mituzas, Antoine Musso, Jens Frank, Zhengzhu,
+ * 2006 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
+ */
+
+/**
+ * @ingroup SpecialPage
+ */
+class SpecialListUsers extends IncludableSpecialPage {
+
+ public function __construct() {
+ parent::__construct( 'Listusers' );
+ }
+
+ /**
+ * @param string|null $par (optional) A group to list users from
+ */
+ public function execute( $par ) {
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $up = new UsersPager( $this->getContext(), $par, $this->including() );
+
+ # getBody() first to check, if empty
+ $usersbody = $up->getBody();
+
+ $s = '';
+ if ( !$this->including() ) {
+ $s = $up->getPageHeader();
+ }
+
+ if ( $usersbody ) {
+ $s .= $up->getNavigationBar();
+ $s .= Html::rawElement( 'ul', [], $usersbody );
+ $s .= $up->getNavigationBar();
+ } else {
+ $s .= $this->msg( 'listusers-noresult' )->parseAsBlock();
+ }
+
+ $this->getOutput()->addHTML( $s );
+ }
+
+ /**
+ * Return an array of subpages that this special page will accept.
+ *
+ * @return string[] subpages
+ */
+ public function getSubpagesForPrefixSearch() {
+ return User::getAllGroups();
+ }
+
+ protected function getGroupName() {
+ return 'users';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Listfiles
- *
- * 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
- */
-
-class SpecialListFiles extends IncludableSpecialPage {
- public function __construct() {
- parent::__construct( 'Listfiles' );
- }
-
- public function execute( $par ) {
- $this->setHeaders();
- $this->outputHeader();
-
- if ( $this->including() ) {
- $userName = $par;
- $search = '';
- $showAll = false;
- } else {
- $userName = $this->getRequest()->getText( 'user', $par );
- $search = $this->getRequest()->getText( 'ilsearch', '' );
- $showAll = $this->getRequest()->getBool( 'ilshowall', false );
- }
-
- $pager = new ImageListPager(
- $this->getContext(),
- $userName,
- $search,
- $this->including(),
- $showAll
- );
-
- $out = $this->getOutput();
- if ( $this->including() ) {
- $out->addParserOutputContent( $pager->getBodyOutput() );
- } else {
- $user = $pager->getRelevantUser();
- $this->getSkin()->setRelevantUser( $user );
- $pager->getForm();
- $out->addParserOutputContent( $pager->getFullOutput() );
- }
- }
-
- /**
- * Return an array of subpages beginning with $search that this special page will accept.
- *
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return (usually 10)
- * @param int $offset Number of results to skip (usually 0)
- * @return string[] Matching subpages
- */
- public function prefixSearchSubpages( $search, $limit, $offset ) {
- $user = User::newFromName( $search );
- if ( !$user ) {
- // No prefix suggestion for invalid user
- return [];
- }
- // Autocomplete subpage as user list - public to allow caching
- return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
- }
-
- protected function getGroupName() {
- return 'media';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Listgrants
- *
- * 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
- */
-
-/**
- * This special page lists all defined rights grants and the associated rights.
- * See also @ref $wgGrantPermissions and @ref $wgGrantPermissionGroups.
- *
- * @ingroup SpecialPage
- */
-class SpecialListGrants extends SpecialPage {
- function __construct() {
- parent::__construct( 'Listgrants' );
- }
-
- /**
- * Show the special page
- * @param string|null $par
- */
- public function execute( $par ) {
- $this->setHeaders();
- $this->outputHeader();
-
- $out = $this->getOutput();
- $out->addModuleStyles( 'mediawiki.special' );
-
- $out->addHTML(
- \Html::openElement( 'table',
- [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
- '<tr>' .
- \Html::element( 'th', null, $this->msg( 'listgrants-grant' )->text() ) .
- \Html::element( 'th', null, $this->msg( 'listgrants-rights' )->text() ) .
- '</tr>'
- );
-
- foreach ( $this->getConfig()->get( 'GrantPermissions' ) as $grant => $rights ) {
- $descs = [];
- $rights = array_filter( $rights ); // remove ones with 'false'
- foreach ( $rights as $permission => $granted ) {
- $descs[] = $this->msg(
- 'listgrouprights-right-display',
- \User::getRightDescription( $permission ),
- '<span class="mw-listgrants-right-name">' . $permission . '</span>'
- )->parse();
- }
- if ( $descs === [] ) {
- $grantCellHtml = '';
- } else {
- sort( $descs );
- $grantCellHtml = '<ul><li>' . implode( "</li>\n<li>", $descs ) . '</li></ul>';
- }
-
- $id = Sanitizer::escapeIdForAttribute( $grant );
- $out->addHTML( \Html::rawElement( 'tr', [ 'id' => $id ],
- "<td>" .
- $this->msg(
- "listgrants-grant-display",
- \User::getGrantName( $grant ),
- "<span class='mw-listgrants-grant-name'>" . $id . "</span>"
- )->parse() .
- "</td>" .
- "<td>" . $grantCellHtml . "</td>"
- ) );
- }
-
- $out->addHTML( \Html::closeElement( 'table' ) );
- }
-
- protected function getGroupName() {
- return 'users';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Listgrouprights
- *
- * 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;
-
-/**
- * This special page lists all defined user groups and the associated rights.
- * See also @ref $wgGroupPermissions.
- *
- * @ingroup SpecialPage
- * @author Petr Kadlec <mormegil@centrum.cz>
- */
-class SpecialListGroupRights extends SpecialPage {
- public function __construct() {
- parent::__construct( 'Listgrouprights' );
- }
-
- /**
- * Show the special page
- * @param string|null $par
- */
- public function execute( $par ) {
- $this->setHeaders();
- $this->outputHeader();
-
- $out = $this->getOutput();
- $out->addModuleStyles( 'mediawiki.special' );
-
- $out->wrapWikiMsg( "<div class=\"mw-listgrouprights-key\">\n$1\n</div>", 'listgrouprights-key' );
-
- $out->addHTML(
- Xml::openElement( 'table', [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
- '<tr>' .
- Xml::element( 'th', null, $this->msg( 'listgrouprights-group' )->text() ) .
- Xml::element( 'th', null, $this->msg( 'listgrouprights-rights' )->text() ) .
- '</tr>'
- );
-
- $config = $this->getConfig();
- $groupPermissions = $config->get( 'GroupPermissions' );
- $revokePermissions = $config->get( 'RevokePermissions' );
- $addGroups = $config->get( 'AddGroups' );
- $removeGroups = $config->get( 'RemoveGroups' );
- $groupsAddToSelf = $config->get( 'GroupsAddToSelf' );
- $groupsRemoveFromSelf = $config->get( 'GroupsRemoveFromSelf' );
- $allGroups = array_unique( array_merge(
- array_keys( $groupPermissions ),
- array_keys( $revokePermissions ),
- array_keys( $addGroups ),
- array_keys( $removeGroups ),
- array_keys( $groupsAddToSelf ),
- array_keys( $groupsRemoveFromSelf )
- ) );
- asort( $allGroups );
-
- $linkRenderer = $this->getLinkRenderer();
-
- foreach ( $allGroups as $group ) {
- $permissions = $groupPermissions[$group] ?? [];
- $groupname = ( $group == '*' ) // Replace * with a more descriptive groupname
- ? 'all'
- : $group;
-
- $groupnameLocalized = UserGroupMembership::getGroupName( $groupname );
-
- $grouppageLocalizedTitle = UserGroupMembership::getGroupPage( $groupname )
- ?: Title::newFromText( MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname );
-
- if ( $group == '*' || !$grouppageLocalizedTitle ) {
- // Do not make a link for the generic * group or group with invalid group page
- $grouppage = htmlspecialchars( $groupnameLocalized );
- } else {
- $grouppage = $linkRenderer->makeLink(
- $grouppageLocalizedTitle,
- $groupnameLocalized
- );
- }
-
- if ( $group === 'user' ) {
- // Link to Special:listusers for implicit group 'user'
- $grouplink = '<br />' . $linkRenderer->makeKnownLink(
- SpecialPage::getTitleFor( 'Listusers' ),
- $this->msg( 'listgrouprights-members' )->text()
- );
- } elseif ( !in_array( $group, $config->get( 'ImplicitGroups' ) ) ) {
- $grouplink = '<br />' . $linkRenderer->makeKnownLink(
- SpecialPage::getTitleFor( 'Listusers' ),
- $this->msg( 'listgrouprights-members' )->text(),
- [],
- [ 'group' => $group ]
- );
- } else {
- // No link to Special:listusers for other implicit groups as they are unlistable
- $grouplink = '';
- }
-
- $revoke = $revokePermissions[$group] ?? [];
- $addgroups = $addGroups[$group] ?? [];
- $removegroups = $removeGroups[$group] ?? [];
- $addgroupsSelf = $groupsAddToSelf[$group] ?? [];
- $removegroupsSelf = $groupsRemoveFromSelf[$group] ?? [];
-
- $id = $group == '*' ? false : Sanitizer::escapeIdForAttribute( $group );
- $out->addHTML( Html::rawElement( 'tr', [ 'id' => $id ], "
- <td>$grouppage$grouplink</td>
- <td>" .
- $this->formatPermissions( $permissions, $revoke, $addgroups, $removegroups,
- $addgroupsSelf, $removegroupsSelf ) .
- '</td>
- '
- ) );
- }
- $out->addHTML( Xml::closeElement( 'table' ) );
- $this->outputNamespaceProtectionInfo();
- }
-
- private function outputNamespaceProtectionInfo() {
- $out = $this->getOutput();
- $namespaceProtection = $this->getConfig()->get( 'NamespaceProtection' );
-
- if ( count( $namespaceProtection ) == 0 ) {
- return;
- }
-
- $header = $this->msg( 'listgrouprights-namespaceprotection-header' )->text();
- $out->addHTML(
- Html::rawElement( 'h2', [], Html::element( 'span', [
- 'class' => 'mw-headline',
- 'id' => substr( Parser::guessSectionNameFromStrippedText( $header ), 1 )
- ], $header ) ) .
- Xml::openElement( 'table', [ 'class' => 'wikitable' ] ) .
- Html::element(
- 'th',
- [],
- $this->msg( 'listgrouprights-namespaceprotection-namespace' )->text()
- ) .
- Html::element(
- 'th',
- [],
- $this->msg( 'listgrouprights-namespaceprotection-restrictedto' )->text()
- )
- );
- $linkRenderer = $this->getLinkRenderer();
- ksort( $namespaceProtection );
- $validNamespaces = MWNamespace::getValidNamespaces();
- $contLang = MediaWikiServices::getInstance()->getContentLanguage();
- foreach ( $namespaceProtection as $namespace => $rights ) {
- if ( !in_array( $namespace, $validNamespaces ) ) {
- continue;
- }
-
- if ( $namespace == NS_MAIN ) {
- $namespaceText = $this->msg( 'blanknamespace' )->text();
- } else {
- $namespaceText = $contLang->convertNamespace( $namespace );
- }
-
- $out->addHTML(
- Xml::openElement( 'tr' ) .
- Html::rawElement(
- 'td',
- [],
- $linkRenderer->makeLink(
- SpecialPage::getTitleFor( 'Allpages' ),
- $namespaceText,
- [],
- [ 'namespace' => $namespace ]
- )
- ) .
- Xml::openElement( 'td' ) . Xml::openElement( 'ul' )
- );
-
- if ( !is_array( $rights ) ) {
- $rights = [ $rights ];
- }
-
- foreach ( $rights as $right ) {
- $out->addHTML(
- Html::rawElement( 'li', [], $this->msg(
- 'listgrouprights-right-display',
- User::getRightDescription( $right ),
- Html::element(
- 'span',
- [ 'class' => 'mw-listgrouprights-right-name' ],
- $right
- )
- )->parse() )
- );
- }
-
- $out->addHTML(
- Xml::closeElement( 'ul' ) .
- Xml::closeElement( 'td' ) .
- Xml::closeElement( 'tr' )
- );
- }
- $out->addHTML( Xml::closeElement( 'table' ) );
- }
-
- /**
- * Create a user-readable list of permissions from the given array.
- *
- * @param array $permissions Array of permission => bool (from $wgGroupPermissions items)
- * @param array $revoke Array of permission => bool (from $wgRevokePermissions items)
- * @param array $add Array of groups this group is allowed to add or true
- * @param array $remove Array of groups this group is allowed to remove or true
- * @param array $addSelf Array of groups this group is allowed to add to self or true
- * @param array $removeSelf Array of group this group is allowed to remove from self or true
- * @return string HTML list of all granted permissions
- */
- private function formatPermissions( $permissions, $revoke, $add, $remove, $addSelf, $removeSelf ) {
- $r = [];
- foreach ( $permissions as $permission => $granted ) {
- // show as granted only if it isn't revoked to prevent duplicate display of permissions
- if ( $granted && ( !isset( $revoke[$permission] ) || !$revoke[$permission] ) ) {
- $r[] = $this->msg( 'listgrouprights-right-display',
- User::getRightDescription( $permission ),
- '<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
- )->parse();
- }
- }
- foreach ( $revoke as $permission => $revoked ) {
- if ( $revoked ) {
- $r[] = $this->msg( 'listgrouprights-right-revoked',
- User::getRightDescription( $permission ),
- '<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
- )->parse();
- }
- }
-
- sort( $r );
-
- $lang = $this->getLanguage();
- $allGroups = User::getAllGroups();
-
- $changeGroups = [
- 'addgroup' => $add,
- 'removegroup' => $remove,
- 'addgroup-self' => $addSelf,
- 'removegroup-self' => $removeSelf
- ];
-
- foreach ( $changeGroups as $messageKey => $changeGroup ) {
- if ( $changeGroup === true ) {
- // For grep: listgrouprights-addgroup-all, listgrouprights-removegroup-all,
- // listgrouprights-addgroup-self-all, listgrouprights-removegroup-self-all
- $r[] = $this->msg( 'listgrouprights-' . $messageKey . '-all' )->escaped();
- } elseif ( is_array( $changeGroup ) ) {
- $changeGroup = array_intersect( array_values( array_unique( $changeGroup ) ), $allGroups );
- if ( count( $changeGroup ) ) {
- $groupLinks = [];
- foreach ( $changeGroup as $group ) {
- $groupLinks[] = UserGroupMembership::getLink( $group, $this->getContext(), 'wiki' );
- }
- // For grep: listgrouprights-addgroup, listgrouprights-removegroup,
- // listgrouprights-addgroup-self, listgrouprights-removegroup-self
- $r[] = $this->msg( 'listgrouprights-' . $messageKey,
- $lang->listToText( $groupLinks ), count( $changeGroup ) )->parse();
- }
- }
- }
-
- if ( empty( $r ) ) {
- return '';
- } else {
- return '<ul><li>' . implode( "</li>\n<li>", $r ) . '</li></ul>';
- }
- }
-
- protected function getGroupName() {
- return 'users';
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Listusers
- *
- * Copyright © 2004 Brion Vibber, lcrocker, Tim Starling,
- * Domas Mituzas, Antoine Musso, Jens Frank, Zhengzhu,
- * 2006 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
- */
-
-/**
- * @ingroup SpecialPage
- */
-class SpecialListUsers extends IncludableSpecialPage {
-
- public function __construct() {
- parent::__construct( 'Listusers' );
- }
-
- /**
- * @param string|null $par (optional) A group to list users from
- */
- public function execute( $par ) {
- $this->setHeaders();
- $this->outputHeader();
-
- $up = new UsersPager( $this->getContext(), $par, $this->including() );
-
- # getBody() first to check, if empty
- $usersbody = $up->getBody();
-
- $s = '';
- if ( !$this->including() ) {
- $s = $up->getPageHeader();
- }
-
- if ( $usersbody ) {
- $s .= $up->getNavigationBar();
- $s .= Html::rawElement( 'ul', [], $usersbody );
- $s .= $up->getNavigationBar();
- } else {
- $s .= $this->msg( 'listusers-noresult' )->parseAsBlock();
- }
-
- $this->getOutput()->addHTML( $s );
- }
-
- /**
- * Return an array of subpages that this special page will accept.
- *
- * @return string[] subpages
- */
- public function getSubpagesForPrefixSearch() {
- return User::getAllGroups();
- }
-
- protected function getGroupName() {
- return 'users';
- }
-}
* Get the preferences form to use.
* @param User $user The user.
* @param IContextSource $context The context.
- * @return PreferencesFormLegacy|HTMLForm
+ * @return PreferencesFormOOUI|HTMLForm
*/
protected function getFormObject( $user, IContextSource $context ) {
$preferencesFactory = MediaWikiServices::getInstance()->getPreferencesFactory();
--- /dev/null
+<?php
+/**
+ * Implements Special:Recentchanges
+ *
+ * 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\FakeResultWrapper;
+
+/**
+ * A special page that lists last changes made to the wiki
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialRecentChanges extends ChangesListSpecialPage {
+
+ protected static $savedQueriesPreferenceName = 'rcfilters-saved-queries';
+ protected static $daysPreferenceName = 'rcdays'; // Use general RecentChanges preference
+ protected static $limitPreferenceName = 'rcfilters-limit'; // Use RCFilters-specific preference
+ protected static $collapsedPreferenceName = 'rcfilters-rc-collapsed';
+
+ private $watchlistFilterGroupDefinition;
+
+ public function __construct( $name = 'Recentchanges', $restriction = '' ) {
+ parent::__construct( $name, $restriction );
+
+ $this->watchlistFilterGroupDefinition = [
+ 'name' => 'watchlist',
+ 'title' => 'rcfilters-filtergroup-watchlist',
+ 'class' => ChangesListStringOptionsFilterGroup::class,
+ 'priority' => -9,
+ 'isFullCoverage' => true,
+ 'filters' => [
+ [
+ 'name' => 'watched',
+ 'label' => 'rcfilters-filter-watchlist-watched-label',
+ 'description' => 'rcfilters-filter-watchlist-watched-description',
+ 'cssClassSuffix' => 'watched',
+ 'isRowApplicableCallable' => function ( $ctx, $rc ) {
+ return $rc->getAttribute( 'wl_user' );
+ }
+ ],
+ [
+ 'name' => 'watchednew',
+ 'label' => 'rcfilters-filter-watchlist-watchednew-label',
+ 'description' => 'rcfilters-filter-watchlist-watchednew-description',
+ 'cssClassSuffix' => 'watchednew',
+ 'isRowApplicableCallable' => function ( $ctx, $rc ) {
+ return $rc->getAttribute( 'wl_user' ) &&
+ $rc->getAttribute( 'rc_timestamp' ) &&
+ $rc->getAttribute( 'wl_notificationtimestamp' ) &&
+ $rc->getAttribute( 'rc_timestamp' ) >= $rc->getAttribute( 'wl_notificationtimestamp' );
+ },
+ ],
+ [
+ 'name' => 'notwatched',
+ 'label' => 'rcfilters-filter-watchlist-notwatched-label',
+ 'description' => 'rcfilters-filter-watchlist-notwatched-description',
+ 'cssClassSuffix' => 'notwatched',
+ 'isRowApplicableCallable' => function ( $ctx, $rc ) {
+ return $rc->getAttribute( 'wl_user' ) === null;
+ },
+ ]
+ ],
+ 'default' => ChangesListStringOptionsFilterGroup::NONE,
+ 'queryCallable' => function ( $specialPageClassName, $context, $dbr,
+ &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedValues ) {
+ sort( $selectedValues );
+ $notwatchedCond = 'wl_user IS NULL';
+ $watchedCond = 'wl_user IS NOT NULL';
+ $newCond = 'rc_timestamp >= wl_notificationtimestamp';
+
+ if ( $selectedValues === [ 'notwatched' ] ) {
+ $conds[] = $notwatchedCond;
+ return;
+ }
+
+ if ( $selectedValues === [ 'watched' ] ) {
+ $conds[] = $watchedCond;
+ return;
+ }
+
+ if ( $selectedValues === [ 'watchednew' ] ) {
+ $conds[] = $dbr->makeList( [
+ $watchedCond,
+ $newCond
+ ], LIST_AND );
+ return;
+ }
+
+ if ( $selectedValues === [ 'notwatched', 'watched' ] ) {
+ // no filters
+ return;
+ }
+
+ if ( $selectedValues === [ 'notwatched', 'watchednew' ] ) {
+ $conds[] = $dbr->makeList( [
+ $notwatchedCond,
+ $dbr->makeList( [
+ $watchedCond,
+ $newCond
+ ], LIST_AND )
+ ], LIST_OR );
+ return;
+ }
+
+ if ( $selectedValues === [ 'watched', 'watchednew' ] ) {
+ $conds[] = $watchedCond;
+ return;
+ }
+
+ if ( $selectedValues === [ 'notwatched', 'watched', 'watchednew' ] ) {
+ // no filters
+ return;
+ }
+ }
+ ];
+ }
+
+ /**
+ * @param string|null $subpage
+ */
+ public function execute( $subpage ) {
+ // Backwards-compatibility: redirect to new feed URLs
+ $feedFormat = $this->getRequest()->getVal( 'feed' );
+ if ( !$this->including() && $feedFormat ) {
+ $query = $this->getFeedQuery();
+ $query['feedformat'] = $feedFormat === 'atom' ? 'atom' : 'rss';
+ $this->getOutput()->redirect( wfAppendQuery( wfScript( 'api' ), $query ) );
+
+ return;
+ }
+
+ // 10 seconds server-side caching max
+ $out = $this->getOutput();
+ $out->setCdnMaxage( 10 );
+ // Check if the client has a cached version
+ $lastmod = $this->checkLastModified();
+ if ( $lastmod === false ) {
+ return;
+ }
+
+ $this->addHelpLink(
+ '//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Recent_changes',
+ true
+ );
+ parent::execute( $subpage );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function transformFilterDefinition( array $filterDefinition ) {
+ if ( isset( $filterDefinition['showHideSuffix'] ) ) {
+ $filterDefinition['showHide'] = 'rc' . $filterDefinition['showHideSuffix'];
+ }
+
+ return $filterDefinition;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function registerFilters() {
+ parent::registerFilters();
+
+ if (
+ !$this->including() &&
+ $this->getUser()->isLoggedIn() &&
+ $this->getUser()->isAllowed( 'viewmywatchlist' )
+ ) {
+ $this->registerFiltersFromDefinitions( [ $this->watchlistFilterGroupDefinition ] );
+ $watchlistGroup = $this->getFilterGroup( 'watchlist' );
+ $watchlistGroup->getFilter( 'watched' )->setAsSupersetOf(
+ $watchlistGroup->getFilter( 'watchednew' )
+ );
+ }
+
+ $user = $this->getUser();
+
+ $significance = $this->getFilterGroup( 'significance' );
+ $hideMinor = $significance->getFilter( 'hideminor' );
+ $hideMinor->setDefault( $user->getBoolOption( 'hideminor' ) );
+
+ $automated = $this->getFilterGroup( 'automated' );
+ $hideBots = $automated->getFilter( 'hidebots' );
+ $hideBots->setDefault( true );
+
+ $reviewStatus = $this->getFilterGroup( 'reviewStatus' );
+ if ( $reviewStatus !== null ) {
+ // Conditional on feature being available and rights
+ if ( $user->getBoolOption( 'hidepatrolled' ) ) {
+ $reviewStatus->setDefault( 'unpatrolled' );
+ $legacyReviewStatus = $this->getFilterGroup( 'legacyReviewStatus' );
+ $legacyHidePatrolled = $legacyReviewStatus->getFilter( 'hidepatrolled' );
+ $legacyHidePatrolled->setDefault( true );
+ }
+ }
+
+ $changeType = $this->getFilterGroup( 'changeType' );
+ $hideCategorization = $changeType->getFilter( 'hidecategorization' );
+ if ( $hideCategorization !== null ) {
+ // Conditional on feature being available
+ $hideCategorization->setDefault( $user->getBoolOption( 'hidecategorization' ) );
+ }
+ }
+
+ /**
+ * Process $par and put options found in $opts. Used when including the page.
+ *
+ * @param string $par
+ * @param FormOptions $opts
+ */
+ public function parseParameters( $par, FormOptions $opts ) {
+ parent::parseParameters( $par, $opts );
+
+ $bits = preg_split( '/\s*,\s*/', trim( $par ) );
+ foreach ( $bits as $bit ) {
+ if ( is_numeric( $bit ) ) {
+ $opts['limit'] = $bit;
+ }
+
+ $m = [];
+ if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
+ $opts['limit'] = $m[1];
+ }
+ if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
+ $opts['days'] = $m[1];
+ }
+ if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
+ $opts['namespace'] = $m[1];
+ }
+ if ( preg_match( '/^tagfilter=(.*)$/', $bit, $m ) ) {
+ $opts['tagfilter'] = $m[1];
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function doMainQuery( $tables, $fields, $conds, $query_options,
+ $join_conds, FormOptions $opts
+ ) {
+ $dbr = $this->getDB();
+ $user = $this->getUser();
+
+ $rcQuery = RecentChange::getQueryInfo();
+ $tables = array_merge( $tables, $rcQuery['tables'] );
+ $fields = array_merge( $rcQuery['fields'], $fields );
+ $join_conds = array_merge( $join_conds, $rcQuery['joins'] );
+
+ // JOIN on watchlist for users
+ if ( $user->isLoggedIn() && $user->isAllowed( 'viewmywatchlist' ) ) {
+ $tables[] = 'watchlist';
+ $fields[] = 'wl_user';
+ $fields[] = 'wl_notificationtimestamp';
+ $join_conds['watchlist'] = [ 'LEFT JOIN', [
+ 'wl_user' => $user->getId(),
+ 'wl_title=rc_title',
+ 'wl_namespace=rc_namespace'
+ ] ];
+ }
+
+ // JOIN on page, used for 'last revision' filter highlight
+ $tables[] = 'page';
+ $fields[] = 'page_latest';
+ $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
+
+ $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
+ ChangeTags::modifyDisplayQuery(
+ $tables,
+ $fields,
+ $conds,
+ $join_conds,
+ $query_options,
+ $tagFilter
+ );
+
+ if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
+ $opts )
+ ) {
+ return false;
+ }
+
+ if ( $this->areFiltersInConflict() ) {
+ return false;
+ }
+
+ $orderByAndLimit = [
+ 'ORDER BY' => 'rc_timestamp DESC',
+ 'LIMIT' => $opts['limit']
+ ];
+ if ( in_array( 'DISTINCT', $query_options ) ) {
+ // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
+ // In order to prevent DISTINCT from causing query performance problems,
+ // we have to GROUP BY the primary key. This in turn requires us to add
+ // the primary key to the end of the ORDER BY, and the old ORDER BY to the
+ // start of the GROUP BY
+ $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
+ $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
+ }
+ // array_merge() is used intentionally here so that hooks can, should
+ // they so desire, override the ORDER BY / LIMIT condition(s); prior to
+ // MediaWiki 1.26 this used to use the plus operator instead, which meant
+ // that extensions weren't able to change these conditions
+ $query_options = array_merge( $orderByAndLimit, $query_options );
+ $rows = $dbr->select(
+ $tables,
+ $fields,
+ // rc_new is not an ENUM, but adding a redundant rc_new IN (0,1) gives mysql enough
+ // knowledge to use an index merge if it wants (it may use some other index though).
+ $conds + [ 'rc_new' => [ 0, 1 ] ],
+ __METHOD__,
+ $query_options,
+ $join_conds
+ );
+
+ return $rows;
+ }
+
+ protected function getDB() {
+ return wfGetDB( DB_REPLICA, 'recentchanges' );
+ }
+
+ public function outputFeedLinks() {
+ $this->addFeedLinks( $this->getFeedQuery() );
+ }
+
+ /**
+ * Get URL query parameters for action=feedrecentchanges API feed of current recent changes view.
+ *
+ * @return array
+ */
+ protected function getFeedQuery() {
+ $query = array_filter( $this->getOptions()->getAllValues(), function ( $value ) {
+ // API handles empty parameters in a different way
+ return $value !== '';
+ } );
+ $query['action'] = 'feedrecentchanges';
+ $feedLimit = $this->getConfig()->get( 'FeedLimit' );
+ if ( $query['limit'] > $feedLimit ) {
+ $query['limit'] = $feedLimit;
+ }
+
+ return $query;
+ }
+
+ /**
+ * Build and output the actual changes list.
+ *
+ * @param IResultWrapper $rows Database rows
+ * @param FormOptions $opts
+ */
+ public function outputChangesList( $rows, $opts ) {
+ $limit = $opts['limit'];
+
+ $showWatcherCount = $this->getConfig()->get( 'RCShowWatchingUsers' )
+ && $this->getUser()->getOption( 'shownumberswatching' );
+ $watcherCache = [];
+
+ $counter = 1;
+ $list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
+ $list->initChangesListRows( $rows );
+
+ $userShowHiddenCats = $this->getUser()->getBoolOption( 'showhiddencats' );
+ $rclistOutput = $list->beginRecentChangesList();
+ if ( $this->isStructuredFilterUiEnabled() ) {
+ $rclistOutput .= $this->makeLegend();
+ }
+
+ foreach ( $rows as $obj ) {
+ if ( $limit == 0 ) {
+ break;
+ }
+ $rc = RecentChange::newFromRow( $obj );
+
+ # Skip CatWatch entries for hidden cats based on user preference
+ if (
+ $rc->getAttribute( 'rc_type' ) == RC_CATEGORIZE &&
+ !$userShowHiddenCats &&
+ $rc->getParam( 'hidden-cat' )
+ ) {
+ continue;
+ }
+
+ $rc->counter = $counter++;
+ # Check if the page has been updated since the last visit
+ if ( $this->getConfig()->get( 'ShowUpdatedMarker' )
+ && !empty( $obj->wl_notificationtimestamp )
+ ) {
+ $rc->notificationtimestamp = ( $obj->rc_timestamp >= $obj->wl_notificationtimestamp );
+ } else {
+ $rc->notificationtimestamp = false; // Default
+ }
+ # Check the number of users watching the page
+ $rc->numberofWatchingusers = 0; // Default
+ if ( $showWatcherCount && $obj->rc_namespace >= 0 ) {
+ if ( !isset( $watcherCache[$obj->rc_namespace][$obj->rc_title] ) ) {
+ $watcherCache[$obj->rc_namespace][$obj->rc_title] =
+ MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchers(
+ new TitleValue( (int)$obj->rc_namespace, $obj->rc_title )
+ );
+ }
+ $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
+ }
+
+ $changeLine = $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter );
+ if ( $changeLine !== false ) {
+ $rclistOutput .= $changeLine;
+ --$limit;
+ }
+ }
+ $rclistOutput .= $list->endRecentChangesList();
+
+ if ( $rows->numRows() === 0 ) {
+ $this->outputNoResults();
+ if ( !$this->including() ) {
+ $this->getOutput()->setStatusCode( 404 );
+ }
+ } else {
+ $this->getOutput()->addHTML( $rclistOutput );
+ }
+ }
+
+ /**
+ * Set the text to be displayed above the changes
+ *
+ * @param FormOptions $opts
+ * @param int $numRows Number of rows in the result to show after this header
+ */
+ public function doHeader( $opts, $numRows ) {
+ $this->setTopText( $opts );
+
+ $defaults = $opts->getAllValues();
+ $nondefaults = $opts->getChangedValues();
+
+ $panel = [];
+ if ( !$this->isStructuredFilterUiEnabled() ) {
+ $panel[] = $this->makeLegend();
+ }
+ $panel[] = $this->optionsPanel( $defaults, $nondefaults, $numRows );
+ $panel[] = '<hr />';
+
+ $extraOpts = $this->getExtraOptions( $opts );
+ $extraOptsCount = count( $extraOpts );
+ $count = 0;
+ $submit = ' ' . Xml::submitButton( $this->msg( 'recentchanges-submit' )->text() );
+
+ $out = Xml::openElement( 'table', [ 'class' => 'mw-recentchanges-table' ] );
+ foreach ( $extraOpts as $name => $optionRow ) {
+ # Add submit button to the last row only
+ ++$count;
+ $addSubmit = ( $count === $extraOptsCount ) ? $submit : '';
+
+ $out .= Xml::openElement( 'tr', [ 'class' => $name . 'Form' ] );
+ if ( is_array( $optionRow ) ) {
+ $out .= Xml::tags(
+ 'td',
+ [ 'class' => 'mw-label mw-' . $name . '-label' ],
+ $optionRow[0]
+ );
+ $out .= Xml::tags(
+ 'td',
+ [ 'class' => 'mw-input' ],
+ $optionRow[1] . $addSubmit
+ );
+ } else {
+ $out .= Xml::tags(
+ 'td',
+ [ 'class' => 'mw-input', 'colspan' => 2 ],
+ $optionRow . $addSubmit
+ );
+ }
+ $out .= Xml::closeElement( 'tr' );
+ }
+ $out .= Xml::closeElement( 'table' );
+
+ $unconsumed = $opts->getUnconsumedValues();
+ foreach ( $unconsumed as $key => $value ) {
+ $out .= Html::hidden( $key, $value );
+ }
+
+ $t = $this->getPageTitle();
+ $out .= Html::hidden( 'title', $t->getPrefixedText() );
+ $form = Xml::tags( 'form', [ 'action' => wfScript() ], $out );
+ $panel[] = $form;
+ $panelString = implode( "\n", $panel );
+
+ $rcoptions = Xml::fieldset(
+ $this->msg( 'recentchanges-legend' )->text(),
+ $panelString,
+ [ 'class' => 'rcoptions cloptions' ]
+ );
+
+ // Insert a placeholder for RCFilters
+ if ( $this->isStructuredFilterUiEnabled() ) {
+ $rcfilterContainer = Html::element(
+ 'div',
+ [ 'class' => 'rcfilters-container' ]
+ );
+
+ $loadingContainer = Html::rawElement(
+ 'div',
+ [ 'class' => 'rcfilters-spinner' ],
+ Html::element(
+ 'div',
+ [ 'class' => 'rcfilters-spinner-bounce' ]
+ )
+ );
+
+ // Wrap both with rcfilters-head
+ $this->getOutput()->addHTML(
+ Html::rawElement(
+ 'div',
+ [ 'class' => 'rcfilters-head' ],
+ $rcfilterContainer . $rcoptions
+ )
+ );
+
+ // Add spinner
+ $this->getOutput()->addHTML( $loadingContainer );
+ } else {
+ $this->getOutput()->addHTML( $rcoptions );
+ }
+
+ $this->setBottomText( $opts );
+ }
+
+ /**
+ * Send the text to be displayed above the options
+ *
+ * @param FormOptions $opts Unused
+ */
+ function setTopText( FormOptions $opts ) {
+ $message = $this->msg( 'recentchangestext' )->inContentLanguage();
+ if ( !$message->isDisabled() ) {
+ $contLang = MediaWikiServices::getInstance()->getContentLanguage();
+ // Parse the message in this weird ugly way to preserve the ability to include interlanguage
+ // links in it (T172461). In the future when T66969 is resolved, perhaps we can just use
+ // $message->parse() instead. This code is copied from Message::parseText().
+ $parserOutput = MessageCache::singleton()->parse(
+ $message->plain(),
+ $this->getPageTitle(),
+ /*linestart*/true,
+ // Message class sets the interface flag to false when parsing in a language different than
+ // user language, and this is wiki content language
+ /*interface*/false,
+ $contLang
+ );
+ $content = $parserOutput->getText( [
+ 'enableSectionEditLinks' => false,
+ ] );
+ // Add only metadata here (including the language links), text is added below
+ $this->getOutput()->addParserOutputMetadata( $parserOutput );
+
+ $langAttributes = [
+ 'lang' => $contLang->getHtmlCode(),
+ 'dir' => $contLang->getDir(),
+ ];
+
+ $topLinksAttributes = [ 'class' => 'mw-recentchanges-toplinks' ];
+
+ if ( $this->isStructuredFilterUiEnabled() ) {
+ // Check whether the widget is already collapsed or expanded
+ $collapsedState = $this->getRequest()->getCookie( 'rcfilters-toplinks-collapsed-state' );
+ // Note that an empty/unset cookie means collapsed, so check for !== 'expanded'
+ $topLinksAttributes[ 'class' ] .= $collapsedState !== 'expanded' ?
+ ' mw-recentchanges-toplinks-collapsed' : '';
+
+ $this->getOutput()->enableOOUI();
+ $contentTitle = new OOUI\ButtonWidget( [
+ 'classes' => [ 'mw-recentchanges-toplinks-title' ],
+ 'label' => new OOUI\HtmlSnippet( $this->msg( 'rcfilters-other-review-tools' )->parse() ),
+ 'framed' => false,
+ 'indicator' => $collapsedState !== 'expanded' ? 'down' : 'up',
+ 'flags' => [ 'progressive' ],
+ ] );
+
+ $contentWrapper = Html::rawElement( 'div',
+ array_merge(
+ [ 'class' => 'mw-recentchanges-toplinks-content mw-collapsible-content' ],
+ $langAttributes
+ ),
+ $content
+ );
+ $content = $contentTitle . $contentWrapper;
+ } else {
+ // Language direction should be on the top div only
+ // if the title is not there. If it is there, it's
+ // interface direction, and the language/dir attributes
+ // should be on the content itself
+ $topLinksAttributes = array_merge( $topLinksAttributes, $langAttributes );
+ }
+
+ $this->getOutput()->addHTML(
+ Html::rawElement( 'div', $topLinksAttributes, $content )
+ );
+ }
+ }
+
+ /**
+ * Get options to be displayed in a form
+ *
+ * @param FormOptions $opts
+ * @return array
+ */
+ function getExtraOptions( $opts ) {
+ $opts->consumeValues( [
+ 'namespace', 'invert', 'associated', 'tagfilter'
+ ] );
+
+ $extraOpts = [];
+ $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
+
+ $tagFilter = ChangeTags::buildTagFilterSelector(
+ $opts['tagfilter'], false, $this->getContext() );
+ if ( count( $tagFilter ) ) {
+ $extraOpts['tagfilter'] = $tagFilter;
+ }
+
+ // Don't fire the hook for subclasses. (Or should we?)
+ if ( $this->getName() === 'Recentchanges' ) {
+ Hooks::run( 'SpecialRecentChangesPanel', [ &$extraOpts, $opts ] );
+ }
+
+ return $extraOpts;
+ }
+
+ /**
+ * Add page-specific modules.
+ */
+ protected function addModules() {
+ parent::addModules();
+ $out = $this->getOutput();
+ $out->addModules( 'mediawiki.special.recentchanges' );
+ }
+
+ /**
+ * Get last modified date, for client caching
+ * Don't use this if we are using the patrol feature, patrol changes don't
+ * update the timestamp
+ *
+ * @return string|bool
+ */
+ public function checkLastModified() {
+ $dbr = $this->getDB();
+ $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
+
+ return $lastmod;
+ }
+
+ /**
+ * Creates the choose namespace selection
+ *
+ * @param FormOptions $opts
+ * @return string[]
+ */
+ protected function namespaceFilterForm( FormOptions $opts ) {
+ $nsSelect = Html::namespaceSelector(
+ [ 'selected' => $opts['namespace'], 'all' => '', 'in-user-lang' => true ],
+ [ 'name' => 'namespace', 'id' => 'namespace' ]
+ );
+ $nsLabel = Xml::label( $this->msg( 'namespace' )->text(), 'namespace' );
+ $attribs = [ 'class' => [ 'mw-input-with-label' ] ];
+ // Hide the checkboxes when the namespace filter is set to 'all'.
+ if ( $opts['namespace'] === '' ) {
+ $attribs['class'][] = 'mw-input-hidden';
+ }
+ $invert = Html::rawElement( 'span', $attribs, Xml::checkLabel(
+ $this->msg( 'invert' )->text(), 'invert', 'nsinvert',
+ $opts['invert'],
+ [ 'title' => $this->msg( 'tooltip-invert' )->text() ]
+ ) );
+ $associated = Html::rawElement( 'span', $attribs, Xml::checkLabel(
+ $this->msg( 'namespace_association' )->text(), 'associated', 'nsassociated',
+ $opts['associated'],
+ [ 'title' => $this->msg( 'tooltip-namespace_association' )->text() ]
+ ) );
+
+ return [ $nsLabel, "$nsSelect $invert $associated" ];
+ }
+
+ /**
+ * Filter $rows by categories set in $opts
+ *
+ * @deprecated since 1.31
+ *
+ * @param IResultWrapper &$rows Database rows
+ * @param FormOptions $opts
+ */
+ function filterByCategories( &$rows, FormOptions $opts ) {
+ wfDeprecated( __METHOD__, '1.31' );
+
+ $categories = array_map( 'trim', explode( '|', $opts['categories'] ) );
+
+ if ( $categories === [] ) {
+ return;
+ }
+
+ # Filter categories
+ $cats = [];
+ foreach ( $categories as $cat ) {
+ $cat = trim( $cat );
+ if ( $cat == '' ) {
+ continue;
+ }
+ $cats[] = $cat;
+ }
+
+ # Filter articles
+ $articles = [];
+ $a2r = [];
+ $rowsarr = [];
+ foreach ( $rows as $k => $r ) {
+ $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
+ $id = $nt->getArticleID();
+ if ( $id == 0 ) {
+ continue; # Page might have been deleted...
+ }
+ if ( !in_array( $id, $articles ) ) {
+ $articles[] = $id;
+ }
+ if ( !isset( $a2r[$id] ) ) {
+ $a2r[$id] = [];
+ }
+ $a2r[$id][] = $k;
+ $rowsarr[$k] = $r;
+ }
+
+ # Shortcut?
+ if ( $articles === [] || $cats === [] ) {
+ return;
+ }
+
+ # Look up
+ $catFind = new CategoryFinder;
+ $catFind->seed( $articles, $cats, $opts['categories_any'] ? 'OR' : 'AND' );
+ $match = $catFind->run();
+
+ # Filter
+ $newrows = [];
+ foreach ( $match as $id ) {
+ foreach ( $a2r[$id] as $rev ) {
+ $k = $rev;
+ $newrows[$k] = $rowsarr[$k];
+ }
+ }
+ $rows = new FakeResultWrapper( array_values( $newrows ) );
+ }
+
+ /**
+ * Makes change an option link which carries all the other options
+ *
+ * @param string $title
+ * @param array $override Options to override
+ * @param array $options Current options
+ * @param bool $active Whether to show the link in bold
+ * @return string
+ */
+ function makeOptionsLink( $title, $override, $options, $active = false ) {
+ $params = $this->convertParamsForLink( $override + $options );
+
+ if ( $active ) {
+ $title = new HtmlArmor( '<strong>' . htmlspecialchars( $title ) . '</strong>' );
+ }
+
+ return $this->getLinkRenderer()->makeKnownLink( $this->getPageTitle(), $title, [
+ 'data-params' => json_encode( $override ),
+ 'data-keys' => implode( ',', array_keys( $override ) ),
+ ], $params );
+ }
+
+ /**
+ * Creates the options panel.
+ *
+ * @param array $defaults
+ * @param array $nondefaults
+ * @param int $numRows Number of rows in the result to show after this header
+ * @return string
+ */
+ function optionsPanel( $defaults, $nondefaults, $numRows ) {
+ $options = $nondefaults + $defaults;
+
+ $note = '';
+ $msg = $this->msg( 'rclegend' );
+ if ( !$msg->isDisabled() ) {
+ $note .= Html::rawElement(
+ 'div',
+ [ 'class' => 'mw-rclegend' ],
+ $msg->parse()
+ );
+ }
+
+ $lang = $this->getLanguage();
+ $user = $this->getUser();
+ $config = $this->getConfig();
+ if ( $options['from'] ) {
+ $resetLink = $this->makeOptionsLink( $this->msg( 'rclistfromreset' ),
+ [ 'from' => '' ], $nondefaults );
+
+ $noteFromMsg = $this->msg( 'rcnotefrom' )
+ ->numParams( $options['limit'] )
+ ->params(
+ $lang->userTimeAndDate( $options['from'], $user ),
+ $lang->userDate( $options['from'], $user ),
+ $lang->userTime( $options['from'], $user )
+ )
+ ->numParams( $numRows );
+ $note .= Html::rawElement(
+ 'span',
+ [ 'class' => 'rcnotefrom' ],
+ $noteFromMsg->parse()
+ ) .
+ ' ' .
+ Html::rawElement(
+ 'span',
+ [ 'class' => 'rcoptions-listfromreset' ],
+ $this->msg( 'parentheses' )->rawParams( $resetLink )->parse()
+ ) .
+ '<br />';
+ }
+
+ # Sort data for display and make sure it's unique after we've added user data.
+ $linkLimits = $config->get( 'RCLinkLimits' );
+ $linkLimits[] = $options['limit'];
+ sort( $linkLimits );
+ $linkLimits = array_unique( $linkLimits );
+
+ $linkDays = $config->get( 'RCLinkDays' );
+ $linkDays[] = $options['days'];
+ sort( $linkDays );
+ $linkDays = array_unique( $linkDays );
+
+ // limit links
+ $cl = [];
+ foreach ( $linkLimits as $value ) {
+ $cl[] = $this->makeOptionsLink( $lang->formatNum( $value ),
+ [ 'limit' => $value ], $nondefaults, $value == $options['limit'] );
+ }
+ $cl = $lang->pipeList( $cl );
+
+ // day links, reset 'from' to none
+ $dl = [];
+ foreach ( $linkDays as $value ) {
+ $dl[] = $this->makeOptionsLink( $lang->formatNum( $value ),
+ [ 'days' => $value, 'from' => '' ], $nondefaults, $value == $options['days'] );
+ }
+ $dl = $lang->pipeList( $dl );
+
+ $showhide = [ 'show', 'hide' ];
+
+ $links = [];
+
+ foreach ( $this->getLegacyShowHideFilters() as $key => $filter ) {
+ $msg = $filter->getShowHide();
+ $linkMessage = $this->msg( $msg . '-' . $showhide[1 - $options[$key]] );
+ // Extensions can define additional filters, but don't need to define the corresponding
+ // messages. If they don't exist, just fall back to 'show' and 'hide'.
+ if ( !$linkMessage->exists() ) {
+ $linkMessage = $this->msg( $showhide[1 - $options[$key]] );
+ }
+
+ $link = $this->makeOptionsLink( $linkMessage->text(),
+ [ $key => 1 - $options[$key] ], $nondefaults );
+
+ $attribs = [
+ 'class' => "$msg rcshowhideoption clshowhideoption",
+ 'data-filter-name' => $filter->getName(),
+ ];
+
+ if ( $filter->isFeatureAvailableOnStructuredUi( $this ) ) {
+ $attribs['data-feature-in-structured-ui'] = true;
+ }
+
+ $links[] = Html::rawElement(
+ 'span',
+ $attribs,
+ $this->msg( $msg )->rawParams( $link )->parse()
+ );
+ }
+
+ // show from this onward link
+ $timestamp = wfTimestampNow();
+ $now = $lang->userTimeAndDate( $timestamp, $user );
+ $timenow = $lang->userTime( $timestamp, $user );
+ $datenow = $lang->userDate( $timestamp, $user );
+ $pipedLinks = '<span class="rcshowhide">' . $lang->pipeList( $links ) . '</span>';
+
+ $rclinks = Html::rawElement(
+ 'span',
+ [ 'class' => 'rclinks' ],
+ $this->msg( 'rclinks' )->rawParams( $cl, $dl, '' )->parse()
+ );
+
+ $rclistfrom = Html::rawElement(
+ 'span',
+ [ 'class' => 'rclistfrom' ],
+ $this->makeOptionsLink(
+ $this->msg( 'rclistfrom' )->plaintextParams( $now, $timenow, $datenow )->parse(),
+ [ 'from' => $timestamp ],
+ $nondefaults
+ )
+ );
+
+ return "{$note}$rclinks<br />$pipedLinks<br />$rclistfrom";
+ }
+
+ public function isIncludable() {
+ return true;
+ }
+
+ protected function getCacheTTL() {
+ return 60 * 5;
+ }
+
+ public function getDefaultLimit() {
+ $systemPrefValue = $this->getUser()->getIntOption( 'rclimit' );
+ // Prefer the RCFilters-specific preference if RCFilters is enabled
+ if ( $this->isStructuredFilterUiEnabled() ) {
+ return $this->getUser()->getIntOption( static::$limitPreferenceName, $systemPrefValue );
+ }
+
+ // Otherwise, use the system rclimit preference value
+ return $systemPrefValue;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Implements Special:Recentchangeslinked
+ *
+ * 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
+ */
+
+/**
+ * This is to display changes made to all articles linked in an article.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialRecentChangesLinked extends SpecialRecentChanges {
+ /** @var bool|Title */
+ protected $rclTargetTitle;
+
+ function __construct() {
+ parent::__construct( 'Recentchangeslinked' );
+ }
+
+ public function getDefaultOptions() {
+ $opts = parent::getDefaultOptions();
+ $opts->add( 'target', '' );
+ $opts->add( 'showlinkedto', false );
+
+ return $opts;
+ }
+
+ public function parseParameters( $par, FormOptions $opts ) {
+ $opts['target'] = $par;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function doMainQuery( $tables, $select, $conds, $query_options,
+ $join_conds, FormOptions $opts
+ ) {
+ $target = $opts['target'];
+ $showlinkedto = $opts['showlinkedto'];
+ $limit = $opts['limit'];
+
+ if ( $target === '' ) {
+ return false;
+ }
+ $outputPage = $this->getOutput();
+ $title = Title::newFromText( $target );
+ if ( !$title || $title->isExternal() ) {
+ $outputPage->addHTML(
+ Html::errorBox( $this->msg( 'allpagesbadtitle' )->parse() )
+ );
+ return false;
+ }
+
+ $outputPage->setPageTitle( $this->msg( 'recentchangeslinked-title', $title->getPrefixedText() ) );
+
+ /*
+ * Ordinary links are in the pagelinks table, while transclusions are
+ * in the templatelinks table, categorizations in categorylinks and
+ * image use in imagelinks. We need to somehow combine all these.
+ * Special:Whatlinkshere does this by firing multiple queries and
+ * merging the results, but the code we inherit from our parent class
+ * expects only one result set so we use UNION instead.
+ */
+
+ $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
+ $id = $title->getArticleID();
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+
+ $rcQuery = RecentChange::getQueryInfo();
+ $tables = array_merge( $tables, $rcQuery['tables'] );
+ $select = array_merge( $rcQuery['fields'], $select );
+ $join_conds = array_merge( $join_conds, $rcQuery['joins'] );
+
+ // left join with watchlist table to highlight watched rows
+ $uid = $this->getUser()->getId();
+ if ( $uid && $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
+ $tables[] = 'watchlist';
+ $select[] = 'wl_user';
+ $join_conds['watchlist'] = [ 'LEFT JOIN', [
+ 'wl_user' => $uid,
+ 'wl_title=rc_title',
+ 'wl_namespace=rc_namespace'
+ ] ];
+ }
+
+ // JOIN on page, used for 'last revision' filter highlight
+ $tables[] = 'page';
+ $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
+ $select[] = 'page_latest';
+
+ $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
+ ChangeTags::modifyDisplayQuery(
+ $tables,
+ $select,
+ $conds,
+ $join_conds,
+ $query_options,
+ $tagFilter
+ );
+
+ if ( $dbr->unionSupportsOrderAndLimit() ) {
+ if ( count( $tagFilter ) > 1 ) {
+ // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
+ // To prevent this from causing query performance problems, we need to add
+ // a GROUP BY, and add rc_id to the ORDER BY.
+ $order = [
+ 'GROUP BY' => 'rc_timestamp, rc_id',
+ 'ORDER BY' => 'rc_timestamp DESC, rc_id DESC'
+ ];
+ } else {
+ $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
+ }
+ } else {
+ $order = [];
+ }
+
+ if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
+ $opts )
+ ) {
+ return false;
+ }
+
+ if ( $ns == NS_CATEGORY && !$showlinkedto ) {
+ // special handling for categories
+ // XXX: should try to make this less kludgy
+ $link_tables = [ 'categorylinks' ];
+ $showlinkedto = true;
+ } else {
+ // for now, always join on these tables; really should be configurable as in whatlinkshere
+ $link_tables = [ 'pagelinks', 'templatelinks' ];
+ // imagelinks only contains links to pages in NS_FILE
+ if ( $ns == NS_FILE || !$showlinkedto ) {
+ $link_tables[] = 'imagelinks';
+ }
+ }
+
+ if ( $id == 0 && !$showlinkedto ) {
+ return false; // nonexistent pages can't link to any pages
+ }
+
+ // field name prefixes for all the various tables we might want to join with
+ $prefix = [
+ 'pagelinks' => 'pl',
+ 'templatelinks' => 'tl',
+ 'categorylinks' => 'cl',
+ 'imagelinks' => 'il'
+ ];
+
+ $subsql = []; // SELECT statements to combine with UNION
+
+ foreach ( $link_tables as $link_table ) {
+ $pfx = $prefix[$link_table];
+
+ // imagelinks and categorylinks tables have no xx_namespace field,
+ // and have xx_to instead of xx_title
+ if ( $link_table == 'imagelinks' ) {
+ $link_ns = NS_FILE;
+ } elseif ( $link_table == 'categorylinks' ) {
+ $link_ns = NS_CATEGORY;
+ } else {
+ $link_ns = 0;
+ }
+
+ if ( $showlinkedto ) {
+ // find changes to pages linking to this page
+ if ( $link_ns ) {
+ if ( $ns != $link_ns ) {
+ continue;
+ } // should never happen, but check anyway
+ $subconds = [ "{$pfx}_to" => $dbkey ];
+ } else {
+ $subconds = [ "{$pfx}_namespace" => $ns, "{$pfx}_title" => $dbkey ];
+ }
+ $subjoin = "rc_cur_id = {$pfx}_from";
+ } else {
+ // find changes to pages linked from this page
+ $subconds = [ "{$pfx}_from" => $id ];
+ if ( $link_table == 'imagelinks' || $link_table == 'categorylinks' ) {
+ $subconds["rc_namespace"] = $link_ns;
+ $subjoin = "rc_title = {$pfx}_to";
+ } else {
+ $subjoin = [ "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" ];
+ }
+ }
+
+ $query = $dbr->selectSQLText(
+ array_merge( $tables, [ $link_table ] ),
+ $select,
+ $conds + $subconds,
+ __METHOD__,
+ $order + $query_options,
+ $join_conds + [ $link_table => [ 'JOIN', $subjoin ] ]
+ );
+
+ if ( $dbr->unionSupportsOrderAndLimit() ) {
+ $query = $dbr->limitResult( $query, $limit );
+ }
+
+ $subsql[] = $query;
+ }
+
+ if ( count( $subsql ) == 0 ) {
+ return false; // should never happen
+ }
+ if ( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) {
+ $sql = $subsql[0];
+ } else {
+ // need to resort and relimit after union
+ $sql = $dbr->unionQueries( $subsql, $dbr::UNION_DISTINCT ) .
+ ' ORDER BY rc_timestamp DESC';
+ $sql = $dbr->limitResult( $sql, $limit, false );
+ }
+
+ $res = $dbr->query( $sql, __METHOD__ );
+
+ if ( $res->numRows() == 0 ) {
+ $this->mResultEmpty = true;
+ }
+
+ return $res;
+ }
+
+ function setTopText( FormOptions $opts ) {
+ $target = $this->getTargetTitle();
+ if ( $target ) {
+ $this->getOutput()->addBacklinkSubtitle( $target );
+ $this->getSkin()->setRelevantTitle( $target );
+ }
+ }
+
+ /**
+ * Get options to be displayed in a form
+ *
+ * @param FormOptions $opts
+ * @return array
+ */
+ function getExtraOptions( $opts ) {
+ $extraOpts = parent::getExtraOptions( $opts );
+
+ $opts->consumeValues( [ 'showlinkedto', 'target' ] );
+
+ $extraOpts['target'] = [ $this->msg( 'recentchangeslinked-page' )->escaped(),
+ Xml::input( 'target', 40, str_replace( '_', ' ', $opts['target'] ) ) .
+ Xml::check( 'showlinkedto', $opts['showlinkedto'], [ 'id' => 'showlinkedto' ] ) . ' ' .
+ Xml::label( $this->msg( 'recentchangeslinked-to' )->text(), 'showlinkedto' ) ];
+
+ $this->addHelpLink( 'Help:Related changes' );
+ return $extraOpts;
+ }
+
+ /**
+ * @return Title
+ */
+ function getTargetTitle() {
+ if ( $this->rclTargetTitle === null ) {
+ $opts = $this->getOptions();
+ if ( isset( $opts['target'] ) && $opts['target'] !== '' ) {
+ $this->rclTargetTitle = Title::newFromText( $opts['target'] );
+ } else {
+ $this->rclTargetTitle = false;
+ }
+ }
+
+ return $this->rclTargetTitle;
+ }
+
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ return $this->prefixSearchString( $search, $limit, $offset );
+ }
+
+ protected function outputNoResults() {
+ $targetTitle = $this->getTargetTitle();
+ if ( $targetTitle === false ) {
+ $this->getOutput()->addHTML(
+ Html::rawElement(
+ 'div',
+ [ 'class' => 'mw-changeslist-empty mw-changeslist-notargetpage' ],
+ $this->msg( 'recentchanges-notargetpage' )->parse()
+ )
+ );
+ } elseif ( !$targetTitle || $targetTitle->isExternal() ) {
+ $this->getOutput()->addHTML(
+ Html::rawElement(
+ 'div',
+ [ 'class' => 'mw-changeslist-empty mw-changeslist-invalidtargetpage' ],
+ $this->msg( 'allpagesbadtitle' )->parse()
+ )
+ );
+ } else {
+ parent::outputNoResults();
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Recentchanges
- *
- * 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\FakeResultWrapper;
-
-/**
- * A special page that lists last changes made to the wiki
- *
- * @ingroup SpecialPage
- */
-class SpecialRecentChanges extends ChangesListSpecialPage {
-
- protected static $savedQueriesPreferenceName = 'rcfilters-saved-queries';
- protected static $daysPreferenceName = 'rcdays'; // Use general RecentChanges preference
- protected static $limitPreferenceName = 'rcfilters-limit'; // Use RCFilters-specific preference
- protected static $collapsedPreferenceName = 'rcfilters-rc-collapsed';
-
- private $watchlistFilterGroupDefinition;
-
- public function __construct( $name = 'Recentchanges', $restriction = '' ) {
- parent::__construct( $name, $restriction );
-
- $this->watchlistFilterGroupDefinition = [
- 'name' => 'watchlist',
- 'title' => 'rcfilters-filtergroup-watchlist',
- 'class' => ChangesListStringOptionsFilterGroup::class,
- 'priority' => -9,
- 'isFullCoverage' => true,
- 'filters' => [
- [
- 'name' => 'watched',
- 'label' => 'rcfilters-filter-watchlist-watched-label',
- 'description' => 'rcfilters-filter-watchlist-watched-description',
- 'cssClassSuffix' => 'watched',
- 'isRowApplicableCallable' => function ( $ctx, $rc ) {
- return $rc->getAttribute( 'wl_user' );
- }
- ],
- [
- 'name' => 'watchednew',
- 'label' => 'rcfilters-filter-watchlist-watchednew-label',
- 'description' => 'rcfilters-filter-watchlist-watchednew-description',
- 'cssClassSuffix' => 'watchednew',
- 'isRowApplicableCallable' => function ( $ctx, $rc ) {
- return $rc->getAttribute( 'wl_user' ) &&
- $rc->getAttribute( 'rc_timestamp' ) &&
- $rc->getAttribute( 'wl_notificationtimestamp' ) &&
- $rc->getAttribute( 'rc_timestamp' ) >= $rc->getAttribute( 'wl_notificationtimestamp' );
- },
- ],
- [
- 'name' => 'notwatched',
- 'label' => 'rcfilters-filter-watchlist-notwatched-label',
- 'description' => 'rcfilters-filter-watchlist-notwatched-description',
- 'cssClassSuffix' => 'notwatched',
- 'isRowApplicableCallable' => function ( $ctx, $rc ) {
- return $rc->getAttribute( 'wl_user' ) === null;
- },
- ]
- ],
- 'default' => ChangesListStringOptionsFilterGroup::NONE,
- 'queryCallable' => function ( $specialPageClassName, $context, $dbr,
- &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedValues ) {
- sort( $selectedValues );
- $notwatchedCond = 'wl_user IS NULL';
- $watchedCond = 'wl_user IS NOT NULL';
- $newCond = 'rc_timestamp >= wl_notificationtimestamp';
-
- if ( $selectedValues === [ 'notwatched' ] ) {
- $conds[] = $notwatchedCond;
- return;
- }
-
- if ( $selectedValues === [ 'watched' ] ) {
- $conds[] = $watchedCond;
- return;
- }
-
- if ( $selectedValues === [ 'watchednew' ] ) {
- $conds[] = $dbr->makeList( [
- $watchedCond,
- $newCond
- ], LIST_AND );
- return;
- }
-
- if ( $selectedValues === [ 'notwatched', 'watched' ] ) {
- // no filters
- return;
- }
-
- if ( $selectedValues === [ 'notwatched', 'watchednew' ] ) {
- $conds[] = $dbr->makeList( [
- $notwatchedCond,
- $dbr->makeList( [
- $watchedCond,
- $newCond
- ], LIST_AND )
- ], LIST_OR );
- return;
- }
-
- if ( $selectedValues === [ 'watched', 'watchednew' ] ) {
- $conds[] = $watchedCond;
- return;
- }
-
- if ( $selectedValues === [ 'notwatched', 'watched', 'watchednew' ] ) {
- // no filters
- return;
- }
- }
- ];
- }
-
- /**
- * @param string|null $subpage
- */
- public function execute( $subpage ) {
- // Backwards-compatibility: redirect to new feed URLs
- $feedFormat = $this->getRequest()->getVal( 'feed' );
- if ( !$this->including() && $feedFormat ) {
- $query = $this->getFeedQuery();
- $query['feedformat'] = $feedFormat === 'atom' ? 'atom' : 'rss';
- $this->getOutput()->redirect( wfAppendQuery( wfScript( 'api' ), $query ) );
-
- return;
- }
-
- // 10 seconds server-side caching max
- $out = $this->getOutput();
- $out->setCdnMaxage( 10 );
- // Check if the client has a cached version
- $lastmod = $this->checkLastModified();
- if ( $lastmod === false ) {
- return;
- }
-
- $this->addHelpLink(
- '//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Recent_changes',
- true
- );
- parent::execute( $subpage );
- }
-
- /**
- * @inheritDoc
- */
- protected function transformFilterDefinition( array $filterDefinition ) {
- if ( isset( $filterDefinition['showHideSuffix'] ) ) {
- $filterDefinition['showHide'] = 'rc' . $filterDefinition['showHideSuffix'];
- }
-
- return $filterDefinition;
- }
-
- /**
- * @inheritDoc
- */
- protected function registerFilters() {
- parent::registerFilters();
-
- if (
- !$this->including() &&
- $this->getUser()->isLoggedIn() &&
- $this->getUser()->isAllowed( 'viewmywatchlist' )
- ) {
- $this->registerFiltersFromDefinitions( [ $this->watchlistFilterGroupDefinition ] );
- $watchlistGroup = $this->getFilterGroup( 'watchlist' );
- $watchlistGroup->getFilter( 'watched' )->setAsSupersetOf(
- $watchlistGroup->getFilter( 'watchednew' )
- );
- }
-
- $user = $this->getUser();
-
- $significance = $this->getFilterGroup( 'significance' );
- $hideMinor = $significance->getFilter( 'hideminor' );
- $hideMinor->setDefault( $user->getBoolOption( 'hideminor' ) );
-
- $automated = $this->getFilterGroup( 'automated' );
- $hideBots = $automated->getFilter( 'hidebots' );
- $hideBots->setDefault( true );
-
- $reviewStatus = $this->getFilterGroup( 'reviewStatus' );
- if ( $reviewStatus !== null ) {
- // Conditional on feature being available and rights
- if ( $user->getBoolOption( 'hidepatrolled' ) ) {
- $reviewStatus->setDefault( 'unpatrolled' );
- $legacyReviewStatus = $this->getFilterGroup( 'legacyReviewStatus' );
- $legacyHidePatrolled = $legacyReviewStatus->getFilter( 'hidepatrolled' );
- $legacyHidePatrolled->setDefault( true );
- }
- }
-
- $changeType = $this->getFilterGroup( 'changeType' );
- $hideCategorization = $changeType->getFilter( 'hidecategorization' );
- if ( $hideCategorization !== null ) {
- // Conditional on feature being available
- $hideCategorization->setDefault( $user->getBoolOption( 'hidecategorization' ) );
- }
- }
-
- /**
- * Process $par and put options found in $opts. Used when including the page.
- *
- * @param string $par
- * @param FormOptions $opts
- */
- public function parseParameters( $par, FormOptions $opts ) {
- parent::parseParameters( $par, $opts );
-
- $bits = preg_split( '/\s*,\s*/', trim( $par ) );
- foreach ( $bits as $bit ) {
- if ( is_numeric( $bit ) ) {
- $opts['limit'] = $bit;
- }
-
- $m = [];
- if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
- $opts['limit'] = $m[1];
- }
- if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
- $opts['days'] = $m[1];
- }
- if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
- $opts['namespace'] = $m[1];
- }
- if ( preg_match( '/^tagfilter=(.*)$/', $bit, $m ) ) {
- $opts['tagfilter'] = $m[1];
- }
- }
- }
-
- /**
- * @inheritDoc
- */
- protected function doMainQuery( $tables, $fields, $conds, $query_options,
- $join_conds, FormOptions $opts
- ) {
- $dbr = $this->getDB();
- $user = $this->getUser();
-
- $rcQuery = RecentChange::getQueryInfo();
- $tables = array_merge( $tables, $rcQuery['tables'] );
- $fields = array_merge( $rcQuery['fields'], $fields );
- $join_conds = array_merge( $join_conds, $rcQuery['joins'] );
-
- // JOIN on watchlist for users
- if ( $user->isLoggedIn() && $user->isAllowed( 'viewmywatchlist' ) ) {
- $tables[] = 'watchlist';
- $fields[] = 'wl_user';
- $fields[] = 'wl_notificationtimestamp';
- $join_conds['watchlist'] = [ 'LEFT JOIN', [
- 'wl_user' => $user->getId(),
- 'wl_title=rc_title',
- 'wl_namespace=rc_namespace'
- ] ];
- }
-
- // JOIN on page, used for 'last revision' filter highlight
- $tables[] = 'page';
- $fields[] = 'page_latest';
- $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
-
- $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
- ChangeTags::modifyDisplayQuery(
- $tables,
- $fields,
- $conds,
- $join_conds,
- $query_options,
- $tagFilter
- );
-
- if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
- $opts )
- ) {
- return false;
- }
-
- if ( $this->areFiltersInConflict() ) {
- return false;
- }
-
- $orderByAndLimit = [
- 'ORDER BY' => 'rc_timestamp DESC',
- 'LIMIT' => $opts['limit']
- ];
- if ( in_array( 'DISTINCT', $query_options ) ) {
- // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
- // In order to prevent DISTINCT from causing query performance problems,
- // we have to GROUP BY the primary key. This in turn requires us to add
- // the primary key to the end of the ORDER BY, and the old ORDER BY to the
- // start of the GROUP BY
- $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
- $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
- }
- // array_merge() is used intentionally here so that hooks can, should
- // they so desire, override the ORDER BY / LIMIT condition(s); prior to
- // MediaWiki 1.26 this used to use the plus operator instead, which meant
- // that extensions weren't able to change these conditions
- $query_options = array_merge( $orderByAndLimit, $query_options );
- $rows = $dbr->select(
- $tables,
- $fields,
- // rc_new is not an ENUM, but adding a redundant rc_new IN (0,1) gives mysql enough
- // knowledge to use an index merge if it wants (it may use some other index though).
- $conds + [ 'rc_new' => [ 0, 1 ] ],
- __METHOD__,
- $query_options,
- $join_conds
- );
-
- return $rows;
- }
-
- protected function getDB() {
- return wfGetDB( DB_REPLICA, 'recentchanges' );
- }
-
- public function outputFeedLinks() {
- $this->addFeedLinks( $this->getFeedQuery() );
- }
-
- /**
- * Get URL query parameters for action=feedrecentchanges API feed of current recent changes view.
- *
- * @return array
- */
- protected function getFeedQuery() {
- $query = array_filter( $this->getOptions()->getAllValues(), function ( $value ) {
- // API handles empty parameters in a different way
- return $value !== '';
- } );
- $query['action'] = 'feedrecentchanges';
- $feedLimit = $this->getConfig()->get( 'FeedLimit' );
- if ( $query['limit'] > $feedLimit ) {
- $query['limit'] = $feedLimit;
- }
-
- return $query;
- }
-
- /**
- * Build and output the actual changes list.
- *
- * @param IResultWrapper $rows Database rows
- * @param FormOptions $opts
- */
- public function outputChangesList( $rows, $opts ) {
- $limit = $opts['limit'];
-
- $showWatcherCount = $this->getConfig()->get( 'RCShowWatchingUsers' )
- && $this->getUser()->getOption( 'shownumberswatching' );
- $watcherCache = [];
-
- $counter = 1;
- $list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
- $list->initChangesListRows( $rows );
-
- $userShowHiddenCats = $this->getUser()->getBoolOption( 'showhiddencats' );
- $rclistOutput = $list->beginRecentChangesList();
- if ( $this->isStructuredFilterUiEnabled() ) {
- $rclistOutput .= $this->makeLegend();
- }
-
- foreach ( $rows as $obj ) {
- if ( $limit == 0 ) {
- break;
- }
- $rc = RecentChange::newFromRow( $obj );
-
- # Skip CatWatch entries for hidden cats based on user preference
- if (
- $rc->getAttribute( 'rc_type' ) == RC_CATEGORIZE &&
- !$userShowHiddenCats &&
- $rc->getParam( 'hidden-cat' )
- ) {
- continue;
- }
-
- $rc->counter = $counter++;
- # Check if the page has been updated since the last visit
- if ( $this->getConfig()->get( 'ShowUpdatedMarker' )
- && !empty( $obj->wl_notificationtimestamp )
- ) {
- $rc->notificationtimestamp = ( $obj->rc_timestamp >= $obj->wl_notificationtimestamp );
- } else {
- $rc->notificationtimestamp = false; // Default
- }
- # Check the number of users watching the page
- $rc->numberofWatchingusers = 0; // Default
- if ( $showWatcherCount && $obj->rc_namespace >= 0 ) {
- if ( !isset( $watcherCache[$obj->rc_namespace][$obj->rc_title] ) ) {
- $watcherCache[$obj->rc_namespace][$obj->rc_title] =
- MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchers(
- new TitleValue( (int)$obj->rc_namespace, $obj->rc_title )
- );
- }
- $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
- }
-
- $changeLine = $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter );
- if ( $changeLine !== false ) {
- $rclistOutput .= $changeLine;
- --$limit;
- }
- }
- $rclistOutput .= $list->endRecentChangesList();
-
- if ( $rows->numRows() === 0 ) {
- $this->outputNoResults();
- if ( !$this->including() ) {
- $this->getOutput()->setStatusCode( 404 );
- }
- } else {
- $this->getOutput()->addHTML( $rclistOutput );
- }
- }
-
- /**
- * Set the text to be displayed above the changes
- *
- * @param FormOptions $opts
- * @param int $numRows Number of rows in the result to show after this header
- */
- public function doHeader( $opts, $numRows ) {
- $this->setTopText( $opts );
-
- $defaults = $opts->getAllValues();
- $nondefaults = $opts->getChangedValues();
-
- $panel = [];
- if ( !$this->isStructuredFilterUiEnabled() ) {
- $panel[] = $this->makeLegend();
- }
- $panel[] = $this->optionsPanel( $defaults, $nondefaults, $numRows );
- $panel[] = '<hr />';
-
- $extraOpts = $this->getExtraOptions( $opts );
- $extraOptsCount = count( $extraOpts );
- $count = 0;
- $submit = ' ' . Xml::submitButton( $this->msg( 'recentchanges-submit' )->text() );
-
- $out = Xml::openElement( 'table', [ 'class' => 'mw-recentchanges-table' ] );
- foreach ( $extraOpts as $name => $optionRow ) {
- # Add submit button to the last row only
- ++$count;
- $addSubmit = ( $count === $extraOptsCount ) ? $submit : '';
-
- $out .= Xml::openElement( 'tr', [ 'class' => $name . 'Form' ] );
- if ( is_array( $optionRow ) ) {
- $out .= Xml::tags(
- 'td',
- [ 'class' => 'mw-label mw-' . $name . '-label' ],
- $optionRow[0]
- );
- $out .= Xml::tags(
- 'td',
- [ 'class' => 'mw-input' ],
- $optionRow[1] . $addSubmit
- );
- } else {
- $out .= Xml::tags(
- 'td',
- [ 'class' => 'mw-input', 'colspan' => 2 ],
- $optionRow . $addSubmit
- );
- }
- $out .= Xml::closeElement( 'tr' );
- }
- $out .= Xml::closeElement( 'table' );
-
- $unconsumed = $opts->getUnconsumedValues();
- foreach ( $unconsumed as $key => $value ) {
- $out .= Html::hidden( $key, $value );
- }
-
- $t = $this->getPageTitle();
- $out .= Html::hidden( 'title', $t->getPrefixedText() );
- $form = Xml::tags( 'form', [ 'action' => wfScript() ], $out );
- $panel[] = $form;
- $panelString = implode( "\n", $panel );
-
- $rcoptions = Xml::fieldset(
- $this->msg( 'recentchanges-legend' )->text(),
- $panelString,
- [ 'class' => 'rcoptions cloptions' ]
- );
-
- // Insert a placeholder for RCFilters
- if ( $this->isStructuredFilterUiEnabled() ) {
- $rcfilterContainer = Html::element(
- 'div',
- [ 'class' => 'rcfilters-container' ]
- );
-
- $loadingContainer = Html::rawElement(
- 'div',
- [ 'class' => 'rcfilters-spinner' ],
- Html::element(
- 'div',
- [ 'class' => 'rcfilters-spinner-bounce' ]
- )
- );
-
- // Wrap both with rcfilters-head
- $this->getOutput()->addHTML(
- Html::rawElement(
- 'div',
- [ 'class' => 'rcfilters-head' ],
- $rcfilterContainer . $rcoptions
- )
- );
-
- // Add spinner
- $this->getOutput()->addHTML( $loadingContainer );
- } else {
- $this->getOutput()->addHTML( $rcoptions );
- }
-
- $this->setBottomText( $opts );
- }
-
- /**
- * Send the text to be displayed above the options
- *
- * @param FormOptions $opts Unused
- */
- function setTopText( FormOptions $opts ) {
- $message = $this->msg( 'recentchangestext' )->inContentLanguage();
- if ( !$message->isDisabled() ) {
- $contLang = MediaWikiServices::getInstance()->getContentLanguage();
- // Parse the message in this weird ugly way to preserve the ability to include interlanguage
- // links in it (T172461). In the future when T66969 is resolved, perhaps we can just use
- // $message->parse() instead. This code is copied from Message::parseText().
- $parserOutput = MessageCache::singleton()->parse(
- $message->plain(),
- $this->getPageTitle(),
- /*linestart*/true,
- // Message class sets the interface flag to false when parsing in a language different than
- // user language, and this is wiki content language
- /*interface*/false,
- $contLang
- );
- $content = $parserOutput->getText( [
- 'enableSectionEditLinks' => false,
- ] );
- // Add only metadata here (including the language links), text is added below
- $this->getOutput()->addParserOutputMetadata( $parserOutput );
-
- $langAttributes = [
- 'lang' => $contLang->getHtmlCode(),
- 'dir' => $contLang->getDir(),
- ];
-
- $topLinksAttributes = [ 'class' => 'mw-recentchanges-toplinks' ];
-
- if ( $this->isStructuredFilterUiEnabled() ) {
- // Check whether the widget is already collapsed or expanded
- $collapsedState = $this->getRequest()->getCookie( 'rcfilters-toplinks-collapsed-state' );
- // Note that an empty/unset cookie means collapsed, so check for !== 'expanded'
- $topLinksAttributes[ 'class' ] .= $collapsedState !== 'expanded' ?
- ' mw-recentchanges-toplinks-collapsed' : '';
-
- $this->getOutput()->enableOOUI();
- $contentTitle = new OOUI\ButtonWidget( [
- 'classes' => [ 'mw-recentchanges-toplinks-title' ],
- 'label' => new OOUI\HtmlSnippet( $this->msg( 'rcfilters-other-review-tools' )->parse() ),
- 'framed' => false,
- 'indicator' => $collapsedState !== 'expanded' ? 'down' : 'up',
- 'flags' => [ 'progressive' ],
- ] );
-
- $contentWrapper = Html::rawElement( 'div',
- array_merge(
- [ 'class' => 'mw-recentchanges-toplinks-content mw-collapsible-content' ],
- $langAttributes
- ),
- $content
- );
- $content = $contentTitle . $contentWrapper;
- } else {
- // Language direction should be on the top div only
- // if the title is not there. If it is there, it's
- // interface direction, and the language/dir attributes
- // should be on the content itself
- $topLinksAttributes = array_merge( $topLinksAttributes, $langAttributes );
- }
-
- $this->getOutput()->addHTML(
- Html::rawElement( 'div', $topLinksAttributes, $content )
- );
- }
- }
-
- /**
- * Get options to be displayed in a form
- *
- * @param FormOptions $opts
- * @return array
- */
- function getExtraOptions( $opts ) {
- $opts->consumeValues( [
- 'namespace', 'invert', 'associated', 'tagfilter'
- ] );
-
- $extraOpts = [];
- $extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
-
- $tagFilter = ChangeTags::buildTagFilterSelector(
- $opts['tagfilter'], false, $this->getContext() );
- if ( count( $tagFilter ) ) {
- $extraOpts['tagfilter'] = $tagFilter;
- }
-
- // Don't fire the hook for subclasses. (Or should we?)
- if ( $this->getName() === 'Recentchanges' ) {
- Hooks::run( 'SpecialRecentChangesPanel', [ &$extraOpts, $opts ] );
- }
-
- return $extraOpts;
- }
-
- /**
- * Add page-specific modules.
- */
- protected function addModules() {
- parent::addModules();
- $out = $this->getOutput();
- $out->addModules( 'mediawiki.special.recentchanges' );
- }
-
- /**
- * Get last modified date, for client caching
- * Don't use this if we are using the patrol feature, patrol changes don't
- * update the timestamp
- *
- * @return string|bool
- */
- public function checkLastModified() {
- $dbr = $this->getDB();
- $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
-
- return $lastmod;
- }
-
- /**
- * Creates the choose namespace selection
- *
- * @param FormOptions $opts
- * @return string[]
- */
- protected function namespaceFilterForm( FormOptions $opts ) {
- $nsSelect = Html::namespaceSelector(
- [ 'selected' => $opts['namespace'], 'all' => '', 'in-user-lang' => true ],
- [ 'name' => 'namespace', 'id' => 'namespace' ]
- );
- $nsLabel = Xml::label( $this->msg( 'namespace' )->text(), 'namespace' );
- $attribs = [ 'class' => [ 'mw-input-with-label' ] ];
- // Hide the checkboxes when the namespace filter is set to 'all'.
- if ( $opts['namespace'] === '' ) {
- $attribs['class'][] = 'mw-input-hidden';
- }
- $invert = Html::rawElement( 'span', $attribs, Xml::checkLabel(
- $this->msg( 'invert' )->text(), 'invert', 'nsinvert',
- $opts['invert'],
- [ 'title' => $this->msg( 'tooltip-invert' )->text() ]
- ) );
- $associated = Html::rawElement( 'span', $attribs, Xml::checkLabel(
- $this->msg( 'namespace_association' )->text(), 'associated', 'nsassociated',
- $opts['associated'],
- [ 'title' => $this->msg( 'tooltip-namespace_association' )->text() ]
- ) );
-
- return [ $nsLabel, "$nsSelect $invert $associated" ];
- }
-
- /**
- * Filter $rows by categories set in $opts
- *
- * @deprecated since 1.31
- *
- * @param IResultWrapper &$rows Database rows
- * @param FormOptions $opts
- */
- function filterByCategories( &$rows, FormOptions $opts ) {
- wfDeprecated( __METHOD__, '1.31' );
-
- $categories = array_map( 'trim', explode( '|', $opts['categories'] ) );
-
- if ( $categories === [] ) {
- return;
- }
-
- # Filter categories
- $cats = [];
- foreach ( $categories as $cat ) {
- $cat = trim( $cat );
- if ( $cat == '' ) {
- continue;
- }
- $cats[] = $cat;
- }
-
- # Filter articles
- $articles = [];
- $a2r = [];
- $rowsarr = [];
- foreach ( $rows as $k => $r ) {
- $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
- $id = $nt->getArticleID();
- if ( $id == 0 ) {
- continue; # Page might have been deleted...
- }
- if ( !in_array( $id, $articles ) ) {
- $articles[] = $id;
- }
- if ( !isset( $a2r[$id] ) ) {
- $a2r[$id] = [];
- }
- $a2r[$id][] = $k;
- $rowsarr[$k] = $r;
- }
-
- # Shortcut?
- if ( $articles === [] || $cats === [] ) {
- return;
- }
-
- # Look up
- $catFind = new CategoryFinder;
- $catFind->seed( $articles, $cats, $opts['categories_any'] ? 'OR' : 'AND' );
- $match = $catFind->run();
-
- # Filter
- $newrows = [];
- foreach ( $match as $id ) {
- foreach ( $a2r[$id] as $rev ) {
- $k = $rev;
- $newrows[$k] = $rowsarr[$k];
- }
- }
- $rows = new FakeResultWrapper( array_values( $newrows ) );
- }
-
- /**
- * Makes change an option link which carries all the other options
- *
- * @param string $title
- * @param array $override Options to override
- * @param array $options Current options
- * @param bool $active Whether to show the link in bold
- * @return string
- */
- function makeOptionsLink( $title, $override, $options, $active = false ) {
- $params = $this->convertParamsForLink( $override + $options );
-
- if ( $active ) {
- $title = new HtmlArmor( '<strong>' . htmlspecialchars( $title ) . '</strong>' );
- }
-
- return $this->getLinkRenderer()->makeKnownLink( $this->getPageTitle(), $title, [
- 'data-params' => json_encode( $override ),
- 'data-keys' => implode( ',', array_keys( $override ) ),
- ], $params );
- }
-
- /**
- * Creates the options panel.
- *
- * @param array $defaults
- * @param array $nondefaults
- * @param int $numRows Number of rows in the result to show after this header
- * @return string
- */
- function optionsPanel( $defaults, $nondefaults, $numRows ) {
- $options = $nondefaults + $defaults;
-
- $note = '';
- $msg = $this->msg( 'rclegend' );
- if ( !$msg->isDisabled() ) {
- $note .= Html::rawElement(
- 'div',
- [ 'class' => 'mw-rclegend' ],
- $msg->parse()
- );
- }
-
- $lang = $this->getLanguage();
- $user = $this->getUser();
- $config = $this->getConfig();
- if ( $options['from'] ) {
- $resetLink = $this->makeOptionsLink( $this->msg( 'rclistfromreset' ),
- [ 'from' => '' ], $nondefaults );
-
- $noteFromMsg = $this->msg( 'rcnotefrom' )
- ->numParams( $options['limit'] )
- ->params(
- $lang->userTimeAndDate( $options['from'], $user ),
- $lang->userDate( $options['from'], $user ),
- $lang->userTime( $options['from'], $user )
- )
- ->numParams( $numRows );
- $note .= Html::rawElement(
- 'span',
- [ 'class' => 'rcnotefrom' ],
- $noteFromMsg->parse()
- ) .
- ' ' .
- Html::rawElement(
- 'span',
- [ 'class' => 'rcoptions-listfromreset' ],
- $this->msg( 'parentheses' )->rawParams( $resetLink )->parse()
- ) .
- '<br />';
- }
-
- # Sort data for display and make sure it's unique after we've added user data.
- $linkLimits = $config->get( 'RCLinkLimits' );
- $linkLimits[] = $options['limit'];
- sort( $linkLimits );
- $linkLimits = array_unique( $linkLimits );
-
- $linkDays = $config->get( 'RCLinkDays' );
- $linkDays[] = $options['days'];
- sort( $linkDays );
- $linkDays = array_unique( $linkDays );
-
- // limit links
- $cl = [];
- foreach ( $linkLimits as $value ) {
- $cl[] = $this->makeOptionsLink( $lang->formatNum( $value ),
- [ 'limit' => $value ], $nondefaults, $value == $options['limit'] );
- }
- $cl = $lang->pipeList( $cl );
-
- // day links, reset 'from' to none
- $dl = [];
- foreach ( $linkDays as $value ) {
- $dl[] = $this->makeOptionsLink( $lang->formatNum( $value ),
- [ 'days' => $value, 'from' => '' ], $nondefaults, $value == $options['days'] );
- }
- $dl = $lang->pipeList( $dl );
-
- $showhide = [ 'show', 'hide' ];
-
- $links = [];
-
- foreach ( $this->getLegacyShowHideFilters() as $key => $filter ) {
- $msg = $filter->getShowHide();
- $linkMessage = $this->msg( $msg . '-' . $showhide[1 - $options[$key]] );
- // Extensions can define additional filters, but don't need to define the corresponding
- // messages. If they don't exist, just fall back to 'show' and 'hide'.
- if ( !$linkMessage->exists() ) {
- $linkMessage = $this->msg( $showhide[1 - $options[$key]] );
- }
-
- $link = $this->makeOptionsLink( $linkMessage->text(),
- [ $key => 1 - $options[$key] ], $nondefaults );
-
- $attribs = [
- 'class' => "$msg rcshowhideoption clshowhideoption",
- 'data-filter-name' => $filter->getName(),
- ];
-
- if ( $filter->isFeatureAvailableOnStructuredUi( $this ) ) {
- $attribs['data-feature-in-structured-ui'] = true;
- }
-
- $links[] = Html::rawElement(
- 'span',
- $attribs,
- $this->msg( $msg )->rawParams( $link )->parse()
- );
- }
-
- // show from this onward link
- $timestamp = wfTimestampNow();
- $now = $lang->userTimeAndDate( $timestamp, $user );
- $timenow = $lang->userTime( $timestamp, $user );
- $datenow = $lang->userDate( $timestamp, $user );
- $pipedLinks = '<span class="rcshowhide">' . $lang->pipeList( $links ) . '</span>';
-
- $rclinks = Html::rawElement(
- 'span',
- [ 'class' => 'rclinks' ],
- $this->msg( 'rclinks' )->rawParams( $cl, $dl, '' )->parse()
- );
-
- $rclistfrom = Html::rawElement(
- 'span',
- [ 'class' => 'rclistfrom' ],
- $this->makeOptionsLink(
- $this->msg( 'rclistfrom' )->plaintextParams( $now, $timenow, $datenow )->parse(),
- [ 'from' => $timestamp ],
- $nondefaults
- )
- );
-
- return "{$note}$rclinks<br />$pipedLinks<br />$rclistfrom";
- }
-
- public function isIncludable() {
- return true;
- }
-
- protected function getCacheTTL() {
- return 60 * 5;
- }
-
- public function getDefaultLimit() {
- $systemPrefValue = $this->getUser()->getIntOption( 'rclimit' );
- // Prefer the RCFilters-specific preference if RCFilters is enabled
- if ( $this->isStructuredFilterUiEnabled() ) {
- return $this->getUser()->getIntOption( static::$limitPreferenceName, $systemPrefValue );
- }
-
- // Otherwise, use the system rclimit preference value
- return $systemPrefValue;
- }
-}
+++ /dev/null
-<?php
-/**
- * Implements Special:Recentchangeslinked
- *
- * 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
- */
-
-/**
- * This is to display changes made to all articles linked in an article.
- *
- * @ingroup SpecialPage
- */
-class SpecialRecentChangesLinked extends SpecialRecentChanges {
- /** @var bool|Title */
- protected $rclTargetTitle;
-
- function __construct() {
- parent::__construct( 'Recentchangeslinked' );
- }
-
- public function getDefaultOptions() {
- $opts = parent::getDefaultOptions();
- $opts->add( 'target', '' );
- $opts->add( 'showlinkedto', false );
-
- return $opts;
- }
-
- public function parseParameters( $par, FormOptions $opts ) {
- $opts['target'] = $par;
- }
-
- /**
- * @inheritDoc
- */
- protected function doMainQuery( $tables, $select, $conds, $query_options,
- $join_conds, FormOptions $opts
- ) {
- $target = $opts['target'];
- $showlinkedto = $opts['showlinkedto'];
- $limit = $opts['limit'];
-
- if ( $target === '' ) {
- return false;
- }
- $outputPage = $this->getOutput();
- $title = Title::newFromText( $target );
- if ( !$title || $title->isExternal() ) {
- $outputPage->addHTML(
- Html::errorBox( $this->msg( 'allpagesbadtitle' )->parse() )
- );
- return false;
- }
-
- $outputPage->setPageTitle( $this->msg( 'recentchangeslinked-title', $title->getPrefixedText() ) );
-
- /*
- * Ordinary links are in the pagelinks table, while transclusions are
- * in the templatelinks table, categorizations in categorylinks and
- * image use in imagelinks. We need to somehow combine all these.
- * Special:Whatlinkshere does this by firing multiple queries and
- * merging the results, but the code we inherit from our parent class
- * expects only one result set so we use UNION instead.
- */
-
- $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
- $id = $title->getArticleID();
- $ns = $title->getNamespace();
- $dbkey = $title->getDBkey();
-
- $rcQuery = RecentChange::getQueryInfo();
- $tables = array_merge( $tables, $rcQuery['tables'] );
- $select = array_merge( $rcQuery['fields'], $select );
- $join_conds = array_merge( $join_conds, $rcQuery['joins'] );
-
- // left join with watchlist table to highlight watched rows
- $uid = $this->getUser()->getId();
- if ( $uid && $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
- $tables[] = 'watchlist';
- $select[] = 'wl_user';
- $join_conds['watchlist'] = [ 'LEFT JOIN', [
- 'wl_user' => $uid,
- 'wl_title=rc_title',
- 'wl_namespace=rc_namespace'
- ] ];
- }
-
- // JOIN on page, used for 'last revision' filter highlight
- $tables[] = 'page';
- $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
- $select[] = 'page_latest';
-
- $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
- ChangeTags::modifyDisplayQuery(
- $tables,
- $select,
- $conds,
- $join_conds,
- $query_options,
- $tagFilter
- );
-
- if ( $dbr->unionSupportsOrderAndLimit() ) {
- if ( count( $tagFilter ) > 1 ) {
- // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
- // To prevent this from causing query performance problems, we need to add
- // a GROUP BY, and add rc_id to the ORDER BY.
- $order = [
- 'GROUP BY' => 'rc_timestamp, rc_id',
- 'ORDER BY' => 'rc_timestamp DESC, rc_id DESC'
- ];
- } else {
- $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
- }
- } else {
- $order = [];
- }
-
- if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
- $opts )
- ) {
- return false;
- }
-
- if ( $ns == NS_CATEGORY && !$showlinkedto ) {
- // special handling for categories
- // XXX: should try to make this less kludgy
- $link_tables = [ 'categorylinks' ];
- $showlinkedto = true;
- } else {
- // for now, always join on these tables; really should be configurable as in whatlinkshere
- $link_tables = [ 'pagelinks', 'templatelinks' ];
- // imagelinks only contains links to pages in NS_FILE
- if ( $ns == NS_FILE || !$showlinkedto ) {
- $link_tables[] = 'imagelinks';
- }
- }
-
- if ( $id == 0 && !$showlinkedto ) {
- return false; // nonexistent pages can't link to any pages
- }
-
- // field name prefixes for all the various tables we might want to join with
- $prefix = [
- 'pagelinks' => 'pl',
- 'templatelinks' => 'tl',
- 'categorylinks' => 'cl',
- 'imagelinks' => 'il'
- ];
-
- $subsql = []; // SELECT statements to combine with UNION
-
- foreach ( $link_tables as $link_table ) {
- $pfx = $prefix[$link_table];
-
- // imagelinks and categorylinks tables have no xx_namespace field,
- // and have xx_to instead of xx_title
- if ( $link_table == 'imagelinks' ) {
- $link_ns = NS_FILE;
- } elseif ( $link_table == 'categorylinks' ) {
- $link_ns = NS_CATEGORY;
- } else {
- $link_ns = 0;
- }
-
- if ( $showlinkedto ) {
- // find changes to pages linking to this page
- if ( $link_ns ) {
- if ( $ns != $link_ns ) {
- continue;
- } // should never happen, but check anyway
- $subconds = [ "{$pfx}_to" => $dbkey ];
- } else {
- $subconds = [ "{$pfx}_namespace" => $ns, "{$pfx}_title" => $dbkey ];
- }
- $subjoin = "rc_cur_id = {$pfx}_from";
- } else {
- // find changes to pages linked from this page
- $subconds = [ "{$pfx}_from" => $id ];
- if ( $link_table == 'imagelinks' || $link_table == 'categorylinks' ) {
- $subconds["rc_namespace"] = $link_ns;
- $subjoin = "rc_title = {$pfx}_to";
- } else {
- $subjoin = [ "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" ];
- }
- }
-
- $query = $dbr->selectSQLText(
- array_merge( $tables, [ $link_table ] ),
- $select,
- $conds + $subconds,
- __METHOD__,
- $order + $query_options,
- $join_conds + [ $link_table => [ 'JOIN', $subjoin ] ]
- );
-
- if ( $dbr->unionSupportsOrderAndLimit() ) {
- $query = $dbr->limitResult( $query, $limit );
- }
-
- $subsql[] = $query;
- }
-
- if ( count( $subsql ) == 0 ) {
- return false; // should never happen
- }
- if ( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) {
- $sql = $subsql[0];
- } else {
- // need to resort and relimit after union
- $sql = $dbr->unionQueries( $subsql, $dbr::UNION_DISTINCT ) .
- ' ORDER BY rc_timestamp DESC';
- $sql = $dbr->limitResult( $sql, $limit, false );
- }
-
- $res = $dbr->query( $sql, __METHOD__ );
-
- if ( $res->numRows() == 0 ) {
- $this->mResultEmpty = true;
- }
-
- return $res;
- }
-
- function setTopText( FormOptions $opts ) {
- $target = $this->getTargetTitle();
- if ( $target ) {
- $this->getOutput()->addBacklinkSubtitle( $target );
- $this->getSkin()->setRelevantTitle( $target );
- }
- }
-
- /**
- * Get options to be displayed in a form
- *
- * @param FormOptions $opts
- * @return array
- */
- function getExtraOptions( $opts ) {
- $extraOpts = parent::getExtraOptions( $opts );
-
- $opts->consumeValues( [ 'showlinkedto', 'target' ] );
-
- $extraOpts['target'] = [ $this->msg( 'recentchangeslinked-page' )->escaped(),
- Xml::input( 'target', 40, str_replace( '_', ' ', $opts['target'] ) ) .
- Xml::check( 'showlinkedto', $opts['showlinkedto'], [ 'id' => 'showlinkedto' ] ) . ' ' .
- Xml::label( $this->msg( 'recentchangeslinked-to' )->text(), 'showlinkedto' ) ];
-
- $this->addHelpLink( 'Help:Related changes' );
- return $extraOpts;
- }
-
- /**
- * @return Title
- */
- function getTargetTitle() {
- if ( $this->rclTargetTitle === null ) {
- $opts = $this->getOptions();
- if ( isset( $opts['target'] ) && $opts['target'] !== '' ) {
- $this->rclTargetTitle = Title::newFromText( $opts['target'] );
- } else {
- $this->rclTargetTitle = false;
- }
- }
-
- return $this->rclTargetTitle;
- }
-
- /**
- * Return an array of subpages beginning with $search that this special page will accept.
- *
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return (usually 10)
- * @param int $offset Number of results to skip (usually 0)
- * @return string[] Matching subpages
- */
- public function prefixSearchSubpages( $search, $limit, $offset ) {
- return $this->prefixSearchString( $search, $limit, $offset );
- }
-
- protected function outputNoResults() {
- $targetTitle = $this->getTargetTitle();
- if ( $targetTitle === false ) {
- $this->getOutput()->addHTML(
- Html::rawElement(
- 'div',
- [ 'class' => 'mw-changeslist-empty mw-changeslist-notargetpage' ],
- $this->msg( 'recentchanges-notargetpage' )->parse()
- )
- );
- } elseif ( !$targetTitle || $targetTitle->isExternal() ) {
- $this->getOutput()->addHTML(
- Html::rawElement(
- 'div',
- [ 'class' => 'mw-changeslist-empty mw-changeslist-invalidtargetpage' ],
- $this->msg( 'allpagesbadtitle' )->parse()
- )
- );
- } else {
- parent::outputNoResults();
- }
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Revisiondelete
+ *
+ * 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 allowing users with the appropriate permissions to view
+ * and hide revisions. Log items can also be hidden.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialRevisionDelete extends UnlistedSpecialPage {
+ /** @var bool Was the DB modified in this request */
+ protected $wasSaved = false;
+
+ /** @var bool True if the submit button was clicked, and the form was posted */
+ private $submitClicked;
+
+ /** @var array Target ID list */
+ private $ids;
+
+ /** @var string Archive name, for reviewing deleted files */
+ private $archiveName;
+
+ /** @var string Edit token for securing image views against XSS */
+ private $token;
+
+ /** @var Title Title object for target parameter */
+ private $targetObj;
+
+ /** @var string Deletion type, may be revision, archive, oldimage, filearchive, logging. */
+ private $typeName;
+
+ /** @var array Array of checkbox specs (message, name, deletion bits) */
+ private $checks;
+
+ /** @var array UI Labels about the current type */
+ private $typeLabels;
+
+ /** @var RevDelList RevDelList object, storing the list of items to be deleted/undeleted */
+ private $revDelList;
+
+ /** @var bool Whether user is allowed to perform the action */
+ private $mIsAllowed;
+
+ /** @var string */
+ private $otherReason;
+
+ /**
+ * UI labels for each type.
+ */
+ private static $UILabels = [
+ 'revision' => [
+ 'check-label' => 'revdelete-hide-text',
+ 'success' => 'revdelete-success',
+ 'failure' => 'revdelete-failure',
+ 'text' => 'revdelete-text-text',
+ 'selected' => 'revdelete-selected-text',
+ ],
+ 'archive' => [
+ 'check-label' => 'revdelete-hide-text',
+ 'success' => 'revdelete-success',
+ 'failure' => 'revdelete-failure',
+ 'text' => 'revdelete-text-text',
+ 'selected' => 'revdelete-selected-text',
+ ],
+ 'oldimage' => [
+ 'check-label' => 'revdelete-hide-image',
+ 'success' => 'revdelete-success',
+ 'failure' => 'revdelete-failure',
+ 'text' => 'revdelete-text-file',
+ 'selected' => 'revdelete-selected-file',
+ ],
+ 'filearchive' => [
+ 'check-label' => 'revdelete-hide-image',
+ 'success' => 'revdelete-success',
+ 'failure' => 'revdelete-failure',
+ 'text' => 'revdelete-text-file',
+ 'selected' => 'revdelete-selected-file',
+ ],
+ 'logging' => [
+ 'check-label' => 'revdelete-hide-name',
+ 'success' => 'logdelete-success',
+ 'failure' => 'logdelete-failure',
+ 'text' => 'logdelete-text',
+ 'selected' => 'logdelete-selected',
+ ],
+ ];
+
+ public function __construct() {
+ parent::__construct( 'Revisiondelete', 'deleterevision' );
+ }
+
+ public function doesWrites() {
+ return true;
+ }
+
+ public function execute( $par ) {
+ $this->useTransactionalTimeLimit();
+
+ $this->checkPermissions();
+ $this->checkReadOnly();
+
+ $output = $this->getOutput();
+ $user = $this->getUser();
+
+ // Check blocks
+ if ( $user->isBlocked() ) {
+ throw new UserBlockedError( $user->getBlock() );
+ }
+
+ $this->setHeaders();
+ $this->outputHeader();
+ $request = $this->getRequest();
+ $this->submitClicked = $request->wasPosted() && $request->getBool( 'wpSubmit' );
+ # Handle our many different possible input types.
+ $ids = $request->getVal( 'ids' );
+ if ( !is_null( $ids ) ) {
+ # Allow CSV, for backwards compatibility, or a single ID for show/hide links
+ $this->ids = explode( ',', $ids );
+ } else {
+ # Array input
+ $this->ids = array_keys( $request->getArray( 'ids', [] ) );
+ }
+ // $this->ids = array_map( 'intval', $this->ids );
+ $this->ids = array_unique( array_filter( $this->ids ) );
+
+ $this->typeName = $request->getVal( 'type' );
+ $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
+
+ # For reviewing deleted files...
+ $this->archiveName = $request->getVal( 'file' );
+ $this->token = $request->getVal( 'token' );
+ if ( $this->archiveName && $this->targetObj ) {
+ $this->tryShowFile( $this->archiveName );
+
+ return;
+ }
+
+ $this->typeName = RevisionDeleter::getCanonicalTypeName( $this->typeName );
+
+ # No targets?
+ if ( !$this->typeName || count( $this->ids ) == 0 ) {
+ throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+ }
+
+ # Allow the list type to adjust the passed target
+ $this->targetObj = RevisionDeleter::suggestTarget(
+ $this->typeName,
+ $this->targetObj,
+ $this->ids
+ );
+
+ # We need a target page!
+ if ( $this->targetObj === null ) {
+ $output->addWikiMsg( 'undelete-header' );
+
+ return;
+ }
+
+ $this->typeLabels = self::$UILabels[$this->typeName];
+ $list = $this->getList();
+ $list->reset();
+ $this->mIsAllowed = $user->isAllowed( RevisionDeleter::getRestriction( $this->typeName ) );
+ $canViewSuppressedOnly = $this->getUser()->isAllowed( 'viewsuppressed' ) &&
+ !$this->getUser()->isAllowed( 'suppressrevision' );
+ $pageIsSuppressed = $list->areAnySuppressed();
+ $this->mIsAllowed = $this->mIsAllowed && !( $canViewSuppressedOnly && $pageIsSuppressed );
+
+ $this->otherReason = $request->getVal( 'wpReason' );
+ # Give a link to the logs/hist for this page
+ $this->showConvenienceLinks();
+
+ # Initialise checkboxes
+ $this->checks = [
+ # Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name
+ [ $this->typeLabels['check-label'], 'wpHidePrimary',
+ RevisionDeleter::getRevdelConstant( $this->typeName )
+ ],
+ [ 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ],
+ [ 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ]
+ ];
+ if ( $user->isAllowed( 'suppressrevision' ) ) {
+ $this->checks[] = [ 'revdelete-hide-restricted',
+ 'wpHideRestricted', Revision::DELETED_RESTRICTED ];
+ }
+
+ # Either submit or create our form
+ if ( $this->mIsAllowed && $this->submitClicked ) {
+ $this->submit();
+ } else {
+ $this->showForm();
+ }
+
+ if ( $user->isAllowed( 'deletedhistory' ) ) {
+ $qc = $this->getLogQueryCond();
+ # Show relevant lines from the deletion log
+ $deleteLogPage = new LogPage( 'delete' );
+ $output->addHTML( "<h2>" . $deleteLogPage->getName()->escaped() . "</h2>\n" );
+ LogEventsList::showLogExtract(
+ $output,
+ 'delete',
+ $this->targetObj,
+ '', /* user */
+ [ 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved ]
+ );
+ }
+ # Show relevant lines from the suppression log
+ if ( $user->isAllowed( 'suppressionlog' ) ) {
+ $suppressLogPage = new LogPage( 'suppress' );
+ $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" );
+ LogEventsList::showLogExtract(
+ $output,
+ 'suppress',
+ $this->targetObj,
+ '',
+ [ 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved ]
+ );
+ }
+ }
+
+ /**
+ * Show some useful links in the subtitle
+ */
+ protected function showConvenienceLinks() {
+ $linkRenderer = $this->getLinkRenderer();
+ # Give a link to the logs/hist for this page
+ if ( $this->targetObj ) {
+ // Also set header tabs to be for the target.
+ $this->getSkin()->setRelevantTitle( $this->targetObj );
+
+ $links = [];
+ $links[] = $linkRenderer->makeKnownLink(
+ SpecialPage::getTitleFor( 'Log' ),
+ $this->msg( 'viewpagelogs' )->text(),
+ [],
+ [ 'page' => $this->targetObj->getPrefixedText() ]
+ );
+ if ( !$this->targetObj->isSpecialPage() ) {
+ # Give a link to the page history
+ $links[] = $linkRenderer->makeKnownLink(
+ $this->targetObj,
+ $this->msg( 'pagehist' )->text(),
+ [],
+ [ 'action' => 'history' ]
+ );
+ # Link to deleted edits
+ if ( $this->getUser()->isAllowed( 'undelete' ) ) {
+ $undelete = SpecialPage::getTitleFor( 'Undelete' );
+ $links[] = $linkRenderer->makeKnownLink(
+ $undelete,
+ $this->msg( 'deletedhist' )->text(),
+ [],
+ [ 'target' => $this->targetObj->getPrefixedDBkey() ]
+ );
+ }
+ }
+ # Logs themselves don't have histories or archived revisions
+ $this->getOutput()->addSubtitle( $this->getLanguage()->pipeList( $links ) );
+ }
+ }
+
+ /**
+ * Get the condition used for fetching log snippets
+ * @return array
+ */
+ protected function getLogQueryCond() {
+ $conds = [];
+ // Revision delete logs for these item
+ $conds['log_type'] = [ 'delete', 'suppress' ];
+ $conds['log_action'] = $this->getList()->getLogAction();
+ $conds['ls_field'] = RevisionDeleter::getRelationType( $this->typeName );
+ $conds['ls_value'] = $this->ids;
+
+ return $conds;
+ }
+
+ /**
+ * Show a deleted file version requested by the visitor.
+ * @todo Mostly copied from Special:Undelete. Refactor.
+ * @param string $archiveName
+ * @throws MWException
+ * @throws PermissionsError
+ */
+ protected function tryShowFile( $archiveName ) {
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ $oimage = $repo->newFromArchiveName( $this->targetObj, $archiveName );
+ $oimage->load();
+ // Check if user is allowed to see this file
+ if ( !$oimage->exists() ) {
+ $this->getOutput()->addWikiMsg( 'revdelete-no-file' );
+
+ return;
+ }
+ $user = $this->getUser();
+ if ( !$oimage->userCan( File::DELETED_FILE, $user ) ) {
+ if ( $oimage->isDeleted( File::DELETED_RESTRICTED ) ) {
+ throw new PermissionsError( 'suppressrevision' );
+ } else {
+ throw new PermissionsError( 'deletedtext' );
+ }
+ }
+ if ( !$user->matchEditToken( $this->token, $archiveName ) ) {
+ $lang = $this->getLanguage();
+ $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm',
+ $this->targetObj->getText(),
+ $lang->userDate( $oimage->getTimestamp(), $user ),
+ $lang->userTime( $oimage->getTimestamp(), $user ) );
+ $this->getOutput()->addHTML(
+ Xml::openElement( 'form', [
+ 'method' => 'POST',
+ 'action' => $this->getPageTitle()->getLocalURL( [
+ 'target' => $this->targetObj->getPrefixedDBkey(),
+ 'file' => $archiveName,
+ 'token' => $user->getEditToken( $archiveName ),
+ ] )
+ ]
+ ) .
+ Xml::submitButton( $this->msg( 'revdelete-show-file-submit' )->text() ) .
+ '</form>'
+ );
+
+ return;
+ }
+ $this->getOutput()->disable();
+ # We mustn't allow the output to be CDN cached, otherwise
+ # if an admin previews a deleted image, and it's cached, then
+ # a user without appropriate permissions can toddle off and
+ # nab the image, and CDN will serve it
+ $this->getRequest()->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+ $this->getRequest()->response()->header(
+ 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate'
+ );
+ $this->getRequest()->response()->header( 'Pragma: no-cache' );
+
+ $key = $oimage->getStorageKey();
+ $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
+ $repo->streamFile( $path );
+ }
+
+ /**
+ * Get the list object for this request
+ * @return RevDelList
+ */
+ protected function getList() {
+ if ( is_null( $this->revDelList ) ) {
+ $this->revDelList = RevisionDeleter::createList(
+ $this->typeName, $this->getContext(), $this->targetObj, $this->ids
+ );
+ }
+
+ return $this->revDelList;
+ }
+
+ /**
+ * Show a list of items that we will operate on, and show a form with checkboxes
+ * which will allow the user to choose new visibility settings.
+ */
+ protected function showForm() {
+ $userAllowed = true;
+
+ // Messages: revdelete-selected-text, revdelete-selected-file, logdelete-selected
+ $out = $this->getOutput();
+ $out->wrapWikiMsg( "<strong>$1</strong>", [ $this->typeLabels['selected'],
+ $this->getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ] );
+
+ $this->addHelpLink( 'Help:RevisionDelete' );
+ $out->addHTML( "<ul>" );
+
+ $numRevisions = 0;
+ // Live revisions...
+ $list = $this->getList();
+ for ( $list->reset(); $list->current(); $list->next() ) {
+ $item = $list->current();
+
+ if ( !$item->canView() ) {
+ if ( !$this->submitClicked ) {
+ throw new PermissionsError( 'suppressrevision' );
+ }
+ $userAllowed = false;
+ }
+
+ $numRevisions++;
+ $out->addHTML( $item->getHTML() );
+ }
+
+ if ( !$numRevisions ) {
+ throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+ }
+
+ $out->addHTML( "</ul>" );
+ // Explanation text
+ $this->addUsageText();
+
+ // Normal sysops can always see what they did, but can't always change it
+ if ( !$userAllowed ) {
+ return;
+ }
+
+ // Show form if the user can submit
+ if ( $this->mIsAllowed ) {
+ $out->addModules( [ 'mediawiki.special.revisionDelete' ] );
+ $out->addModuleStyles( [ 'mediawiki.special',
+ 'mediawiki.interface.helpers.styles' ] );
+
+ $form = Xml::openElement( 'form', [ 'method' => 'post',
+ 'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ),
+ 'id' => 'mw-revdel-form-revisions' ] ) .
+ Xml::fieldset( $this->msg( 'revdelete-legend' )->text() ) .
+ $this->buildCheckBoxes() .
+ Xml::openElement( 'table' ) .
+ "<tr>\n" .
+ '<td class="mw-label">' .
+ Xml::label( $this->msg( 'revdelete-log' )->text(), 'wpRevDeleteReasonList' ) .
+ '</td>' .
+ '<td class="mw-input">' .
+ Xml::listDropDown( 'wpRevDeleteReasonList',
+ $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->text(),
+ $this->msg( 'revdelete-reasonotherlist' )->inContentLanguage()->text(),
+ $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' ), 'wpReasonDropDown'
+ ) .
+ '</td>' .
+ "</tr><tr>\n" .
+ '<td class="mw-label">' .
+ Xml::label( $this->msg( 'revdelete-otherreason' )->text(), 'wpReason' ) .
+ '</td>' .
+ '<td class="mw-input">' .
+ Xml::input( 'wpReason', 60, $this->otherReason, [
+ 'id' => 'wpReason',
+ // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+ // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+ // Unicode codepoints.
+ // "- 155" is to leave room for the 'wpRevDeleteReasonList' value.
+ 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT - 155,
+ ] ) .
+ '</td>' .
+ "</tr><tr>\n" .
+ '<td></td>' .
+ '<td class="mw-submit">' .
+ Xml::submitButton( $this->msg( 'revdelete-submit', $numRevisions )->text(),
+ [ 'name' => 'wpSubmit' ] ) .
+ '</td>' .
+ "</tr>\n" .
+ Xml::closeElement( 'table' ) .
+ Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) .
+ Html::hidden( 'target', $this->targetObj->getPrefixedText() ) .
+ Html::hidden( 'type', $this->typeName ) .
+ Html::hidden( 'ids', implode( ',', $this->ids ) ) .
+ Xml::closeElement( 'fieldset' ) . "\n" .
+ Xml::closeElement( 'form' ) . "\n";
+ // Show link to edit the dropdown reasons
+ if ( $this->getUser()->isAllowed( 'editinterface' ) ) {
+ $link = $this->getLinkRenderer()->makeKnownLink(
+ $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->getTitle(),
+ $this->msg( 'revdelete-edit-reasonlist' )->text(),
+ [],
+ [ 'action' => 'edit' ]
+ );
+ $form .= Xml::tags( 'p', [ 'class' => 'mw-revdel-editreasons' ], $link ) . "\n";
+ }
+ } else {
+ $form = '';
+ }
+ $out->addHTML( $form );
+ }
+
+ /**
+ * Show some introductory text
+ * @todo FIXME: Wikimedia-specific policy text
+ */
+ protected function addUsageText() {
+ // Messages: revdelete-text-text, revdelete-text-file, logdelete-text
+ $this->getOutput()->wrapWikiMsg(
+ "<strong>$1</strong>\n$2", $this->typeLabels['text'],
+ 'revdelete-text-others'
+ );
+
+ if ( $this->getUser()->isAllowed( 'suppressrevision' ) ) {
+ $this->getOutput()->addWikiMsg( 'revdelete-suppress-text' );
+ }
+
+ if ( $this->mIsAllowed ) {
+ $this->getOutput()->addWikiMsg( 'revdelete-confirm' );
+ }
+ }
+
+ /**
+ * @return string HTML
+ */
+ protected function buildCheckBoxes() {
+ $html = '<table>';
+ // If there is just one item, use checkboxes
+ $list = $this->getList();
+ if ( $list->length() == 1 ) {
+ $list->reset();
+ $bitfield = $list->current()->getBits(); // existing field
+
+ if ( $this->submitClicked ) {
+ $bitfield = RevisionDeleter::extractBitfield( $this->extractBitParams(), $bitfield );
+ }
+
+ foreach ( $this->checks as $item ) {
+ // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
+ // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
+ list( $message, $name, $field ) = $item;
+ $innerHTML = Xml::checkLabel(
+ $this->msg( $message )->text(),
+ $name,
+ $name,
+ $bitfield & $field
+ );
+
+ if ( $field == Revision::DELETED_RESTRICTED ) {
+ $innerHTML = "<b>$innerHTML</b>";
+ }
+
+ $line = Xml::tags( 'td', [ 'class' => 'mw-input' ], $innerHTML );
+ $html .= "<tr>$line</tr>\n";
+ }
+ } else {
+ // Otherwise, use tri-state radios
+ $html .= '<tr>';
+ $html .= '<th class="mw-revdel-checkbox">'
+ . $this->msg( 'revdelete-radio-same' )->escaped() . '</th>';
+ $html .= '<th class="mw-revdel-checkbox">'
+ . $this->msg( 'revdelete-radio-unset' )->escaped() . '</th>';
+ $html .= '<th class="mw-revdel-checkbox">'
+ . $this->msg( 'revdelete-radio-set' )->escaped() . '</th>';
+ $html .= "<th></th></tr>\n";
+ foreach ( $this->checks as $item ) {
+ // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
+ // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
+ list( $message, $name, $field ) = $item;
+ // If there are several items, use third state by default...
+ if ( $this->submitClicked ) {
+ $selected = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
+ } else {
+ $selected = -1; // use existing field
+ }
+ $line = '<td class="mw-revdel-checkbox">' . Xml::radio( $name, -1, $selected == -1 ) . '</td>';
+ $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 0, $selected == 0 ) . '</td>';
+ $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 1, $selected == 1 ) . '</td>';
+ $label = $this->msg( $message )->escaped();
+ if ( $field == Revision::DELETED_RESTRICTED ) {
+ $label = "<b>$label</b>";
+ }
+ $line .= "<td>$label</td>";
+ $html .= "<tr>$line</tr>\n";
+ }
+ }
+
+ $html .= '</table>';
+
+ return $html;
+ }
+
+ /**
+ * UI entry point for form submission.
+ * @throws PermissionsError
+ * @return bool
+ */
+ protected function submit() {
+ # Check edit token on submission
+ $token = $this->getRequest()->getVal( 'wpEditToken' );
+ if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) {
+ $this->getOutput()->addWikiMsg( 'sessionfailure' );
+
+ return false;
+ }
+ $bitParams = $this->extractBitParams();
+ // from dropdown
+ $listReason = $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' );
+ $comment = $listReason;
+ if ( $comment === 'other' ) {
+ $comment = $this->otherReason;
+ } elseif ( $this->otherReason !== '' ) {
+ // Entry from drop down menu + additional comment
+ $comment .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
+ . $this->otherReason;
+ }
+ # Can the user set this field?
+ if ( $bitParams[Revision::DELETED_RESTRICTED] == 1
+ && !$this->getUser()->isAllowed( 'suppressrevision' )
+ ) {
+ throw new PermissionsError( 'suppressrevision' );
+ }
+ # If the save went through, go to success message...
+ $status = $this->save( $bitParams, $comment );
+ if ( $status->isGood() ) {
+ $this->success();
+
+ return true;
+ } else {
+ # ...otherwise, bounce back to form...
+ $this->failure( $status );
+ }
+
+ return false;
+ }
+
+ /**
+ * Report that the submit operation succeeded
+ */
+ protected function success() {
+ // Messages: revdelete-success, logdelete-success
+ $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
+ $this->getOutput()->wrapWikiMsg(
+ "<div class=\"successbox\">\n$1\n</div>",
+ $this->typeLabels['success']
+ );
+ $this->wasSaved = true;
+ $this->revDelList->reloadFromMaster();
+ $this->showForm();
+ }
+
+ /**
+ * Report that the submit operation failed
+ * @param Status $status
+ */
+ protected function failure( $status ) {
+ // Messages: revdelete-failure, logdelete-failure
+ $this->getOutput()->setPageTitle( $this->msg( 'actionfailed' ) );
+ $this->getOutput()->wrapWikiTextAsInterface(
+ 'errorbox',
+ $status->getWikiText( $this->typeLabels['failure'] )
+ );
+ $this->showForm();
+ }
+
+ /**
+ * Put together an array that contains -1, 0, or the *_deleted const for each bit
+ *
+ * @return array
+ */
+ protected function extractBitParams() {
+ $bitfield = [];
+ foreach ( $this->checks as $item ) {
+ list( /* message */, $name, $field ) = $item;
+ $val = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
+ if ( $val < -1 || $val > 1 ) {
+ $val = -1; // -1 for existing value
+ }
+ $bitfield[$field] = $val;
+ }
+ if ( !isset( $bitfield[Revision::DELETED_RESTRICTED] ) ) {
+ $bitfield[Revision::DELETED_RESTRICTED] = 0;
+ }
+
+ return $bitfield;
+ }
+
+ /**
+ * Do the write operations. Simple wrapper for RevDel*List::setVisibility().
+ * @param array $bitPars ExtractBitParams() bitfield array
+ * @param string $reason
+ * @return Status
+ */
+ protected function save( array $bitPars, $reason ) {
+ return $this->getList()->setVisibility(
+ [ 'value' => $bitPars, 'comment' => $reason ]
+ );
+ }
+
+ protected function getGroupName() {
+ return 'pagetools';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Revisiondelete
- *
- * 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 allowing users with the appropriate permissions to view
- * and hide revisions. Log items can also be hidden.
- *
- * @ingroup SpecialPage
- */
-class SpecialRevisionDelete extends UnlistedSpecialPage {
- /** @var bool Was the DB modified in this request */
- protected $wasSaved = false;
-
- /** @var bool True if the submit button was clicked, and the form was posted */
- private $submitClicked;
-
- /** @var array Target ID list */
- private $ids;
-
- /** @var string Archive name, for reviewing deleted files */
- private $archiveName;
-
- /** @var string Edit token for securing image views against XSS */
- private $token;
-
- /** @var Title Title object for target parameter */
- private $targetObj;
-
- /** @var string Deletion type, may be revision, archive, oldimage, filearchive, logging. */
- private $typeName;
-
- /** @var array Array of checkbox specs (message, name, deletion bits) */
- private $checks;
-
- /** @var array UI Labels about the current type */
- private $typeLabels;
-
- /** @var RevDelList RevDelList object, storing the list of items to be deleted/undeleted */
- private $revDelList;
-
- /** @var bool Whether user is allowed to perform the action */
- private $mIsAllowed;
-
- /** @var string */
- private $otherReason;
-
- /**
- * UI labels for each type.
- */
- private static $UILabels = [
- 'revision' => [
- 'check-label' => 'revdelete-hide-text',
- 'success' => 'revdelete-success',
- 'failure' => 'revdelete-failure',
- 'text' => 'revdelete-text-text',
- 'selected' => 'revdelete-selected-text',
- ],
- 'archive' => [
- 'check-label' => 'revdelete-hide-text',
- 'success' => 'revdelete-success',
- 'failure' => 'revdelete-failure',
- 'text' => 'revdelete-text-text',
- 'selected' => 'revdelete-selected-text',
- ],
- 'oldimage' => [
- 'check-label' => 'revdelete-hide-image',
- 'success' => 'revdelete-success',
- 'failure' => 'revdelete-failure',
- 'text' => 'revdelete-text-file',
- 'selected' => 'revdelete-selected-file',
- ],
- 'filearchive' => [
- 'check-label' => 'revdelete-hide-image',
- 'success' => 'revdelete-success',
- 'failure' => 'revdelete-failure',
- 'text' => 'revdelete-text-file',
- 'selected' => 'revdelete-selected-file',
- ],
- 'logging' => [
- 'check-label' => 'revdelete-hide-name',
- 'success' => 'logdelete-success',
- 'failure' => 'logdelete-failure',
- 'text' => 'logdelete-text',
- 'selected' => 'logdelete-selected',
- ],
- ];
-
- public function __construct() {
- parent::__construct( 'Revisiondelete', 'deleterevision' );
- }
-
- public function doesWrites() {
- return true;
- }
-
- public function execute( $par ) {
- $this->useTransactionalTimeLimit();
-
- $this->checkPermissions();
- $this->checkReadOnly();
-
- $output = $this->getOutput();
- $user = $this->getUser();
-
- // Check blocks
- if ( $user->isBlocked() ) {
- throw new UserBlockedError( $user->getBlock() );
- }
-
- $this->setHeaders();
- $this->outputHeader();
- $request = $this->getRequest();
- $this->submitClicked = $request->wasPosted() && $request->getBool( 'wpSubmit' );
- # Handle our many different possible input types.
- $ids = $request->getVal( 'ids' );
- if ( !is_null( $ids ) ) {
- # Allow CSV, for backwards compatibility, or a single ID for show/hide links
- $this->ids = explode( ',', $ids );
- } else {
- # Array input
- $this->ids = array_keys( $request->getArray( 'ids', [] ) );
- }
- // $this->ids = array_map( 'intval', $this->ids );
- $this->ids = array_unique( array_filter( $this->ids ) );
-
- $this->typeName = $request->getVal( 'type' );
- $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
-
- # For reviewing deleted files...
- $this->archiveName = $request->getVal( 'file' );
- $this->token = $request->getVal( 'token' );
- if ( $this->archiveName && $this->targetObj ) {
- $this->tryShowFile( $this->archiveName );
-
- return;
- }
-
- $this->typeName = RevisionDeleter::getCanonicalTypeName( $this->typeName );
-
- # No targets?
- if ( !$this->typeName || count( $this->ids ) == 0 ) {
- throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
- }
-
- # Allow the list type to adjust the passed target
- $this->targetObj = RevisionDeleter::suggestTarget(
- $this->typeName,
- $this->targetObj,
- $this->ids
- );
-
- # We need a target page!
- if ( $this->targetObj === null ) {
- $output->addWikiMsg( 'undelete-header' );
-
- return;
- }
-
- $this->typeLabels = self::$UILabels[$this->typeName];
- $list = $this->getList();
- $list->reset();
- $this->mIsAllowed = $user->isAllowed( RevisionDeleter::getRestriction( $this->typeName ) );
- $canViewSuppressedOnly = $this->getUser()->isAllowed( 'viewsuppressed' ) &&
- !$this->getUser()->isAllowed( 'suppressrevision' );
- $pageIsSuppressed = $list->areAnySuppressed();
- $this->mIsAllowed = $this->mIsAllowed && !( $canViewSuppressedOnly && $pageIsSuppressed );
-
- $this->otherReason = $request->getVal( 'wpReason' );
- # Give a link to the logs/hist for this page
- $this->showConvenienceLinks();
-
- # Initialise checkboxes
- $this->checks = [
- # Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name
- [ $this->typeLabels['check-label'], 'wpHidePrimary',
- RevisionDeleter::getRevdelConstant( $this->typeName )
- ],
- [ 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ],
- [ 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ]
- ];
- if ( $user->isAllowed( 'suppressrevision' ) ) {
- $this->checks[] = [ 'revdelete-hide-restricted',
- 'wpHideRestricted', Revision::DELETED_RESTRICTED ];
- }
-
- # Either submit or create our form
- if ( $this->mIsAllowed && $this->submitClicked ) {
- $this->submit();
- } else {
- $this->showForm();
- }
-
- if ( $user->isAllowed( 'deletedhistory' ) ) {
- $qc = $this->getLogQueryCond();
- # Show relevant lines from the deletion log
- $deleteLogPage = new LogPage( 'delete' );
- $output->addHTML( "<h2>" . $deleteLogPage->getName()->escaped() . "</h2>\n" );
- LogEventsList::showLogExtract(
- $output,
- 'delete',
- $this->targetObj,
- '', /* user */
- [ 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved ]
- );
- }
- # Show relevant lines from the suppression log
- if ( $user->isAllowed( 'suppressionlog' ) ) {
- $suppressLogPage = new LogPage( 'suppress' );
- $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" );
- LogEventsList::showLogExtract(
- $output,
- 'suppress',
- $this->targetObj,
- '',
- [ 'lim' => 25, 'conds' => $qc, 'useMaster' => $this->wasSaved ]
- );
- }
- }
-
- /**
- * Show some useful links in the subtitle
- */
- protected function showConvenienceLinks() {
- $linkRenderer = $this->getLinkRenderer();
- # Give a link to the logs/hist for this page
- if ( $this->targetObj ) {
- // Also set header tabs to be for the target.
- $this->getSkin()->setRelevantTitle( $this->targetObj );
-
- $links = [];
- $links[] = $linkRenderer->makeKnownLink(
- SpecialPage::getTitleFor( 'Log' ),
- $this->msg( 'viewpagelogs' )->text(),
- [],
- [ 'page' => $this->targetObj->getPrefixedText() ]
- );
- if ( !$this->targetObj->isSpecialPage() ) {
- # Give a link to the page history
- $links[] = $linkRenderer->makeKnownLink(
- $this->targetObj,
- $this->msg( 'pagehist' )->text(),
- [],
- [ 'action' => 'history' ]
- );
- # Link to deleted edits
- if ( $this->getUser()->isAllowed( 'undelete' ) ) {
- $undelete = SpecialPage::getTitleFor( 'Undelete' );
- $links[] = $linkRenderer->makeKnownLink(
- $undelete,
- $this->msg( 'deletedhist' )->text(),
- [],
- [ 'target' => $this->targetObj->getPrefixedDBkey() ]
- );
- }
- }
- # Logs themselves don't have histories or archived revisions
- $this->getOutput()->addSubtitle( $this->getLanguage()->pipeList( $links ) );
- }
- }
-
- /**
- * Get the condition used for fetching log snippets
- * @return array
- */
- protected function getLogQueryCond() {
- $conds = [];
- // Revision delete logs for these item
- $conds['log_type'] = [ 'delete', 'suppress' ];
- $conds['log_action'] = $this->getList()->getLogAction();
- $conds['ls_field'] = RevisionDeleter::getRelationType( $this->typeName );
- $conds['ls_value'] = $this->ids;
-
- return $conds;
- }
-
- /**
- * Show a deleted file version requested by the visitor.
- * @todo Mostly copied from Special:Undelete. Refactor.
- * @param string $archiveName
- * @throws MWException
- * @throws PermissionsError
- */
- protected function tryShowFile( $archiveName ) {
- $repo = RepoGroup::singleton()->getLocalRepo();
- $oimage = $repo->newFromArchiveName( $this->targetObj, $archiveName );
- $oimage->load();
- // Check if user is allowed to see this file
- if ( !$oimage->exists() ) {
- $this->getOutput()->addWikiMsg( 'revdelete-no-file' );
-
- return;
- }
- $user = $this->getUser();
- if ( !$oimage->userCan( File::DELETED_FILE, $user ) ) {
- if ( $oimage->isDeleted( File::DELETED_RESTRICTED ) ) {
- throw new PermissionsError( 'suppressrevision' );
- } else {
- throw new PermissionsError( 'deletedtext' );
- }
- }
- if ( !$user->matchEditToken( $this->token, $archiveName ) ) {
- $lang = $this->getLanguage();
- $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm',
- $this->targetObj->getText(),
- $lang->userDate( $oimage->getTimestamp(), $user ),
- $lang->userTime( $oimage->getTimestamp(), $user ) );
- $this->getOutput()->addHTML(
- Xml::openElement( 'form', [
- 'method' => 'POST',
- 'action' => $this->getPageTitle()->getLocalURL( [
- 'target' => $this->targetObj->getPrefixedDBkey(),
- 'file' => $archiveName,
- 'token' => $user->getEditToken( $archiveName ),
- ] )
- ]
- ) .
- Xml::submitButton( $this->msg( 'revdelete-show-file-submit' )->text() ) .
- '</form>'
- );
-
- return;
- }
- $this->getOutput()->disable();
- # We mustn't allow the output to be CDN cached, otherwise
- # if an admin previews a deleted image, and it's cached, then
- # a user without appropriate permissions can toddle off and
- # nab the image, and CDN will serve it
- $this->getRequest()->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
- $this->getRequest()->response()->header(
- 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate'
- );
- $this->getRequest()->response()->header( 'Pragma: no-cache' );
-
- $key = $oimage->getStorageKey();
- $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
- $repo->streamFile( $path );
- }
-
- /**
- * Get the list object for this request
- * @return RevDelList
- */
- protected function getList() {
- if ( is_null( $this->revDelList ) ) {
- $this->revDelList = RevisionDeleter::createList(
- $this->typeName, $this->getContext(), $this->targetObj, $this->ids
- );
- }
-
- return $this->revDelList;
- }
-
- /**
- * Show a list of items that we will operate on, and show a form with checkboxes
- * which will allow the user to choose new visibility settings.
- */
- protected function showForm() {
- $userAllowed = true;
-
- // Messages: revdelete-selected-text, revdelete-selected-file, logdelete-selected
- $out = $this->getOutput();
- $out->wrapWikiMsg( "<strong>$1</strong>", [ $this->typeLabels['selected'],
- $this->getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ] );
-
- $this->addHelpLink( 'Help:RevisionDelete' );
- $out->addHTML( "<ul>" );
-
- $numRevisions = 0;
- // Live revisions...
- $list = $this->getList();
- for ( $list->reset(); $list->current(); $list->next() ) {
- $item = $list->current();
-
- if ( !$item->canView() ) {
- if ( !$this->submitClicked ) {
- throw new PermissionsError( 'suppressrevision' );
- }
- $userAllowed = false;
- }
-
- $numRevisions++;
- $out->addHTML( $item->getHTML() );
- }
-
- if ( !$numRevisions ) {
- throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
- }
-
- $out->addHTML( "</ul>" );
- // Explanation text
- $this->addUsageText();
-
- // Normal sysops can always see what they did, but can't always change it
- if ( !$userAllowed ) {
- return;
- }
-
- // Show form if the user can submit
- if ( $this->mIsAllowed ) {
- $out->addModules( [ 'mediawiki.special.revisionDelete' ] );
- $out->addModuleStyles( [ 'mediawiki.special',
- 'mediawiki.interface.helpers.styles' ] );
-
- $form = Xml::openElement( 'form', [ 'method' => 'post',
- 'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ),
- 'id' => 'mw-revdel-form-revisions' ] ) .
- Xml::fieldset( $this->msg( 'revdelete-legend' )->text() ) .
- $this->buildCheckBoxes() .
- Xml::openElement( 'table' ) .
- "<tr>\n" .
- '<td class="mw-label">' .
- Xml::label( $this->msg( 'revdelete-log' )->text(), 'wpRevDeleteReasonList' ) .
- '</td>' .
- '<td class="mw-input">' .
- Xml::listDropDown( 'wpRevDeleteReasonList',
- $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->text(),
- $this->msg( 'revdelete-reasonotherlist' )->inContentLanguage()->text(),
- $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' ), 'wpReasonDropDown'
- ) .
- '</td>' .
- "</tr><tr>\n" .
- '<td class="mw-label">' .
- Xml::label( $this->msg( 'revdelete-otherreason' )->text(), 'wpReason' ) .
- '</td>' .
- '<td class="mw-input">' .
- Xml::input( 'wpReason', 60, $this->otherReason, [
- 'id' => 'wpReason',
- // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
- // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
- // Unicode codepoints.
- // "- 155" is to leave room for the 'wpRevDeleteReasonList' value.
- 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT - 155,
- ] ) .
- '</td>' .
- "</tr><tr>\n" .
- '<td></td>' .
- '<td class="mw-submit">' .
- Xml::submitButton( $this->msg( 'revdelete-submit', $numRevisions )->text(),
- [ 'name' => 'wpSubmit' ] ) .
- '</td>' .
- "</tr>\n" .
- Xml::closeElement( 'table' ) .
- Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) .
- Html::hidden( 'target', $this->targetObj->getPrefixedText() ) .
- Html::hidden( 'type', $this->typeName ) .
- Html::hidden( 'ids', implode( ',', $this->ids ) ) .
- Xml::closeElement( 'fieldset' ) . "\n" .
- Xml::closeElement( 'form' ) . "\n";
- // Show link to edit the dropdown reasons
- if ( $this->getUser()->isAllowed( 'editinterface' ) ) {
- $link = $this->getLinkRenderer()->makeKnownLink(
- $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->getTitle(),
- $this->msg( 'revdelete-edit-reasonlist' )->text(),
- [],
- [ 'action' => 'edit' ]
- );
- $form .= Xml::tags( 'p', [ 'class' => 'mw-revdel-editreasons' ], $link ) . "\n";
- }
- } else {
- $form = '';
- }
- $out->addHTML( $form );
- }
-
- /**
- * Show some introductory text
- * @todo FIXME: Wikimedia-specific policy text
- */
- protected function addUsageText() {
- // Messages: revdelete-text-text, revdelete-text-file, logdelete-text
- $this->getOutput()->wrapWikiMsg(
- "<strong>$1</strong>\n$2", $this->typeLabels['text'],
- 'revdelete-text-others'
- );
-
- if ( $this->getUser()->isAllowed( 'suppressrevision' ) ) {
- $this->getOutput()->addWikiMsg( 'revdelete-suppress-text' );
- }
-
- if ( $this->mIsAllowed ) {
- $this->getOutput()->addWikiMsg( 'revdelete-confirm' );
- }
- }
-
- /**
- * @return string HTML
- */
- protected function buildCheckBoxes() {
- $html = '<table>';
- // If there is just one item, use checkboxes
- $list = $this->getList();
- if ( $list->length() == 1 ) {
- $list->reset();
- $bitfield = $list->current()->getBits(); // existing field
-
- if ( $this->submitClicked ) {
- $bitfield = RevisionDeleter::extractBitfield( $this->extractBitParams(), $bitfield );
- }
-
- foreach ( $this->checks as $item ) {
- // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
- // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
- list( $message, $name, $field ) = $item;
- $innerHTML = Xml::checkLabel(
- $this->msg( $message )->text(),
- $name,
- $name,
- $bitfield & $field
- );
-
- if ( $field == Revision::DELETED_RESTRICTED ) {
- $innerHTML = "<b>$innerHTML</b>";
- }
-
- $line = Xml::tags( 'td', [ 'class' => 'mw-input' ], $innerHTML );
- $html .= "<tr>$line</tr>\n";
- }
- } else {
- // Otherwise, use tri-state radios
- $html .= '<tr>';
- $html .= '<th class="mw-revdel-checkbox">'
- . $this->msg( 'revdelete-radio-same' )->escaped() . '</th>';
- $html .= '<th class="mw-revdel-checkbox">'
- . $this->msg( 'revdelete-radio-unset' )->escaped() . '</th>';
- $html .= '<th class="mw-revdel-checkbox">'
- . $this->msg( 'revdelete-radio-set' )->escaped() . '</th>';
- $html .= "<th></th></tr>\n";
- foreach ( $this->checks as $item ) {
- // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
- // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
- list( $message, $name, $field ) = $item;
- // If there are several items, use third state by default...
- if ( $this->submitClicked ) {
- $selected = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
- } else {
- $selected = -1; // use existing field
- }
- $line = '<td class="mw-revdel-checkbox">' . Xml::radio( $name, -1, $selected == -1 ) . '</td>';
- $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 0, $selected == 0 ) . '</td>';
- $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 1, $selected == 1 ) . '</td>';
- $label = $this->msg( $message )->escaped();
- if ( $field == Revision::DELETED_RESTRICTED ) {
- $label = "<b>$label</b>";
- }
- $line .= "<td>$label</td>";
- $html .= "<tr>$line</tr>\n";
- }
- }
-
- $html .= '</table>';
-
- return $html;
- }
-
- /**
- * UI entry point for form submission.
- * @throws PermissionsError
- * @return bool
- */
- protected function submit() {
- # Check edit token on submission
- $token = $this->getRequest()->getVal( 'wpEditToken' );
- if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) {
- $this->getOutput()->addWikiMsg( 'sessionfailure' );
-
- return false;
- }
- $bitParams = $this->extractBitParams();
- // from dropdown
- $listReason = $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' );
- $comment = $listReason;
- if ( $comment === 'other' ) {
- $comment = $this->otherReason;
- } elseif ( $this->otherReason !== '' ) {
- // Entry from drop down menu + additional comment
- $comment .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
- . $this->otherReason;
- }
- # Can the user set this field?
- if ( $bitParams[Revision::DELETED_RESTRICTED] == 1
- && !$this->getUser()->isAllowed( 'suppressrevision' )
- ) {
- throw new PermissionsError( 'suppressrevision' );
- }
- # If the save went through, go to success message...
- $status = $this->save( $bitParams, $comment );
- if ( $status->isGood() ) {
- $this->success();
-
- return true;
- } else {
- # ...otherwise, bounce back to form...
- $this->failure( $status );
- }
-
- return false;
- }
-
- /**
- * Report that the submit operation succeeded
- */
- protected function success() {
- // Messages: revdelete-success, logdelete-success
- $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
- $this->getOutput()->wrapWikiMsg(
- "<div class=\"successbox\">\n$1\n</div>",
- $this->typeLabels['success']
- );
- $this->wasSaved = true;
- $this->revDelList->reloadFromMaster();
- $this->showForm();
- }
-
- /**
- * Report that the submit operation failed
- * @param Status $status
- */
- protected function failure( $status ) {
- // Messages: revdelete-failure, logdelete-failure
- $this->getOutput()->setPageTitle( $this->msg( 'actionfailed' ) );
- $this->getOutput()->wrapWikiTextAsInterface(
- 'errorbox',
- $status->getWikiText( $this->typeLabels['failure'] )
- );
- $this->showForm();
- }
-
- /**
- * Put together an array that contains -1, 0, or the *_deleted const for each bit
- *
- * @return array
- */
- protected function extractBitParams() {
- $bitfield = [];
- foreach ( $this->checks as $item ) {
- list( /* message */, $name, $field ) = $item;
- $val = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
- if ( $val < -1 || $val > 1 ) {
- $val = -1; // -1 for existing value
- }
- $bitfield[$field] = $val;
- }
- if ( !isset( $bitfield[Revision::DELETED_RESTRICTED] ) ) {
- $bitfield[Revision::DELETED_RESTRICTED] = 0;
- }
-
- return $bitfield;
- }
-
- /**
- * Do the write operations. Simple wrapper for RevDel*List::setVisibility().
- * @param array $bitPars ExtractBitParams() bitfield array
- * @param string $reason
- * @return Status
- */
- protected function save( array $bitPars, $reason ) {
- return $this->getList()->setVisibility(
- [ 'value' => $bitPars, 'comment' => $reason ]
- );
- }
-
- protected function getGroupName() {
- return 'pagetools';
- }
-}
--- /dev/null
+<?php
+/**
+ * Implements Special:Whatlinkshere
+ *
+ * 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
+ * @todo Use some variant of Pager or something; the pagination here is lousy.
+ */
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Implements Special:Whatlinkshere
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialWhatLinksHere extends IncludableSpecialPage {
+ /** @var FormOptions */
+ protected $opts;
+
+ protected $selfTitle;
+
+ /** @var Title */
+ protected $target;
+
+ protected $limits = [ 20, 50, 100, 250, 500 ];
+
+ public function __construct() {
+ parent::__construct( 'Whatlinkshere' );
+ }
+
+ function execute( $par ) {
+ $out = $this->getOutput();
+
+ $this->setHeaders();
+ $this->outputHeader();
+ $this->addHelpLink( 'Help:What links here' );
+
+ $opts = new FormOptions();
+
+ $opts->add( 'target', '' );
+ $opts->add( 'namespace', '', FormOptions::INTNULL );
+ $opts->add( 'limit', $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
+ $opts->add( 'from', 0 );
+ $opts->add( 'back', 0 );
+ $opts->add( 'hideredirs', false );
+ $opts->add( 'hidetrans', false );
+ $opts->add( 'hidelinks', false );
+ $opts->add( 'hideimages', false );
+ $opts->add( 'invert', false );
+
+ $opts->fetchValuesFromRequest( $this->getRequest() );
+ $opts->validateIntBounds( 'limit', 0, 5000 );
+
+ // Give precedence to subpage syntax
+ if ( $par !== null ) {
+ $opts->setValue( 'target', $par );
+ }
+
+ // Bind to member variable
+ $this->opts = $opts;
+
+ $this->target = Title::newFromText( $opts->getValue( 'target' ) );
+ if ( !$this->target ) {
+ if ( !$this->including() ) {
+ $out->addHTML( $this->whatlinkshereForm() );
+ }
+
+ return;
+ }
+
+ $this->getSkin()->setRelevantTitle( $this->target );
+
+ $this->selfTitle = $this->getPageTitle( $this->target->getPrefixedDBkey() );
+
+ $out->setPageTitle( $this->msg( 'whatlinkshere-title', $this->target->getPrefixedText() ) );
+ $out->addBacklinkSubtitle( $this->target );
+ $this->showIndirectLinks(
+ 0,
+ $this->target,
+ $opts->getValue( 'limit' ),
+ $opts->getValue( 'from' ),
+ $opts->getValue( 'back' )
+ );
+ }
+
+ /**
+ * @param int $level Recursion level
+ * @param Title $target Target title
+ * @param int $limit Number of entries to display
+ * @param int $from Display from this article ID (default: 0)
+ * @param int $back Display from this article ID at backwards scrolling (default: 0)
+ */
+ function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
+ $out = $this->getOutput();
+ $dbr = wfGetDB( DB_REPLICA );
+
+ $hidelinks = $this->opts->getValue( 'hidelinks' );
+ $hideredirs = $this->opts->getValue( 'hideredirs' );
+ $hidetrans = $this->opts->getValue( 'hidetrans' );
+ $hideimages = $target->getNamespace() != NS_FILE || $this->opts->getValue( 'hideimages' );
+
+ $fetchlinks = ( !$hidelinks || !$hideredirs );
+
+ // Build query conds in concert for all three tables...
+ $conds['pagelinks'] = [
+ 'pl_namespace' => $target->getNamespace(),
+ 'pl_title' => $target->getDBkey(),
+ ];
+ $conds['templatelinks'] = [
+ 'tl_namespace' => $target->getNamespace(),
+ 'tl_title' => $target->getDBkey(),
+ ];
+ $conds['imagelinks'] = [
+ 'il_to' => $target->getDBkey(),
+ ];
+
+ $namespace = $this->opts->getValue( 'namespace' );
+ $invert = $this->opts->getValue( 'invert' );
+ $nsComparison = ( $invert ? '!= ' : '= ' ) . $dbr->addQuotes( $namespace );
+ if ( is_int( $namespace ) ) {
+ $conds['pagelinks'][] = "pl_from_namespace $nsComparison";
+ $conds['templatelinks'][] = "tl_from_namespace $nsComparison";
+ $conds['imagelinks'][] = "il_from_namespace $nsComparison";
+ }
+
+ if ( $from ) {
+ $conds['templatelinks'][] = "tl_from >= $from";
+ $conds['pagelinks'][] = "pl_from >= $from";
+ $conds['imagelinks'][] = "il_from >= $from";
+ }
+
+ if ( $hideredirs ) {
+ $conds['pagelinks']['rd_from'] = null;
+ } elseif ( $hidelinks ) {
+ $conds['pagelinks'][] = 'rd_from is NOT NULL';
+ }
+
+ $queryFunc = function ( IDatabase $dbr, $table, $fromCol ) use (
+ $conds, $target, $limit
+ ) {
+ // Read an extra row as an at-end check
+ $queryLimit = $limit + 1;
+ $on = [
+ "rd_from = $fromCol",
+ 'rd_title' => $target->getDBkey(),
+ 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL'
+ ];
+ $on['rd_namespace'] = $target->getNamespace();
+ // Inner LIMIT is 2X in case of stale backlinks with wrong namespaces
+ $subQuery = $dbr->buildSelectSubquery(
+ [ $table, 'redirect', 'page' ],
+ [ $fromCol, 'rd_from' ],
+ $conds[$table],
+ __CLASS__ . '::showIndirectLinks',
+ // Force JOIN order per T106682 to avoid large filesorts
+ [ 'ORDER BY' => $fromCol, 'LIMIT' => 2 * $queryLimit, 'STRAIGHT_JOIN' ],
+ [
+ 'page' => [ 'JOIN', "$fromCol = page_id" ],
+ 'redirect' => [ 'LEFT JOIN', $on ]
+ ]
+ );
+ return $dbr->select(
+ [ 'page', 'temp_backlink_range' => $subQuery ],
+ [ 'page_id', 'page_namespace', 'page_title', 'rd_from', 'page_is_redirect' ],
+ [],
+ __CLASS__ . '::showIndirectLinks',
+ [ 'ORDER BY' => 'page_id', 'LIMIT' => $queryLimit ],
+ [ 'page' => [ 'JOIN', "$fromCol = page_id" ] ]
+ );
+ };
+
+ if ( $fetchlinks ) {
+ $plRes = $queryFunc( $dbr, 'pagelinks', 'pl_from' );
+ }
+
+ if ( !$hidetrans ) {
+ $tlRes = $queryFunc( $dbr, 'templatelinks', 'tl_from' );
+ }
+
+ if ( !$hideimages ) {
+ $ilRes = $queryFunc( $dbr, 'imagelinks', 'il_from' );
+ }
+
+ if ( ( !$fetchlinks || !$plRes->numRows() )
+ && ( $hidetrans || !$tlRes->numRows() )
+ && ( $hideimages || !$ilRes->numRows() )
+ ) {
+ if ( $level == 0 && !$this->including() ) {
+ $out->addHTML( $this->whatlinkshereForm() );
+
+ // Show filters only if there are links
+ if ( $hidelinks || $hidetrans || $hideredirs || $hideimages ) {
+ $out->addHTML( $this->getFilterPanel() );
+ }
+ $msgKey = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere';
+ $link = $this->getLinkRenderer()->makeLink(
+ $this->target,
+ null,
+ [],
+ $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
+ );
+
+ $errMsg = $this->msg( $msgKey )
+ ->params( $this->target->getPrefixedText() )
+ ->rawParams( $link )
+ ->parseAsBlock();
+ $out->addHTML( $errMsg );
+ $out->setStatusCode( 404 );
+ }
+
+ return;
+ }
+
+ // Read the rows into an array and remove duplicates
+ // templatelinks comes second so that the templatelinks row overwrites the
+ // pagelinks row, so we get (inclusion) rather than nothing
+ if ( $fetchlinks ) {
+ foreach ( $plRes as $row ) {
+ $row->is_template = 0;
+ $row->is_image = 0;
+ $rows[$row->page_id] = $row;
+ }
+ }
+ if ( !$hidetrans ) {
+ foreach ( $tlRes as $row ) {
+ $row->is_template = 1;
+ $row->is_image = 0;
+ $rows[$row->page_id] = $row;
+ }
+ }
+ if ( !$hideimages ) {
+ foreach ( $ilRes as $row ) {
+ $row->is_template = 0;
+ $row->is_image = 1;
+ $rows[$row->page_id] = $row;
+ }
+ }
+
+ // Sort by key and then change the keys to 0-based indices
+ ksort( $rows );
+ $rows = array_values( $rows );
+
+ $numRows = count( $rows );
+
+ // Work out the start and end IDs, for prev/next links
+ if ( $numRows > $limit ) {
+ // More rows available after these ones
+ // Get the ID from the last row in the result set
+ $nextId = $rows[$limit]->page_id;
+ // Remove undisplayed rows
+ $rows = array_slice( $rows, 0, $limit );
+ } else {
+ // No more rows after
+ $nextId = false;
+ }
+ $prevId = $from;
+
+ // use LinkBatch to make sure, that all required data (associated with Titles)
+ // is loaded in one query
+ $lb = new LinkBatch();
+ foreach ( $rows as $row ) {
+ $lb->add( $row->page_namespace, $row->page_title );
+ }
+ $lb->execute();
+
+ if ( $level == 0 && !$this->including() ) {
+ $out->addHTML( $this->whatlinkshereForm() );
+ $out->addHTML( $this->getFilterPanel() );
+
+ $link = $this->getLinkRenderer()->makeLink(
+ $this->target,
+ null,
+ [],
+ $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
+ );
+
+ $msg = $this->msg( 'linkshere' )
+ ->params( $this->target->getPrefixedText() )
+ ->rawParams( $link )
+ ->parseAsBlock();
+ $out->addHTML( $msg );
+
+ $prevnext = $this->getPrevNext( $prevId, $nextId );
+ $out->addHTML( $prevnext );
+ }
+ $out->addHTML( $this->listStart( $level ) );
+ foreach ( $rows as $row ) {
+ $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
+
+ if ( $row->rd_from && $level < 2 ) {
+ $out->addHTML( $this->listItem( $row, $nt, $target, true ) );
+ $this->showIndirectLinks(
+ $level + 1,
+ $nt,
+ $this->getConfig()->get( 'MaxRedirectLinksRetrieved' )
+ );
+ $out->addHTML( Xml::closeElement( 'li' ) );
+ } else {
+ $out->addHTML( $this->listItem( $row, $nt, $target ) );
+ }
+ }
+
+ $out->addHTML( $this->listEnd() );
+
+ if ( $level == 0 && !$this->including() ) {
+ $out->addHTML( $prevnext );
+ }
+ }
+
+ protected function listStart( $level ) {
+ return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) );
+ }
+
+ protected function listItem( $row, $nt, $target, $notClose = false ) {
+ $dirmark = $this->getLanguage()->getDirMark();
+
+ # local message cache
+ static $msgcache = null;
+ if ( $msgcache === null ) {
+ static $msgs = [ 'isredirect', 'istemplate', 'semicolon-separator',
+ 'whatlinkshere-links', 'isimage', 'editlink' ];
+ $msgcache = [];
+ foreach ( $msgs as $msg ) {
+ $msgcache[$msg] = $this->msg( $msg )->escaped();
+ }
+ }
+
+ if ( $row->rd_from ) {
+ $query = [ 'redirect' => 'no' ];
+ } else {
+ $query = [];
+ }
+
+ $link = $this->getLinkRenderer()->makeKnownLink(
+ $nt,
+ null,
+ $row->page_is_redirect ? [ 'class' => 'mw-redirect' ] : [],
+ $query
+ );
+
+ // Display properties (redirect or template)
+ $propsText = '';
+ $props = [];
+ if ( $row->rd_from ) {
+ $props[] = $msgcache['isredirect'];
+ }
+ if ( $row->is_template ) {
+ $props[] = $msgcache['istemplate'];
+ }
+ if ( $row->is_image ) {
+ $props[] = $msgcache['isimage'];
+ }
+
+ Hooks::run( 'WhatLinksHereProps', [ $row, $nt, $target, &$props ] );
+
+ if ( count( $props ) ) {
+ $propsText = $this->msg( 'parentheses' )
+ ->rawParams( implode( $msgcache['semicolon-separator'], $props ) )->escaped();
+ }
+
+ # Space for utilities links, with a what-links-here link provided
+ $wlhLink = $this->wlhLink( $nt, $msgcache['whatlinkshere-links'], $msgcache['editlink'] );
+ $wlh = Xml::wrapClass(
+ $this->msg( 'parentheses' )->rawParams( $wlhLink )->escaped(),
+ 'mw-whatlinkshere-tools'
+ );
+
+ return $notClose ?
+ Xml::openElement( 'li' ) . "$link $propsText $dirmark $wlh\n" :
+ Xml::tags( 'li', null, "$link $propsText $dirmark $wlh" ) . "\n";
+ }
+
+ protected function listEnd() {
+ return Xml::closeElement( 'ul' );
+ }
+
+ protected function wlhLink( Title $target, $text, $editText ) {
+ static $title = null;
+ if ( $title === null ) {
+ $title = $this->getPageTitle();
+ }
+
+ $linkRenderer = $this->getLinkRenderer();
+
+ if ( $text !== null ) {
+ $text = new HtmlArmor( $text );
+ }
+
+ // always show a "<- Links" link
+ $links = [
+ 'links' => $linkRenderer->makeKnownLink(
+ $title,
+ $text,
+ [],
+ [ 'target' => $target->getPrefixedText() ]
+ ),
+ ];
+
+ // if the page is editable, add an edit link
+ if (
+ // check user permissions
+ $this->getUser()->isAllowed( 'edit' ) &&
+ // check, if the content model is editable through action=edit
+ ContentHandler::getForTitle( $target )->supportsDirectEditing()
+ ) {
+ if ( $editText !== null ) {
+ $editText = new HtmlArmor( $editText );
+ }
+
+ $links['edit'] = $linkRenderer->makeKnownLink(
+ $target,
+ $editText,
+ [],
+ [ 'action' => 'edit' ]
+ );
+ }
+
+ // build the links html
+ return $this->getLanguage()->pipeList( $links );
+ }
+
+ function makeSelfLink( $text, $query ) {
+ if ( $text !== null ) {
+ $text = new HtmlArmor( $text );
+ }
+
+ return $this->getLinkRenderer()->makeKnownLink(
+ $this->selfTitle,
+ $text,
+ [],
+ $query
+ );
+ }
+
+ function getPrevNext( $prevId, $nextId ) {
+ $currentLimit = $this->opts->getValue( 'limit' );
+ $prev = $this->msg( 'whatlinkshere-prev' )->numParams( $currentLimit )->escaped();
+ $next = $this->msg( 'whatlinkshere-next' )->numParams( $currentLimit )->escaped();
+
+ $changed = $this->opts->getChangedValues();
+ unset( $changed['target'] ); // Already in the request title
+
+ if ( $prevId != 0 ) {
+ $overrides = [ 'from' => $this->opts->getValue( 'back' ) ];
+ $prev = $this->makeSelfLink( $prev, array_merge( $changed, $overrides ) );
+ }
+ if ( $nextId != 0 ) {
+ $overrides = [ 'from' => $nextId, 'back' => $prevId ];
+ $next = $this->makeSelfLink( $next, array_merge( $changed, $overrides ) );
+ }
+
+ $limitLinks = [];
+ $lang = $this->getLanguage();
+ foreach ( $this->limits as $limit ) {
+ $prettyLimit = htmlspecialchars( $lang->formatNum( $limit ) );
+ $overrides = [ 'limit' => $limit ];
+ $limitLinks[] = $this->makeSelfLink( $prettyLimit, array_merge( $changed, $overrides ) );
+ }
+
+ $nums = $lang->pipeList( $limitLinks );
+
+ return $this->msg( 'viewprevnext' )->rawParams( $prev, $next, $nums )->escaped();
+ }
+
+ function whatlinkshereForm() {
+ // We get nicer value from the title object
+ $this->opts->consumeValue( 'target' );
+ // Reset these for new requests
+ $this->opts->consumeValues( [ 'back', 'from' ] );
+
+ $target = $this->target ? $this->target->getPrefixedText() : '';
+ $namespace = $this->opts->consumeValue( 'namespace' );
+ $nsinvert = $this->opts->consumeValue( 'invert' );
+
+ # Build up the form
+ $f = Xml::openElement( 'form', [ 'action' => wfScript() ] );
+
+ # Values that should not be forgotten
+ $f .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
+ foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
+ $f .= Html::hidden( $name, $value );
+ }
+
+ $f .= Xml::fieldset( $this->msg( 'whatlinkshere' )->text() );
+
+ # Target input (.mw-searchInput enables suggestions)
+ $f .= Xml::inputLabel( $this->msg( 'whatlinkshere-page' )->text(), 'target',
+ 'mw-whatlinkshere-target', 40, $target, [ 'class' => 'mw-searchInput' ] );
+
+ $f .= ' ';
+
+ # Namespace selector
+ $f .= Html::namespaceSelector(
+ [
+ 'selected' => $namespace,
+ 'all' => '',
+ 'label' => $this->msg( 'namespace' )->text(),
+ 'in-user-lang' => true,
+ ], [
+ 'name' => 'namespace',
+ 'id' => 'namespace',
+ 'class' => 'namespaceselector',
+ ]
+ );
+
+ $f .= "\u{00A0}" .
+ Xml::checkLabel(
+ $this->msg( 'invert' )->text(),
+ 'invert',
+ 'nsinvert',
+ $nsinvert,
+ [ 'title' => $this->msg( 'tooltip-whatlinkshere-invert' )->text() ]
+ );
+
+ $f .= ' ';
+
+ # Submit
+ $f .= Xml::submitButton( $this->msg( 'whatlinkshere-submit' )->text() );
+
+ # Close
+ $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
+
+ return $f;
+ }
+
+ /**
+ * Create filter panel
+ *
+ * @return string HTML fieldset and filter panel with the show/hide links
+ */
+ function getFilterPanel() {
+ $show = $this->msg( 'show' )->escaped();
+ $hide = $this->msg( 'hide' )->escaped();
+
+ $changed = $this->opts->getChangedValues();
+ unset( $changed['target'] ); // Already in the request title
+
+ $links = [];
+ $types = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
+ if ( $this->target->getNamespace() == NS_FILE ) {
+ $types[] = 'hideimages';
+ }
+
+ // Combined message keys: 'whatlinkshere-hideredirs', 'whatlinkshere-hidetrans',
+ // 'whatlinkshere-hidelinks', 'whatlinkshere-hideimages'
+ // To be sure they will be found by grep
+ foreach ( $types as $type ) {
+ $chosen = $this->opts->getValue( $type );
+ $msg = $chosen ? $show : $hide;
+ $overrides = [ $type => !$chosen ];
+ $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams(
+ $this->makeSelfLink( $msg, array_merge( $changed, $overrides ) ) )->escaped();
+ }
+
+ return Xml::fieldset(
+ $this->msg( 'whatlinkshere-filters' )->text(),
+ $this->getLanguage()->pipeList( $links )
+ );
+ }
+
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ return $this->prefixSearchString( $search, $limit, $offset );
+ }
+
+ protected function getGroupName() {
+ return 'pagetools';
+ }
+}
+++ /dev/null
-<?php
-/**
- * Implements Special:Whatlinkshere
- *
- * 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
- * @todo Use some variant of Pager or something; the pagination here is lousy.
- */
-
-use Wikimedia\Rdbms\IDatabase;
-
-/**
- * Implements Special:Whatlinkshere
- *
- * @ingroup SpecialPage
- */
-class SpecialWhatLinksHere extends IncludableSpecialPage {
- /** @var FormOptions */
- protected $opts;
-
- protected $selfTitle;
-
- /** @var Title */
- protected $target;
-
- protected $limits = [ 20, 50, 100, 250, 500 ];
-
- public function __construct() {
- parent::__construct( 'Whatlinkshere' );
- }
-
- function execute( $par ) {
- $out = $this->getOutput();
-
- $this->setHeaders();
- $this->outputHeader();
- $this->addHelpLink( 'Help:What links here' );
-
- $opts = new FormOptions();
-
- $opts->add( 'target', '' );
- $opts->add( 'namespace', '', FormOptions::INTNULL );
- $opts->add( 'limit', $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
- $opts->add( 'from', 0 );
- $opts->add( 'back', 0 );
- $opts->add( 'hideredirs', false );
- $opts->add( 'hidetrans', false );
- $opts->add( 'hidelinks', false );
- $opts->add( 'hideimages', false );
- $opts->add( 'invert', false );
-
- $opts->fetchValuesFromRequest( $this->getRequest() );
- $opts->validateIntBounds( 'limit', 0, 5000 );
-
- // Give precedence to subpage syntax
- if ( $par !== null ) {
- $opts->setValue( 'target', $par );
- }
-
- // Bind to member variable
- $this->opts = $opts;
-
- $this->target = Title::newFromText( $opts->getValue( 'target' ) );
- if ( !$this->target ) {
- if ( !$this->including() ) {
- $out->addHTML( $this->whatlinkshereForm() );
- }
-
- return;
- }
-
- $this->getSkin()->setRelevantTitle( $this->target );
-
- $this->selfTitle = $this->getPageTitle( $this->target->getPrefixedDBkey() );
-
- $out->setPageTitle( $this->msg( 'whatlinkshere-title', $this->target->getPrefixedText() ) );
- $out->addBacklinkSubtitle( $this->target );
- $this->showIndirectLinks(
- 0,
- $this->target,
- $opts->getValue( 'limit' ),
- $opts->getValue( 'from' ),
- $opts->getValue( 'back' )
- );
- }
-
- /**
- * @param int $level Recursion level
- * @param Title $target Target title
- * @param int $limit Number of entries to display
- * @param int $from Display from this article ID (default: 0)
- * @param int $back Display from this article ID at backwards scrolling (default: 0)
- */
- function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
- $out = $this->getOutput();
- $dbr = wfGetDB( DB_REPLICA );
-
- $hidelinks = $this->opts->getValue( 'hidelinks' );
- $hideredirs = $this->opts->getValue( 'hideredirs' );
- $hidetrans = $this->opts->getValue( 'hidetrans' );
- $hideimages = $target->getNamespace() != NS_FILE || $this->opts->getValue( 'hideimages' );
-
- $fetchlinks = ( !$hidelinks || !$hideredirs );
-
- // Build query conds in concert for all three tables...
- $conds['pagelinks'] = [
- 'pl_namespace' => $target->getNamespace(),
- 'pl_title' => $target->getDBkey(),
- ];
- $conds['templatelinks'] = [
- 'tl_namespace' => $target->getNamespace(),
- 'tl_title' => $target->getDBkey(),
- ];
- $conds['imagelinks'] = [
- 'il_to' => $target->getDBkey(),
- ];
-
- $namespace = $this->opts->getValue( 'namespace' );
- $invert = $this->opts->getValue( 'invert' );
- $nsComparison = ( $invert ? '!= ' : '= ' ) . $dbr->addQuotes( $namespace );
- if ( is_int( $namespace ) ) {
- $conds['pagelinks'][] = "pl_from_namespace $nsComparison";
- $conds['templatelinks'][] = "tl_from_namespace $nsComparison";
- $conds['imagelinks'][] = "il_from_namespace $nsComparison";
- }
-
- if ( $from ) {
- $conds['templatelinks'][] = "tl_from >= $from";
- $conds['pagelinks'][] = "pl_from >= $from";
- $conds['imagelinks'][] = "il_from >= $from";
- }
-
- if ( $hideredirs ) {
- $conds['pagelinks']['rd_from'] = null;
- } elseif ( $hidelinks ) {
- $conds['pagelinks'][] = 'rd_from is NOT NULL';
- }
-
- $queryFunc = function ( IDatabase $dbr, $table, $fromCol ) use (
- $conds, $target, $limit
- ) {
- // Read an extra row as an at-end check
- $queryLimit = $limit + 1;
- $on = [
- "rd_from = $fromCol",
- 'rd_title' => $target->getDBkey(),
- 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL'
- ];
- $on['rd_namespace'] = $target->getNamespace();
- // Inner LIMIT is 2X in case of stale backlinks with wrong namespaces
- $subQuery = $dbr->buildSelectSubquery(
- [ $table, 'redirect', 'page' ],
- [ $fromCol, 'rd_from' ],
- $conds[$table],
- __CLASS__ . '::showIndirectLinks',
- // Force JOIN order per T106682 to avoid large filesorts
- [ 'ORDER BY' => $fromCol, 'LIMIT' => 2 * $queryLimit, 'STRAIGHT_JOIN' ],
- [
- 'page' => [ 'JOIN', "$fromCol = page_id" ],
- 'redirect' => [ 'LEFT JOIN', $on ]
- ]
- );
- return $dbr->select(
- [ 'page', 'temp_backlink_range' => $subQuery ],
- [ 'page_id', 'page_namespace', 'page_title', 'rd_from', 'page_is_redirect' ],
- [],
- __CLASS__ . '::showIndirectLinks',
- [ 'ORDER BY' => 'page_id', 'LIMIT' => $queryLimit ],
- [ 'page' => [ 'JOIN', "$fromCol = page_id" ] ]
- );
- };
-
- if ( $fetchlinks ) {
- $plRes = $queryFunc( $dbr, 'pagelinks', 'pl_from' );
- }
-
- if ( !$hidetrans ) {
- $tlRes = $queryFunc( $dbr, 'templatelinks', 'tl_from' );
- }
-
- if ( !$hideimages ) {
- $ilRes = $queryFunc( $dbr, 'imagelinks', 'il_from' );
- }
-
- if ( ( !$fetchlinks || !$plRes->numRows() )
- && ( $hidetrans || !$tlRes->numRows() )
- && ( $hideimages || !$ilRes->numRows() )
- ) {
- if ( $level == 0 && !$this->including() ) {
- $out->addHTML( $this->whatlinkshereForm() );
-
- // Show filters only if there are links
- if ( $hidelinks || $hidetrans || $hideredirs || $hideimages ) {
- $out->addHTML( $this->getFilterPanel() );
- }
- $msgKey = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere';
- $link = $this->getLinkRenderer()->makeLink(
- $this->target,
- null,
- [],
- $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
- );
-
- $errMsg = $this->msg( $msgKey )
- ->params( $this->target->getPrefixedText() )
- ->rawParams( $link )
- ->parseAsBlock();
- $out->addHTML( $errMsg );
- $out->setStatusCode( 404 );
- }
-
- return;
- }
-
- // Read the rows into an array and remove duplicates
- // templatelinks comes second so that the templatelinks row overwrites the
- // pagelinks row, so we get (inclusion) rather than nothing
- if ( $fetchlinks ) {
- foreach ( $plRes as $row ) {
- $row->is_template = 0;
- $row->is_image = 0;
- $rows[$row->page_id] = $row;
- }
- }
- if ( !$hidetrans ) {
- foreach ( $tlRes as $row ) {
- $row->is_template = 1;
- $row->is_image = 0;
- $rows[$row->page_id] = $row;
- }
- }
- if ( !$hideimages ) {
- foreach ( $ilRes as $row ) {
- $row->is_template = 0;
- $row->is_image = 1;
- $rows[$row->page_id] = $row;
- }
- }
-
- // Sort by key and then change the keys to 0-based indices
- ksort( $rows );
- $rows = array_values( $rows );
-
- $numRows = count( $rows );
-
- // Work out the start and end IDs, for prev/next links
- if ( $numRows > $limit ) {
- // More rows available after these ones
- // Get the ID from the last row in the result set
- $nextId = $rows[$limit]->page_id;
- // Remove undisplayed rows
- $rows = array_slice( $rows, 0, $limit );
- } else {
- // No more rows after
- $nextId = false;
- }
- $prevId = $from;
-
- // use LinkBatch to make sure, that all required data (associated with Titles)
- // is loaded in one query
- $lb = new LinkBatch();
- foreach ( $rows as $row ) {
- $lb->add( $row->page_namespace, $row->page_title );
- }
- $lb->execute();
-
- if ( $level == 0 && !$this->including() ) {
- $out->addHTML( $this->whatlinkshereForm() );
- $out->addHTML( $this->getFilterPanel() );
-
- $link = $this->getLinkRenderer()->makeLink(
- $this->target,
- null,
- [],
- $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
- );
-
- $msg = $this->msg( 'linkshere' )
- ->params( $this->target->getPrefixedText() )
- ->rawParams( $link )
- ->parseAsBlock();
- $out->addHTML( $msg );
-
- $prevnext = $this->getPrevNext( $prevId, $nextId );
- $out->addHTML( $prevnext );
- }
- $out->addHTML( $this->listStart( $level ) );
- foreach ( $rows as $row ) {
- $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
-
- if ( $row->rd_from && $level < 2 ) {
- $out->addHTML( $this->listItem( $row, $nt, $target, true ) );
- $this->showIndirectLinks(
- $level + 1,
- $nt,
- $this->getConfig()->get( 'MaxRedirectLinksRetrieved' )
- );
- $out->addHTML( Xml::closeElement( 'li' ) );
- } else {
- $out->addHTML( $this->listItem( $row, $nt, $target ) );
- }
- }
-
- $out->addHTML( $this->listEnd() );
-
- if ( $level == 0 && !$this->including() ) {
- $out->addHTML( $prevnext );
- }
- }
-
- protected function listStart( $level ) {
- return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) );
- }
-
- protected function listItem( $row, $nt, $target, $notClose = false ) {
- $dirmark = $this->getLanguage()->getDirMark();
-
- # local message cache
- static $msgcache = null;
- if ( $msgcache === null ) {
- static $msgs = [ 'isredirect', 'istemplate', 'semicolon-separator',
- 'whatlinkshere-links', 'isimage', 'editlink' ];
- $msgcache = [];
- foreach ( $msgs as $msg ) {
- $msgcache[$msg] = $this->msg( $msg )->escaped();
- }
- }
-
- if ( $row->rd_from ) {
- $query = [ 'redirect' => 'no' ];
- } else {
- $query = [];
- }
-
- $link = $this->getLinkRenderer()->makeKnownLink(
- $nt,
- null,
- $row->page_is_redirect ? [ 'class' => 'mw-redirect' ] : [],
- $query
- );
-
- // Display properties (redirect or template)
- $propsText = '';
- $props = [];
- if ( $row->rd_from ) {
- $props[] = $msgcache['isredirect'];
- }
- if ( $row->is_template ) {
- $props[] = $msgcache['istemplate'];
- }
- if ( $row->is_image ) {
- $props[] = $msgcache['isimage'];
- }
-
- Hooks::run( 'WhatLinksHereProps', [ $row, $nt, $target, &$props ] );
-
- if ( count( $props ) ) {
- $propsText = $this->msg( 'parentheses' )
- ->rawParams( implode( $msgcache['semicolon-separator'], $props ) )->escaped();
- }
-
- # Space for utilities links, with a what-links-here link provided
- $wlhLink = $this->wlhLink( $nt, $msgcache['whatlinkshere-links'], $msgcache['editlink'] );
- $wlh = Xml::wrapClass(
- $this->msg( 'parentheses' )->rawParams( $wlhLink )->escaped(),
- 'mw-whatlinkshere-tools'
- );
-
- return $notClose ?
- Xml::openElement( 'li' ) . "$link $propsText $dirmark $wlh\n" :
- Xml::tags( 'li', null, "$link $propsText $dirmark $wlh" ) . "\n";
- }
-
- protected function listEnd() {
- return Xml::closeElement( 'ul' );
- }
-
- protected function wlhLink( Title $target, $text, $editText ) {
- static $title = null;
- if ( $title === null ) {
- $title = $this->getPageTitle();
- }
-
- $linkRenderer = $this->getLinkRenderer();
-
- if ( $text !== null ) {
- $text = new HtmlArmor( $text );
- }
-
- // always show a "<- Links" link
- $links = [
- 'links' => $linkRenderer->makeKnownLink(
- $title,
- $text,
- [],
- [ 'target' => $target->getPrefixedText() ]
- ),
- ];
-
- // if the page is editable, add an edit link
- if (
- // check user permissions
- $this->getUser()->isAllowed( 'edit' ) &&
- // check, if the content model is editable through action=edit
- ContentHandler::getForTitle( $target )->supportsDirectEditing()
- ) {
- if ( $editText !== null ) {
- $editText = new HtmlArmor( $editText );
- }
-
- $links['edit'] = $linkRenderer->makeKnownLink(
- $target,
- $editText,
- [],
- [ 'action' => 'edit' ]
- );
- }
-
- // build the links html
- return $this->getLanguage()->pipeList( $links );
- }
-
- function makeSelfLink( $text, $query ) {
- if ( $text !== null ) {
- $text = new HtmlArmor( $text );
- }
-
- return $this->getLinkRenderer()->makeKnownLink(
- $this->selfTitle,
- $text,
- [],
- $query
- );
- }
-
- function getPrevNext( $prevId, $nextId ) {
- $currentLimit = $this->opts->getValue( 'limit' );
- $prev = $this->msg( 'whatlinkshere-prev' )->numParams( $currentLimit )->escaped();
- $next = $this->msg( 'whatlinkshere-next' )->numParams( $currentLimit )->escaped();
-
- $changed = $this->opts->getChangedValues();
- unset( $changed['target'] ); // Already in the request title
-
- if ( $prevId != 0 ) {
- $overrides = [ 'from' => $this->opts->getValue( 'back' ) ];
- $prev = $this->makeSelfLink( $prev, array_merge( $changed, $overrides ) );
- }
- if ( $nextId != 0 ) {
- $overrides = [ 'from' => $nextId, 'back' => $prevId ];
- $next = $this->makeSelfLink( $next, array_merge( $changed, $overrides ) );
- }
-
- $limitLinks = [];
- $lang = $this->getLanguage();
- foreach ( $this->limits as $limit ) {
- $prettyLimit = htmlspecialchars( $lang->formatNum( $limit ) );
- $overrides = [ 'limit' => $limit ];
- $limitLinks[] = $this->makeSelfLink( $prettyLimit, array_merge( $changed, $overrides ) );
- }
-
- $nums = $lang->pipeList( $limitLinks );
-
- return $this->msg( 'viewprevnext' )->rawParams( $prev, $next, $nums )->escaped();
- }
-
- function whatlinkshereForm() {
- // We get nicer value from the title object
- $this->opts->consumeValue( 'target' );
- // Reset these for new requests
- $this->opts->consumeValues( [ 'back', 'from' ] );
-
- $target = $this->target ? $this->target->getPrefixedText() : '';
- $namespace = $this->opts->consumeValue( 'namespace' );
- $nsinvert = $this->opts->consumeValue( 'invert' );
-
- # Build up the form
- $f = Xml::openElement( 'form', [ 'action' => wfScript() ] );
-
- # Values that should not be forgotten
- $f .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
- foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
- $f .= Html::hidden( $name, $value );
- }
-
- $f .= Xml::fieldset( $this->msg( 'whatlinkshere' )->text() );
-
- # Target input (.mw-searchInput enables suggestions)
- $f .= Xml::inputLabel( $this->msg( 'whatlinkshere-page' )->text(), 'target',
- 'mw-whatlinkshere-target', 40, $target, [ 'class' => 'mw-searchInput' ] );
-
- $f .= ' ';
-
- # Namespace selector
- $f .= Html::namespaceSelector(
- [
- 'selected' => $namespace,
- 'all' => '',
- 'label' => $this->msg( 'namespace' )->text(),
- 'in-user-lang' => true,
- ], [
- 'name' => 'namespace',
- 'id' => 'namespace',
- 'class' => 'namespaceselector',
- ]
- );
-
- $f .= "\u{00A0}" .
- Xml::checkLabel(
- $this->msg( 'invert' )->text(),
- 'invert',
- 'nsinvert',
- $nsinvert,
- [ 'title' => $this->msg( 'tooltip-whatlinkshere-invert' )->text() ]
- );
-
- $f .= ' ';
-
- # Submit
- $f .= Xml::submitButton( $this->msg( 'whatlinkshere-submit' )->text() );
-
- # Close
- $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
-
- return $f;
- }
-
- /**
- * Create filter panel
- *
- * @return string HTML fieldset and filter panel with the show/hide links
- */
- function getFilterPanel() {
- $show = $this->msg( 'show' )->escaped();
- $hide = $this->msg( 'hide' )->escaped();
-
- $changed = $this->opts->getChangedValues();
- unset( $changed['target'] ); // Already in the request title
-
- $links = [];
- $types = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
- if ( $this->target->getNamespace() == NS_FILE ) {
- $types[] = 'hideimages';
- }
-
- // Combined message keys: 'whatlinkshere-hideredirs', 'whatlinkshere-hidetrans',
- // 'whatlinkshere-hidelinks', 'whatlinkshere-hideimages'
- // To be sure they will be found by grep
- foreach ( $types as $type ) {
- $chosen = $this->opts->getValue( $type );
- $msg = $chosen ? $show : $hide;
- $overrides = [ $type => !$chosen ];
- $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams(
- $this->makeSelfLink( $msg, array_merge( $changed, $overrides ) ) )->escaped();
- }
-
- return Xml::fieldset(
- $this->msg( 'whatlinkshere-filters' )->text(),
- $this->getLanguage()->pipeList( $links )
- );
- }
-
- /**
- * Return an array of subpages beginning with $search that this special page will accept.
- *
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return (usually 10)
- * @param int $offset Number of results to skip (usually 0)
- * @return string[] Matching subpages
- */
- public function prefixSearchSubpages( $search, $limit, $offset ) {
- return $this->prefixSearchString( $search, $limit, $offset );
- }
-
- protected function getGroupName() {
- return 'pagetools';
- }
-}
+++ /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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * Form to edit user preferences.
- *
- * @since 1.32
- */
-class PreferencesFormLegacy extends PreferencesFormOOUI {
- // No-op
-}
-
-/**
- * Retain the old class name for backwards compatibility.
- *
- * @deprecated since 1.32
- */
-class_alias( PreferencesFormLegacy::class, 'PreferencesForm' );
);
}
- function reportLogItem( /* ... */ ) {
+ function reportLogItem( ...$args ) {
$this->mLogItemCount++;
if ( is_callable( $this->mOriginalLogCallback ) ) {
- call_user_func_array( $this->mOriginalLogCallback, func_get_args() );
+ call_user_func_array( $this->mOriginalLogCallback, $args );
}
}
*/
public function reportPage( $title, $foreignTitle, $revisionCount,
$successCount, $pageInfo ) {
- $args = func_get_args();
- call_user_func_array( $this->mOriginalPageOutCallback, $args );
+ call_user_func_array( $this->mOriginalPageOutCallback, func_get_args() );
if ( $title === null ) {
# Invalid or non-importable title; a notice is already displayed
if ( isset( $conds['orconds']['actor'] ) ) {
// @todo: This will need changing when revision_comment_temp goes away
$queryInfo['options']['USE INDEX']['temp_rev_user'] = 'actor_timestamp';
- // Alias 'rev_timestamp' => 'revactor_timestamp' so "ORDER BY rev_timestamp" is interpreted to
- // use revactor_timestamp instead.
+ // Alias 'rev_timestamp' => 'revactor_timestamp' and 'rev_id' => 'revactor_rev' so
+ // "ORDER BY rev_timestamp, rev_id" is interpreted to use denormalized revision_actor_temp
+ // fields instead.
$queryInfo['fields'] = array_merge(
- array_diff( $queryInfo['fields'], [ 'rev_timestamp' ] ),
- [ 'rev_timestamp' => 'revactor_timestamp' ]
+ array_diff( $queryInfo['fields'], [ 'rev_timestamp', 'rev_id' ] ),
+ [ 'rev_timestamp' => 'revactor_timestamp', 'rev_id' => 'revactor_rev' ]
);
} else {
$queryInfo['options']['USE INDEX']['revision'] =
return true;
}
}
-
-/**
- * @ingroup Upload
- */
-class UploadStashFile extends UnregisteredLocalFile {
- private $fileKey;
- private $urlName;
- protected $url;
-
- /**
- * A LocalFile wrapper around a file that has been temporarily stashed,
- * so we can do things like create thumbnails for it. Arguably
- * UnregisteredLocalFile should be handling its own file repo but that
- * class is a bit retarded currently.
- *
- * @param FileRepo $repo Repository where we should find the path
- * @param string $path Path to file
- * @param string $key Key to store the path and any stashed data under
- * @throws UploadStashBadPathException
- * @throws UploadStashFileNotFoundException
- */
- public function __construct( $repo, $path, $key ) {
- $this->fileKey = $key;
-
- // resolve mwrepo:// urls
- if ( FileRepo::isVirtualUrl( $path ) ) {
- $path = $repo->resolveVirtualUrl( $path );
- } else {
- // check if path appears to be sane, no parent traversals,
- // and is in this repo's temp zone.
- $repoTempPath = $repo->getZonePath( 'temp' );
- if ( ( !$repo->validateFilename( $path ) ) ||
- ( strpos( $path, $repoTempPath ) !== 0 )
- ) {
- wfDebug( "UploadStash: tried to construct an UploadStashFile "
- . "from a file that should already exist at '$path', but path is not valid\n" );
- throw new UploadStashBadPathException(
- wfMessage( 'uploadstash-bad-path-invalid' )
- );
- }
-
- // check if path exists! and is a plain file.
- if ( !$repo->fileExists( $path ) ) {
- wfDebug( "UploadStash: tried to construct an UploadStashFile from "
- . "a file that should already exist at '$path', but path is not found\n" );
- throw new UploadStashFileNotFoundException(
- wfMessage( 'uploadstash-file-not-found-not-exists' )
- );
- }
- }
-
- parent::__construct( false, $repo, $path, false );
-
- $this->name = basename( $this->path );
- }
-
- /**
- * A method needed by the file transforming and scaling routines in File.php
- * We do not necessarily care about doing the description at this point
- * However, we also can't return the empty string, as the rest of MediaWiki
- * demands this (and calls to imagemagick convert require it to be there)
- *
- * @return string Dummy value
- */
- public function getDescriptionUrl() {
- return $this->getUrl();
- }
-
- /**
- * Get the path for the thumbnail (actually any transformation of this file)
- * The actual argument is the result of thumbName although we seem to have
- * buggy code elsewhere that expects a boolean 'suffix'
- *
- * @param string $thumbName Name of thumbnail (e.g. "120px-123456.jpg" ),
- * or false to just get the path
- * @return string Path thumbnail should take on filesystem, or containing
- * directory if thumbname is false
- */
- public function getThumbPath( $thumbName = false ) {
- $path = dirname( $this->path );
- if ( $thumbName !== false ) {
- $path .= "/$thumbName";
- }
-
- return $path;
- }
-
- /**
- * Return the file/url base name of a thumbnail with the specified parameters.
- * We override this because we want to use the pretty url name instead of the
- * ugly file name.
- *
- * @param array $params Handler-specific parameters
- * @param int $flags Bitfield that supports THUMB_* constants
- * @return string|null Base name for URL, like '120px-12345.jpg', or null if there is no handler
- */
- function thumbName( $params, $flags = 0 ) {
- return $this->generateThumbName( $this->getUrlName(), $params );
- }
-
- /**
- * Helper function -- given a 'subpage', return the local URL,
- * e.g. /wiki/Special:UploadStash/subpage
- * @param string $subPage
- * @return string Local URL for this subpage in the Special:UploadStash space.
- */
- private function getSpecialUrl( $subPage ) {
- return SpecialPage::getTitleFor( 'UploadStash', $subPage )->getLocalURL();
- }
-
- /**
- * Get a URL to access the thumbnail
- * This is required because the model of how files work requires that
- * the thumbnail urls be predictable. However, in our model the URL is
- * not based on the filename (that's hidden in the db)
- *
- * @param string $thumbName Basename of thumbnail file -- however, we don't
- * want to use the file exactly
- * @return string URL to access thumbnail, or URL with partial path
- */
- public function getThumbUrl( $thumbName = false ) {
- wfDebug( __METHOD__ . " getting for $thumbName \n" );
-
- return $this->getSpecialUrl( 'thumb/' . $this->getUrlName() . '/' . $thumbName );
- }
-
- /**
- * The basename for the URL, which we want to not be related to the filename.
- * Will also be used as the lookup key for a thumbnail file.
- *
- * @return string Base url name, like '120px-123456.jpg'
- */
- public function getUrlName() {
- if ( !$this->urlName ) {
- $this->urlName = $this->fileKey;
- }
-
- return $this->urlName;
- }
-
- /**
- * Return the URL of the file, if for some reason we wanted to download it
- * We tend not to do this for the original file, but we do want thumb icons
- *
- * @return string Url
- */
- public function getUrl() {
- if ( !isset( $this->url ) ) {
- $this->url = $this->getSpecialUrl( 'file/' . $this->getUrlName() );
- }
-
- return $this->url;
- }
-
- /**
- * Parent classes use this method, for no obvious reason, to return the path
- * (relative to wiki root, I assume). But with this class, the URL is
- * unrelated to the path.
- *
- * @return string Url
- */
- public function getFullUrl() {
- return $this->getUrl();
- }
-
- /**
- * Getter for file key (the unique id by which this file's location &
- * metadata is stored in the db)
- *
- * @return string File key
- */
- public function getFileKey() {
- return $this->fileKey;
- }
-
- /**
- * Remove the associated temporary file
- * @return status Success
- */
- public function remove() {
- if ( !$this->repo->fileExists( $this->path ) ) {
- // Maybe the file's already been removed? This could totally happen in UploadBase.
- return true;
- }
-
- return $this->repo->freeTemp( $this->path );
- }
-
- public function exists() {
- return $this->repo->fileExists( $this->path );
- }
-}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * @ingroup Upload
+ */
+class UploadStashFile extends UnregisteredLocalFile {
+ private $fileKey;
+ private $urlName;
+ protected $url;
+
+ /**
+ * A LocalFile wrapper around a file that has been temporarily stashed,
+ * so we can do things like create thumbnails for it. Arguably
+ * UnregisteredLocalFile should be handling its own file repo but that
+ * class is a bit retarded currently.
+ *
+ * @param FileRepo $repo Repository where we should find the path
+ * @param string $path Path to file
+ * @param string $key Key to store the path and any stashed data under
+ * @throws UploadStashBadPathException
+ * @throws UploadStashFileNotFoundException
+ */
+ public function __construct( $repo, $path, $key ) {
+ $this->fileKey = $key;
+
+ // resolve mwrepo:// urls
+ if ( FileRepo::isVirtualUrl( $path ) ) {
+ $path = $repo->resolveVirtualUrl( $path );
+ } else {
+ // check if path appears to be sane, no parent traversals,
+ // and is in this repo's temp zone.
+ $repoTempPath = $repo->getZonePath( 'temp' );
+ if ( ( !$repo->validateFilename( $path ) ) ||
+ ( strpos( $path, $repoTempPath ) !== 0 )
+ ) {
+ wfDebug( "UploadStash: tried to construct an UploadStashFile "
+ . "from a file that should already exist at '$path', but path is not valid\n" );
+ throw new UploadStashBadPathException(
+ wfMessage( 'uploadstash-bad-path-invalid' )
+ );
+ }
+
+ // check if path exists! and is a plain file.
+ if ( !$repo->fileExists( $path ) ) {
+ wfDebug( "UploadStash: tried to construct an UploadStashFile from "
+ . "a file that should already exist at '$path', but path is not found\n" );
+ throw new UploadStashFileNotFoundException(
+ wfMessage( 'uploadstash-file-not-found-not-exists' )
+ );
+ }
+ }
+
+ parent::__construct( false, $repo, $path, false );
+
+ $this->name = basename( $this->path );
+ }
+
+ /**
+ * A method needed by the file transforming and scaling routines in File.php
+ * We do not necessarily care about doing the description at this point
+ * However, we also can't return the empty string, as the rest of MediaWiki
+ * demands this (and calls to imagemagick convert require it to be there)
+ *
+ * @return string Dummy value
+ */
+ public function getDescriptionUrl() {
+ return $this->getUrl();
+ }
+
+ /**
+ * Get the path for the thumbnail (actually any transformation of this file)
+ * The actual argument is the result of thumbName although we seem to have
+ * buggy code elsewhere that expects a boolean 'suffix'
+ *
+ * @param string $thumbName Name of thumbnail (e.g. "120px-123456.jpg" ),
+ * or false to just get the path
+ * @return string Path thumbnail should take on filesystem, or containing
+ * directory if thumbname is false
+ */
+ public function getThumbPath( $thumbName = false ) {
+ $path = dirname( $this->path );
+ if ( $thumbName !== false ) {
+ $path .= "/$thumbName";
+ }
+
+ return $path;
+ }
+
+ /**
+ * Return the file/url base name of a thumbnail with the specified parameters.
+ * We override this because we want to use the pretty url name instead of the
+ * ugly file name.
+ *
+ * @param array $params Handler-specific parameters
+ * @param int $flags Bitfield that supports THUMB_* constants
+ * @return string|null Base name for URL, like '120px-12345.jpg', or null if there is no handler
+ */
+ function thumbName( $params, $flags = 0 ) {
+ return $this->generateThumbName( $this->getUrlName(), $params );
+ }
+
+ /**
+ * Helper function -- given a 'subpage', return the local URL,
+ * e.g. /wiki/Special:UploadStash/subpage
+ * @param string $subPage
+ * @return string Local URL for this subpage in the Special:UploadStash space.
+ */
+ private function getSpecialUrl( $subPage ) {
+ return SpecialPage::getTitleFor( 'UploadStash', $subPage )->getLocalURL();
+ }
+
+ /**
+ * Get a URL to access the thumbnail
+ * This is required because the model of how files work requires that
+ * the thumbnail urls be predictable. However, in our model the URL is
+ * not based on the filename (that's hidden in the db)
+ *
+ * @param string $thumbName Basename of thumbnail file -- however, we don't
+ * want to use the file exactly
+ * @return string URL to access thumbnail, or URL with partial path
+ */
+ public function getThumbUrl( $thumbName = false ) {
+ wfDebug( __METHOD__ . " getting for $thumbName \n" );
+
+ return $this->getSpecialUrl( 'thumb/' . $this->getUrlName() . '/' . $thumbName );
+ }
+
+ /**
+ * The basename for the URL, which we want to not be related to the filename.
+ * Will also be used as the lookup key for a thumbnail file.
+ *
+ * @return string Base url name, like '120px-123456.jpg'
+ */
+ public function getUrlName() {
+ if ( !$this->urlName ) {
+ $this->urlName = $this->fileKey;
+ }
+
+ return $this->urlName;
+ }
+
+ /**
+ * Return the URL of the file, if for some reason we wanted to download it
+ * We tend not to do this for the original file, but we do want thumb icons
+ *
+ * @return string Url
+ */
+ public function getUrl() {
+ if ( !isset( $this->url ) ) {
+ $this->url = $this->getSpecialUrl( 'file/' . $this->getUrlName() );
+ }
+
+ return $this->url;
+ }
+
+ /**
+ * Parent classes use this method, for no obvious reason, to return the path
+ * (relative to wiki root, I assume). But with this class, the URL is
+ * unrelated to the path.
+ *
+ * @return string Url
+ */
+ public function getFullUrl() {
+ return $this->getUrl();
+ }
+
+ /**
+ * Getter for file key (the unique id by which this file's location &
+ * metadata is stored in the db)
+ *
+ * @return string File key
+ */
+ public function getFileKey() {
+ return $this->fileKey;
+ }
+
+ /**
+ * Remove the associated temporary file
+ * @return bool Success
+ */
+ public function remove() {
+ if ( !$this->repo->fileExists( $this->path ) ) {
+ // Maybe the file's already been removed? This could totally happen in UploadBase.
+ return true;
+ }
+
+ return $this->repo->freeTemp( $this->path );
+ }
+
+ public function exists() {
+ return $this->repo->fileExists( $this->path );
+ }
+}
* of the database.
*/
class User implements IDBAccessObject, UserIdentity {
+
/**
- * @const int Number of characters in user_token field.
+ * Number of characters required for the user_token field.
*/
const TOKEN_LENGTH = 32;
/**
- * @const string An invalid value for user_token
+ * An invalid string value for the user_token field.
*/
const INVALID_TOKEN = '*** INVALID ***';
/**
- * @const int Serialized record version.
+ * 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;
<?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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
/**
* Accepts a list of files and directories to search for
}
}
}
-
-/**
- * Reads PHP code and returns the FQCN of every class defined within it.
- */
-class ClassCollector {
-
- /**
- * @var string Current namespace
- */
- protected $namespace = '';
-
- /**
- * @var array List of FQCN detected in this pass
- */
- protected $classes;
-
- /**
- * @var array Token from token_get_all() that started an expect sequence
- */
- protected $startToken;
-
- /**
- * @var array List of tokens that are members of the current expect sequence
- */
- protected $tokens;
-
- /**
- * @var array Class alias with target/name fields
- */
- protected $alias;
-
- /**
- * @param string $code PHP code (including <?php) to detect class names from
- * @return array List of FQCN detected within the tokens
- */
- public function getClasses( $code ) {
- $this->namespace = '';
- $this->classes = [];
- $this->startToken = null;
- $this->alias = null;
- $this->tokens = [];
-
- foreach ( token_get_all( $code ) as $token ) {
- if ( $this->startToken === null ) {
- $this->tryBeginExpect( $token );
- } else {
- $this->tryEndExpect( $token );
- }
- }
-
- return $this->classes;
- }
-
- /**
- * Determine if $token begins the next expect sequence.
- *
- * @param array $token
- */
- protected function tryBeginExpect( $token ) {
- if ( is_string( $token ) ) {
- return;
- }
- // Note: When changing class name discovery logic,
- // AutoLoaderStructureTest.php may also need to be updated.
- switch ( $token[0] ) {
- case T_NAMESPACE:
- case T_CLASS:
- case T_INTERFACE:
- case T_TRAIT:
- case T_DOUBLE_COLON:
- case T_NEW:
- $this->startToken = $token;
- break;
- case T_STRING:
- if ( $token[1] === 'class_alias' ) {
- $this->startToken = $token;
- $this->alias = [];
- }
- }
- }
-
- /**
- * Accepts the next token in an expect sequence
- *
- * @param array $token
- */
- protected function tryEndExpect( $token ) {
- switch ( $this->startToken[0] ) {
- case T_DOUBLE_COLON:
- // Skip over T_CLASS after T_DOUBLE_COLON because this is something like
- // "self::static" which accesses the class name. It doens't define a new class.
- $this->startToken = null;
- break;
- case T_NEW:
- // Skip over T_CLASS after T_NEW because this is a PHP 7 anonymous class.
- if ( !is_array( $token ) || $token[0] !== T_WHITESPACE ) {
- $this->startToken = null;
- }
- break;
- case T_NAMESPACE:
- if ( $token === ';' || $token === '{' ) {
- $this->namespace = $this->implodeTokens() . '\\';
- } else {
- $this->tokens[] = $token;
- }
- break;
-
- case T_STRING:
- if ( $this->alias !== null ) {
- // Flow 1 - Two string literals:
- // - T_STRING class_alias
- // - '('
- // - T_CONSTANT_ENCAPSED_STRING 'TargetClass'
- // - ','
- // - T_WHITESPACE
- // - T_CONSTANT_ENCAPSED_STRING 'AliasName'
- // - ')'
- // Flow 2 - Use of ::class syntax for first parameter
- // - T_STRING class_alias
- // - '('
- // - T_STRING TargetClass
- // - T_DOUBLE_COLON ::
- // - T_CLASS class
- // - ','
- // - T_WHITESPACE
- // - T_CONSTANT_ENCAPSED_STRING 'AliasName'
- // - ')'
- if ( $token === '(' ) {
- // Start of a function call to class_alias()
- $this->alias = [ 'target' => false, 'name' => false ];
- } elseif ( $token === ',' ) {
- // Record that we're past the first parameter
- if ( $this->alias['target'] === false ) {
- $this->alias['target'] = true;
- }
- } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) {
- if ( $this->alias['target'] === true ) {
- // We already saw a first argument, this must be the second.
- // Strip quotes from the string literal.
- $this->alias['name'] = substr( $token[1], 1, -1 );
- }
- } elseif ( $token === ')' ) {
- // End of function call
- $this->classes[] = $this->alias['name'];
- $this->alias = null;
- $this->startToken = null;
- } elseif ( !is_array( $token ) || (
- $token[0] !== T_STRING &&
- $token[0] !== T_DOUBLE_COLON &&
- $token[0] !== T_CLASS &&
- $token[0] !== T_WHITESPACE
- ) ) {
- // Ignore this call to class_alias() - compat/Timestamp.php
- $this->alias = null;
- $this->startToken = null;
- }
- }
- break;
-
- case T_CLASS:
- case T_INTERFACE:
- case T_TRAIT:
- $this->tokens[] = $token;
- if ( is_array( $token ) && $token[0] === T_STRING ) {
- $this->classes[] = $this->namespace . $this->implodeTokens();
- }
- }
- }
-
- /**
- * Returns the string representation of the tokens within the
- * current expect sequence and resets the sequence.
- *
- * @return string
- */
- protected function implodeTokens() {
- $content = [];
- foreach ( $this->tokens as $token ) {
- $content[] = is_string( $token ) ? $token : $token[1];
- }
-
- $this->tokens = [];
- $this->startToken = null;
-
- return trim( implode( '', $content ), " \n\t" );
- }
-}
--- /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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Reads PHP code and returns the FQCN of every class defined within it.
+ */
+class ClassCollector {
+
+ /**
+ * @var string Current namespace
+ */
+ protected $namespace = '';
+
+ /**
+ * @var array List of FQCN detected in this pass
+ */
+ protected $classes;
+
+ /**
+ * @var array Token from token_get_all() that started an expect sequence
+ */
+ protected $startToken;
+
+ /**
+ * @var array List of tokens that are members of the current expect sequence
+ */
+ protected $tokens;
+
+ /**
+ * @var array Class alias with target/name fields
+ */
+ protected $alias;
+
+ /**
+ * @param string $code PHP code (including <?php) to detect class names from
+ * @return array List of FQCN detected within the tokens
+ */
+ public function getClasses( $code ) {
+ $this->namespace = '';
+ $this->classes = [];
+ $this->startToken = null;
+ $this->alias = null;
+ $this->tokens = [];
+
+ foreach ( token_get_all( $code ) as $token ) {
+ if ( $this->startToken === null ) {
+ $this->tryBeginExpect( $token );
+ } else {
+ $this->tryEndExpect( $token );
+ }
+ }
+
+ return $this->classes;
+ }
+
+ /**
+ * Determine if $token begins the next expect sequence.
+ *
+ * @param array $token
+ */
+ protected function tryBeginExpect( $token ) {
+ if ( is_string( $token ) ) {
+ return;
+ }
+ // Note: When changing class name discovery logic,
+ // AutoLoaderStructureTest.php may also need to be updated.
+ switch ( $token[0] ) {
+ case T_NAMESPACE:
+ case T_CLASS:
+ case T_INTERFACE:
+ case T_TRAIT:
+ case T_DOUBLE_COLON:
+ case T_NEW:
+ $this->startToken = $token;
+ break;
+ case T_STRING:
+ if ( $token[1] === 'class_alias' ) {
+ $this->startToken = $token;
+ $this->alias = [];
+ }
+ }
+ }
+
+ /**
+ * Accepts the next token in an expect sequence
+ *
+ * @param array $token
+ */
+ protected function tryEndExpect( $token ) {
+ switch ( $this->startToken[0] ) {
+ case T_DOUBLE_COLON:
+ // Skip over T_CLASS after T_DOUBLE_COLON because this is something like
+ // "self::static" which accesses the class name. It doens't define a new class.
+ $this->startToken = null;
+ break;
+ case T_NEW:
+ // Skip over T_CLASS after T_NEW because this is a PHP 7 anonymous class.
+ if ( !is_array( $token ) || $token[0] !== T_WHITESPACE ) {
+ $this->startToken = null;
+ }
+ break;
+ case T_NAMESPACE:
+ if ( $token === ';' || $token === '{' ) {
+ $this->namespace = $this->implodeTokens() . '\\';
+ } else {
+ $this->tokens[] = $token;
+ }
+ break;
+
+ case T_STRING:
+ if ( $this->alias !== null ) {
+ // Flow 1 - Two string literals:
+ // - T_STRING class_alias
+ // - '('
+ // - T_CONSTANT_ENCAPSED_STRING 'TargetClass'
+ // - ','
+ // - T_WHITESPACE
+ // - T_CONSTANT_ENCAPSED_STRING 'AliasName'
+ // - ')'
+ // Flow 2 - Use of ::class syntax for first parameter
+ // - T_STRING class_alias
+ // - '('
+ // - T_STRING TargetClass
+ // - T_DOUBLE_COLON ::
+ // - T_CLASS class
+ // - ','
+ // - T_WHITESPACE
+ // - T_CONSTANT_ENCAPSED_STRING 'AliasName'
+ // - ')'
+ if ( $token === '(' ) {
+ // Start of a function call to class_alias()
+ $this->alias = [ 'target' => false, 'name' => false ];
+ } elseif ( $token === ',' ) {
+ // Record that we're past the first parameter
+ if ( $this->alias['target'] === false ) {
+ $this->alias['target'] = true;
+ }
+ } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) {
+ if ( $this->alias['target'] === true ) {
+ // We already saw a first argument, this must be the second.
+ // Strip quotes from the string literal.
+ $this->alias['name'] = substr( $token[1], 1, -1 );
+ }
+ } elseif ( $token === ')' ) {
+ // End of function call
+ $this->classes[] = $this->alias['name'];
+ $this->alias = null;
+ $this->startToken = null;
+ } elseif ( !is_array( $token ) || (
+ $token[0] !== T_STRING &&
+ $token[0] !== T_DOUBLE_COLON &&
+ $token[0] !== T_CLASS &&
+ $token[0] !== T_WHITESPACE
+ ) ) {
+ // Ignore this call to class_alias() - compat/Timestamp.php
+ $this->alias = null;
+ $this->startToken = null;
+ }
+ }
+ break;
+
+ case T_CLASS:
+ case T_INTERFACE:
+ case T_TRAIT:
+ $this->tokens[] = $token;
+ if ( is_array( $token ) && $token[0] === T_STRING ) {
+ $this->classes[] = $this->namespace . $this->implodeTokens();
+ }
+ }
+ }
+
+ /**
+ * Returns the string representation of the tokens within the
+ * current expect sequence and resets the sequence.
+ *
+ * @return string
+ */
+ protected function implodeTokens() {
+ $content = [];
+ foreach ( $this->tokens as $token ) {
+ $content[] = is_string( $token ) ? $token : $token[1];
+ }
+
+ $this->tokens = [];
+ $this->startToken = null;
+
+ return trim( implode( '', $content ), " \n\t" );
+ }
+}
$start = microtime( true );
do {
$ct = time();
- // https://secure.php.net/manual/en/language.operators.comparison.php
+ // https://www.php.net/manual/en/language.operators.comparison.php
if ( $ct >= $time ) {
// current time is higher than or equal to than $time
return $ct;
*/
private $actualStore;
- /**
- * @var string
- */
const DB_READONLY_ERROR = 'The watchlist is currently readonly.';
/**
* the results are from.
*/
class InterwikiSearchResultSetWidget implements SearchResultSetWidget {
- /** @var SpecialSearch */
+ /** @var SpecialSearch $specialSearch */
protected $specialSearch;
- /** @var SearchResultWidget */
+ /** @var SearchResultWidget $resultWidget */
protected $resultWidget;
- /** @var string[]|null */
+ /** @var string[]|null $customCaptions */
protected $customCaptions;
- /** @var LinkRenderer */
+ /** @var LinkRenderer $linkRenderer */
protected $linkRenderer;
- /** @var InterwikiLookup */
+ /** @var InterwikiLookup $iwLookup */
protected $iwLookup;
- /** @var $output */
+ /** @var \OutputPage $output */
protected $output;
/** @var bool $showMultimedia */
protected $showMultimedia;
"action-changetags": "أضف وأزل وسوما في مراجعات ومدخلات سجل فردية",
"action-deletechangetags": "حذف الوسوم من قاعدة البيانات",
"action-purge": "إفراغ كاش هذه الصفحة",
+ "action-apihighlimits": "استخدام حدود أعلى في استعلامات API",
+ "action-autoconfirmed": "غير متأثر بحدود المعدل المستندة إلى الآيبي",
+ "action-bigdelete": "حذف الصفحات ذات التواريخ الكبيرة",
+ "action-blockemail": "منع مستخدم من إرسال بريد إلكتروني",
+ "action-bot": "يًعامَل كعملية آلية",
+ "action-editprotected": "تعديل الصفحات التي حمايتها \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "تعديل الصفحات التي حمايتها \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "تعديل واجهة المستخدم",
+ "action-editusercss": "تعديل ملفات CSS للمستخدمين الآخرين",
+ "action-edituserjson": "تعديل ملفات جسون للمستخدمين الآخرين",
+ "action-edituserjs": "تعديل ملفات جافاسكريبت للمستخدمين الآخرين",
+ "action-editsitecss": "تحرير CSS للموقع بأكمله",
+ "action-editsitejson": "تعديل جسون على مستوى الموقع",
+ "action-editsitejs": "تعديل جافاسكريبت على مستوى الموقع",
+ "action-editmyusercss": "تحرير ملفات CSS المستخدم الخاصة بك",
+ "action-editmyuserjson": "تحرير ملفات جسون المستخدم الخاصة بك",
+ "action-editmyuserjs": "تحرير ملفات جافا سكريبت المستخدم الخاصة بك",
+ "action-viewsuppressed": "عرض المراجعات المخفية بواسطة أي مستخدم",
+ "action-hideuser": "منع اسم مستخدم، مخفيا إياه عن العامة",
+ "action-ipblock-exempt": "تفادي عمليات منع الأيبي والمنع التلقائي ومنع النطاق",
+ "action-unblockself": "رفع المنع عن أنفسهم",
+ "action-noratelimit": "غير متأثر بحدود المعدل",
+ "action-reupload-own": "الكتابة على ملف موجود تم رفعه بواسطة نفس المستخدم",
+ "action-nominornewtalk": "عدم وجود تعديلات طفيفة على صفحات النقاشة تؤدي إلى ظهور رسائل جديدة",
+ "action-markbotedits": "التعليم على تعديلات الاسترجاع كتعديلات بوت",
+ "action-patrolmarks": "رؤية علامات المراجعة في أحدث التغييرات",
+ "action-override-export-depth": "تصدير الصفحات متضمنة الصفحات الموصولة حتى عمق 5",
+ "action-suppressredirect": "عدم إنشاء تحويلة من الاسم القديم عند نقل صفحة",
"nchanges": "{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|منذ الزيارة الأخيرة}}",
"enhancedrc-history": "تاريخ",
"action-changetags": "дадаваньне і выдаленьне адвольных метак да асобных вэрсіяў і запісаў у журнале падзеяў",
"action-deletechangetags": "выдаленьне метак з базы зьвестак",
"action-purge": "ачыстку кэшу гэтай старонкі",
+ "action-apihighlimits": "ўжываньне падвышаных лімітаў у API-запытах",
+ "action-autoconfirmed": "адсутнасьць абмежаваньня хуткасьці паводле IP-адрасу",
"nchanges": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з апошняга візыту}}",
"enhancedrc-history": "гісторыя",
"allpagesbadtitle": "Пададзеная назва старонкі была няслушная ці пачыналася зь міжмоўнай ці міжвікі спасылкі. Яна яшчэ можа ўтрымліваць сымбалі, якія ня могуць ужывацца ў назвах.",
"allpages-bad-ns": "{{SITENAME}} ня мае прасторы назваў «$1».",
"allpages-hide-redirects": "Схаваць перанакіраваньні",
- "cachedspecial-viewing-cached-ttl": "Ð\92Ñ\8b пÑ\80аглÑ\8fдаеÑ\86е закÑ\8dÑ\88аванÑ\83Ñ\8e вÑ\8dÑ\80Ñ\81Ñ\96Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\96, Ñ\8fкаÑ\8f магла бÑ\8bÑ\86Ñ\8c абноÑ\9eленаÑ\8f $1 Ñ\82амÑ\83.",
- "cachedspecial-viewing-cached-ts": "Ð\92Ñ\8b пÑ\80аглÑ\8fдаеÑ\86е закÑ\8dÑ\88аванÑ\83Ñ\8e вÑ\8dÑ\80Ñ\81Ñ\96Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\96, Ñ\8fкаÑ\8f можа бÑ\8bÑ\86Ñ\8c неакÑ\82Ñ\83алÑ\8cнай.",
+ "cachedspecial-viewing-cached-ttl": "Вы праглядаеце кэшаваную вэрсію старонкі, якая магла быць абноўленая $1 таму.",
+ "cachedspecial-viewing-cached-ts": "Вы праглядаеце кэшаваную вэрсію старонкі, якая можа быць неактуальнай.",
"cachedspecial-refresh-now": "Пабачыць апошнюю вэрсію.",
"categories": "Катэгорыі",
"categories-submit": "Паказаць",
"index-category": "Pajennoù menegeret",
"noindex-category": "Pajennoù n'int ket menegeret",
"broken-file-category": "Pajennoù enno liamm torr war-zu restroù",
+ "categoryviewer-pagedlinks": "($1) ($2)",
+ "category-header-numerals": "$1–$2",
"about": "Diwar-benn",
"article": "Pennad",
"newwindow": "(digeriñ en ur prenestr nevez)",
"returnto": "Distreiñ d'ar bajenn $1.",
"tagline": "Eus {{SITENAME}}",
"help": "Skoazell",
+ "help-mediawiki": "Skoazell evit MediaWiki",
"search": "Klask",
"search-ignored-headings": " #<!-- lezel al linenn-mañ tre evel m'emañ --> <pre>\n# Titloù a vo lezet a-gostez gant ar c'hlask.\n# Ar c'hemmoù graet amañ a vo lakaet e pleustr kerkent ha menegeret ar bajenn gant an titl.\n# Gallout a rit forsiñ un advenegeriñ ma rit ur c'hemm goullo en ur bajenn.\n# Setu penaos emañ an ereadur :\n# * Kement linenn zo, adalek an arouezenn \"#\" betek dibenn al linenn a zo un evezhiadenn.\n# * Kement linenn ha n'eo ket goullo a verk an titl rik a zo da vezañ lezet a-gostez, pennlizherennoù hag all.\nDaveennoù\nLiammoù diavaez\nGwelet ivez\n #</pre> <!-- lezel al linenn-mañ tre evel m'emañ -->",
"searchbutton": "Klask",
"versionrequired": "Rekis eo Stumm $1 MediaWiki",
"versionrequiredtext": "Rekis eo stumm $1 MediaWiki evit implijout ar bajenn-mañ. Sellit ouzh [[Special:Version]]",
"ok": "Mat eo",
+ "pagetitle": "$1 - {{SITENAME}}",
+ "pagetitle-view-mainpage": "{{SITENAME}}",
+ "backlinksubtitle": "← $1",
"retrievedfrom": "Adtapet diwar « $1 »",
"youhavenewmessages": "$1 zo ganeoc'h ($2).",
"youhavenewmessagesfromusers": "{{PLURAL:$4|Bez' hoc'h eus}} $1 a-berzh {{PLURAL:$3|un implijer all|$3 implijer}} ($2).",
"site-atom-feed": "Lanv Atom evit $1",
"page-rss-feed": "Lanv RSS evit \"$1\"",
"page-atom-feed": "Lanv Atom evit \"$1\"",
+ "feed-atom": "Atom",
+ "feed-rss": "RSS",
"red-link-title": "$1 (n'eus ket eus ar bajenn-mañ)",
"sort-descending": "Urzhiañ war-draoñ",
"sort-ascending": "Urzhiañ war-laez",
"nocookiesnew": "Krouet eo bet ar gont implijer met n'oc'h ket kevreet. {{SITENAME}} a implij toupinoù evit ar c'hevreañ met diweredekaet eo an toupinoù ganeoc'h. Trugarez da weredekaat anezho ha da gevreañ en-dro.",
"nocookieslogin": "{{SITENAME}} a implij toupinoù evit kevreañ met diweredekaet eo an toupinoù ganeoc'h. Trugarez da weredekaat anezho ha da gevreañ en-dro.",
"nocookiesfornew": "N'eo ket bet krouet ar gont implijer peogwir n'eus ket bet gallet gwiriañ an orin anezhi.\nGwiriit eo bet gweredekaet an toupinoù, adkargit ar bajenn ha klaskit en-dro.",
+ "nocookiesforlogin": "{{int:nocookieslogin}}",
"createacct-loginerror": "Krouet eo bet ar gont ervat met n'hallit ket kevreañ en un doare emgefre. [[Special:UserLogin|Kevreit gant an dorn]].",
"noname": "N'hoc'h eus lakaet anv implijer ebet.",
"loginsuccesstitle": "Kevreet oc'h.",
"template-semiprotected": "(damwarezet)",
"hiddencategories": "{{PLURAL:$1|1 rummad kuzhet|$1 rummad kuzhet}} m'emañ rollet ar bajenn-mañ :",
"edittools": "<!-- Diskouezet e vo an destenn kinniget amañ dindan ar sternioù kemmañ ha kargañ. -->",
+ "edittools-upload": "-",
"nocreatetext": "Strishaet eo bet an tu da grouiñ pajennoù nevez war {{SITENAME}}.\nGallout a rit mont war-gil ha lakaat kemmañ ur bajenn zo anezhi dija, pe [[Special:UserLogin|en em enrollañ ha krouiñ ur gont]].",
"nocreate-loggedin": "N'oc'h ket aotreet da grouiñ pajennoù nevez.",
"sectioneditnotsupported-title": "N'eo ket skoret ar c'hemmañ rannoù",
"editpage-invalidcontentmodel-text": "N'eo ket skoret ar patrom danvez \"$1\".",
"editpage-notsupportedcontentformat-title": "Furmad endalc'had ha n'eo ket kemeret e karg",
"editpage-notsupportedcontentformat-text": "N'eo ket skoret ar patrom $1 gant ar patrom danvez $2.",
+ "slot-name-main": "Pennañ",
"content-model-wikitext": "wikitestenn",
"content-model-text": "testenn blaen",
"content-model-javascript": "Javascript",
"content-model-css": "CSS",
+ "content-model-json": "JSON",
"content-json-empty-object": "Elfenn goullo",
"content-json-empty-array": "Taolenn c'houllo",
"deprecated-self-close-category": "Pajennoù a ra gant tikedennoù HTML emserriñ direizh",
"page_first": "kentañ",
"page_last": "diwezhañ",
"histlegend": "Sellet ouzh an diforc'hioù : lakait un ask adal d'ar stummoù a fell deoc'h keñveriañ ha pouezit war kadarnaat pe war ar bouton en traoñ.<br />\nAlc'hwez : (red) = diforc'hioù gant ar stumm a-vremañ,\n(diwez) = diforc'hioù gant ar stumm kent, D = kemm dister",
- "history-fieldset-title": "Klask adweladennoù",
+ "history-fieldset-title": "Silañ an adweladennoù",
"history-show-deleted": "Stumm diverket hepken",
"histfirst": "koshañ",
"histlast": "nevezañ",
"historysize": "({{PLURAL:$1|$1 okted|$1 okted}})",
- "historyempty": "(goullo)",
+ "historyempty": "goullo",
"history-feed-title": "Istor ar c'hemmoù",
"history-feed-description": "Istor ar c'hemmoù degaset war ar bajenn-mañ eus ar wiki",
"history-feed-item-nocomment": "$1 d'an $2",
"mergehistory-comment": "Kendeuzet [[:$1]] gant [[:$2]] : $3",
"mergehistory-same-destination": "N'hall ket ar pajennoù kein hag ar pajennoù tal bezañ an hevelep re",
"mergehistory-reason": "Abeg :",
+ "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
"mergelog": "Marilh ar c'hendeuzadennoù",
"revertmerge": "Nullañ ar c'hendeuziñ",
"mergelogpagetext": "Setu aze roll kendeuzadennoù diwezhañ un eil pajenn istor gant eben.",
"youremail": "Postel :",
"username": "{{GENDER:$1|Anv implijer|Anv implijerez}}:",
"prefs-memberingroups": "{{GENDER:$2|Ezel}} eus {{PLURAL:$1|ar strollad|ar strolladoù}}:",
+ "prefs-memberingroups-type": "$1",
"group-membership-link-with-expiry": "$1 (betek $2)",
"prefs-registration": "Deiziad enskrivañ :",
+ "prefs-registration-date-time": "$1",
"yourrealname": "Anv gwir*",
"yourlanguage": "Yezh an etrefas ",
"yourvariant": "Adstumm yezh :",
"prefs-advancedwatchlist": "Dibarzhioù araokaet",
"prefs-displayrc": "Dibarzhioù diskwel",
"prefs-displaywatchlist": "Dibarzhioù diskwel",
+ "prefs-changesrc": "Kemmoù war wel",
+ "prefs-changeswatchlist": "Kemmoù war wel",
+ "prefs-pageswatchlist": "Pajennoù evezhiet",
"prefs-tokenwatchlist": "Jedouer",
"prefs-diffs": "Diforc'hioù",
"prefs-help-prefershttps": "Efediñ a ray an dibarzh-mañ kentañ gwech ma kevreoc'h.",
"saveusergroups": "Enrollañ strolladoù an {{GENDER:$1|implijer|implijerez}}",
"userrights-groupsmember": "Ezel eus :",
"userrights-groupsmember-auto": "Ezel emplegat eus :",
+ "userrights-groupsmember-type": "$1",
"userrights-groups-help": "Cheñch strolladoù an implijer a c'hallit ober.\n* Ul log asket a verk emañ an implijer er strollad.\n* Ul log diask a verk n'emañ ket an implijer er strollad.\n* Ur * a verk n'hallit ket dilemel ar strollad ur wech bet ouzhpennet ganeoc'h, pe ar c'hontrol.\n* Ur # a verk e c'hallit astenn termen an emezelañ er strollad hepken ; n'hallit ket berraat anezhañ.",
"userrights-reason": "Abeg :",
"userrights-no-interwiki": "N'oc'h ket aotreet da gemmañ ar gwirioù implijer war wikioù all.",
"userrights-nodatabase": "N'eus ket eus an diaz titouroù $1 pe n'eo ket lec'hel.",
"userrights-changeable-col": "Ar strolladoù a c'hallit cheñch",
"userrights-unchangeable-col": "Ar strolladoù n'hallit ket cheñch",
+ "userrights-irreversible-marker": "$1*",
+ "userrights-no-shorten-expiry-marker": "$1#",
"userrights-expiry-current": "A ya d'e dermen d'an $1",
"userrights-expiry-none": "Didermen",
"userrights-expiry": "Termen :",
"group-autoconfirmed": "Implijerien bet kadarnaet ent emgefre",
"group-bot": "Robotoù",
"group-sysop": "Merourien",
+ "group-interface-admin": "Merourien an etrefas",
"group-bureaucrat": "Burevidi",
"group-suppress": "Diverkerien",
"group-all": "(pep tra)",
"group-autoconfirmed-member": "{{GENDER:$1|Implijer bet kadarnaet ent emgefre}}",
"group-bot-member": "{{GENDER:$1|robot}}",
"group-sysop-member": "{{GENDER:$1|merour}}",
+ "group-interface-admin-member": "{{GENDER:$1|merour an etrefas|merourez an etrefas}}",
"group-bureaucrat-member": "{{GENDER:$1|bureviad}}",
"group-suppress-member": "{{GENDER:$1|diverker|diverkerez}}",
"grouppage-user": "{{ns:project}}:Implijerien",
"grouppage-autoconfirmed": "{{ns:project}}: Implijerien bet kadarnaet ent emgefre",
"grouppage-bot": "{{ns:project}}:Botoù",
"grouppage-sysop": "{{ns:project}}:Merourien",
+ "grouppage-interface-admin": "{{ns:project}}:Merourien etrefas",
"grouppage-bureaucrat": "{{ns:project}}: Burevidi",
"grouppage-suppress": "{{ns:project}}: Diverkerien",
"right-read": "Lenn ar pajennoù",
"right-reupload-own": "Frikañ ur restr bet pellgarget gant an-unan",
"right-reupload-shared": "Gwaskañ restroù ent lec'hel war an diellaoueg vedia rannet",
"right-upload_by_url": "Enporzhiañ ur restr adal ur chomlec'h URL",
- "right-purge": "Spujañ krubuilh ar pajennoù hep kadarnaat",
+ "right-purge": "Spurjañ krubuilh ur bajenn eus al lec'hienn",
"right-autoconfirmed": "Na vezañ trubuilhet gant ar bevennoù kas liammet ouzh ar chomlec'hioù IP",
"right-bot": "Plediñ ganti evel gant un argerzh emgefre",
"right-nominornewtalk": "Arabat diskouez ar c'hemenn \"Kemennoù nevez zo ganeoc'h\" pa vez lakaet kemmoù dister e pajenn gaozeal un implijer",
"action-deletechangetags": "Diverkañ tikedennoù a-ziwar an diaz-roadennoù",
"action-purge": "spurjañ ar bajenn-mañ",
"nchanges": "$1 {{PLURAL:$1|kemm|kemm}}",
+ "ntimes": "$1×",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|abaoe ho kweladenn diwezhañ}}",
"enhancedrc-history": "istor",
"recentchanges": "Kemmoù diwezhañ",
"recentchanges-label-plusminus": "Kemmet eo ment ar bajenn eus an niver-mañ a oktedoù",
"recentchanges-legend-heading": "<strong>Alc'hwez :</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (gwelet ivez [[Special:NewPages|roll ar pajennoù nevez]])",
+ "recentchanges-legend-plusminus": "(<em>±123</em>)",
"recentchanges-submit": "Diskouez",
"rcfilters-tag-remove": "Dilemel '$1'",
"rcfilters-other-review-tools": "Ostilhoù reizhañ all",
"rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
"rcfilters-exclude-button-off": "Skarzhañ ar re dibabet",
"rcfilters-exclude-button-on": "Re dibabet skarzhet",
+ "rcfilters-view-tags": "Kemmoù merket",
"rcfilters-filter-showlinkedfrom-label": "Diskouez ar c'hemmoù war ar bajennoù liammet",
"rcfilters-filter-showlinkedto-label": "Diskouez ar c'hemmoù war ar bajennoù liammet",
"rcfilters-target-page-placeholder": "Skrivañ anv ar bajenn (pe rummad)",
"minoreditletter": "D",
"newpageletter": "N",
"boteditletter": "b",
+ "unpatrolledletter": "!",
"number_of_watching_users_pageview": "[$1 {{PLURAL:$1|implijer o heuliañ|implijer}} o heuliañ]",
+ "rc-change-size": "$1",
"rc-change-size-new": "$1 {{PLURAL:$1|okted|okted}} goude kemmañ",
"newsectionsummary": "/* $1 */ rann nevez",
"rc-enhanced-expand": "Diskouez ar munudoù",
"uploadnewversion-linktext": "Kargañ ur stumm nevez eus ar restr-mañ",
"shared-repo-from": "eus $1",
"shared-repo": "ur sanailh rannet",
+ "shared-repo-name-wikimediacommons": "Wikimedia Commons",
"upload-disallowed-here": "Ne c'hallit ket erlec'hiañ ar restr-mañ",
"filerevert": "Disteuler $1",
"filerevert-legend": "Disteuler ar restr",
"apisandbox-request-selectformat-label": "Diskouez roadennoù ar reked evel :",
"apisandbox-request-format-url-label": "Neudennad reked an URL",
"apisandbox-request-url-label": "Goulenn URL :",
+ "apisandbox-request-format-json-label": "JSON",
"apisandbox-request-json-label": "Goulenn JSON :",
"apisandbox-request-time": "Pad ar goulenn : {{PLURAL:$1|$1 ms}}",
"apisandbox-results-fixtoken": "Reizhañ ar jedouer ha kas en-dro",
"deleteprotected": "Ne c'hallit ket dilemel ar bajenn-mañ rak gwarezet eo bet.",
"deleting-backlinks-warning": "<strong>Taolit pled :</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Pajennoù all]] zo enno liammoù war-zu ar bajenn emaoc'h o vont da zilemel pe a zo treuzkludet ar bajenn enno.",
"rollback": "disteuler ar c'hemmoù",
+ "rollback-confirmation-no": "Nullañ",
"rollbacklink": "disteuler",
"rollbacklinkcount": "disteurel $1 {{PLURAL:$1|kemm}}",
"rollbacklinkcount-morethan": "disteurel ouzhpenn $1 {{PLURAL:$1|kemm}}",
"protect-fallback": "Degemer hepken an implijerien gant an aotre \"$1\"",
"protect-level-autoconfirmed": "Degemer hepken an implijerien emgadarnaet",
"protect-level-sysop": "Aotren ar verourien hepken",
+ "protect-summary-desc": "[$1=$2] ($3)",
"protect-summary-cascade": "Gwareziñ dre skalierad",
"protect-expiring": "a zeu d'e dermen d'an $1",
"protect-expiring-local": "a ya d'e dermen d'an $1",
"undelete-error-long": "Fazioù zo bet kavet e-ser diziverkañ ar restr :\n\n$1",
"undelete-show-file-confirm": "Ha sur oc'h e fell deoc'h sellet ouzh ur stumm diverket eus ar restr \"<nowiki>$1</nowiki>\" a sav d'an $2 da $3?",
"undelete-show-file-submit": "Ya",
+ "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
"namespace": "Esaouenn anv :",
"invert": "Eilpennañ an dibab",
"tooltip-invert": "Askañ ar voest-mañ da guzhat ar c'hemmoù er pajennoù stag ouzh an esaouenn anv diuzet (hag an esaouenn anv stag, m'emañ asket)",
"uctop": "red",
"month": "Abaoe miz (hag a-raok) :",
"year": "Abaoe bloaz (hag a-raok) :",
+ "date": "Abaoe an (hag a-raok) :",
"sp-contributions-newbies": "Diskouez hepken degasadennoù ar c'hontoù nevez",
"sp-contributions-newbies-sub": "Evit an implijerien nevez",
"sp-contributions-newbies-title": "Degasadennoù implijer evit ar c'hontoù nevez",
"ipb-change-block": "Adstankañ an implijer-mañ gant an hevelep arventennoù",
"ipb-confirm": "Kadarnaat ar stankadenn",
"ipb-pages-label": "Pajennoù",
+ "ipb-namespaces-label": "Esaouennoù anv",
"badipaddress": "Kamm eo ar chomlec'h IP.",
"blockipsuccesssub": "Stankadenn deuet da benn vat",
"blockipsuccesstext": "Stanket eo bet [[Special:Contributions/$1|$1]].<br />\nSellit ouzh [[Special:BlockList|roll ar chomlec'hioù IP ha kontoù stanket]] evit gwiriañ ar stankadennoù.",
"ipb-blocklist-contribs": "Degasadennoù evit $1",
"ipb-blocklist-duration-left": "$1 a chom",
"block-expiry": "Pad ar stankadenn",
+ "block-prevent-edit": "O kemmañ",
+ "block-reason": "Abeg :",
+ "block-target": "Anv implijer pe chomlec'h IP :",
"unblockip": "Distankañ ur chomlec'h IP",
"unblockiptext": "Grit gant ar furmskrid a-is evit adsevel ar moned skrivañ ouzh ur chomlec'h IP bet stanket a-gent.",
"ipusubmit": "Paouez gant ar stankadenn-mañ",
"blocklist-nousertalk": "n'hall ket kemmañ e bajenn gaozeal dezhañ e-unan",
"blocklist-editing": "O kemmañ",
"blocklist-editing-sitewide": "o kemmañ (al lec'hienn a-bezh)",
+ "blocklist-editing-page": "pajennoù",
+ "blocklist-editing-ns": "esaouennoù anv",
"ipblocklist-empty": "Goullo eo roll ar stankadennoù.",
"ipblocklist-no-results": "An anv implijer pe ar chomlec'h IP goulennet n'eo ket stanket anezhañ.",
"blocklink": "stankañ",
"ip_range_toolarge": "N'eo ket aotreet stankañ pajennoù brasoc'h evit /$1.",
"proxyblocker": "Stanker proksi",
"proxyblockreason": "Stanket eo bet hoc'h IP rak ur proksi digor eo. Trugarez da gelaouiñ ho pourvezer moned ouzh ar Genrouedad pe ho skoazell deknikel eus ar gudenn surentez-mañ.",
+ "sorbs": "DNSBL",
"sorbsreason": "Rollet eo ho chomlec'h IP evel ur proksi digor en DNSBL implijet gant {{SITENAME}}.",
"sorbs_create_account_reason": "Rollet eo ho chomlec'h IP evel ur proksi digor war an DNSBL implijet gant {{SITENAME}}. N'hallit ket krouiñ ur gont",
"softblockrangesreason": "N'eo ket aotreet kemmañ netra ebet gan tho chomlec'h IP ($1). Kevreit mar plij.",
"tooltip-undo": "\"Dizober\" a zistaol ar c'hemm-mañ hag a zigor ar prenestr skridaozañ er mod rakwelet.\nTalvezout a ra da ouzhpennañ un displegadenn er c'hombod diverrañ.",
"tooltip-preferences-save": "Enrollañ ar penndibaboù",
"tooltip-summary": "Skrivit un diveradenn verr",
+ "interlanguage-link-title": "$1 – $2",
+ "interlanguage-link-title-nonlang": "$1 – $2",
"common.css": "/** Talvezout a raio ar CSS lakaet amañ evit an holl wiskadurioù */",
"print.css": "/* Talvezout a raio ar CSS lakaet amañ evit ar moullañ */",
"noscript.css": "/* Talvezout a raio ar CSS lakaet amañ evit an implijerien o deus diweredekaet JavaScript */",
"pageinfo-few-watchers": "Nebeutoc'h eget $1 {{PLURAL:$1|lenner}}",
"pageinfo-few-visiting-watchers": "Gallout a ra bezañ, pe get, un implijer o teurel ur sell ouzh ar c'hemmoù diwezhañ",
"pageinfo-redirects-name": "Niver a adkasoù war-zu ar bajenn-mañ",
+ "pageinfo-redirects-value": "$1",
"pageinfo-subpages-name": "Ispajennoù eus ar bajenn-mañ",
"pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|kasadur|kasadurioù}}; $3 {{PLURAL:$3|nann kasaduri|nann kasadurioù}})",
"pageinfo-firstuser": "Krouer ar bajenn",
"confirm-mcrundo-title": "Dizober ur c'hemm",
"mcrundofailed": "Dizober c'hwitet",
"mcrundo-missingparam": "Arventennoù rekis d'ar reked a vank.",
+ "ellipsis": "...",
+ "percent": "$1%",
+ "parentheses": "($1)",
+ "parentheses-start": "(",
+ "parentheses-end": ")",
+ "brackets": "[$1]",
"quotation-marks": "« $1 »",
"imgmultipageprev": "← pajenn gent",
"imgmultipagenext": "pajenn war-lerc'h →",
"imgmultigo": "Mont !",
"imgmultigoto": "Mont d'ar bajenn $1",
+ "img-lang-opt": "$2 ($1)",
"img-lang-default": "(yezh dre ziouer)",
"img-lang-info": "Diskouez ar skeudenn-mañ e $1. $2",
"img-lang-go": "Mont",
"log-action-filter-suppress-reblock": "Diverkañ implijerien dre stankadennoù lies",
"log-action-filter-upload-upload": "Enporzhiadenn nevez",
"log-action-filter-upload-overwrite": "Adenporzhiañ",
+ "log-action-filter-upload-revert": "Disteuler",
"authmanager-authn-not-in-progress": "Ne'z a ket war-raok an dilesadur pe kollet ez eus bet roadennoù dalc'h marteze. Adkrogit adalek ar penn-kentañ.",
"authmanager-authn-no-primary": "N'eus ket bet gallet gwiriañ an titouroù kred lakaet.",
"authmanager-authn-no-local-user": "An titouroù anaout pourchaset n'int ket liammet gant implijer ebet er wiki-mañ.",
"action-changetags": "beliebige Markierungen zu einzelnen Versionen und Logbucheinträgen hinzuzufügen und zu entfernen",
"action-deletechangetags": "Markierungen aus der Datenbank zu löschen",
"action-purge": "den Cache dieser Seite zu leeren",
+ "action-apihighlimits": "höhere Beschränkungen in API-Anfragen zu verwenden",
+ "action-autoconfirmed": "nicht von IP-basierten Limits betroffen zu sein",
+ "action-bigdelete": "Seiten mit großer Versionsgeschichte zu löschen",
+ "action-blockemail": "Benutzer am Versenden von E-Mails zu hindern",
+ "action-bot": "als automatischer Prozess behandelt zu werden",
+ "action-editprotected": "Seiten zu bearbeiten, die als „{{int:protect-level-sysop}}“ geschützt sind",
+ "action-editsemiprotected": "Seiten zu bearbeiten, die als „{{int:protect-level-autoconfirmed}}“ geschützt sind",
+ "action-editinterface": "Systemnachrichten und Benutzeroberflächen zu bearbeiten",
+ "action-editusercss": "fremde CSS-Dateien zu bearbeiten",
+ "action-edituserjson": "JSON-Dateien anderer Benutzer zu bearbeiten",
+ "action-edituserjs": "fremde JavaScript-Dateien zu bearbeiten",
+ "action-editsitecss": "wikiweit CSS zu bearbeiten",
+ "action-editsitejson": "wikiweites JSON zu bearbeiten",
+ "action-editsitejs": "wikiweites JavaScript zu bearbeiten",
+ "action-editmyusercss": "eigene Benutzer-CSS-Dateien zu bearbeiten",
+ "action-editmyuserjson": "eigene Benutzer-JSON-Dateien zu bearbeiten",
+ "action-editmyuserjs": "eigene Benutzer-JavaScript-Dateien zu bearbeiten",
+ "action-viewsuppressed": "vor jedem Benutzer versteckte Versionen anzusehen",
+ "action-hideuser": "Benutzernamen zu sperren und zu verbergen",
+ "action-ipblock-exempt": "IP-Sperren, automatische Sperren und Bereichssperren zu umgehen",
+ "action-unblockself": "dich zu entsperren",
+ "action-noratelimit": "nicht von Limits betroffen zu sein",
+ "action-reupload-own": "eine zuvor selbst hochgeladene Datei zu überschreiben",
+ "action-nominornewtalk": "dass kleine Bearbeitungen an Diskussionsseiten nicht die „Neue Nachrichten“-Anzeige auslösen",
+ "action-markbotedits": "schnell zurückgesetzte Bearbeitungen als Bot-Bearbeitung zu markieren",
+ "action-patrolmarks": "Kontrollmarkierungen in den letzten Änderungen anzusehen",
+ "action-override-export-depth": "Seiten einschließlich verlinkter Seiten bis zu einer Tiefe von 5 zu exportieren",
+ "action-suppressredirect": "beim Verschieben die Erstellung einer Weiterleitung zu unterdrücken",
"nchanges": "$1 {{PLURAL:$1|Änderung|Änderungen}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|seit dem letzten Besuch}}",
"enhancedrc-history": "Versionsgeschichte",
"acct_creation_throttle_hit": "Yew ten IP adresê şıma xebıtnayo u kewto no wiki, $2roco peyin de {{PLURAL:$1|1 hesabi|$1 hesaban}} vıraşto.\nxulasa ney kesê ke IP adresê şıma xebıtneni hini nêeşkeni ney ra zêdêr hesab akeri.",
"emailauthenticated": "E-postay şıma $2 sehat $3 dı biya araşt",
"emailnotauthenticated": "Adresa e-pota da şıma qebul nébiya.\nQandé céréna şımaré teba do nérışiyo.",
- "noemailprefs": "Seba gurenayışê nê xısusiyetayew adresê e-posta cı kerê .",
+ "noemailprefs": "Seba gurenayışê nê xısusiyetan yew adresa e-posteyi cı kerê.",
"emailconfirmlink": "E-postayê xo araşt kerê",
"invalidemailaddress": "No format de nuştışê e-postayi qebul nêbeno. Yew formato meqbul de adresê e-posta bınuse ya zi veng bıverde.",
"cannotchangeemail": "E-postay hesabi ena wiki sera nêvurneyêno.",
"page_first": "verên",
"page_last": "peyên",
"histlegend": "Ferqê weçinayışi: Qutiya versiyonan qandé têversanayış işaret ke u dest be ''enter''i ya zi gocega cêrêne rone.<br />\nCetwel: <strong>({{int:ferq}})</strong> = ferqê versiyonê peyêni, <strong>({{int:peyên}})</strong> = ferqê versiyonê verêni, <strong>{{int:q}}</strong> = vırnayışo werdiyo.",
- "history-fieldset-title": "Çımraviyarnayışan cı geyre",
+ "history-fieldset-title": "Çımraviyarnayışan parzûn ke",
"history-show-deleted": "Tenya çımraviyarnayışanê esterıteyan bımocne",
"histfirst": "Verênêr",
"histlast": "Peyênêr",
"prefs-misc": "ê bini",
"prefs-resetpass": "Parola bıvurne",
"prefs-changeemail": "E-postay bıvurne ya zi wedarne",
- "prefs-setemail": "Adresê eposta cı kerê",
+ "prefs-setemail": "Adresa eposteyi cı kerê",
"prefs-email": "Tercihê e-maili",
"prefs-rendering": "Asayış",
"saveprefs": "Qeyd ke",
"email-blacklist-label": "Wa nê karberi mı rê mesac nêrışê:",
"prefs-searchoptions": "Cı geyre",
"prefs-namespaces": "Heruna nameyan",
- "default": "Keyfi",
+ "default": "hesabiyaye",
"prefs-files": "Dosyeyi",
"prefs-custom-css": "CSSê xasi",
"prefs-custom-json": "JSONo xısusi",
"prefs-help-signature": "Mışewreyê ke pelanê werênayışi derê, gani be \"<nowiki>~~~~</nowiki>\" ra imza bıbê, no bahdo beno çerxê imza û wadeyê zemani.",
"badsig": "Îmzayê tu raşt niyo.\nEtiketê HTMLî kontrol bike.",
"badsiglength": "İmzaya şıma zaf derga.\nA gani be $1 {{PLURAL:$1|karakter|karakteran}} ra zêde mebo.",
- "yourgender": "Şıma yê xo seni tarif kenê?",
- "gender-unknown": "Software şıma ra bahs kerdış dê, hendık cı dest ra biro cınsiyeti ya aleqeyın çekuy nêvaco.",
- "gender-male": "Oyo pelanê wiki vurneno",
- "gender-female": "Aya pelanê wiki vırnena.",
- "prefs-help-gender": "Na eyara tercihi keyfiya.\nNa software seraniya ercê çekuyan de gorey cınsiyeta hol Gramer kar nêna. \nNê melumati herkesi rê asenê.",
+ "yourgender": "Şımayê xo seni tarif kenê?",
+ "gender-unknown": "Nuşteker şıma ra behskerdış de, hende ke cı dest ra bêro, cınsiyeto elaqeyın gureneno.",
+ "gender-male": "Oyo pelanê wikiyi vurneno",
+ "gender-female": "Aya pelanê wikiyi vurnena",
+ "prefs-help-gender": "No eyarê tercihi keyfiyo.\nNo nuşteker xıtabkerdış de ercê xo u ê binan rê şıma ra behskerdış de cınsiyetê grameriyê hewli gureneno.\nNo melumat her kesi rê aseno.",
"email": "E-posta",
"prefs-help-realname": "Nameyo raşt waştena şıma rê mendo.\nEka tu wazene ke nameyo raşt xo bide, ma nameyo raşt ti iştirakanê ti de mocnenê.",
"prefs-help-email": "Dayışê adresa e-postey keyfiyo, labelê seba eyarê parola lazıma, wexto ke şıma naye xo vira kerê.",
"userrights-conflict": "Heqan de karberi de dıbare vıcyayo! Kerem ke vurnayışane xo çımser ra ravyarne u tesdiq keri.",
"group": "Grube:",
"group-user": "Karberi",
- "group-autoconfirmed": "Otomatik raşt bıyaye karberi",
+ "group-autoconfirmed": "Karberê otomatikraştbiyayeyi",
"group-bot": "Boti",
"group-sysop": "İdarekari",
"group-interface-admin": "İdarekarê namnişani",
"group-suppress": "Pawıteri",
"group-all": "(pêro)",
"group-user-member": "{{GENDER:$1|karber}}",
- "group-autoconfirmed-member": "{{GENDER:$1|Otomatik raşt bıyaye karberi}}",
+ "group-autoconfirmed-member": "{{GENDER:$1|Karberê otomatikraştbiyayeyi}}",
"group-bot-member": "{{GENDER:$1|bot}}",
"group-sysop-member": "{{GENDER:$1|İdarekar}}",
"group-interface-admin-member": "{{GENDER:$1|idarekarê namnişani}}",
"group-bureaucrat-member": "{{GENDER:$1|buroqrat}}",
"group-suppress-member": "{{GENDER:$1|Temaşekar}}",
"grouppage-user": "{{ns:project}}:Karberi",
- "grouppage-autoconfirmed": "{{ns:project}}:Otomatik raşt bıyaye karberi",
+ "grouppage-autoconfirmed": "{{ns:project}}:Karberê otomatikraştbiyayeyi",
"grouppage-bot": "{{ns:project}}:Boti",
"grouppage-sysop": "{{ns:project}}:İdarekeri",
"grouppage-interface-admin": "{{ns:project}}:İdarekarê namnişani",
"rcfilters-filtergroup-authorship": "Wayiriya iştırakan",
"rcfilters-filter-editsbyself-label": "Vurnayışê şıma",
"rcfilters-filter-editsbyself-description": "İştırakê şıma.",
- "rcfilters-filter-editsbyother-label": "Ê binan ra vurnayışi",
+ "rcfilters-filter-editsbyother-label": "vurnayışê merdımanê binan",
"rcfilters-filter-editsbyother-description": "Bê vurnayışanê şıma vurnayışi pêro.",
"rcfilters-filtergroup-userExpLevel": "Qeydê karberi u tecrube",
"rcfilters-filter-user-experience-level-registered-label": "Qeydıni",
"rcfilters-filter-user-experience-level-newcomer-label": "Ameyayeyê neweyi",
"rcfilters-filter-user-experience-level-newcomer-description": "Karberê qeydınê ke 10 ra kemi vurnayışi ya zi 4 rocan ra fealiyetê xo estê.",
"rcfilters-filter-user-experience-level-learner-label": "Musayoği",
+ "rcfilters-filter-user-experience-level-learner-description": "Cerbnayışê cı \"Newe ameyeni\" u arey \"Karberê wustay\" dê editorê qeydıni",
"rcfilters-filter-user-experience-level-experienced-label": "Karberê mısayeyi",
+ "rcfilters-filter-user-experience-level-experienced-description": "Editorê qeydıni ke 30 roce ra zeder aktivite u wayırê 500 ra zeder vurnayışa",
"rcfilters-filtergroup-automated": "İştırakê otomatiki",
"rcfilters-filter-bots-label": "Bot",
"rcfilters-filter-bots-description": "Terefê hacetanê otomatikan ra vurnayışi vıraziyayi.",
"rcfilters-filter-watchlistactivity-seen-label": "Vuriyayışê ke vêniyê",
"rcfilters-filtergroup-changetype": "Tewrê vurnayışi",
"rcfilters-filter-pageedits-label": "Vuriyayışê pelan",
+ "rcfilters-filter-pageedits-description": "Zerrekê wiki, werênayış, vurnayışê şınasiya Kategoriye...",
"rcfilters-filter-newpages-label": "Vıraştışê pelan",
"rcfilters-filter-newpages-description": "Vurnayışê ke pelanê newiyab vırazenê.",
"rcfilters-filter-categorization-label": "Vuriyayışê kategoriyan",
"rcfilters-filter-categorization-description": "Kategoriyan ra qeydê cıkerdış u wedardışê pelan.",
"rcfilters-filter-logactions-label": "Rocekê iştırakan",
+ "rcfilters-filter-logactions-description": "Aksiyonê xızmetkaran, hesab vıraştış, esternayışê pela, barkerdışi...",
"rcfilters-filtergroup-lastRevision": "Çımraviyarnayışê tewr peyêni",
"rcfilters-filter-lastrevision-label": "Çımraviyarnayışo peyên",
"rcfilters-filter-lastrevision-description": "Tenya vurnayışê yew peleyo tewr peyên.",
"rcfilters-filter-previousrevision-label": "Çımraviyarnayışo peyên niyo",
+ "rcfilters-filter-previousrevision-description": "Vurnayışê pêroyê ke \"Çımraviyarnayışo peyên\" niyo",
"rcfilters-filter-excluded": "Xarıc",
"rcfilters-tag-prefix-namespace-inverted": "$1 <strong>:nê</strong>",
"rcfilters-exclude-button-off": "Weçinayeyi ciya bıtepışê",
"tooltip-preferences-save": "Terciha qeyd ke",
"tooltip-summary": "Xulasa kılmek bınuse",
"interlanguage-link-title": "$1 - $2",
- "group-autoconfirmed.css": "/* Ronışyaye CSS seba otomatik raşt bıyaye karberan rê tesir keno */",
- "group-autoconfirmed.js": "/* No JavaScript teyna seba otomatik raşt bıyaye karberan rê do bar bo */",
+ "group-autoconfirmed.css": "/* CSSyo ke tiya roniyo, karberanê otomatikraştbiyayeyan rê tesir keno */",
+ "group-autoconfirmed.js": "/* No JavaScript teyna seba karberanê otomatikraştbiyayeyan rê do bar bo */",
"anonymous": "{{PLURAL:$1|karberê|karberê}} anonimi yê keyepelê {{SITENAME}}i",
"siteuser": "karberê {{SITENAME}}i $1",
"anonuser": "karberê anonim o {{SITENAME}}i $1",
"nextdiff": "Vurnayışo peyên →",
"mediawarning": "'''Teme''': Na dosya de belkia kodê xırabıni estê.\nGurênayışê nae de, beno ke sistemê şıma zerar bıvêno.",
"imagemaxsize": "Sinorê ebadê resımiyo ke pelanê şınasnayışê dosyeyan dero:",
- "thumbsize": "Ebado werdı",
+ "thumbsize": "Ebado werdi:",
"widthheight": "$1 - $2",
"widthheightpage": "$1 × $2, $3 {{PLURAL:$3|pele|peli}}",
"file-info": "ebatê dosyayi: $1, MIME tip: $2",
"userlogin-yourname": "Όνομα χρήστη",
"userlogin-yourname-ph": "Εισαγάγετε το όνομα χρήστη σας",
"createacct-another-username-ph": "Εισαγάγετε το όνομα χρήστη",
- "yourpassword": "Î\9aÏ\89δικÏ\8cÏ\82:",
- "userlogin-yourpassword": "Î\9aÏ\89δικÏ\8cÏ\82",
+ "yourpassword": "ΣÏ\85νθημαÏ\84ικÏ\8c:",
+ "userlogin-yourpassword": "ΣÏ\85νθημαÏ\84ικÏ\8c",
"userlogin-yourpassword-ph": "Εισαγάγετε το συνθηματικό σας",
"createacct-yourpassword-ph": "Εισαγωγή κωδικού",
"yourpasswordagain": "Επαναπληκτρολόγηση κωδικού:",
"nosuchusershort": "Δεν υπάρχει χρήστης με το όνομα \"$1\". Παρακαλούμε ελέγξτε την ορθογραφία.",
"nouserspecified": "Πρέπει να ορίσετε ένα όνομα χρήστη.",
"login-userblocked": "Αυτός ο χρήστης έχει αποκλειστεί. Δεν επιτρέπεται σύνδεση.",
- "wrongpassword": "Εισήχθη λανθασμένο όνομα χρήστη ή κωδικός.\nΠαρακαλούμε προσπαθήστε ξανά.",
- "wrongpasswordempty": "Î\9f κÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\80οÏ\85 ειÏ\83άÏ\87θηκε ήÏ\84αν κενÏ\8cÏ\82. Παρακαλούμε προσπαθήστε ξανά.",
+ "wrongpassword": "Εισήχθη λανθασμένο όνομα χρήστη ή συνθηματικό.\nΠαρακαλούμε προσπαθήστε ξανά.",
+ "wrongpasswordempty": "Το Ï\83Ï\85νθημαÏ\84ικÏ\8c Ï\80οÏ\85 ειÏ\83ήÏ\87θη ήÏ\84αν κενÏ\8c. Παρακαλούμε προσπαθήστε ξανά.",
"passwordtooshort": "Οι κωδικοί πρέπει να περιέχουν τουλάχιστον {{PLURAL:$1|1 χαρακτήρα|$1 χαρακτήρες}}.",
"passwordtoolong": "Οι κωδικοί πρόσβασης δεν μπορούν να υπερβαίνουν {{PLURAL:$1|τον 1 χαρακτήρα|τους $1 χαρακτήρες}}.",
"passwordtoopopular": "Κοινότυπα συνθηματικά που τείνουν να επιλέγονται συχνά από τους χρήστες δεν μπορούν να χρησιμοποιηθούν. Παρακαλούμε επιλέξτε ένα συνθηματικό που είναι πιο δύσκολο να μαντεφθεί.",
- "passwordinlargeblacklist": "Î\9f κÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\80οÏ\85 ειÏ\83ήÏ\87θηκε είναι Ï\83ε μια λίÏ\83Ï\84α Ï\80ολÏ\8d κοινÏ\8eν κÏ\89δικÏ\8eν Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82 Ï\80οÏ\85 Ï\87Ï\81ηÏ\83ιμοÏ\80οιοÏ\8dνÏ\84αι. ΠαÏ\81ακαλÏ\8e διαλÎξÏ\84ε Îναν Ï\80ιο Ï\83Ï\85νηθιÏ\83μÎνο κÏ\89δικÏ\8c Ï\80Ï\81Ï\8cÏ\83βαÏ\83ηÏ\82.",
+ "passwordinlargeblacklist": "Το Ï\83Ï\85νθημαÏ\84ικÏ\8c Ï\80οÏ\85 ειÏ\83ήÏ\87θη είναι Ï\83ε μια λίÏ\83Ï\84α Ï\80ολÏ\8d Ï\83Ï\85Ï\87νά Ï\87Ï\81ηÏ\83ιμοÏ\80οιοÏ\8dμενÏ\89ν Ï\83Ï\85νθημαÏ\84ικÏ\8eν. ΠαÏ\81ακαλοÏ\8dμε εÏ\80ιλÎξÏ\84ε κάÏ\80οιο Ï\80ιο μοναδικÏ\8c Ï\83Ï\85νθημαÏ\84ικÏ\8c.",
"password-name-match": "Ο κωδικός σου θα πρέπει να είναι διαφορετικός από το όνομα χρήστη σου.",
"password-login-forbidden": "Η χρήση αυτού του ονόματος χρήστη και συνθηματικού έχουν απαγορευτεί.",
"mailmypassword": "Επαναφορά κωδικού",
- "passwordremindertitle": "Καινούργιος προσωρινός κωδικός για το {{SITENAME}}",
+ "passwordremindertitle": "Καινούργιο προσωρινό συνθηματικό για το {{SITENAME}}",
"passwordremindertext": "Κάποιος (από τη διεύθυνση IP $1) αιτήθηκε νέο συνθηματικό για τον ιστότοπο {{SITENAME}} ($4). Έχει δημιουργηθεί το προσωρινό συνθηματικό «$3» για το χρήστη «$2». Αν ήταν αυτή η πρόθεσή σας, θα πρέπει να συνδεθείτε και να αλλάξετε το συνθηματικό σας τώρα. Το προσωρινό σας συνθηματικό θα λήξει σε {{PLURAL:$5|μια ημέρα|$5 ημέρες}}.\n\nΕάν αυτό το αίτημα έγινε από κάποιον τρίτο, ή αν θυμηθήκατε το συνθηματικό σας και δεν επιθυμείτε πλέον να το αλλάξετε, μπορείτε να αγνοήσετε αυτό το μήνυμα και να συνεχίσετε να χρησιμοποιείτε το παλιό σας συνθηματικό.",
"noemail": "Δεν έχει καθοριστεί ηλεκτρονική διεύθυνση για τον χρήστη \"$1\".",
"noemailcreate": "Είναι απαραίτητο να υποβάλλετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου",
- "passwordsent": "Σας έχει σταλεί ένας νέος κωδικός στην ηλεκτρονική διέθυνση που δηλώσατε για \"$1\".\nΣας παρακαλούμε να ξανασυνδεθείτε μόλις τον λάβετε.",
+ "passwordsent": "Σας έχει σταλεί ένα νέο συνθηματικό στη διεύθυνση ηλεκτρονικού ταχυδρομείου που δηλώσατε για «$1».\nΠαρακαλούμε να ξανασυνδεθείτε μόλις το λάβετε.",
"blocked-mailpassword": "Η διεύθυνση IP σας δεν έχει δικαίωμα επεξεργασίας. Για την αποφυγή κατάχρησης, δεν επιτρέπεται να χρησιμοποιήσετε την λειτουργία ανάκτησης κωδικού πρόσβασης από αυτήν την IP.",
"eauthentsent": "Ένα μήνυμα επαλήθευσης έχει σταλεί στην ηλεκτρονική διεύθυνση που έχετε δηλώσει.\nΠριν αρχίσει η αποστολή μηνυμάτων στη συγκεκριμένη διεύθυνση, πρέπει να ακολουθήσετε τις οδηγίες που βρίσκονται στο μήνυμα που σας έχει σταλεί, για να επαληθεύσετε ότι η συγκεκριμένη ηλεκτρονική διεύθυνση ανήκει πραγματικά σε εσάς.",
"throttled-mailpassword": "Ένα email επαναφοράς κωδικού έχει ήδη αποσταλεί, μέσα {{PLURAL:$1|στην τελευταία ώρα|στις τελευταίες $1 ώρες}}.\nΓια την αποφυγή κατάχρησης, μόνο ένα email επαναφοράς κωδικού θα στέλνεται ανά {{PLURAL:$1|ώρα|$1 ώρες}}.",
"resetpass_announce": "Για να ολοκληρώσετε τη σύνδεση πρέπει να καθορίσετε νέο συνθηματικό.",
"resetpass_text": "<!-- Προσθέστε κείμενο εδώ -->",
"resetpass_header": "Αλλαγή κωδικού πρόσβασης",
- "oldpassword": "Παλιός κωδικός",
- "newpassword": "Νέος κωδικός πρόσβασης",
+ "oldpassword": "Παλιό συνθηματικό:",
+ "newpassword": "Νέο συνθηματικό:",
"retypenew": "Πληκτρολογήστε ξανά το νέο συνθηματικό:",
"resetpass_submit": "Καθορίστε συνθηματικό και συνδεθείτε",
- "changepassword-success": "Î\9f κÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ήÏ\82 σας άλλαξε!",
+ "changepassword-success": "Το Ï\83Ï\85νθημαÏ\84ικÏ\8c σας άλλαξε!",
"changepassword-throttled": "Κάνατε πάρα πολλές πρόσφατες απόπειρες σύνδεσης.\nΠαρακαλούμε περιμένετε $1 προτού ξαναδοκιμάσετε.",
"botpasswords": "Συνθηματικά για ρομπότ",
"botpasswords-summary": "<em>Τα συνθηματικά για ρομπότ</em> επιτρέπουν πρόσβαση σε λογαριασμό χρήστη μέσω του API χωρίς να χρησιμοποιούνται τα κύρια διαπιστευτήρια σύνδεσης του λογαριασμού. Τα διαθέσιμα δικαιώματα χρήστη όταν είναι σε σύνδεση μέσω συνθηματικού για ρομπότ μπορεί να είναι περιορισμένα.\n\nΑν δεν ξέρετε γιατί να το κάνετε αυτό, καλύτερα να μην το κάνετε. Κανείς ποτέ δεν πρέπει να σας ζητήσει να δημιουργήσετε ένα τέτοιο συνθηματικό και να του το γνωστοποιήσετε.",
"resetpass-wrong-oldpass": "Μη έγκυρο προσωρινό ή τρέχον συνθηματικό.\nΜπορεί να έχετε ήδη αλλάξει το συνθηματικό σας ή να έχετε αιτηθεί νέο προσωρινό συνθηματικό.",
"resetpass-recycled": "Παρακαλούμε επαναφέρετε το συνθηματικό σας επιλέγοντας κάτι διαφορετικό από το τρέχον συνθηματικό σας.",
"resetpass-temp-emailed": "Έχετε συνδεθεί με έναν προσωρινό κωδικό μέσω ηλεκτρονικού ταχυδρομείου.\nΓια να ολοκληρώσετε τη σύνδεσή σας, πρέπει να ορίσετε νέο συνθηματικό εδώ:",
- "resetpass-temp-password": "Προσωρινός κωδικός:",
+ "resetpass-temp-password": "Προσωρινό συνθηματικό:",
"resetpass-abort-generic": "Η αλλαγή του κωδικού έχει απορριφθεί από μια προέκταση.",
"resetpass-expired": "Το συνθηματικό σας έχει λήξει. Παρακαλούμε καθορίστε νέο συνθηματικό για να συνδεθείτε.",
"resetpass-expired-soft": "Το συνθηματικό σας έχει λήξει και πρέπει να γίνει επαναφορά του. Παρακαλούμε επιλέξτε νέο συνθηματικό τώρα, ή κάντε κλικ στο «{{int:authprovider-resetpass-skip-label}}» για το επαναφέρετε αργότερα.",
"passwordreset-emailtitle": "Λεπτομέρειες λογαριασμού για {{SITENAME}}",
"passwordreset-emailtext-ip": "Κάποιος (κατά πάσα πιθανότητα εσείς, από την διεύθυνση IP $1) αιτήθηκε επαναφορά του συνθηματικού σας στον ιστότοπο {{SITENAME}} ($4). {{PLURAL:$3|Ο ακόλουθος λογαριασμός|Οι ακόλουθοι λογαριασμοί}} χρήστη συνδέονται με αυτήν τη διεύθυνση ηλεκτρονικού ταχυδρομείου:\n\n$2\n\n{{PLURAL:$3|Αυτό το προσωρινό συνθηματικό θα λήξει| Αυτά τα προσωρινά συνθηματικά θα λήξουν}} σε {{PLURAL:$5|μία ημέρα|$5 ημέρες}}.\nΘα πρέπει να συνδεθείτε και να επιλέξετε νέο συνθηματικό τώρα. Αν κάποιος άλλος έκανε αυτό το αίτημα ή αν θυμηθήκατε το αρχικό συνθηματικό σας και δεν επιθυμείτε πια να το αλλάξετε, μπορείτε να αγνοήσετε αυτό το μήνυμα και να συνεχίσετε να χρησιμοποιείτε το παλιό σας συνθηματικό.",
"passwordreset-emailtext-user": "Ο χρήστης $1 στον ιστότοπο {{SITENAME}} ζήτησε μια επαναφορά του συνθηματικού σας για τον ιστότοπο {{SITENAME}} ($4). {{PLURAL:$3|Ο ακόλουθος λογαριασμός|Οι ακόλουθοι λογαριασμοί}} χρήστη συνδέονται με αυτήν τη διεύθυνση ηλεκτρονικού ταχυδρομείου:\n\n$2\n\n{{PLURAL:$3|Αυτό το προσωρινό συνθηματικό θα λήξει| Αυτά τα προσωρινά συνθηματικά θα λήξουν}} σε {{PLURAL:$5|μία ημέρα|$5 ημέρες}}.\nΘα πρέπει να συνδεθείτε και να επιλέξετε νέο συνθηματικό τώρα. Αν κάποιος άλλος έκανε αυτό το αίτημα ή αν θυμηθήκατε το αρχικό συνθηματικό σας και δεν επιθυμείτε πια να το αλλάξετε, μπορείτε να αγνοήσετε αυτό το μήνυμα και να συνεχίσετε να χρησιμοποιείτε το παλιό σας συνθηματικό.",
- "passwordreset-emailelement": "Όνομα χρήστη: \n$1\n\nΠροσωρινός κωδικός πρόσβασης:\n$2",
+ "passwordreset-emailelement": "Όνομα χρήστη: \n$1\n\nΠροσωρινό συνθηματικό:\n$2",
"passwordreset-emailsentemail": "Αν αυτή η διεύθυνση ηλεκτρονικού ταχυδρομείου συνδέεται με το λογαριασμό σας, τότε θα σας αποσταλεί μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
"passwordreset-emailsentusername": "Αν υπάρχει μια διεύθυνση ηλεκτρονικού ταχυδρομείου που συνδέεται με αυτό το όνομα χρήστη, τότε θα σας αποσταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
"passwordreset-nocaller": "Πρέπει να γίνει κλήση",
"changeemail-newemail": "Νέα διεύθυνση ηλεκτρονικού ταχυδρομείου:",
"changeemail-newemail-help": "Αυτό το πεδίο θα πρέπει να μείνει κενό αν θέλετε να καταργήσετε τη διεύθυνσή σας ηλεκτρονικού ταχυδρομείου. Αν η διεύθυνση ηλεκτρονικού ταχυδρομείου καταργηθεί, δεν θα μπορείτε να επαναφέρετε τον κωδικό πρόσβασης σε περίπτωση που τον ξεχάσετε και δεν θα λαμβάνετε μηνύματα ηλεκτρονικού ταχυδρομείου από αυτό το wiki.",
"changeemail-none": "(κανένα)",
- "changeemail-password": "Î\9f κÏ\89δικÏ\8cÏ\82 Ï\80Ï\81Ï\8cÏ\83βαÏ\83ήÏ\82 σας στο εγχείρημα {{SITENAME}}:",
+ "changeemail-password": "Το Ï\83Ï\85νθημαÏ\84ικÏ\8c σας στο εγχείρημα {{SITENAME}}:",
"changeemail-submit": "Αλλαγή διεύθυνσης ηλεκτρονικού ταχυδρομείου",
"changeemail-throttled": "Κάνατε πάρα πολλές απόπειρες σύνδεσης.\nΠαρακαλούμε περιμένετε $1 προτού ξαναδοκιμάσετε.",
"changeemail-nochange": "Παρακαλούμε εισαγάγετε διαφορετική νέα διεύθυνση ηλεκτρονικού ταχυδρομείου.",
"loginreqtitle": "Απαιτείται η σύνδεση του χρήστη.",
"loginreqlink": "συνδεθείτε",
"loginreqpagetext": "Πρέπει να $1 για να δείτε άλλες σελίδες.",
- "accmailtitle": "Î\9f κÏ\89δικÏ\8cÏ\82 έχει σταλεί.",
+ "accmailtitle": "Το Ï\83Ï\85νθημαÏ\84ικÏ\8c έχει σταλεί.",
"accmailtext": "Ένας τυχαία παρηγμένος κωδικός για {{GENDER:$1|τον|την}} [[User talk:$1|$1]] έχει σταλεί στο $2.\n\nΜπορεί να αλλαχθεί από την σελίδα ''[[Special:ChangePassword|αλλαγή κωδικού]]'' μετά τη σύνδεση.",
"newarticle": "(Νέο)",
"newarticletext": "Ακολουθήσατε ένα σύνδεσμο προς μια σελίδα που δεν υπάρχει ακόμα. \nΓια να δημιουργήσετε τη σελίδα, αρχίστε να πληκτρολογείτε στο παρακάτω πλαίσιο (δείτε τη [$1 σελίδα βοήθειας] για περισσότερες πληροφορίες).\nΑν έχετε βρεθεί εδώ κατά λάθος, πατήστε το κουμπί '''πίσω''' στον περιηγητή σας.",
"prefs-watchlist-token": "Σημείο λίστας παρακολούθησης:",
"prefs-watchlist-managetokens": "Διαχείριση tokens",
"prefs-misc": "Διάφορες ρυθμίσεις",
- "prefs-resetpass": "Αλλαγή κωδικού",
+ "prefs-resetpass": "Αλλαγή συνθηματικού",
"prefs-changeemail": "Αλλαγή ή αφαίρεση της διεύθυνσης ηλεκτρονικού ταχυδρομείου",
"prefs-setemail": "Ορίστε μια διεύθυνση ηλεκτρονικού ταχυδρομείου",
"prefs-email": "Επιλογές διεύθυνσης ηλεκτρονικού ταχυδρομείου",
"statistics-articles": "Σελίδες περιεχομένου",
"statistics-pages": "Σελίδες",
"statistics-pages-desc": "Όλες οι σελίδες του wiki, συμπεριλαμβανομένων των σελίδων συζήτησης, ανακατευθύνσεων, κλπ.",
- "statistics-files": "Αρχεία που έχουν επιφορτωθεί",
+ "statistics-files": "Ανεβασμένα αρχεία",
"statistics-edits": "Επεξεργασίες σελίδων από τη δημιουργία του εγχειρήματος {{SITENAME}}",
"statistics-edits-average": "Μέσος όρος επεξεργασιών ανά σελίδα",
"statistics-users": "Εγγεγραμμένοι χρήστες",
"booksources-text": "Παρακάτω είναι μια λίστα συνδέσμων σε άλλους ιστοτόπους οι οποίοι πωλούν νέα και μεταχειρισμένα βιβλία, και μπορεί επίσης να έχουν περισσότερες πληροφορίες για βιβλία για τα οποία ψάχνετε:",
"booksources-invalid-isbn": "Το δοσμένο ISBN δεν φαίνεται να είναι έγκυρο· ελέγξτε για λάθη κατά την αντιγραφή από την αρχική πηγή.",
"specialloguserlabel": "",
- "speciallogtitlelabel": "Στόχος (τίτλος ή {{ns:user}}:χρήστης για χρήστη):",
+ "speciallogtitlelabel": "Στόχος (τίτλος ή «{{ns:user}}:Τάδε» για χρήστη):",
"log": "Αρχεία καταγραφών",
"logeventslist-submit": "Προβολή",
+ "logeventslist-more-filters": "Εμφάνιση επιπρόσθετων καταγραφών:",
+ "logeventslist-tag-log": "Καταγραφές ετικετών",
"all-logs-page": "Όλες οι δημόσιες καταγραφές γεγονότων",
"alllogstext": "Εποπτική εμφάνιση όλων των ενεργειών φόρτωσης αρχείων, διαγραφής, προστασίας, φραγής και όλων των καταγραφών των διαχειριστών στο αρχείο γεγονότων του {{SITENAME}}. Μπορείτε να περιορίσετε τα αποτελέσματα που εμφανίζονται επιλέγοντας συγκεκριμένο είδος γεγονότων, όνομα χρήστη ή τη σελίδα που επηρεάστηκε.",
"logempty": "Δεν υπάρχουν στοιχεία που να ταιριάζουν στο αρχείο καταγραφών.",
- "log-title-wildcard": "Î\91ναζήÏ\84ηÏ\83ε Ï\84ίÏ\84λοÏ\85Ï\82 που αρχίζουν με αυτό το κείμενο",
+ "log-title-wildcard": "Î\91ναζήÏ\84ηÏ\83η Ï\84ίÏ\84λÏ\89ν που αρχίζουν με αυτό το κείμενο",
"showhideselectedlogentries": "Αλλαγή ορατότητας των επιλεγμένων καταχωρήσεων στο αρχείο καταγραφής συμβάντων",
"log-edit-tags": "Επεξεργασία ετικετών των επιλεγμένων καταχωρήσεων του αρχείου καταγραφής",
"checkbox-select": "Επιλογή: $1",
"uctop": "τρέχουσα",
"month": "Από το μήνα (και νωρίτερα):",
"year": "Από το έτος (και νωρίτερα):",
+ "date": "Από ημερομηνία (και νωρίτερα):",
"sp-contributions-newbies": "Εμφάνιση των συνεισφορών των νέων λογαριασμών μόνο",
"sp-contributions-newbies-sub": "Για νέους λογαριασμούς",
"sp-contributions-newbies-title": "Συνεισφορές χρηστών για νέους λογαριασμούς",
"markedaspatrollederror-noautopatrol": "Δεν επιτρέπεται να σημάνετε τις δικές σας αλλάγες ως υπό περιπολία.",
"markedaspatrollednotify": "Αυτή η αλλαγή σε $1 έχει επισημανθεί ως ελεγμένη.",
"markedaspatrollederrornotify": "Σήμανση ως ελεγμένη απέτυχε.",
- "patrol-log-page": "Î\91Ï\81Ï\87είο καÏ\84αγÏ\81αÏ\86ήÏ\82 Ï\80εÏ\81ιÏ\80ολιών",
+ "patrol-log-page": "Î\9aαÏ\84αγÏ\81αÏ\86ÎÏ\82 ελÎγÏ\87Ï\89ν εÏ\80εξεÏ\81γαÏ\83ιών",
"patrol-log-header": "Αυτό είναι μητρώο ελεγμένων αναθεωρήσεων.",
"confirm-markpatrolled-button": "Εντάξει",
"deletedrevision": "Η παλιά έκδοση της $1 διαγράφτηκε",
"authmanager-email-help": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
"authmanager-realname-label": "Πραγματικό όνομα",
"authmanager-realname-help": "Πραγματικό όνομα του χρήστη",
- "authmanager-provider-temporarypassword": "Προσωρινός κωδικός",
+ "authmanager-provider-temporarypassword": "Προσωρινό συνθηματικό",
"authprovider-resetpass-skip-label": "Παράβλεψη",
"authprovider-resetpass-skip-help": "Παράβλεψη της επαναφοράς του κωδικού πρόσβασης.",
"specialpage-securitylevel-not-allowed-title": "Δεν επιτρέπεται",
"action-changetags": "add and remove arbitrary tags on individual revisions and log entries",
"action-deletechangetags": "delete tags from the database",
"action-purge": "purge this page",
+ "action-apihighlimits": "use higher limits in API queries",
+ "action-autoconfirmed": "not be affected by IP-based rate limits",
+ "action-bigdelete": "delete pages with large histories",
+ "action-blockemail": "block a user from sending email",
+ "action-bot": "be treated as an automated process",
+ "action-editprotected": "edit pages protected as \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "edit pages protected as \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "edit the user interface",
+ "action-editusercss": "edit other users' CSS files",
+ "action-edituserjson": "edit other users' JSON files",
+ "action-edituserjs": "edit other users' JavaScript files",
+ "action-editsitecss": "edit sitewide CSS",
+ "action-editsitejson": "edit sitewide JSON",
+ "action-editsitejs": "edit sitewide JavaScript",
+ "action-editmyusercss": "edit your own user CSS files",
+ "action-editmyuserjson": "edit your own user JSON files",
+ "action-editmyuserjs": "edit your own user JavaScript files",
+ "action-viewsuppressed": "view revisions hidden from any user",
+ "action-hideuser": "block a username, hiding it from the public",
+ "action-ipblock-exempt": "bypass IP blocks, auto-blocks and range blocks",
+ "action-unblockself": "unblock oneself",
+ "action-noratelimit": "not be affected by rate limits",
+ "action-reupload-own": "overwrite existing files uploaded by oneself",
+ "action-nominornewtalk": "not have minor edits to discussion pages trigger the new messages prompt",
+ "action-markbotedits": "mark rolled-back edits as bot edits",
+ "action-patrolmarks": "view recent changes patrol marks",
+ "action-override-export-depth": "export pages including linked pages up to a depth of 5",
+ "action-suppressredirect": "not create redirects from source pages when moving pages",
"nchanges": "$1 {{PLURAL:$1|change|changes}}",
"ntimes": "$1×",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|since last visit}}",
"page_first": "ensimmäinen sivu",
"page_last": "viimeinen sivu",
"histlegend": "Eroavaisuuksien valinta: Merkitse niiden versioiden valintaympyrät, joita haluat vertailla, ja paina enter tai alhaalla olevaa nappia.<br />\nSelitys: '''({{int:cur}})''' = eroavaisuudet uusimpaan versioon, '''({{int:last}})''' = eroavaisuudet edeltävään versioon, '''{{int:minoreditletter}}''' = pieni muutos.",
- "history-fieldset-title": "Etsi versioita",
+ "history-fieldset-title": "Suodata versioita",
"history-show-deleted": "Vain poistetut versiot",
"histfirst": "vanhimmat",
"histlast": "uusimmat",
"action-changetags": "lisätä ja poistaa satunnaisia merkkauksia yksittäisissä sivuversioissa ja lokimerkinnöissä",
"action-deletechangetags": "poistaa merkkauksia tietokannasta",
"action-purge": "päivittää tämän sivun välimuistia",
+ "action-apihighlimits": "käyttää korkeampia rajoja API-kyselyissä",
+ "action-bigdelete": "poistaa sivuja, joilla on pitkä historia",
+ "action-blockemail": "estää käyttäjää lähettämästä sähköpostia",
+ "action-editusercss": "muokata toisten käyttäjien CSS-tiedostoja",
+ "action-edituserjson": "muokata toisten käyttäjien JSON-tiedostoja",
+ "action-edituserjs": "muokata toisten käyttäjien JavaScript-tiedostoja",
+ "action-editsitecss": "muokata CSS-koodia koko sivustolla",
+ "action-editsitejson": "muokata JSON-koodia koko sivustolla",
+ "action-editsitejs": "muokata JavaScriptiä koko sivustolla",
+ "action-editmyusercss": "muokata omia CSS-tiedostoja",
+ "action-editmyuserjson": "muokkata omia JSON-tiedostoja",
+ "action-editmyuserjs": "muokata omia JavaScript-tiedostoja",
+ "action-viewsuppressed": "katsoa versioita, jotka on piilotettu jokaiselta käyttäjältä",
+ "action-hideuser": "estää käyttäjätunnus ja piilottaa se näkyvistä",
+ "action-ipblock-exempt": "ohittaa IP-, automaattiset ja osoitealue-estot",
+ "action-unblockself": "poistaa esto itseltään",
+ "action-noratelimit": "ohittaa nopeusrajoitukset",
+ "action-reupload-own": "korvata itsetallennettu tiedosto uudella tiedostolla",
+ "action-nominornewtalk": "tehdä pieniä muokkauksia käyttäjien keskustelusivuille siten, että käyttäjälle ei ilmoiteta siitä uutena viestinä",
+ "action-markbotedits": "merkitä muokkausten palauttaminen botilla tehdyksi",
+ "action-patrolmarks": "nähdä tarkastusmerkit tuoreissa muutoksissa",
+ "action-override-export-depth": "viedä sivuja sisältäen viitatut sivut viiden syvyydellä",
+ "action-suppressredirect": "siirtää sivuja luomatta automaattisia ohjauksia",
"nchanges": "$1 {{PLURAL:$1|muutos|muutosta}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|viimeisen käynnin jälkeen}}",
"enhancedrc-history": "historia",
"rcfilters-watchlist-edit-watchlist-button": "Muokkaa tarkkailemiasi sivuja",
"rcfilters-watchlist-showupdated": "Muutokset sivuihin, joilla et ole vieraillut sen jälkeen kun muutokset on tehty, on <strong>lihavoitu</strong> ja värimerkitty.",
"rcfilters-preference-label": "Käytä ilman JavaScriptiä toimivaa käyttöliittymää",
- "rcfilters-preference-help": "Lataa tuoreimmat muutokset -näkymän ilman suodattimia tai korostustoimintoa.",
+ "rcfilters-preference-help": "Lataa tuoreimmat muutokset -näkymän ilman suodattimien haku- tai korostustoimintoa.",
"rcfilters-watchlist-preference-label": "Käytä ilman JavaScriptiä toimivaa käyttöliittymää",
- "rcfilters-watchlist-preference-help": "Lataa tarkkailulistan ilman suodattimia tai korostustoimintoa.",
+ "rcfilters-watchlist-preference-help": "Lataa tarkkailulistan ilman suodattimien haku- tai korostustoimintoa.",
"rcfilters-filter-showlinkedfrom-label": "Näytä muutokset sivuilla, jonne on linkki sivulta",
"rcfilters-filter-showlinkedfrom-option-label": "<strong>Sivut, joihin linkitetään</strong> valitulta sivulta",
"rcfilters-filter-showlinkedto-label": "Näytä muutokset sivuilla, joista on linkki sivulle",
"log-description-pagelang": "Tämä on loki, johon merkitään muutokset sivujen kieliasetuksissa.",
"logentry-pagelang-pagelang": "$1 {{GENDER:$2|muutti}} kielen sivulla $3 kielestä $4 kieleksi $5.",
"default-skin-not-found": "Hupsista! Oletuksena tuleva ulkoasu wikillesi, joka on määritelty koodissa <code dir=\"ltr\">$wgDefaultSkin</code> muotoon <code>$1</code>, ei ole saatavilla.\n\nAlla on ohjeet englannin kielellä:\n\nYour installation seems to include the following {{PLURAL:$4|skin|skins}}. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable {{PLURAL:$4|it|them and choose the default}}.\n\n$2\n\n; If you have just installed MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by:\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Using Git to download skins].\n: Doing this should not interfere with your git repository if you're a MediaWiki developer.\n\n; If you have just upgraded MediaWiki:\n: MediaWiki 1.24 and newer no longer automatically enables installed skins (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). You can paste the following {{PLURAL:$5|line|lines}} into <code>LocalSettings.php</code> to enable {{PLURAL:$5|the|all}} installed {{PLURAL:$5|skin|skins}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; If you have just modified <code>LocalSettings.php</code>:\n: Double-check the skin names for typos.",
- "default-skin-not-found-no-skins": "Hupsista! Oletusulkoasua sinun wikillesi ei ole saatavilla. Se on määritelty ulkoasuksi <code>$1</code> kohteessa <code>$wgDefaultSkin</code>.\n\nAlla on ohjeet englannin kielellä:\n\nYou have no installed skins.\n\n; If you have just installed or upgraded MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected. MediaWiki 1.24 and newer doesn't include any skins in the main repository. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by:\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Using Git to download skins].\n: Doing this should not interfere with your git repository if you're a MediaWiki developer. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable skins and choose the default.",
+ "default-skin-not-found-no-skins": "Hupsista! Oletusulkoasua wikillesi ei ole saatavilla. Se on määritelty ulkoasuksi <code>$1</code> kohteessa <code>$wgDefaultSkin</code>.\n\nAlla on ohjeet englannin kielellä:\n\nYou have no installed skins.\n\n; If you have just installed or upgraded MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected. MediaWiki 1.24 and newer doesn't include any skins in the main repository. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by:\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Using Git to download skins].\n: Doing this should not interfere with your git repository if you're a MediaWiki developer. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable skins and choose the default.",
"default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (käytössä)",
"default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>ei käytössä</strong>)",
"mediastatistics": "Median tilastotiedot",
"Alacabe",
"Eihel",
"Tektasc",
- "DSwissK"
+ "DSwissK",
+ "Moyogo"
]
},
"tog-underline": "Soulignement des liens :",
"oct": "oct.",
"nov": "nov.",
"dec": "déc.",
- "january-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} janvier",
- "february-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} février",
- "march-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} mars",
- "april-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} avril",
- "may-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} mai",
- "june-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} juin",
- "july-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} juillet",
- "august-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} août",
- "september-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} septembre",
- "october-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} octobre",
- "november-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} novembre",
- "december-date": "{{PLURAL:$1|1=1ᵉʳ|$1}} décembre",
+ "january-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} janvier",
+ "february-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} février",
+ "march-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} mars",
+ "april-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} avril",
+ "may-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} mai",
+ "june-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} juin",
+ "july-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} juillet",
+ "august-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} août",
+ "september-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} septembre",
+ "october-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} octobre",
+ "november-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} novembre",
+ "december-date": "{{PLURAL:$1|1=1<sup>er</sup>|$1}} décembre",
"period-am": "AM",
"period-pm": "PM",
"pagecategories": "{{PLURAL:$1|Catégorie|Catégories}}",
"action-changetags": "ajouter et supprimer de façon arbitraire des balises sur des révisions individuelles et des entrées de journal",
"action-deletechangetags": "supprimer des balises de la base de données",
"action-purge": "purger cette page",
+ "action-apihighlimits": "utiliser de limites plus élevées dans les requêtes à l’API",
+ "action-autoconfirmed": "ne pas être impacté par les limites de taux basées sur l’IP",
+ "action-bigdelete": "supprimer des pages avec de grands historiques",
+ "action-blockemail": "empêcher un utilisateur d’envoyer des courriels",
+ "action-bot": "être traité comme un processus automatisé",
+ "action-editsemiprotected": "modifier des pages protégées avec le statut « {{int:protect-level-autoconfirmed}} »",
+ "action-editinterface": "modifier l’interface utilisateur",
+ "action-editusercss": "modifier les fichiers CSS d’autres utilisateurs",
+ "action-edituserjson": "modifier les fichiers JSON d’autres utilisateurs",
+ "action-edituserjs": "modifier les fichiers JavaScript d’autres utilisateurs",
+ "action-editsitecss": "modifier le CSS du site",
+ "action-editsitejson": "modifier le JSON du site",
+ "action-editsitejs": "modifier le JavaScript du site",
+ "action-editmyusercss": "modifier vos propres fichiers CSS utilisateur",
+ "action-editmyuserjson": "modifier vos propres fichiers JSON utilisateur",
+ "action-editmyuserjs": "modifier vos propres fichiers JavaScript utilisateur",
+ "action-viewsuppressed": "afficher les révisions masquées pour n’importe quel utilisateur",
+ "action-hideuser": "bloquer un nom d’utilisateur, en le masquant au public",
+ "action-ipblock-exempt": "contourner les blocages d’IP, blocages automatiques et blocages de plages d’IP",
+ "action-unblockself": "vous débloquer vous-même",
+ "action-noratelimit": "ne pas être impacté par les limites de taux",
+ "action-reupload-own": "écraser des fichiers que vous avez vous-même importés",
+ "action-nominornewtalk": "ne pas déclencher la notification de nouveau message lors d’une modification mineure sur une pages de discussion",
+ "action-markbotedits": "marquer des modifications révoquées comme ayant été faites par un robot",
+ "action-patrolmarks": "voir les indications de relecture dans les modifications récentes",
+ "action-override-export-depth": "exporter les pages en incluant les pages liées jusqu’à une profondeur de 5 niveaux",
+ "action-suppressredirect": "ne pas créer de redirection depuis le titre d’origine en renommant les pages",
"nchanges": "$1 modification{{PLURAL:$1||s}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|depuis la dernière visite}}",
"enhancedrc-history": "historique",
"recentchanges-legend-heading": "<strong>Leginda:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}}<br />(sjoch ek de [[Special:NewPages|list mei nije siden]])",
"rcfilters-legend-heading": "<strong>List fan ôfkoartings:</strong>",
+ "rcfilters-group-results-by-page": "Resultaten op side groepearje",
+ "rcfilters-activefilters": "Aktive filters",
+ "rcfilters-activefilters-hide": "Ynklappe",
+ "rcfilters-activefilters-show": "Utklappe",
+ "rcfilters-advancedfilters": "Utwreide filters",
+ "rcfilters-limit-title": "Resultaten werjaan",
+ "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|feroaring|feroarings}}, $2",
+ "rcfilters-date-popup-title": "Tiidsperioade trochsykje",
+ "rcfilters-days-title": "Lêste dagen",
+ "rcfilters-hours-title": "Lêste oeren",
+ "rcfilters-days-show-days": "$1 {{PLURAL:$1|dei|dagen}}",
+ "rcfilters-days-show-hours": "$1 {{PLURAL:$1|oere|oeren}}",
+ "rcfilters-quickfilters": "Bewarre filters",
+ "rcfilters-savedqueries-defaultlabel": "Bewarre filters",
+ "rcfilters-savedqueries-add-new-title": "De filterynstellings fan no bewarje",
+ "rcfilters-clear-all-filters": "Alle filters wiskje",
"rcfilters-search-placeholder": "Feroarings filterje (brûk it menu of sykje op filternamme)",
"rcfilters-filterlist-feedbacklink": "Lit ús hearre wat jo fan dit filterark fine",
"rcfilters-highlightbutton-title": "Resultaten aksintuearje",
"rcfilters-view-tags-tooltip": "Resultaten mei bewurkingslebels filterje",
"rcfilters-view-return-to-default-tooltip": "Werom nei it filterhaadmenu",
"rcfilters-view-tags-help-icon-tooltip": "Kom mear oan 'e weet oer Lebele bewurkings",
+ "rcfilters-liveupdates-button": "Daliks bywurkje",
+ "rcfilters-liveupdates-button-title-on": "Daliks bywurkjen stopje",
+ "rcfilters-liveupdates-button-title-off": "Nije feroarings op it stuit werjaan",
"rcnotefrom": "Hjirûnder {{PLURAL:$5|stiet de feroaring|steane de feroarings}} sûnt <strong>$3, $4</strong> (maksimaal <strong>$1</strong> werjûn).",
"rclistfrom": "Jou nije feroarings, begjinnende mei $3 $2",
"rcshowhideminor": "Lytse feroarings $1",
"action-changetags": "להוסיף או להסיר תגיות מגרסאות ומרשומות יומן",
"action-deletechangetags": "למחוק תגיות מבסיס הנתונים",
"action-purge": "לנקות את זיכרון המטמון של דף זה",
+ "action-apihighlimits": "להשתמש בהגבלות גבוהות יותר בשאילתות API",
+ "action-autoconfirmed": "לא להיות תחת השפעת הגבלות קצב מבוססות IP",
+ "action-bigdelete": "למחוק דפים עם היסטוריות גדולות",
+ "action-blockemail": "לחסום משתמש מפני שליחת דור אלקטרוני",
+ "action-bot": "לקבל טיפול של תהליך אוטומטי",
+ "action-editprotected": "לערוך דפים שמוגנים עם \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "לערוך דפים שמוגנים עם \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "לערוך את ממשק המשתמש",
+ "action-editusercss": "לערוך קובצי CSS של משתמשים אחרים",
+ "action-edituserjson": "לערוך קובצי JSON של משתמשים אחרים",
+ "action-edituserjs": "לערוך קובצי JavaSript של משתמשים אחרים",
+ "action-editsitecss": "לערוך CSS שחל על כל האתר",
+ "action-editsitejson": "לערוך JSON שחל על כל האתר",
+ "action-editsitejs": "לערוך JavaScript שחל על כל האתר",
+ "action-editmyusercss": "לערוך קובצי CSS של עצמך",
+ "action-editmyuserjson": "לערוך קובצי JSON של עצמך",
+ "action-editmyuserjs": "לערוך קובצי JavaScript של עצמך",
+ "action-viewsuppressed": "להציג גרסאות שמוסתרות מכל משתמש",
+ "action-hideuser": "לחסום שם משתמש, ולהסתיר אותו מהציבור",
+ "action-ipblock-exempt": "לעקוף חסימות IP, חסימות אוטומטיות, וחסימות טווח",
+ "action-unblockself": "לשחרר את עצמך מחסימה",
+ "action-noratelimit": "לא להיות תחת השפעה של הגבלות קצב עריכה",
+ "action-reupload-own": "לדרוס קבצים קיימים שהעלית בעצמך",
+ "action-nominornewtalk": "לא לגרום לעריכות משניות לדפי שיחה לגרום לשאלת הודעות חדשות",
+ "action-markbotedits": "לסמן עריכות ששוחזרו בתור עריכות של בוט",
+ "action-patrolmarks": "להציג סימוני בדיקה של שינויים אחרונים",
+ "action-override-export-depth": "לייצא דפים כולל דפים מקושרים עד עומק של 5 רמות",
+ "action-suppressredirect": "לא ליצור הפניות מדף המקור בעת העברת דפים",
"nchanges": "{{PLURAL:$1|שינוי אחד|$1 שינויים}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|מאז ביקורך האחרון}}",
"enhancedrc-history": "היסטוריה",
"createacct-another-submit": "Ստեղծել հաշիվ",
"createacct-continue-submit": "Շարունակել ստեղծել հաշիվ",
"createacct-another-continue-submit": "Շարունակել ստեղծել հաշիվ",
- "createacct-benefit-heading": "{{SITENAME}}՝ ստեղծվում է ձեր պես մարդկանց կողմից։",
+ "createacct-benefit-heading": "{{SITENAME}}՝ ստեղծվում է ձեզ պես մարդկանց կողմից։",
"createacct-benefit-body1": "{{PLURAL:$1|խմբագրում}}",
"createacct-benefit-body2": "{{PLURAL:$1|էջ}}",
"createacct-benefit-body3": "վերջերս ակտիվ {{PLURAL:$1|մասնակից}}",
"search-result-category-size": "{{PLURAL:$1|անդամ}} ({{PLURAL:$2|ենթակատեգորիա}}, {{PLURAL:$3|նիշք}})",
"search-redirect": "(վերահղում $1 էջից)",
"search-section": "(բաժին $1)",
+ "search-category": "(կատեգորիա $1)",
"search-file-match": "(համապատասխանում է նիշքի բովանդակությանը)",
"search-suggest": "Գուցե նկատի ունե՞ք՝ $1",
"search-interwiki-caption": "Կից նախագծեր",
"upload-dialog-button-upload": "Բեռնել",
"upload-form-label-infoform-title": "Մանրամասներ",
"upload-form-label-infoform-name": "Անուն",
+ "upload-form-label-infoform-description": "Նկարագրություն",
"upload-form-label-usage-filename": "Նիշքի անուն",
"upload-form-label-own-work": "Սա իմ անձնական աշխատանքն է",
"upload-form-label-infoform-categories": "Կատեգորիաներ",
"listgrouprights-rights": "Իրավունքներ",
"listgrouprights-members": "(անդամների ցանկ)",
"listgrouprights-addgroup": "Ավելացնեել {{PLURAL:$2|խումբ|խմբեր}}՝ $1",
+ "listgrouprights-namespaceprotection-namespace": "Անվանատարածք",
"listgrants-rights": "Իրավունքներ",
"mailnologin": "Ուղարկման հասցե չկա",
"mailnologintext": "Անհրաժեշտ է [[Special:UserLogin|մտնել համակարգ]] և ունենալ գործող էլ-փոստի հասցե ձեր [[Special:Preferences|նախընտրություններում]]՝ ուրիշ մասնակիցներին էլեկտրոնային նամակներ ուղարկելու համար։",
"tooltip-invert": "Նշե՛ք տվյալ անվանատարածքի և կից անվանատարածքների (եթե նշված է) էջերի փոփոխությունները թաքցնելու համար։",
"namespace_association": "Կից անվանատարածք",
"tooltip-namespace_association": "Նշեք տվյալ անվանատարածքի հետ կապված քննարկումների անվանատարածքը նույնպես ներառելու համար։",
- "blanknamespace": "(Գլխավոր)",
+ "blanknamespace": "(Հիմնական)",
"contributions": "{{GENDER:$1|Մասնակցի}} ներդրում",
"contributions-title": "$1 մասնակցի ներդրումը",
"mycontris": "Ներդրում",
"movepage-page-unmoved": "$1 էջը հնարավոր չէր վերանվանել $2",
"movelogpage": "Տեղափոխման տեղեկամատյան",
"movelogpagetext": "Ստորև բերված է վերանվանված էջերի ցանկը։",
+ "movesubpage": "{{PLURAL:$1|Ենթաէջ|Ենթաէջեր}}",
"movenosubpage": "Այս էջը ենթաէջեր չունի",
"movereason": "Պատճառ.",
"revertmove": "հետ շրջել",
"minutes": "{{PLURAL:$1|$1 րոպե|$1 րոպե}}",
"hours": "{{PLURAL:$1|$1 ժամ|$1 ժամ}}",
"days": "{{PLURAL:$1|$1 օր|$1 օր}}",
+ "weeks": "{{PLURAL:$1|$1 շաբաթ|$1 շաբաթներ}}",
+ "months": "{{PLURAL:$1|$1 ամիս|$1 ամիսներ}}",
+ "years": "{{PLURAL:$1|$1 տարի|$1 տարիներ}}",
"ago": "$1 առաջ",
"bad_image_list": "Գրաձևը հետևյալն է.\n\nՀաշվի են առնվելու միայն ցանկի տարրերը (* սիմվոլով սկսվող տողերը)։\nՏողի առաջին հղումը պետք է լինի դեպի արգելված պատկերը։\nՏողի հետագա հղումները ընկալվելու են որպես բացառություններ, այսինքն էջեր, որտեղ նշված պատկերի փակցնումը չի արգելվում։",
"metadata": "Մետատվյալներ",
"htmlform-title-not-exists": "$1՝ գոյություն չունի",
"logentry-delete-delete": "$1 {{GENDER:$2|ջնջեց}} $3 էջը",
"logentry-delete-restore": "$1 վերականգնեց $3 ($4) էջը",
+ "restore-count-files": "{{PLURAL:$1|1 նիշք|$1 նիշքեր}}",
"logentry-delete-event": "$1 փոխեց տեղեկամատյանի {{PLURAL:$5|1 գրանցման|$5 գրանցումների}} տեսանելությունը $3-ում. $4",
"logentry-delete-revision": "$1 {{GENDER:$2|փոխեց}} {{PLURAL:$5|1 խմբագրման|$5 խմբագրումների}} տեսանելիությունը $3 էջում․ $4",
"logentry-delete-event-legacy": "$1 փոխեց տեղեկամատյանի գրանցումների տեսանելությունը $3-ում",
"expandtemplates": "Կաղապարների ընդարձակում",
"expand_templates_ok": "Լավ",
"pagelang-name": "Էջ",
+ "pagelang-language": "Լեզու",
"pagelang-select-lang": "Ընտրեք լեզուն",
"pagelang-reason": "Պատճառ",
"pagelang-submit": "Հաստատել",
"pagelang-nonexistent-page": "$1 էջը գոյություն չունի",
+ "mediastatistics-nbytes": "{{PLURAL:$1|$1 բայթ|$1 բայթեր}} ($2; $3%)",
"mediastatistics-header-total": "Բոլոր նիշքեր",
"special-characters-group-latin": "Լատիներեն",
"special-characters-group-latinextended": "Լատիներեն ընդլայնված",
"views": "Տեսնուած",
"toolbox": "Գործիքներ",
"tool-link-userrights": "Փոխել {{GENDER:$1|գործածող}} խումբեր",
- "tool-link-userrights-readonly": "Õ\8fÕ¥Õ½Õ¶Õ¡լ {{GENDER:$1|գործածող}} խումբեր",
+ "tool-link-userrights-readonly": "Õ\8fÕ¥Õ½Õ¶Õ¥լ {{GENDER:$1|գործածող}} խումբեր",
"tool-link-emailuser": "Ղրկել ասիկա էլ-նամակով {{GENDER:$1|գործածողին}}",
"imagepage": "Դիտել նիշքի էջը",
"mediawikipage": "Դիտել հաղորդագրութեան էջը",
"action-changetags": "個々の版および記録項目への任意のタグの追加と除去",
"action-deletechangetags": "データベースからタグの削除",
"action-purge": "このページのキャッシュ破棄",
+ "action-apihighlimits": "API要求でのより高い制限値の使用",
+ "action-bigdelete": "大きな履歴があるページの削除",
+ "action-blockemail": "利用者のメール送信のブロック",
+ "action-editprotected": "「{{int:protect-level-sysop}}」の保護を設定されたページの編集",
+ "action-editsemiprotected": "「{{int:protect-level-autoconfirmed}}」の保護を設定されたページの編集",
+ "action-editinterface": "ユーザーインターフェースの編集",
+ "action-editusercss": "他の利用者のCSSファイルの編集",
+ "action-edituserjson": "他の利用者のJSONファイルの編集",
+ "action-edituserjs": "他の利用者のJavaScriptファイルの編集",
+ "action-editsitecss": "サイト全体のCSSの編集",
+ "action-editsitejson": "サイト全体のJSONの編集",
+ "action-editsitejs": "サイト全体のJavaScriptの編集",
+ "action-editmyusercss": "自分のCSSファイルの編集",
+ "action-editmyuserjson": "自分のJSONファイルの編集",
+ "action-editmyuserjs": "自分のJavaScriptファイルの編集",
+ "action-viewsuppressed": "すべての利用者から隠された版の閲覧",
+ "action-ipblock-exempt": "IPブロック、自動ブロック、広域ブロックの回避",
+ "action-unblockself": "自分に対するブロックの解除",
+ "action-reupload-own": "自分がアップロードした既存のファイルへの上書き",
+ "action-patrolmarks": "最近の更新での巡回済み印の閲覧",
+ "action-override-export-depth": "リンク先ページの5階層まで含めた書き出し",
+ "action-suppressredirect": "転送ページの作成を伴わないページの移動",
"nchanges": "$1 {{PLURAL:$1|回の変更}}",
"ntimes": "$1×",
"enhancedrc-since-last-visit": "最終閲覧以降 $1 {{PLURAL:$1|件}}",
"uploadstash-file-not-found-missing-content-type": "コンテンツタイプヘッダーがありません。",
"uploadstash-file-not-found-not-exists": "パスが見つからないか、またはプレーンファイルではありません。",
"uploadstash-file-too-large": "$1バイトを超えるファイルは提供できません。",
- "uploadstash-not-logged-in": "ユーザーはログインしていません。ファイルはユーザーに属している必要があります。",
- "uploadstash-wrong-owner": "このファイル($1)は現在のユーザーに属していません。",
+ "uploadstash-not-logged-in": "利用者はログインしていません。ファイルは利用者に属している必要があります。",
+ "uploadstash-wrong-owner": "このファイル ($1) は現在の利用者に属していません。",
"uploadstash-no-such-key": "そのような鍵($1)は削除できません。",
"uploadstash-no-extension": "拡張機能がnullです。",
"uploadstash-zero-length": "ファイルのサイズがゼロです。",
"delete-confirm": "「$1」の削除",
"delete-legend": "削除",
"historywarning": "<strong>警告:</strong> 削除しようとしているページには、$1版の履歴があります:",
- "historyaction-submit": "æ\9b´æ\96°ã\81®表示",
+ "historyaction-submit": "æ\9b´æ\96°ã\82\92表示",
"confirmdeletetext": "ページをすべての履歴とともに削除しようとしています。\n本当にこの操作を行いたいか、操作の結果を理解しているか、およびこの操作が[[{{MediaWiki:Policy-url}}|方針]]に従っているかどうか、確認してください。",
"actioncomplete": "操作を完了しました",
"actionfailed": "操作に失敗しました",
"import-mapping-subpage": "次のページの下位ページとしてインポート:",
"import-upload-filename": "ファイル名:",
"import-upload-username-prefix": "インターウィキ接頭辞:",
- "import-assign-known-users": "指定されたユーザーがこのウィキに存在する場合そのユーザーに編集を割り当てる",
+ "import-assign-known-users": "指定された利用者がこのウィキに存在する場合その利用者に編集を割り当てる",
"import-comment": "コメント:",
"importtext": "元のウィキで[[Special:Export|書き出し機能]]を使用してファイルに書き出してください。\nそれをコンピューターに保存した後、こちらへアップロードしてください。",
"importstart": "ページを取り込み中...",
"badretype": "입력한 비밀번호가 일치하지 않습니다.",
"usernameinprogress": "이 사용자 이름에 대한 계정 생성이 이미 시작되었습니다. 기다려 주세요.",
"userexists": "입력한 사용자 계정 이름이 이미 사용되고 있습니다.\n다른 이름을 선택하세요.",
+ "createacct-normalization": "당신의 사용자 이름은 기술적 한계로 인해 $2로 조정됩니다.",
"loginerror": "로그인 오류",
"createacct-error": "계정 만들기 오류",
"createaccounterror": "계정을 만들 수 없습니다: $1",
"accmailtext": "[[User talk:$1|$1]]님의 비밀번호를 임의로 만들어 $2(으)로 보냈습니다. 로그인하고 나서 <em>[[Special:ChangePassword|비밀번호를 바꿀]]</em> 수 있습니다.",
"newarticle": "(새 문서)",
"newarticletext": "아직 없는 문서의 링크를 따라왔습니다.\n새 문서를 만들려면 아래 상자에 내용을 입력하면 됩니다. (자세한 내용은 [$1 도움말 문서]를 참조하세요)\n만약 잘못 찾아왔다면, 브라우저의 '''뒤로''' 버튼을 눌러 주세요.",
- "anontalkpagetext": "----\n여기는 계정을 만들지 않았거나 사용하고 있지 않은 익명 사용자를 위한 토론 문서입니다. 익명 사용자를 구별하기 위해서는 숫자로 된 IP 주소를 사용해야만 합니다. IP 주소는 여러 사람이 공유할 수 있습니다. 자신과 관계없는 의견이 남겨져 있어 불쾌하다는 생각이 든다면 [[Special:CreateAccount|계정을 만들고]] [[Special:UserLogin|로그인해서]] 다른 사용자들에게 줄 혼란을 줄일 수 있습니다.",
+ "anontalkpagetext": "----\n<em>여기는 계정을 만들지 않았거나 사용하고 있지 않은 익명 사용자를 위한 토론 문서입니다.</em> 익명 사용자를 구별하기 위해서는 숫자로 된 IP 주소를 사용해야만 합니다. IP 주소는 여러 사람이 공유할 수 있습니다. 자신과 관계없는 의견이 남겨져 있어 불쾌하다는 생각이 든다면 [[Special:CreateAccount|계정을 만들거나]] [[Special:UserLogin|로그인해서]] 다른 사용자들에게 줄 혼란을 줄일 수 있습니다.",
"noarticletext": "이 문서가 현재 존재하지 않습니다.\n이 문서와 제목이 비슷한 문서가 있는지 [[Special:Search/{{PAGENAME}}|검색하거나]],\n이 문서에 관련된 <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 기록]을 확인하거나,\n문서를 직접 [{{fullurl:{{FULLPAGENAME}}|action=edit}} 생성]</span>할 수 있습니다.",
"noarticletext-nopermission": "이 문서가 현재 존재하지 않습니다.\n이 문서와 제목이 비슷한 문서가 있는지 [[Special:Search/{{PAGENAME}}|검색하거나]], 이 문서에 관련된 <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 기록]을 확인할 수 있습니다.</span> 그러나 이 문서를 만들 수 있는 권한은 없습니다.",
"missing-revision": "\"{{FULLPAGENAME}}\"이라는 문서의 #$1판이 존재하지 않습니다.\n\n이 문제는 주로 삭제된 문서를 가리키는 오래된 문서 역사 링크로 인해 발생합니다.\n자세한 내용은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 확인할 수 있습니다.",
"action-changetags": "문서의 특정 판과 특정 기록 항목에 임의의 태그를 추가하거나 제거하기",
"action-deletechangetags": "데이터베이스에서 태그를 지우기",
"action-purge": "이 문서 새로 고침",
+ "action-editinterface": "사용자 인터페이스 문서 편집",
+ "action-editusercss": "다른 사용자의 CSS 파일 편집",
+ "action-edituserjson": "다른 사용자의 JSON 파일 편집",
+ "action-edituserjs": "다른 사용자의 자바스크립트 파일 편집",
"nchanges": "$1개 {{PLURAL:$1|바뀜}}",
"enhancedrc-since-last-visit": "{{PLURAL:$1|마지막 방문 이후}} $1개",
"enhancedrc-history": "역사",
"october": "اوکتوبر",
"november": "نوڤامر",
"december": "دسامر",
- "january-gen": "جانڤیٱ",
+ "january-gen": "ژانڤیٱ",
"february-gen": "فڤریٱ",
"march-gen": "مارس",
"april-gen": "آڤریل",
"category-article-count-limited": "نئها {{PLURAL:$1|بألگە هی|$1بألگە یا هئن}} د دأسە ئیسئنی.",
"category-file-count": "{{PLURAL:$2|ای دٱسٱ فقٱت د ڤٱرگرتٱ جانؽا نهاییٱ.| نهایی {{PLURAL:$1|جانؽا هؽ|$1 جانیایا هؽسن}} د اؽ دٱسٱ، ڤ دٱر د کولٛ $2 .}}",
"category-file-count-limited": " {{PLURAL:$1|[جانیا هی|1$جانیایا هین}} نئهایی هان د دأسە ئیسئنی.",
- "listingcontinuesabbrev": "دومالٱ",
+ "listingcontinuesabbrev": "دۏمبالٱ",
"index-category": "بألگە یا سیاە دار",
"noindex-category": "بٱلگٱیا بؽ سؽاهٱ",
"broken-file-category": "بٱلگٱیایی کاْ هوم پاٛڤٱن جانؽایا اْشگس ناْ دارٱن",
"viewtalkpage": "دیئن چأک چئنە یا",
"otherlanguages": "ڤ زڤونیٛا هنی",
"redirectedfrom": "(ڤاگٱردونی د$1)",
- "redirectpagesub": "بألگە ڤاگأردوٙنی",
+ "redirectpagesub": "بٱلگٱ ڤاگٱردونی",
"redirectto": "ڤاگٱردونی سی:",
"lastmodifiedat": "اؽ بٱلگٱ ایسنؽا آلشت بیٱ د $1، د $2.",
"viewcount": "ئی بألگە ها د دأسرئسی {{PLURAL:$1|یئ گئل|$1 چأن گئل}}.",
"resettokens-resetbutton": "نئشوٙنە گولئ ڤورچیە د نوٙ زئنە بینە",
"bold_sample": "نیسسٱ مؽن پور",
"bold_tip": "نیسسٱ مؽن پور",
- "italic_sample": "نیسسٱ کٱج ۉ هار",
- "italic_tip": "نیسسٱیا کٱج Û\89 Ú©Ù\88Ù\84Ù±",
+ "italic_sample": "نیسسٱیا هٱلٛ ۉ هار",
+ "italic_tip": "نیسسٱیا Ù\87Ù±Ù\84Ù\9b Û\89 Ù\87ار",
"link_sample": "داسون هوم پاٛڤٱن",
"link_tip": "هوم پاٛڤٱن مؽنجایی",
"extlink_sample": "http://www.example.com داسون هوم پاٛڤٱن",
"savearticle": "آمادٱ کردن بٱلگٱ",
"preview": "پيش ساٛلٛ",
"showpreview": "نشوݩ داٛئن پیش ساٛلٛ",
- "showdiff": "نشوݩ داٛین آلشتکاریا",
+ "showdiff": "نشوݩ داٛئن آلشتکاریا",
"blankarticle": "<strong>زنئار:</strong> بلگه ای که شما دروس کردیته حالیه.\nار شما د نو ری \"$1\" بپورنیت, بلگه وه شکل که هیچ مینونه ای دش نبا دروس بوئه.",
- "anoneditwarning": "<strong>زٱنڳیار:</strong> شما هنی نۏمایتٱ ڤامؽن. تیرنشوݩ آی پی شما سی هٱر گاتؽ کاْ آلشتکاری بٱکؽت سی کولٛ خٱلک دؽاری مؽکٱ. ٱر <strong>[$1 رۉییت ڤامؽن]</strong> یا <strong>[$2 یاٛ هساو کاریاری دۏرس بٱکؽت]</strong>، ڤیرایشتؽا شما ڤ نوم کاریاری خوتو دیاری مؽکٱ ۉ سی شما بؽترٱ.",
+ "anoneditwarning": "<strong>زٱنڳیار:</strong> شما هنی نۏمایتٱ ڤامؽن. تیرنشوݩ آی پی شما سی هٱر گاتؽ کاْ آلشتکاری بٱکؽت سی کولٛ خٱلک دؽاری مؽکٱ. ٱر <strong>[$1 رۉؽت ڤامؽن]</strong> یا <strong>[$2 یاٛ هساو کاریاری دۏرس بٱکؽت]</strong>، ڤیرایشتؽا شما ڤ نوم کاریاری خوتو دؽاری مؽکٱ ۉ سی شما بؽترٱ.",
"anonpreviewwarning": "<em>شوما نیوٙمایتە ڤامین. تیرنئشوٙن آی پی شوما د ڤیرگار ڤیرایئشت ئی بألگە ئمایە بوٙە.</em>",
"missingsummary": "<strong>ڤیر دیارکو:</strong> شوما هأنی یئ گئل چئکئسە ڤیرایئشتی نە نئها ئمایە کاری نأکئردیتە.\nأر شوما د نۊ د ری \"$1\" بأپوٙرنیت، ڤیرایئشت کاری شوما حالی ئمایە بوٙە.",
"selfredirect": "<strong>هوشدار:</strong> شوما د حال و بال ڤاگأردوٙنی ئی بألگە د خوش هیین.\nگاسی دال ئشتئڤایی سی ڤاگأردوٙنی ئنتئخاڤ کئردیتە، یا گاسی بألگە نە ئشتئڤایی ڤیرایئشت کاری می کیت.\nأر ری \"$1\" دۊ گئل بأپوٙرنیت، ڤاگأردوٙنی راس موٙە.",
"newarticle": "تازە",
"newarticletext": "شما هائؽت ڤا دما هوم پاٛڤٱنؽ اْ ڤوجۊد نارٱ.\nسی رٱڤٱندؽاری بٱلگٱ.شرۊ بٱکؽت مؽن جٱڤٱ هوری بٱنیسؽت (سی دونسن بؽشتر ساٛلٛ [$1 ] بٱکؽت).\nٱر شما سی اْشتبا کردن هائؽت ایچاْ، ری دۏگمٱ ڤادما رٱتن دوئارتٱ نیٱر بٱپۊرنؽت.",
"anontalkpagetext": "----",
- "noarticletext": "د ایسنؽا اؽ بٱلگٱ نیسسٱ ڤجۊد ناشتٱ.\nشما مؽ تونؽت د[[Special:Search/{{PAGENAME}}|بٱگٱردؽد]] د اؽ بٱلگٱ اؽ د بٱلگٱ هنی یا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} د نۊ پاٛجۊری بۊئٱ]</span>، <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}} یا ای بٱلگٱ ناْ ڤیرایش بٱکؽت]</span>.",
+ "noarticletext": "د ایسنؽا اؽ بٱلگٱ نیسسٱ ڤجۊد ناشتٱ.\nشما مؽ تونؽت د[[Special:Search/{{PAGENAME}}|ڤگٱردؽد]] د اؽ بٱلگٱ ڤ بٱلگٱ هنی یا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} د نۊ پاٛجۊری بۊئٱ]</span>، <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}} یا ای بٱلگٱ ناْ ڤیرایش بٱکؽت]</span>.",
"noarticletext-nopermission": "د ایسنؽا اؽ بٱلگٱ نیسساٛیؽ ڤجۊد ناشتٱ.\nشما مؽ تونؽت د[[Special:Search/{{PAGENAME}}|بٱگردؽد]] د اؽ بٱلگٱیا د بٱلگٱ هنی یا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} د نۊ پاٛجۊری بۊئٱ]</span>، <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}}</span>.ڤلی شما سلا یٱناْ کاْ اؽ بٱلگٱ ناْ دۏرس بٱکؽت نارؽت.",
"missing-revision": "ڤانئیأری #$1 د بألگە یی کئ نومئش ڤئنە \"{{FULLPAGENAME}}\" ڤوجوٙد نارە.\n\nگاسی سی یئ گئل ڤیرگار ڤئ هئنگوم نأبییە کئ د یئ گئل بألگە پاکسا بییە هوم پئیڤأند بییە رأڤأندیاری بییە.\nگاسی جوزئیات د[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log] دیاری بأکأن.",
"userpage-userdoesnotexist": "حئساڤ کاریاری \"$1\" ثأڤت نأم نأبییە.\nأر میھایت ئی بألگە نئ بأسازیت یا ڤیرایئشت کاری بأکیت یئ گئل ڤارئسی أنجوم بئیتوٙ.",
"templatesusedpreview": "{{PLURAL:$1| چۊئٱ|چۊئٱ یا}} ڤ کار گرتٱ بیٱ د پیش ساٛلٛ :",
"templatesusedsection": "{{PLURAL:$1|چۊئٱ|چۊئٱ یا}} ڤ کار گرتٱ بیٱ د اؽ بٱلگٱ:",
"template-protected": "(پر ۉ پیم بیٱ)",
- "template-semiprotected": "(نسم ۉ نیمٱ پٱر ۉ پیم بیٱ)",
+ "template-semiprotected": "(نسم ۉ نیمٱ پر ۉ پیم بیٱ)",
"hiddencategories": "اؽ بٱلگٱ یٱکؽ د ٱندومؽا ٱ {{PLURAL:$1|1 hidden category|$1 hidden categories}} :",
"edittools-upload": "-",
"nocreatetext": "{{SITENAME}} سی رأڤأندیاری بألگە یا تازە نئھاگئری بییە.\nشوما می توٙنیت روئیت ڤادئما و بألگە ئی کئ بییشە ڤیرایئشت کاری بأکیت،[[Special:ڤامین ئوٙمائن کاریار|بیائیت ڤامین یا یە کئ یئ گئل حئساڤ دوروس بأکیت]].",
"sectioneditnotsupported-text": "ڤیرایئشت بأرجایی د ئی بألگە نیئش.",
"permissionserrors": "خٱتا سلا داٛئن",
"permissionserrorstext": "شوما حأق ناریت ڤئنە أنجوم بئیت، سی{{PLURAL:$1|دألیل|دألیلیا}} نئھایی:",
- "permissionserrorstext-withaction": "شما سی $2 سلا \nنھاگری نارؽت {{PLURAL:$1|دلٛیلٛ|دلٛیلٛؽا}}:",
- "recreate-moveddeleted-warn": "'''ڤ ڤیرتو با:شما بٱلگاٛیی کاْ ھا ڤادما ۉ پاکسا بیٱ د نۊ دۏرس کردؽتٱ.'''\nبایٱد د ڤیرتو با کاْ آیا ھنی نوئاگیری ڤیرایش اؽ بٱلگٱ خۊئٱ.\nپاکسا کاری ۉ جا ڤ جا کاری اؽ بٱلگٱ سی هال ۉ بال پٱلٛٱمار شما آمادٱ بیٱ:",
- "moveddeleted-notice": "اؽ بٱلگٱ پاکسا بیٱ.\nپاکسا کاری ۉ جا ڤ جا کاری اؽ بٱلگٱ سی هال ۉ بال پٱلٛٱمار شما آمادٱ بیٱ.",
+ "permissionserrorstext-withaction": "شما سی $2 سلا \nنھاگیری نارؽت {{PLURAL:$1|دلٛیلٛ|دلٛیلٛؽا}}:",
+ "recreate-moveddeleted-warn": "'''ڤ ڤیرتو با:شما بٱلگاٛیی کاْ ھا ڤادما ۉ پاکسا بیٱ د نۊ دۏرس کردؽتٱ.'''\nبایٱد د ڤیرتو با کاْ آیا ھنی نوئاگیری ڤیرایش اؽ بٱلگٱ خۊئٱ.\nپاکسا کاری ۉ جا ڤ جا کاری اؽ بٱلگٱ سی هال ۉ بار پٱلٛٱمار شما آمادٱ بیٱ:",
+ "moveddeleted-notice": "اؽ بٱلگٱ پاکسا بیٱ.\nپاکسا کاری ۉ جا ڤ جا کاری اؽ بٱلگٱ سی هال ۉ بار پٱلٛٱمار شما آمادٱ بیٱ.",
"log-fulllog": "دیئن هأمە پئهئرستنوٙمە یا",
"edit-hook-aborted": "ڤیرایئشت ڤا قولاڤ نئھاگئری بییە.\nھیچ توضیی سیش نی.",
"edit-gone-missing": "نأبوٙە ئی بألگە نە ڤئ ھئنگوم بأکیت.\nچئنی ڤئ نأظأر میا کئ ڤئ پاکسا بییە.",
"previousrevision": "ڤانیٱری زیتری ←",
"nextrevision": "ڤانؽٱری تازٱتر",
"currentrevisionlink": "آخری ڤانؽٱری",
- "cur": "تازٱ باو",
+ "cur": "تازٱ بۊ",
"next": "نئهایی",
"last": "دمایی",
"page_first": "أڤئلی",
"page_last": "آخئر",
- "histlegend": "اÙ\92Ù\86تخاب Ù\81ٱرخدار:جٱڤٱÛ\8cا رادÛ\8cÙ\88 Ù\86اÙ\92 سÛ\8c دÙ\88ئارٱ دÛ\8cئÙ\86 Û\89 ڤارسÛ\8c Ù\86Ø´Ù\88Ý© دار بٱکؽت Û\89 Û\8cا رÛ\8c رٱتÙ\86 Ú©Ù\84Ù\9bÛ\8cÚ© بٱکؽت .<br />\nØ´Ø±Ø Ù\86Ù\88شتÙ\87: '''({{int:cur}})''' = ڤا آخری دوئارٱ دیئن فٱرخ دارٱ '''({{ int:last}})'''= ڤا دوئارٱ دیئن ٱنجوم داٛئنی فٱرخ دارٱ '''{{int:minoreditletter}}''' =ڤیرایش کوچک.",
+ "histlegend": "اÙ\92Ù\86تخاب Ù\81ٱرخدار:جٱڤٱÛ\8cا رادÛ\8cÙ\88 Ù\86اÙ\92 سÛ\8c دÙ\88ئارٱ دÛ\8cئÙ\86 Û\89 ڤارسÛ\8c Ù\86Ø´Ù\88Ý© دار بٱکؽت Û\89 Û\8cا رÛ\8c رٱتÙ\86 Ú©Ù\84Ù\9bÛ\8cÚ© بٱکؽت .<br />\nتÛ\89سÛ\8cÙ\81 Ù\86Ú¤Û\8cسٱ: '''({{int:cur}})''' = ڤا آخری دوئارٱ دیئن فٱرخ دارٱ '''({{ int:last}})'''= ڤا دوئارٱ دیئن ٱنجوم داٛئنی فٱرخ دارٱ '''{{int:minoreditletter}}''' =ڤیرایش کوچک.",
"history-fieldset-title": "ڤیرگار دوئارٱ نیٱری",
"history-show-deleted": "فقط پاكسا بيه",
"histfirst": "قاٛیمی تریݩ",
"rev-suppressed-unhide-diff": "وانئری ای بلگه <strong>پاکساگری بیه</strong>.\nجزئیات هان د [{{fullurl:{{#Special:Log}}/پاکساگری کردن|بلگه={{نوم کامل بلگه}}}} پهرستنومه پاکساگری کردن].\nشما هنی تونیت [$1ای وانئری نه بونیت] ار بهاییت.",
"rev-deleted-diff-view": "وانئری ای بلگه <strong>پاکسا بیه</strong>.\nجزئیات هان د [{{fullurl:{{#Special:Log}}/پاکسا کردن|بلگه={{نوم کامل بلگه}}}} پهرستنومه پاکساکردن].",
"rev-suppressed-diff-view": "وانئری بلگه <strong>پاکساگری</strong>.\nجزئیات هان د [{{fullurl:{{#Special:Log}}/پاکساگری کردن|بلگه={{نوم کامل بلگه}}}} پهرستنومه پاکساگری کردن].",
- "rev-delundel": "آلشت هال ۉ بال ديئن",
+ "rev-delundel": "آلشت هال ۉ بار ديئن",
"rev-showdeleted": "نئشوٙ دأئن",
"revisiondelete": "پاکسا کردن/زنه کردن وانئریا",
"revdelete-nooldid-title": "وانیری تمارزی بیه نامعتوره",
"difference-title-multipage": "فرخ مینجا بلگه یا \"$1\" و \"$2\"",
"difference-multipage": "(فرخ مینجا بلگه یا)",
"lineno": "خٱت $1:",
- "compareselectedversions": "دوئارٱ دی ینؽایؽ کاْ اْنتخاو بینٱ ناْ موقایسٱ بٱکؽت",
+ "compareselectedversions": "دوئارٱ دیئنؽایؽ کاْ اْنتخاو بینٱ ناْ موقایسٱ بٱکؽت",
"showhideselectedversions": "شلک دیئن وانیریا انتخاو بیه نه آلشت بکید",
"editundo": "ناٱنجومگر کردن",
"diff-empty": "(بؽ فٱرق)",
"prev-page": "بلگه دمايی",
"next-page": "بلگه نهایی",
"prevn-title": "زیتر $1 {{PLURAL:$1|نٱتیجٱ|نٱتيجٱيا}}",
- "nextn-title": "دمایی $1 {{PLURAL:$1|نتيجٱ|نتيجيا}}",
+ "nextn-title": "دمایی $1 {{PLURAL:$1|نٱتيجٱ|نٱتيجؽا}}",
"shown-title": "نشوݩ داٛین $1 {{PLURAL:$1|نتیجٱ|نتیجٱیا}} سی هٱر بٱلگٱ",
- "viewprevnext": "ديین ($1 {{int:pipe-separator}} $2) ($3)",
+ "viewprevnext": "ديئن ($1 {{int:pipe-separator}} $2) ($3)",
"searchmenu-exists": "'''ایچاْ بٱلگاٛیؽ هؽ ڤ نوم\"[[:$1]]\" کاْ ها د اؽ ڤیکی'''",
"searchmenu-new": "'''اؽ بٱلگٱ ناْ دۏرس كو \"[[:$1]]\" د اؽ ڤیکی!'''",
"searchprofile-articles": "بٱلگٱيا مؽنونٱ دار",
"search-interwiki-more": "(بیشتر)",
"search-relatedarticle": "مرتوط",
"searchrelated": "مرتوط",
- "searchall": "Ù\87Ù±Ù\85Ù±",
+ "searchall": "همٱ",
"showingresults": "نمائشت بیشترونه {{PLURAL:$1|'''۱''' نتیجه|'''$1''' نتیجه}} د هار، شرو د شماره'''$2'''.",
"showingresultsinrange": "نمائشت بیشترونه {{PLURAL:$1|'''۱''' نتیجه|'''$1''' نتیجه}} د هار، شرو د شماره'''$2''' تا شماره '''$3'''.",
"search-showingresults": "{{PLURAL:$4|نٱتیجٱیا<strong>$1</strong> د <strong>$3</strong>|نٱتیجٱیا<strong>$1 - $2</strong د <strong>$3</strong>}}",
"enhancedrc-history": "ڤیرگار",
"recentchanges": "آلشتؽا ایسنی",
"recentchanges-legend": "گوزینٱیا آلشتؽا ایسنی",
- "recentchanges-summary": "دۏ بؽشتر آلشتؽا تازباو ناْ د ڤیکی د اؽ بٱلگٱ پاٛجۊری کو.",
- "recentchanges-noresult": "هیچ آلشتؽ د درازا دۉرٱ دؽار بیٱ ڤا اؽ ماٛعیاؽا یٱکی ناٛی.",
+ "recentchanges-summary": "دۏ بؽشتر آلشتؽا تازٱبۊ ناْ د ڤیکی د اؽ بٱلگٱ پاٛجۊری کو.",
+ "recentchanges-noresult": "Ù\87Û\8cÚ\86 Ø¢Ù\84شتؽ د درازا دÛ\89رٱ دؽار بÛ\8cÙ± ڤا اؽ Ù\85اÙ\9bعÛ\8cارؽا Û\8cÙ±Ú©Û\8c Ù\86اÙ\9bÛ\8c.",
"recentchanges-feed-description": "دۏ بؽشتر آلشتؽا تازباو ناْ د ڤیکی کا ها د هڤال هون پاٛگیری کو.",
"recentchanges-label-newpage": "اؽ ڤیرایش یاٛ بٱلگٱ تازٱ دۏرس کردٱ.",
"recentchanges-label-minor": "یٱ یاٛ ڤیرایش کوچکٱ",
"recentchanges-label-bot": "اؽ ڤيرايش ناْ ياٛ بوت ٱنجوم داٛیٱ",
- "recentchanges-label-unpatrolled": "اؽ ڤيرايش هنی تيٱ ڤاداشت ناٛیٱ",
+ "recentchanges-label-unpatrolled": "اؽ ڤيرايش هنی تيٱ ڤاداشت ناٛیئٱ",
"recentchanges-label-plusminus": "ٱندازٱ بٱلگٱ ڤ شماراٛ اؽ بایتؽا آلشت کردٱ.",
"recentchanges-legend-heading": "<strong>میرات:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (همچنو باٛینؽت [[ڤیژٱ:بٱلگٱیا تازٱ|نوم گٱ بٱلگٱیا تازٱ]])",
"rcshowhideminor-show": "نشوݩ داٛئن",
"rcshowhideminor-hide": "قایم کردن",
"rcshowhidebots": "$1 روباتؽا یا بوتؽا",
- "rcshowhidebots-show": "نشوݩ داٛین",
+ "rcshowhidebots-show": "نشوݩ داٛئن",
"rcshowhidebots-hide": "قایم کردن",
"rcshowhideliu": "$1 کاریاریا سٱبت نوم کردٱ",
"rcshowhideliu-show": "نشوݩ داٛئن",
"diff": "فٱرق",
"hist": "ڤیرگار",
"hide": "قایم کردن",
- "show": "نشوݩ داٛین",
+ "show": "نشوݩ داٛئن",
"minoreditletter": "م",
"newpageletter": "ن",
"boteditletter": "ب",
"newsectionsummary": "/* $1 */ بهرجا تازه",
"rc-enhanced-expand": "جزيات نشون بيئه",
"rc-enhanced-hide": "جزياته قام كو",
- "rc-old-title": "ذاتا چی \"$1\" راس بیه",
+ "rc-old-title": "کاملٱن چی \"$1\"دۏرس بیٱ",
"recentchangeslinked": "آلشتؽا تاٛ یٱکؽ",
"recentchangeslinked-feed": "آلشتؽا تاٛ یٱک",
"recentchangeslinked-toolbox": "آلشتؽا تاٛ یٱک",
"upload-curl-error6-text": "تیرنشون اینترنتی که دئیته د دسرس نئ.\nلطف بکیت درستیشه و یه نه که تیرنشون اینترنی میزونکاری بیه وارسی بکیت.",
"upload-curl-error28": "تموم بیئن مئلت سی سوار کرد",
"upload-curl-error28-text": "ای دیارگه فره دیر دتو واکنشت نشو دئه.\nلطف بکیت سی یه که دیارگه کنشگتر و ری خطه یه گل وارسی بکیت، اوسه یه گر واستید و هنی تلاش بکیت.\nشایت بیتر با که د گات خلوتری هنی تلاش بکیت.",
- "license": "ليانس دار بيئن",
- "license-header": "د هال ۉ بال لیسانس دار بیئن",
+ "license": "Ù\84Ù\8aساÙ\86س دار بÙ\8aئÙ\86",
+ "license-header": "د هال ۉ بار لیسانس دار بیئن",
"nolicense": "هیچی انتخاو نبیه",
"licenses-edit": "گزینه یا مجوز ویرایشت",
"license-nopreview": "(پیش سیل د دسرس نئ)",
"imagelinks": "ڤ کار گرتن جانؽا",
"linkstoimage": "دۏنبال بيٱ {{PLURAL:$1|ديس ڤنؽا بٱلگٱ|$1 ديس ڤنؽا بٱلگٱيا}} د اؽ فایلٛ:",
"linkstoimage-more": "بؽشتر د $1 بٱلگٱ د اؽ جانؽا هوم پاٛڤٱن {{PLURAL:$1|بٱ|بینٱ}}.\nنومگٱ هاری تٱنڳؽا{{PLURAL:$1|ٱڤلی هوم پاٛڤٱن|ٱڤلی $1 هوم پاٛڤٱن}} د ؽ بٱلگٱ ناْ نشوݩ مؽ یٱ.\n[[Special:WhatLinksHere/$2|نومگٱ کامل]] ٱم هؽسش.",
- "nolinkstoimage": "ایچاْ هیچ بٱلگاٛیی سی هوم پیاٛڤٱن بیین ڤا اؽ جانؽا نؽ",
+ "nolinkstoimage": "ایچاْ هیچ بٱلگاٛیی سی هوم پیاٛڤٱن بیئن ڤا اؽ جانؽا نؽ",
"morelinkstoimage": " [[ویجه:چه هوم پیوندی ها ایچه/$1|هوم پیوندیا هنی]]سی ای جانیا نه بونیت.",
"linkstoimage-redirect": "$1 (ڤاگٱردونی جانؽا) $2",
"duplicatesoffile": "{{PLURAL:$1|جانیا|جانیایا}} هاری نسقه تکراری ای جانیا {{PLURAL:$1|هئ|هئن}} ([[Special:FileDuplicateSearch/$2|دونسمنیا هنی]]):",
"speciallogtitlelabel": "هاستنی(داسون یا نوم کاریاری سی کاریار):",
"log": "پهرستنومٱیا",
"all-logs-page": "همٱ پهرستنومٱیا عومۊمی",
- "alllogstext": "نمایش یٱ جا همٱ پهرستنومٱیا کاْ هان د{{SITENAME}}.\nمؽ تونؽت ڤا اْنتخاو نۉع پهرستنومٱ، نوم کاریاری(هٱساس ڤ کوچکی ۉ گٱپی هرفؽا) ۉ بٱلگٱیا آلشت کردٱ(هٱساس ۉ گٱپی ۉ کوچکی هرنیا) نمایش ناْ دیر د ڤیرتر بٱکؽت.\n\n{{SITENAME}}.",
+ "alllogstext": "نمایش یاٛ جا همٱ پهرستنومٱیا کاْ هان د{{SITENAME}}.\nمؽ تونؽت ڤا اْنتخاو نۉع پهرستنومٱ، نوم کاریاری(هٱساس ڤ کوچکی ۉ گٱپی هرفؽا) ۉ بٱلگٱیا آلشت کردٱ(هٱساس ۉ گٱپی ۉ کوچکی هٱرفؽا) نمایش ناْ دیر د ڤیرتر بٱکؽت.\n\n{{SITENAME}}.",
"logempty": "او چی کاْ شما مؽهایت د پهرستنومٱ نؽسش.",
"log-title-wildcard": "بلگه یایی نه پی جوری کو که وا ای سرون شرو موئن",
"showhideselectedlogentries": "آلشت دئن ورتیه گر پهرستنومه یا انتخاو بیه",
"notvisiblerev": "آخری وانئری که د دس یه کاریار هنی انجوم بیه پاکسا بیه.",
"watchlist-details": "{{PLURAL:$1|$1 بلگٱ|$1 بلگٱیا}} د ساٛلٛ بٱرگتو هیچ بٱلگٱ قسٱ کردنی نؽ.",
"wlheader-enotif": "ڤارئسیاری أنجومانامە کونئشتکار بییە.",
- "wlheader-showupdated": "بٱلگٱیایی کاٛ د آخری گلٛؽ کاْ کاْ شما ساٛیلٛشو کردؽتٱ د <strong>تۊپور</strong>نشوݩ داٛئٱ بۊئٱن",
+ "wlheader-showupdated": "بٱلگٱیایی کاٛ د آخری گلٛؽ کاْ شما ساٛیلٛشو کردؽتٱ د <strong>تۊپور</strong>نشوݩ داٛئٱ بۊئٱن",
"wlnote": "د هار {{PLURAL:$1|آلشت|<strong>$1</strong> آلشتؽ}} کاْ د {{PLURAL:$2|ساعت|<strong>$2</strong> ساعٱت}} دماتر ٱنجوم بیٱ هؽسش، ڤیرگار آخری ڤاجۊری ٱنجوم بیٱ هؽسش، ڤیرگار آخری ڤاجۊری: $3، $4",
"wlshowlast": "آخری$1 ساعٱتؽا $2ۉ رۊزؽا نشوݩ باٛیٱ",
"wlshowtime": "نئشوٙ دأئن د آخأر",
"changecontentmodel-success-title": "حال و بال مینوٙنە آلئشتکاری بی",
"logentry-contentmodel-change-revertlink": "لئرنیئن",
"logentry-contentmodel-change-revert": "لئرنیئن",
- "protectlogpage": "پٱر ۉ پیم کاری پهرستنومٱ",
+ "protectlogpage": "پر ۉ پیم کاری پهرستنومٱ",
"protectlogtext": "د ھار یئ گئل نومگە د آلئشتیا ریتئراز پأر و پیم کاری بألگە یا ئوٙماە.\n[[Special:ProtectedPages|نومگە بألگە یا پأر و پیم کاری بییە]] نە سی دیئن نومگە پأر و پیم کاری کارگئرا بألگە یا نە سئیل بأکیت.",
- "protectedarticle": "پأر Ù\88 Ù¾Û\8cÙ\85 کارÛ\8c بÛ\8cÛ\8cÛ\95 [[$1]]",
+ "protectedarticle": "پر Û\89 Ù¾Û\8cÙ\85 کارÛ\8c بÛ\8cÙ± [[$1]]",
"modifiedarticleprotection": "ریتراز هفازٱت د \"[[$1]]\" آلشت بیٱ",
"unprotectedarticle": "بلگه«[[$1]]» نه د پر و پیم دراورد",
"movedarticleprotection": "میزونکاری پر و پیم بیین د «[[$2]]» وه «[[$1]]» جا وه جا بیه",
"sp-contributions-blocked-notice-anon": "ای آی پی ایسنی دسرسی ناره.\nآخری برشت د پهرستنومه ها د سرچشمه هاری:",
"sp-contributions-search": "سی هومیارؽا پاٛ جۊر با",
"sp-contributions-username": "نوم نشوݩ آی پی يا نوم كارڤٱری:",
- "sp-contributions-toponly": "فقٱت ڤیرایشؽایی کاْ جۏز آخری دۉرٱن نشو باٛیٱ",
+ "sp-contributions-toponly": "فقٱت ڤیرایشؽایؽ کاْ جۏز آخری دۉرٱن نشو باٛیٱ",
"sp-contributions-newonly": "فقٱت ڤیرایشؽایؽ کاْ هؽن دۏرس کردن بٱلگان نشوݩ باٛیٱ.",
"sp-contributions-submit": "پاٛ جۊری",
"whatlinkshere": "کوم هوم پاٛڤٱنؽا هان ایچاْ",
"whatlinkshere-prev": "{{PLURAL:$1|نوئایی|نوئایی $1}}",
"whatlinkshere-next": "{{PLURAL:$1|دمایی|دمایی $1}}",
"whatlinkshere-links": "هوم پاٛڤٱنؽا",
- "whatlinkshere-hideredirs": "$1 ڤرگٱردونیا",
- "whatlinkshere-hidetrans": "$1 چٱن نتیجاٛیی",
+ "whatlinkshere-hideredirs": "$1 ڤرگٱردونؽا",
+ "whatlinkshere-hidetrans": "$1 چٱن نٱتیجاٛیی",
"whatlinkshere-hidelinks": "هوم پاٛڤٱنؽا $1",
"whatlinkshere-hideimages": "جانؽا هوم پاٛڤٱنؽا $1",
"whatlinkshere-filters": "فيلٛترؽا",
"blocklog-showlog": "تیرنشون ای آی پی دماتر نهاگیری بیه.\nنهاگری ای پهرستنومه وا سرچشمه هاری دروس بیه:",
"blocklog-showsuppresslog": "تیرنشون ای آی پی دماتر نهاگیری بیه.\nنهاگری ای پهرستنومه وا سرچشمه هاری دروس بیه:",
"blocklogentry": " [[$1]] ڤا یاٛ ڤٱخت تموم بیئن $2 ۉ $3 قلف بییٱ",
- "reblock-logentry": "میزوکاری سی نهاگری[[$1]] آلشت بیه سی آخر نهاگری د $2 $3",
+ "reblock-logentry": "میزوکاری سی نهاگیری[[$1]] آلشت بیٱ سی آخر نهاگیری د $2 $3",
"blocklogtext": "ای پهرستنومه سی نهاگری یا واز کردن دسرسی کاریاریائه.\nتیرنشونیا آی پی که خود انجومن نهاگری بینه د ایسنی نومگه کاری نبینه.\nسی نومگه نهاگریا و بسه بینیا د ایسنی روئیت د [[Special:BlockList|نومگه نهاگری بیه یا]].",
"unblocklogentry": "وا کردن قلف $1",
"block-log-flags-anononly": "فقط کاریاریایی که نادیارن",
"tooltip-ca-history": "دوئرٱ دیئن اؽ بٱلگٱ",
"tooltip-ca-protect": "اؽ بٱلگٱ ناْ ڤیردار بٱکؽت",
"tooltip-ca-unprotect": "پر و پیم گیری د ای بلگه نه آلشت بکیت",
- "tooltip-ca-delete": "ای بلگه نه پاکسا کو",
+ "tooltip-ca-delete": "اؽ بٱلگٱ ناْ پاکسا کو",
"tooltip-ca-undelete": "د نو زنه کردن ویرایشتیا ری ای بلگه دما یه که پاکساگری بان",
"tooltip-ca-move": "اؽ بٱلگٱ ناْ جا ڤ جا كو",
"tooltip-ca-watch": "اْزاف کردن اؽ بٱلگٱ ڤ نوم نڤشت پاٛگیریاتو",
"tooltip-recreate": "د نو راس کردن بلگه بی یه که و پاکساگری دماتر وه سیل بکیم",
"tooltip-upload": "شرو د سوار کرد",
"tooltip-rollback": "\"ڤرگٱشتن\" لٛرسن د هال و بار ٱڤٱل سی اؽ بٱلگٱ سی یٱ کاْ هومیاری نؽایی بؽتر کاری بیٱ ڤا یاٛ پۊرنیئن.",
- "tooltip-undo": "Ù±Ù\86جÙ\88Ù\85 Ù\86ٱگرتÙ\86 اؽ Ú¤Û\8cراÛ\8cØ´. ڤرگٱرد Û\89 Ù\87Ù±Ù\85Ù± Ù\81Ù\88رÙ\85ؽا Ú¤Û\8cراÛ\8cشؽاÙ\86اÙ\92 د Ù\87اÙ\84ٱت Ù¾Û\8cØ´ ساÙ\9bÙ\84Ù\9b ڤاکÙ\88.Û\8cÙ± اÙ\92جازٱ Ù\85اÙ\9bئٱ سÛ\8c اÙ\92زاÙ\81 کردÙ\86 Û\8cاÙ\9b دٱÙ\84Ù\9bÛ\8cÙ\84Ù\9b د Ú\86کسٱ.",
+ "tooltip-undo": "ٱنجوم نٱگرتن اؽ ڤیرایش. ڤرگٱرد ۉ همٱ فورمؽا ڤیرایشؽاناْ د هالٱت پیش ساٛلٛ ڤاکو.یٱ اْجازٱ ماٛئٱ سی اْزاف کردن یاٛ دٱلٛیلٛ د چکسٱ.",
"tooltip-preferences-save": "اولويتيا نه ذخيره بكيد",
"tooltip-summary": "ياٛ چكسٱ کوچک ڤارد بٱکؽت",
"interlanguage-link-title": "$1-$2",
"pageinfo-redirects-name": "شمارٱ ڤاگٱردونیا اؽ بٱلگٱ",
"pageinfo-redirects-value": "$1",
"pageinfo-subpages-name": "ٱندازٱ زؽر بٱلگٱیا اؽ بٱلگٱ",
- "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|ڤاگٱردونی|ڤاگٱردونیا}}; $3 {{PLURAL:$3|بؽ ڤاگٱردونی|بؽ ڤاگٱردونیا}})",
+ "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|ڤاگٱردونی|ڤاگٱردونؽا}}; $3 {{PLURAL:$3|بؽ ڤاگٱردونی|بؽ ڤاگٱردونؽا}})",
"pageinfo-firstuser": "بٱلگٱ دۏرس کن",
"pageinfo-firsttime": "گات دۏرس بیئن بٱلگٱ",
"pageinfo-lastuser": "آخری ڤیرایشکار",
"widthheight": "$1 × $2",
"widthheightpage": "$1 × $2, $3 {{PLURAL:$3|بٱلگٱ|بٱلگٱیا}}",
"file-info": "انازه جانیا: $1, MIME type: $2",
- "file-info-size": "$1 × $2 پیکسل, ٱندازٱ فایل: $3, MIME نوع: $4",
+ "file-info-size": "$1 × $2 پیکسل, ٱندازٱ فایلٛ: $3, MIME نوع: $4",
"file-info-size-pages": "$1 × $2 pixels, ٱندازٱ جانؽا: $3, MIME type: $4, $5 {{PLURAL:$5|بٱلگٱ|بٱلگٱیا}}",
- "file-nohires": "عٱسک ڤٱن بالاترؽ دش نؽ",
+ "file-nohires": "عٱسگ ڤٱن بالاترؽ دش نؽ",
"svg-long-desc": "جانؽا اْس ڤی جی, نومی $1 × $2 پیکسل, ٱندازٱ جانؽا: $3",
"svg-long-desc-animated": "جانیا جمشدار اس وی جی .نومنا $1 × $2 پيكسل،انازه جانیا:$3",
"svg-long-error": "جانیا اس وی جی نامعتور:$1",
"yesterday-at": "دیرو د $1",
"bad_image_list": "دونسمنديانه وه ای شلگ وارد بكيت:\n\nفقط سرخط يایی که وا * شرو بوئن د وير گرته بوئن. اولی چسب ون مئن هر سرخط، باید چسب ونی وه یک عسگ گن با.\nچسب ونيا نيایی د همو سرخط، وه عنوان چيا استثنادار د وير گرته بوئن",
"metadata": "رسینٱیا فرٱ گٱپ",
- "metadata-help": "اؽ فایلٛ شامل دونسمنیا هنی یٱ.شایت د دیر بیین رقٱم ڤٱن یا اسکنری کاْ سی راس کردنشو اْستفادٱ بیٱ، ڤ ایچاْ اْزاف بیٱ",
+ "metadata-help": "اؽ فایلٛ شامل دونسمنیا هنی یٱ.شایٱت د دیر بیئن رقٱم ڤٱن یا اْسکٱنرؽ کاْ سی دۏرس کردنشو اْستفادٱ بیٱ، ڤ ایچاْ اْزاف بیٱ",
"metadata-expand": "نشو دئن جزییات دمادیسگری",
"metadata-collapse": "قام کردن جزییات دمادیسگری",
"metadata-fields": "رشتٱیا یا گٱپ دونسمنیا کاْ د اؽ پاٛغوم نومگٱ کاری بینٱ د ڤٱر گرتٱ بٱلگٱ عٱسک کاْ ڤٱختؽ جٱدڤٱل گٱپ دونسمنیا ڤاز مۊئٱ نشوݩ داٛیٱ بۊئٱن.\nچی یا هنی سی یٱ کا پیش فٱرزٱن قایم مۊئٱن.\n*راس کو\n*مودل\n*دم ڤٱخت ٱسل\n*ڤٱخت آشگار\n*اْف اْن شمارٱ\n*ایزو نرخ من سرعت\n*فوکالنس\n*هونٱرمٱن\n*کوپی رایت\n*هالٱت جی پی اْس \n*جی پی اْس گٱپ هالٱت\n*جی پی اْس هٱمٱ هالٱت",
"metadata-langitem": "<strong>$2:</strong> $1",
"metadata-langitem-default": "$1",
"namespacesall": "همٱشو",
- "monthsall": "Ù\87Ù±Ù\85Ù±",
+ "monthsall": "همٱ",
"confirmemail": "پشت راس کردن تیرنشون انجومانامه",
"confirmemail_noemail": "شما د بلگه [[Special:Preferences|ترجیحات کاریاری]] خوتو یه گل تیرنشون انجومانامه نامعتور نه دئیته.",
"confirmemail_text": "ای ویکی، شما نه مژبور می که وه پشت راسکاری تیرنشون انجومانامه خوتو، دما د یه که خدمات انجومانامه نه وه کار د ایچه وه کار بئیریت می که.دگمه هاری نه کنشتیار بکیت تا یه گل انجومانامه پشت راسکاری سی تیرنشون انجومانامه شما کل بوئه. ای انجومانامه د ور گرته یه گل رازینه ئه. هوم پیوند نه د دوارته نیئر خوتو واز بکیت تا تیرنشون انجومانامه تو پشت راسکاری با.",
"watchlistedit-too-many": "ایچه بلگه یا فره ای سی نشو دئن هئ.",
"watchlisttools-clear": "پاک کردن ساٛلٛ بٱرگ",
"watchlisttools-view": "آلشتؽا مٱربوت ناْ بونؽت",
- "watchlisttools-edit": "ساٛلٛ بٱرگ بوینؽتو ۉ ڤیرایش بٱکؽت",
+ "watchlisttools-edit": "ساٛلٛ بٱرگ ناْ باٛینؽتو ۉ ڤیرایش بٱکؽت",
"watchlisttools-raw": "ساٛلٛ بٱرگ ناْ ردیفی ڤیرایش کو",
"iranian-calendar-m1": "فروردین",
"iranian-calendar-m2": "اردیبهشت",
"tog-shownumberswatching": "Прикажи го бројот на корисници кои набљудуваат",
"tog-oldsig": "Вашиот постоечки потпис:",
"tog-fancysig": "Сметај го потписот за викитекст (без автоматска врска)",
- "tog-uselivepreview": "Ð\9fÑ\80егледи во живо без превчитување на страницата",
+ "tog-uselivepreview": "Ð\9fÑ\80икажÑ\83ваÑ\98 пÑ\80егледи без превчитување на страницата",
"tog-forceeditsummary": "Извести ме кога нема опис на промените",
"tog-watchlisthideown": "Скриј мои уредувања од набљудуваните",
"tog-watchlisthidebots": "Скриј ботовски уредувања од набљудуваните",
"missingarticle-rev": "(измена#: $1)",
"missingarticle-diff": "(разлика: $1, $2)",
"readonly_lag": "Базата е автоматски заклучена додека помошните опслужувачи не се усогласат",
- "nonwrite-api-promise-error": "HTTP-заглавиеÑ\82о â\80\9ePromise-Non-Write-API-Actionâ\80\9c беÑ\88е иÑ\81пÑ\80аÑ\82ено, но баÑ\80аÑ\9aеÑ\82о беÑ\88е Ñ\83паÑ\82ено кон запиÑ\81ен модÑ\83л на API.",
+ "nonwrite-api-promise-error": "HTTP-заглавиеÑ\82о â\80\9ePromise-Non-Write-API-Actionâ\80\9c беÑ\88е иÑ\81пÑ\80аÑ\82ено, но баÑ\80аÑ\9aеÑ\82о беÑ\88е Ñ\83паÑ\82ено кон модÑ\83л за пиÑ\88Ñ\83ваÑ\9aе на API.",
"internalerror": "Внатрешна грешка",
"internalerror_info": "Внатрешна грешка: $1",
"internalerror-fatal-exception": "Кобен исклучок на типот „$1“",
"blankarticle": "<strong>Предупредување:</strong> Страницата што ја создавате е празна.\nАко повторно стиснете на „$1“, страницата ќе биде создадена без никаква содржина во неа.",
"anoneditwarning": "<strong>Предупредување:</strong> Не сте најавени. Вашата IP-адреса ќе биде јавно видлива ако уредувате. Ако <strong>[$1 се најавите]</strong> или <strong>[$2 направите сметка]</strong>, тогаш уредувањата ќе се припишуваат на вашето корисничко име, покрај другите погодности.",
"anonpreviewwarning": "''Не сте најавени. Ако ја зачувате, Вашата IP-адреса ќе биде заведена во историјата на уредување на страницата.''",
- "missingsummary": "'''Потсетник:''' Не внесовте опис на измените. Ако притиснете Зачувај повторно, вашите измени ќе се зачуваат без опис.",
+ "missingsummary": "<strong>Потсетник:</strong> Не внесовте опис на измените.\nАко притиснете „$1“ повторно, вашите измени ќе се зачуваат без опис.",
"selfredirect": "<strong>Предупредување:</strong> Создавате пренасочување кон истата статија.\nМоже да сте укажале грешна целна страница, или пак уредувате погрешна страница.\nАко стиснете на „$1“ повторно, тогаш пренасочувањето бездруго ќе се создаде.",
"missingcommenttext": "Внесете коментар.",
"missingcommentheader": "<strong>Потсетување:</strong> Не внесовте наслов за овој коментар.\nАко повторно стиснете на „$1“, уредувањето ќе биде зачувано без наслов.",
"right-suppressredirect": "Не прави пренасочување од старото име при преместување на страница",
"right-upload": "Подигни податотеки",
"right-reupload": "Заменување на постоечки податотеки",
- "right-reupload-own": "Преснимување на постоечка податотека подигната од вас",
+ "right-reupload-own": "Презапишување врз постоечка податотека подигната од вас",
"right-reupload-shared": "Презапис на едни податотеки врз други на заедничкото мултимедијално складиште месно",
"right-upload_by_url": "Подигање на податотека од URL-адреса",
"right-purge": "Бришење на меѓусклад на страница",
"right-block": "Оневозможување на останати корисници да уредуваат",
"right-blockemail": "Оневозможување корисници да праќаат е-пошта",
"right-hideuser": "Блокирање корисници, сокривање од јавноста",
- "right-ipblock-exempt": "Заобиколување на IP блокирања, авто-блокирања и блокирања на IP рангови",
+ "right-ipblock-exempt": "Заобиколување на IP-блокови, автоблокови и опсежни блокови",
"right-unblockself": "Сопствено одблокирање",
"right-protect": "Менување на степени на заштита и уредување на каскадно заштитени страници",
"right-editprotected": "Уредување на страници заштитени како „{{int:protect-level-sysop}}“",
"action-changetags": "додавање и отстранување на произволни ознаки во поединечни преработки и дневнички записи",
"action-deletechangetags": "бришење ознаки од базата",
"action-purge": "превчитување на оваа страница",
+ "action-apihighlimits": "користење на помалку ограничени барања од извршникот",
+ "action-autoconfirmed": "без ограничувања на стапки за IP-адреса",
+ "action-bigdelete": "бришење на страници со долга историја",
+ "action-blockemail": "оневозможување корисници да праќаат е-пошта",
+ "action-bot": "бидете третирани како автоматски процес",
+ "action-editprotected": "уредување на страници заштитени како „{{int:protect-level-sysop}}“",
+ "action-editsemiprotected": "уредување на страници заштитени како „{{int:protect-level-autoconfirmed}}“",
+ "action-editinterface": "уредување на корисничкиот посредник",
+ "action-editusercss": "уредување на CSS-податотеки на други корисници",
+ "action-edituserjson": "уредување на JSON-податотеките на други корисници",
+ "action-edituserjs": "уредување на JavaScript-податотеки на други корисници",
+ "action-editsitecss": "уредување на CSS за цело вики",
+ "action-editsitejson": "уредување на JSON за цело вики",
+ "action-editsitejs": "уредување на JavaScript за цело вики",
+ "action-editmyusercss": "уредување на сопствените кориснички каскадни стилски податотеки (CSS)",
+ "action-editmyuserjson": "уредување на сопствените кориснички JSON-податотеки",
+ "action-editmyuserjs": "уредување на сопствените кориснички податотеки со JavaScript",
+ "action-viewsuppressed": "преглед на преработки скриени од било кој корисник",
+ "action-hideuser": "блокирање корисници, сокривање од јавноста",
+ "action-ipblock-exempt": "заобиколување на IP-блокови, автоблокови и опсежни блокови",
+ "action-unblockself": "блокирање на самите себеси",
+ "action-noratelimit": "да не бидете засегнати од временски ограничувања на уредување",
+ "action-reupload-own": "презапишување врз постоечка податотека подигната од вас",
+ "action-nominornewtalk": "ситните уредувања на разговорни страници да не поттикнуваат потсетник за нова порака",
+ "action-markbotedits": "означување на вратени уредувања како ботовски уредувања",
+ "action-patrolmarks": "преглед на одбележаните патролирања на скорешните промени",
+ "action-override-export-depth": "извезување на страници вклучувајќи поврзани страници со продорност до 5",
+ "action-suppressredirect": "не се прави пренасочување од старото име при преместување на страница",
"nchanges": "$1 {{PLURAL:$1|промена|промени}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|од последната посаета}}",
"enhancedrc-history": "историја",
"upload-tryagain": "Поднеси изменет опис на податотеката",
"upload-tryagain-nostash": "Поднеси преподигната податотека и изменет опис",
"uploadnologin": "Не сте најавени",
- "uploadnologintext": "Ð\9cоÑ\80а да Ñ\81Ñ\82е $1 за да можеÑ\82е да подигаÑ\82е.",
+ "uploadnologintext": "Ð\9cоÑ\80а да Ñ\81Ñ\82е $1 за да подигнÑ\83ваÑ\82е подаÑ\82оÑ\82еки.",
"upload_directory_missing": "Папката за подигање на слики ($1) не постои и не може да биде создадена од опслужувачот.",
"upload_directory_read_only": "Опслужувачот не може да запишува во именикот за подигање ($1).",
"uploaderror": "Грешка во подигањето",
"upload-preferred": "{{PLURAL:$2|Претпочитан податотечен тип|Претпочитани податотечни типови}}: $1.",
"upload-prohibited": "{{PLURAL:$2|Недозволен податотечен тип|Недозволени податотечни типови}}: $1.",
"uploadlogpage": "Дневник на подигања",
- "uploadlogpagetext": "Ð\9dаведен е список на најновите подигања на податотеки.\nПогледнете ја [[Special:NewFiles|галеријата на нови податотеки]] за нагледен преглед.",
+ "uploadlogpagetext": "Ð\9fодолÑ\83 е наведен список на најновите подигања на податотеки.\nПогледнете ја [[Special:NewFiles|галеријата на нови податотеки]] за нагледен преглед.",
"filename": "Име на податотеката",
"filedesc": "Опис",
"fileuploadsummary": "Опис:",
"ignorewarning": "Занемари ги предупредувањата и зачувај ја податотеката",
"ignorewarnings": "Занемари предупредувања",
"minlength1": "Името на податотеката мора да содржи барем една буква.",
- "illegalfilename": "Името на податотеката „$1“ содржи знаци што не се дозволени во наслови на страници.\nПреименувајте ја подигнете ја повторно.",
+ "illegalfilename": "Ð\98меÑ\82о на подаÑ\82оÑ\82екаÑ\82а â\80\9e$1â\80\9c Ñ\81одÑ\80жи знаÑ\86и Ñ\88Ñ\82о не Ñ\81е дозволени во наÑ\81лови на Ñ\81Ñ\82Ñ\80аниÑ\86и.\nÐ\9fÑ\80еименÑ\83ваÑ\98Ñ\82е Ñ\98а подаÑ\82оÑ\82екаÑ\82а и подигнеÑ\82е Ñ\98а повÑ\82оÑ\80но.",
"filename-toolong": "Имињата на податотеките не смеат да бидат подолги од 240 бајти.",
"badfilename": "Името на податотеката е променето во „$1“.",
"filetype-mime-mismatch": "Податотечната наставка „.$1“ не одговара на утврдениот MIME-тип на податотеката ($2).",
"undeletepagetext": "{{PLURAL:$1|Следната страница била избришана но сè уште е во архивот и може да биде вратена.|Следните $1 страници биле избришани но сè уште се во архивот и можат да бидат вратени.}}\nАрхивот може периодично да се чисти.",
"undelete-fieldset-title": "Врати преработки",
"undeleteextrahelp": "За да вратите целосна историја на една страница, отштиклирајте ги сите полиња и притиснете на „'''{{int:undeletebtn}}'''“.\nЗа да извршите делумно враќање, штиклирајте ги соодветните преработки за враќање и притиснете на „'''{{int:undeletebtn}}'''“.",
- "undeleterevisions": "{{PLURAL:$1|Избришана една преработка|Избришани $1 преработки}}",
+ "undeleterevisions": "$1 {{PLURAL:$1|преработка е избришана|преработки се избришани}}",
"undeletehistory": "Ако ја обновите страницата, сите преработки ќе бидат вратени во историјата.\nАко нова страница со исто име е создадена по бришењето, обновените преработки ќе се појават во претходната историја.",
"undeleterevdel": "Избришаното нема да биде вратено ако тоа значи дека со тоа најгорната страница или преработката на податотеката делумно ќе се избрише.\nВо такви случаи, морате да ја отштиклирате или откриете (ако е скриена) најновата избришана преработка.",
"undeletehistorynoadmin": "Оваа статија е избришана. Причината за бришењето е наведена подолу,\nзаедно со информации за корисникот кој ја уредувал страницата пред бришењето. Целиот текст\nод избришаните верзии е достапен само за администраторите.",
"block-prevent-edit": "Уредување",
"block-reason": "Причина:",
"block-target": "Корисничко име или IP-адреса:",
- "unblockip": "Ð\94еблокирај корисник",
+ "unblockip": "Ð\9eдблокирај корисник",
"unblockiptext": "Користете го долниот образец да го вратите правото на пишување на претходно блокирана IP-адреса или корисничко име.",
"ipusubmit": "Избриши го ова блокирање",
"unblocked": "[[User:$1|$1]] беше деблокиран",
"proxyblocker": "Блокер на застапници (proxy)",
"proxyblockreason": "Вашата IP-адреса е блокирана бидејќи претставува отворен застапник (proxy).\nВе молиме контактирајте со вашиот семрежен услужник и или техничката поддршка и информирајте ги за овој сериозен безбедносен проблем.",
"sorbs": "DNSBL",
- "sorbsreason": "Вашата IP-адреса е запишана како отворен застапник (proxy) во DNSBL кој го користи {{SITENAME}}..",
+ "sorbsreason": "Вашата IP-адреса е запишана како отворен застапник (прокси) во DNSBL кој го користи {{SITENAME}}.",
"sorbs_create_account_reason": "Вашата IP-адреса е наведена како отворен застапник (прокси) во DNSBL користена од {{SITENAME}}.\nНе можете да создадете корисничка сметка.",
"softblockrangesreason": "Анонимните придонеси не се дозволени од вашата IP-адреса ($1). Најавете се.",
"xffblockreason": "Блокирана е IP-адреса присутна во заглавието X-Forwarded-For, која е ваша или на застапничкиот опслужувач што го користите. Наведеното образложение гласи: $1",
"tags-deactivate-question": "На пат сте да ја деактивирате ознаката „$1“.",
"tags-deactivate-reason": "Причина:",
"tags-deactivate-not-allowed": "Не можам да ја деактивирам ознаката „$1“.",
- "tags-deactivate-submit": "Ð\94екативирај",
+ "tags-deactivate-submit": "Ð\94еактивирај",
"tags-apply-no-permission": "Немате дозвола да ставате ознаки за промени заедно со измените што ги правите.",
"tags-apply-blocked": "Не можете да задавате ознаки за промени додека {{GENDER:$1|сте}} блокирани.",
"tags-apply-not-allowed-one": "Не е дозволено ознаката „$1“ да се става рачно.",
"tags-edit-manage-link": "Раководство со ознаки",
"tags-edit-revision-selected": "{{PLURAL:$1|Одбрана преработка|Одбрани преработки}} на [[:$2]]:",
"tags-edit-logentry-selected": "{{PLURAL:$1|Одбран настан од дневник|Одбрани настани од дневник}}:",
- "tags-edit-revision-legend": "Ð\94одаÑ\98Ñ\82е или оÑ\82Ñ\81Ñ\82Ñ\80анеÑ\82е ознаки од {{PLURAL:$1|пÑ\80еÑ\80абоÑ\82кава|сите $1 преработки}}",
+ "tags-edit-revision-legend": "Ð\94одаÑ\98Ñ\82е или оÑ\82Ñ\81Ñ\82Ñ\80анеÑ\82е ознаки од {{PLURAL:$1|оваа пÑ\80еÑ\80абоÑ\82ка|сите $1 преработки}}",
"tags-edit-logentry-legend": "Додајте или отстранете ознаки од {{PLURAL:$1|овој дневнички запис|сите $1 дневнички записи}}",
"tags-edit-existing-tags": "Постоечки ознаки",
"tags-edit-existing-tags-none": "<em>Нема</em>",
"tags-edit-new-tags": "Нови ознаки:",
- "tags-edit-add": "Додај ги следниве ознаки:",
- "tags-edit-remove": "Отстрани ги следниве ознаки:",
+ "tags-edit-add": "Додај ги овие ознаки:",
+ "tags-edit-remove": "Отстрани ги овие ознаки:",
"tags-edit-remove-all-tags": "(отстрани ги сите ознаки)",
"tags-edit-chosen-placeholder": "Одберете некои ознаки",
"tags-edit-chosen-no-results": "Не пронајдов одговарачки ознаки",
"mediastatistics-header-audio": "Audio",
"mediastatistics-header-video": "Video's",
"mediastatistics-header-multimedia": "Interaktieve media",
- "special-characters-group-latin": "Latien",
- "special-characters-group-latinextended": "Latien uutgebreid",
- "special-characters-group-ipa": "Internasionaal Klankeschrift",
+ "special-characters-group-latin": "Latyn",
+ "special-characters-group-latinextended": "Latyn üütgebreided",
+ "special-characters-group-ipa": "Internationål Fonetisk Alfabet",
"special-characters-group-symbols": "Symbolen",
- "special-characters-group-greek": "Grieks",
- "special-characters-group-cyrillic": "Kyrillies",
- "special-characters-group-arabic": "Arabies",
- "special-characters-group-arabicextended": "Arabies uutgebreid",
+ "special-characters-group-greek": "Gryksk",
+ "special-characters-group-greekextended": "Gryksk üütgebreided",
+ "special-characters-group-cyrillic": "Kyrillisk",
+ "special-characters-group-arabic": "Arabisk",
+ "special-characters-group-arabicextended": "Arabisk üütgebreided",
"special-characters-group-persian": "Perzies",
- "special-characters-group-hebrew": "Hebreeuws",
- "special-characters-group-bangla": "Bengaals",
+ "special-characters-group-hebrew": "Hebreywsk",
+ "special-characters-group-bangla": "Bengaalsk",
"special-characters-group-tamil": "Tamil",
"special-characters-group-telugu": "Telugu",
- "special-characters-group-sinhala": "Singalees",
+ "special-characters-group-sinhala": "Singalääsk",
"special-characters-group-gujarati": "Gujarati",
"special-characters-group-devanagari": "Devanagari",
- "special-characters-group-thai": "Thai",
- "special-characters-group-lao": "Laotiaans",
+ "special-characters-group-thai": "Taisk",
+ "special-characters-group-lao": "Laotiaansk",
"special-characters-group-khmer": "Khmer",
+ "special-characters-group-canadianaboriginal": "Kanadääsk lettergreapenskrivt",
"special-characters-title-endash": "liggend streepjen",
"special-characters-title-emdash": "gedachtenstreepjen",
"special-characters-title-minus": "minteken",
"right-browsearchive": "Verwijderde pagina's zoeken",
"right-undelete": "Verwijderde pagina's terugplaatsen",
"right-suppressrevision": "Specifieke versies bekijken, verbergen en weer zichtbaar maken op pagina's van elke gebruiker",
- "right-viewsuppressed": "Versies verborgen door elke gebruiker bekijken",
+ "right-viewsuppressed": "Versies verborgen voor elke gebruiker bekijken",
"right-suppressionlog": "Niet-openbare logboeken bekijken",
"right-block": "Andere gebruikers de mogelijkheid ontnemen te bewerken",
"right-blockemail": "Een gebruiker het recht ontnemen om e-mail te versturen",
"right-hideuser": "Een gebruiker voor de overige gebruikers verbergen",
- "right-ipblock-exempt": "IP-blokkades omzeilen",
+ "right-ipblock-exempt": "IP-blokkades, automatische blokkades en IP-bereik blokkades omzeilen",
"right-unblockself": "Eigen gebruiker deblokkeren",
"right-protect": "Beveiligingsniveaus wijzigen",
"right-editprotected": "Pagina's bewerken die beveiligd zijn als \"{{int:protect-level-sysop}}\"",
"action-changetags": "willekeurige labels toe te voegen aan en te verwijderen van versies en logboekregels",
"action-deletechangetags": "labels uit de database te verwijderen",
"action-purge": "deze pagina te verversen",
+ "action-apihighlimits": "de hogere limieten in API-zoekopdrachten te gebruiken",
+ "action-autoconfirmed": "uitgezonderd te zijn van IP-adresgebaseerde tijdsafhankelijke beperkingen",
+ "action-bigdelete": "pagina's met een grote geschiedenis te verwijderen",
+ "action-blockemail": "een gebruiker het recht te ontnemen om e-mail te versturen",
+ "action-bot": "behandeld te worden als een geautomatiseerd proces",
+ "action-editprotected": "pagina's bewerken die beveiligd zijn als \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "pagina's bewerken die beveiligd zijn als \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "de gebruikersinterface te bewerken",
+ "action-editusercss": "de CSS-bestanden van andere gebruikers te bewerken",
+ "action-edituserjson": "de JSON-bestanden van andere gebruikers te bewerken",
+ "action-edituserjs": "de JavaScriptbestanden van andere gebruikers te bewerken",
+ "action-editmyusercss": "uw eigen CSS-pagina's te bewerken",
+ "action-editmyuserjson": "uw eigen JSON-pagina's te bewerken",
+ "action-editmyuserjs": "uw eigen JavaScriptpagina's te bewerken",
+ "action-viewsuppressed": "versies verborgen voor elke gebruiker te bekijken",
+ "action-hideuser": "een gebruiker voor de overige gebruikers te verbergen",
+ "action-ipblock-exempt": "IP-blokkades, automatische blokkades en IP-bereik blokkades te omzeilen",
+ "action-unblockself": "uzelf te deblokkeren",
+ "action-noratelimit": "tijdsafhankelijke beperkingen te negeren",
+ "action-reupload-own": "uw eigen bestandsuploads te overschrijven",
+ "action-nominornewtalk": "kleine bewerkingen aan een overlegpagina niet te laten leiden tot een melding 'nieuwe berichten'",
+ "action-markbotedits": "teruggedraaide bewerkingen te markeren als botbewerkingen",
+ "action-override-export-depth": "pagina's te exporteren, inclusief pagina's waarnaar verwezen wordt, tot een diepte van vijf",
+ "action-suppressredirect": "geen doorverwijzingen achter te laten bij het hernoemen van pagina's",
"nchanges": "$1 {{PLURAL:$1|bewerking|bewerkingen}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sinds uw laatste bezoek}}",
"enhancedrc-history": "geschiedenis",
"right-reupload-shared": "Lokalne nadpisywanie pliku istniejącego w repozytorium mediów",
"right-upload_by_url": "Przesyłanie plików z adresu URL",
"right-purge": "Czyszczenie pamięci podręcznej stron",
- "right-autoconfirmed": "Wyłączenie z ograniczeń prędkości zakładanych na IP",
+ "right-autoconfirmed": "Wyłączenie z ograniczeń przepustowości nałożonych na IP",
"right-bot": "Oznaczanie edycji jako wykonanych automatycznie",
"right-nominornewtalk": "Drobne zmiany na stronach dyskusji użytkowników nie włączają powiadomienia o nowej wiadomości",
"right-apihighlimits": "Zwiększony limit w zapytaniach wykonywanych poprzez interfejs API",
"action-changetags": "dodawania i usuwania dowolnych znaczników z poszczególnych wersji i wpisów w rejestrze",
"action-deletechangetags": "usuwania znaczników z bazy danych",
"action-purge": "wyczyść pamięć podręczną tej strony",
+ "action-apihighlimits": "używania zwiększonych limitów w zapytaniach wykonywanych poprzez interfejs API",
+ "action-autoconfirmed": "nie bycia objętym ograniczeniem przepustowości nałożonym na IP",
+ "action-bigdelete": "usuwania stron z długą historią edycji",
+ "action-blockemail": "blokowania użytkownikom możliwości wysyłania wiadomości",
+ "action-bot": "oznaczania edycji jako wykonanych automatycznie",
+ "action-editprotected": "edytowania stron zabezpieczonych na poziomie „{{int:protect-level-sysop}}”",
+ "action-editsemiprotected": "edytowania stron zabezpieczonych na poziomie „{{int:protect-level-autoconfirmed}}”",
+ "action-editinterface": "edytowania interfejsu użytkownika",
+ "action-editusercss": "edytowania plików CSS innych użytkowników",
+ "action-edituserjson": "edytowania plików JSON innych użytkowników",
+ "action-edituserjs": "edytowania plików JavaScript innych użytkowników",
+ "action-editsitecss": "edytowania plików CSS projektu",
+ "action-editsitejson": "edytowania plików JSON projektu",
+ "action-editsitejs": "edytowania plików JavaScript projektu",
+ "action-editmyusercss": "edytowania własnych plików CSS",
+ "action-editmyuserjson": "edytowania własnych plików JSON",
+ "action-editmyuserjs": "edytowania własnych plików JavaScript",
+ "action-viewsuppressed": "podglądu wersji ukrytych przed każdym użytkownikiem",
+ "action-hideuser": "blokowania użytkownika i ukrywania go od publiczności",
+ "action-ipblock-exempt": "obchodzenia blokad i blokad zakresów adresów IP",
+ "action-unblockself": "odblokowania samego siebie",
+ "action-noratelimit": "nie bycia objętym ograniczeniem przepustowości",
+ "action-reupload-own": "nadpisywania wcześniej przesłanych plików",
+ "action-nominornewtalk": "nie włączania powiadomień o nowej wiadomości na stronach dyskusji użytkowników poprzez wykonanie drobnej edycji",
+ "action-markbotedits": "oznaczania rewertu jako edycji bota",
+ "action-patrolmarks": "podglądu znaczników patrolowania ostatnich zmian",
+ "action-override-export-depth": "eksportowania stron wraz z linkowanymi do głębokości 5 linków",
+ "action-suppressredirect": "przenoszenia stron bez tworzenia przekierowania w miejscu starej nazwy",
"nchanges": "$1 {{PLURAL:$1|zmiana|zmiany|zmian}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|od ostatniej wizyty}}",
"enhancedrc-history": "historia",
"action-changetags": "adicionar e remover etiquetas arbitrárias em revisões e ''logs'' individuais",
"action-deletechangetags": "deletar marcações da base de dados",
"action-purge": "purgar esta página",
+ "action-apihighlimits": "usar limites mais altos em consultas da API",
+ "action-autoconfirmed": "não ser afetado por limites de taxa baseados em IP",
+ "action-bigdelete": "excluir páginas com grandes históricos",
+ "action-blockemail": "bloquear um usuário de enviar e-mail",
+ "action-bot": "ser tratado como um processo automatizado",
+ "action-editprotected": "editar páginas protegidas como \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "editar páginas protegidas como \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "editar a interface do usuário",
+ "action-editusercss": "editar arquivos CSS de outros usuários",
+ "action-edituserjson": "editar arquivos JSON de outros usuários",
+ "action-edituserjs": "editar arquivos JavaScript de outros usuários",
+ "action-editsitecss": "editar CSS de todo o site",
+ "action-editsitejson": "editar JSON em todo o site",
+ "action-editsitejs": "edite JavaScript em todo o site",
+ "action-editmyusercss": "edite seus próprios arquivos CSS de usuário",
+ "action-editmyuserjson": "editar seus próprios arquivos JSON do usuário",
+ "action-editmyuserjs": "editar seus próprios arquivos JavaScript de usuário",
+ "action-viewsuppressed": "ver revisões ocultas de qualquer usuário",
+ "action-hideuser": "bloquear um nome de usuário, escondendo-o do público",
+ "action-ipblock-exempt": "ignorar bloqueios IP, bloqueios automáticos e bloqueios de alcance",
+ "action-unblockself": "desbloquear a si mesmo",
+ "action-noratelimit": "não ser afetado por limites de taxa",
+ "action-reupload-own": "sobrescrever os arquivos existentes carregados por você",
+ "action-nominornewtalk": "não ter edições menores nas páginas de discussão aciona o novo prompt de mensagens",
+ "action-markbotedits": "marcar edições revertidas como edições de robô",
+ "action-patrolmarks": "ver marcas de patrulha de mudanças recentes",
+ "action-override-export-depth": "Exportar páginas incluindo páginas ligadas até uma profundidade de 5",
+ "action-suppressredirect": "Não crie redirecionamentos de páginas de origem ao mover páginas",
"nchanges": "$1 {{PLURAL:$1|alteração|alterações}}",
"ntimes": "$1×",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
"The Discoverer",
"Bencemac",
"Zoranzoki21",
- "Woytecr"
+ "Woytecr",
+ "PiefPafPier"
]
},
"sidebar": "{{notranslate}}",
"action-changetags": "{{doc-action|changetags}}",
"action-deletechangetags": "{{doc-action|deletechangetags}}",
"action-purge": "{{doc-action|purge}}",
+ "action-apihighlimits": "{{doc-action|apihighlimits}}",
+ "action-autoconfirmed": "{{doc-action|autoconfirmed}}",
+ "action-bigdelete": "{{doc-action|bigdelete}}",
+ "action-blockemail": "{{doc-action|blockemail}}",
+ "action-bot": "{{doc-action|bot}}",
+ "action-editprotected": "{{doc-action|editprotected}}",
+ "action-editsemiprotected": "{{doc-action|editsemiprotected}}",
+ "action-editinterface": "{{doc-action|editinterface}}",
+ "action-editusercss": "{{doc-action|editusercss}}",
+ "action-edituserjson": "{{doc-action|edituserjson}}",
+ "action-edituserjs": "{{doc-action|edituserjs}}",
+ "action-editsitecss": "{{doc-action|editsitecss}}",
+ "action-editsitejson": "{{doc-action|editsitejson}}",
+ "action-editsitejs": "{{doc-action|editsitejs}}",
+ "action-editmyusercss": "{{doc-action|editmyusercss}}",
+ "action-editmyuserjson": "{{doc-action|editmyuserjson}}",
+ "action-editmyuserjs": "{{doc-action|editmyuserjs}}",
+ "action-viewsuppressed": "{{doc-action|viewsuppressed}}",
+ "action-hideuser": "{{doc-action|hideuser}}",
+ "action-ipblock-exempt": "{{doc-action|ipblock-exempt}}",
+ "action-unblockself": "{{doc-action|unblockself}}",
+ "action-noratelimit": "{{doc-action|noratelimit}}",
+ "action-reupload-own": "{{doc-action|reupload-own}}",
+ "action-nominornewtalk": "{{doc-action|nominornewtalk}}",
+ "action-markbotedits": "{{doc-action|markbotedits}}",
+ "action-patrolmarks": "{{doc-action|patrolmarks}}",
+ "action-override-export-depth": "{{doc-action|override-export-depth}}",
+ "action-suppressredirect": "{{doc-action|suppressredirect}}",
"nchanges": "Appears on enhanced watchlist and recent changes when page has more than one change on given date, linking to a diff of the changes.\n\nParameters:\n* $1 - the number of changes on that day (2 or more)\nThree messages are shown side-by-side: ({{msg-mw|Nchanges}} | {{msg-mw|Enhancedrc-since-last-visit}} | {{msg-mw|Enhancedrc-history}}).",
"ntimes": "Used to indicate how many times an event occurred (eg. on enhanced recent change when a user did more than one change to the page). Parameters:\n* $1 - number (integer)",
"enhancedrc-since-last-visit": "Appears on enhanced watchlist and recent changes when page has more than one change on given date and at least one that the user hasn't seen yet, linking to a diff of the unviewed changes.\n\nParameters:\n* $1 - the number of unviewed changes (1 or more)\nThree messages are shown side-by-side: ({{msg-mw|nchanges}} | {{msg-mw|enhancedrc-since-last-visit}} | {{msg-mw|enhancedrc-history}}).",
"rcfilters-activefilters-show-tooltip": "Tooltip for the button that shows the active filters list and dropdown in [[Special:RecentChanges]].",
"rcfilters-advancedfilters": "Title for the buttons allowing the user to switch to the various advanced filters views.",
"rcfilters-limit-title": "Title for the options to change the number of results shown.",
- "rcfilters-limit-and-date-label": "Title for the button that opens the operation to control how many results to show and in which time period to search. \n\nParameters: $1 - Number of results shown\n\n$2 - Time period to search. One of {{msg-mw|rcfilters-days-title}} or {{msg-mw|rcfilters-hours-title}} is used as $2\n{{Identical|Change}}",
+ "rcfilters-limit-and-date-label": "Title for the button that opens the operation to control how many results to show and in which time period to search. \n\nParameters: $1 - Number of results shown\n\n$2 - Time period to search. One of {{msg-mw|Rcfilters-days-show-days}} or {{msg-mw|Rcfilters-days-show-hours}} is used as $2\n{{Identical|Change}}",
"rcfilters-date-popup-title": "Section title of date options on recent changes results",
"rcfilters-days-title": "Title for the options to change the number of days for the results shown.",
"rcfilters-hours-title": "Title for the options to change the number of hours for the results shown.",
"mediastatistics-header-3d": "Header on [[Special:MediaStatistics]] for file types that are in the 3D category. Includes STL files.",
"mediastatistics-header-total": "Header on [[Special:MediaStatistics]] for a summary of all file types.",
"json-warn-trailing-comma": "A warning message notifying that JSON text was automatically corrected by removing erroneous commas.\n\nParameters:\n* $1 - number of commas that were removed\n{{Related|Json-error}}",
- "json-error-unknown": "User error message when there’s an unknown error.\n\nThis error is shown if we received an unexpected value from PHP. See https://secure.php.net/manual/en/function.json-last-error.php\n\nParameters:\n* $1 - integer error code\n{{Related|Json-error}}",
- "json-error-depth": "User error message when the maximum stack depth is exceeded.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-state-mismatch": "User error message when underflow or the modes mismatch.\n\n'''Underflow''': A data-processing error arising when the absolute value of a computed quantity is smaller than the limits of precision of the computing device, retaining at least one significant digit.\n\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-ctrl-char": "User error message when an unexpected control character has been found.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-syntax": "User error message when there is a syntax error; a malformed JSON.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}\n{{Identical|Syntax error}}",
- "json-error-utf8": "User error message when there are malformed UTF-8 characters, possibly incorrectly encoded.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-recursion": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-inf-or-nan": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
- "json-error-unsupported-type": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-unknown": "User error message when there’s an unknown error.\n\nThis error is shown if we received an unexpected value from PHP. See https://www.php.net/manual/en/function.json-last-error.php\n\nParameters:\n* $1 - integer error code\n{{Related|Json-error}}",
+ "json-error-depth": "User error message when the maximum stack depth is exceeded.\nSee https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-state-mismatch": "User error message when underflow or the modes mismatch.\n\n'''Underflow''': A data-processing error arising when the absolute value of a computed quantity is smaller than the limits of precision of the computing device, retaining at least one significant digit.\n\nSee https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-ctrl-char": "User error message when an unexpected control character has been found.\nSee https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-syntax": "User error message when there is a syntax error; a malformed JSON.\nSee https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}\n{{Identical|Syntax error}}",
+ "json-error-utf8": "User error message when there are malformed UTF-8 characters, possibly incorrectly encoded.\nSee https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-recursion": "PHP JSON encoding/decoding error. See https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-inf-or-nan": "PHP JSON encoding/decoding error. See https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
+ "json-error-unsupported-type": "PHP JSON encoding/decoding error. See https://www.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
"headline-anchor-title": "Title tooltip for the section anchor symbol, which is a link to the current section. Can be interpreted both as a noun (\"this is a link\") or as a verb (\"use this to link\").",
"special-characters-group-latin": "This is the name of a script, or alphabet, not a language.",
"special-characters-group-latinextended": "The name of the Latin Extended character set.",
"subject-preview": "Pretpregled naslova:",
"previewerrortext": "Dogodila se greška prilikom prikazivanja vaših izmjena.",
"blockedtitle": "Korisnik je blokiran",
- "blockedtext": "'''Vaše korisničko ime ili IP adresa je blokirana.'''\n\nBlokada izvršena od strane $1.\nDati razlog je slijedeći: ''$2''.\n\n*Početak blokade: $8\n*Kraj perioda blokade: $6\n*Ime blokiranog korisnika: $7\n\nMožete kontaktirati $1 ili nekog drugog [[{{MediaWiki:Grouppage-sysop}}|administratora]] da biste razgovarali o blokadi.\n\nNe možete koristiti opciju ''Pošalji e-mail korisniku'' osim ako niste unijeli e-mail adresu u [[Special:Preferences|Vaše postavke]].\nVaša trenutna IP adresa je $3, a oznaka blokade je #$5.\nMolimo Vas da navedete gornje podatke u zahtjevu za deblokadu.",
- "autoblockedtext": "Vaša IP adresa je automatski blokirana jer je korištena od strane drugog korisnika, a blokirao ju je $1.\nNaveden je slijedeći razlog:\n\n:''$2''\n\n* Početak blokade: $8\n* Kraj blokade: $6\n* Blokirani korisnik: $7\n\nMožete kontaktirati $1 ili nekog drugog iz grupe [[{{MediaWiki:Grouppage-sysop}}|administratora]] i zahtijevati da Vas deblokira.\n\nZapamtite da ne možete koristiti opciju \"pošalji e-mail ovom korisniku\" sve dok ne unesete validnu e-mail adresu pri registraciji u Vašim [[Special:Preferences|korisničkim postavkama]] te Vas ne spriječava ga je koristite.\n\nVaša trenutna IP adresa je $3, a ID blokade je $5.\nMolimo da navedete sve gore navedene detalje u zahtjevu za deblokadu.",
+ "blocked-email-user": "<strong>Vašem korisničkom imenu je zabranjeno slanje e-poruka. Još uvijek možete da uređujete druge stranice na ovom wikiju.</strong> \nSve pojedinosti o zabrani naći ćete u [[Special:MyContributions|doprinosima naloga]].\n\nZabranu je dao/la $1.\n\nNaveden je sljedeći razlog: <em>$2</em>.\n\n* Početak zabrane: $8\n* Istek zabrane: $6\n* Namijenjena korisniku/ci ili IP adresi: $7\n* ID zabrane #$5",
+ "blockedtext-partial": "<strong>Vašem korisničkom imenu ili IP adresi je zabranjeno pravljenje izmjena na ovoj stranici. Još uvijek možete da uređujete druge stranice na ovom wikiju.</strong> Sve pojedinosti o zabrani naći ćete u [[Special:MyContributions|doprinosima naloga]].\n\nZabranu je dao/la $1.\n\nNaveden je sljedeći razlog: <em>$2</em>.\n\n* Početak zabrane: $8\n* Istek zabrane: $6\n* Namijenjena korisniku/ci ili IP adresi: $7\n* ID zabrane #$5",
+ "blockedtext": "<strong>Vaše korisničko ime ili IP adresa je blokirana.</strong>\n\nBlokada izvršena od strane $1.\nDati razlog je slijedeći: <em>$2</em>.\n\n* Početak blokade: $8\n* Kraj perioda blokade: $6\n* Ime blokiranog korisnika: $7\n\nMožete kontaktirati $1 ili nekog drugog [[{{MediaWiki:Grouppage-sysop}}|administratora]] da biste razgovarali o blokadi.\nMožete koristiti opciju \"{{int:emailuser}}\" ako je navedena valjana adresa e-pošte u [[Special:Preferences|Vašim postavkama]] i Vam nije zabranjeno je koristiti.\nVaša trenutna IP adresa je $3, a oznaka blokade je #$5.\nMolimo Vas da navedete gornje podatke u zahtjevu za deblokadu.",
+ "autoblockedtext": "Vaša IP adresa je automatski blokirana jer je korištena od strane drugog korisnika, a blokirao ju je $1.\nNaveden je slijedeći razlog:\n\n:<em>$2</em>\n\n* Početak blokade: $8\n* Kraj blokade: $6\n* Blokirani korisnik: $7\n\nMožete kontaktirati $1 ili nekog drugog iz grupe [[{{MediaWiki:Grouppage-sysop}}|administratora]] i zahtijevati da Vas deblokira.\n\nZapamtite da ne možete koristiti opciju \"{{int:emailuser}}\" ukoliko nije unesena validna e-mail adresa u [[Special:Preferences|Vašim postavkama]] te Vas ne spriječava ga je koristite.\n\nVaša trenutna IP adresa je $3, a ID blokade je $5.\nMolimo da navedete sve gore navedene detalje u zahtjevu za deblokadu.",
+ "systemblockedtext": "MediaWiki je automatski blokirao Vaše korisničko ime ili IP-adresu.\nDat je sljedeći razlog:\n\n:<em>$2</em>\n\n* Početak bloka: $8\n* Istek bloka: $6\n* Blok je namijenjen za: $7\n\nVaša trenutna IP-adresa je $3.\nPrepišite sve gorenavedene pojedinosti ukoliko želite da vlasti pitaju za blok.",
"blockednoreason": "razlog nije naveden",
"whitelistedittext": "Da bi ste uređivali stranice, morate se $1.",
"confirmedittext": "Morate potvrditi Vašu e-mail adresu prije nego počnete mijenjati stranice.\nMolimo da postavite i verifikujete Vašu e-mail adresu putem Vaših [[Special:Preferences|korisničkih opcija]].",
"continue-editing": "Idi na područje uređivanja",
"previewconflict": "Ovaj pretpregled reflektuje tekst u gornjem polju\nkako će izgledati ako pritisnete \"Snimi stranicu\".",
"session_fail_preview": "Nažalost, nisam mogao obraditi izmjenu zbog gubitka podataka o sesiji.\n\nMožda ste odjavljeni. <strong>Provjerite jeste li i dalje prijavljeni i pokušajte ponovno.<strong>\nAko se problem nastavi pojavljivati, [[Special:UserLogout|odjavite se]] i ponovno prijavite, te provjerite da preglednik dopušta kolačiće s ovog sajta.",
- "session_fail_preview_html": "'''Žao nam je! Nismo mogli da obradimo vašu izmjenu zbog gubitka podataka.'''\n\n''Zbog toga što {{SITENAME}} ima omogućen izvorni HTML, predpregled je sakriven kao predostrožnost protiv JavaScript napada.''\n\n'''Ako ste pokušali da napravite pravu izmjenu, molimo pokušajte ponovo. Ako i dalje ne radi, pokušajte da se [[Special:UserLogout|odjavite]] i ponovo prijavite.'''",
+ "session_fail_preview_html": "Žao nam je! Nismo mogli da obradimo vašu izmjenu zbog gubitka sesijskih podataka.\n\n<em>Zbog toga što {{SITENAME}} ima omogućen izvorni HTML, predpregled je sakriven kao predostrožnost protiv JavaScript napada.</em>\n\n<strong>Ako ste pokušali da napravite pravu izmjenu, molimo pokušajte ponovo.</strong>\nAko to ne riješi problem, pokušajte da se [[Special:UserLogout|odjavite]] i ponovo prijavite, i provjerite da preglednik dopušta kolačiće s ovog sajta.",
"token_suffix_mismatch": "'''Vaša izmjena nije prihvaćena jer je Vaš web preglednik ubacio znakove interpunkcije u token uređivanja.'''\nIzmjena je odbačena da bi se spriječilo uništavanje teksta stranice.\nTo se događa ponekad kad korisite problematični anonimni proxy koji je baziran na web-u.",
"edit_form_incomplete": "'''Neki dijelovi uređivačkog obrasca nisu došli do servera; dvaput provjerite da su vaše izmjene nepromjenjene i pokušajte ponovno.'''",
"editing": "Uređujete $1",
"editingsection": "Uređujete $1 (sekciju)",
"editingcomment": "Uređujete $1 (nova sekcija)",
"editconflict": "Sukobljenje izmjene: $1",
- "explainconflict": "Neko drugi je promujenio ovu stranicu otkad ste Vi počeli da je mijenjate.\nGornje tekstualno polje sadrži tekst stranice koji trenutno postoji.\nVaše izmjene su prikazane u donjem tekstu.\nMoraćete da unesete svoje promjene u postojeći tekst.\n'''Samo''' tekst u gornjem tekstualnom polju će biti snimljen kad pritisnete \"$1\".",
+ "explainconflict": "Neko drugi je promijenio ovu stranicu otkad ste Vi počeli da je mijenjate.\nGornje tekstualno polje sadrži tekst stranice koji trenutno postoji.\nVaše izmjene su prikazane u donjem tekstu.\nMoraćete da unesete svoje promjene u postojeći tekst.\n<strong>Samo</strong> tekst u gornjem tekstualnom polju će biti snimljen kad pritisnete \"$1\".",
"yourtext": "Vaš tekst / Ваш текст",
"storedversion": "Uskladištena verzija",
"editingold": "'''PAŽNJA: Vi mijenjate stariju reviziju ove stranice.\nAko je snimite, sve promjene učinjene od ove revizije će biti izgubljene.'''",
"page_first": "prva",
"page_last": "zadnja",
"histlegend": "Odabir razlika: označite radio dugme verzija za usporedbu i pritisnite enter ili dugme na dnu.<br />\nObjašnjenje: '''({{int:cur}})''' = razlika sa trenutnom verzijom,\n'''({{int:last}})''' = razlika sa prethodnom verzijom, '''{{int:minoreditletter}}''' = manja izmjena.",
- "history-fieldset-title": "Pretraga izmjena",
+ "history-fieldset-title": "Filtriraj izmjene",
"history-show-deleted": "Samo obrisane izmjene",
"histfirst": "najstarije",
"histlast": "najnovije",
"recentchangesdays-max": "(najviše $1 {{PLURAL:$1|dan|dana}})",
"recentchangescount": "Podrazumevani broj izmjena za prikaz u skorašnjim izmjenama, istorijama stranica i dnevnicima:",
"prefs-help-recentchangescount": "Najveći broj: 1000",
- "prefs-help-watchlist-token2": "Ovo je tajni ključ prema sažetku Vašeg popisa praćenja. Svaki suradnik kojem je poznat, moći će čitati Vaš popis praćenih stranica. Ne dijelite ga ni s kim. [[Special:ResetTokens|Kliknite ovdje ako ga želite ponovo postaviti]].",
+ "prefs-help-watchlist-token2": "Ovo je tajni ključ prema sažetku Vašeg popisa praćenja.\nSvaki suradnik kojem je poznat, moći će čitati Vaš popis praćenih stranica, pa zato ne dijelite ga ni s kim.\nAko je potrebno, [[Special:ResetTokens|možete staviti novi]].",
+ "prefs-help-tokenmanagement": "Možete pogledati i odnovno zadati tajni ključ za svoj nalog koji može da pristupi svemrežnom fidu Vašeg popisa praćenih. Svako ko zna ključ moći će da vam čita spisak praćenih — zato nikome ne govori.",
"savedprefs": "Vaša postavke su snimljene.",
"savedrights": "Korisnička prava {{GENDER:$1|$1}} su snimljena.",
"timezonelegend": "Vremenska zona / Временска зона",
"userrights-expiry": "Ističe:",
"userrights-expiry-existing": "Postojeće vrijeme isticanja: $3, $2",
"userrights-expiry-othertime": "Drugo vrijeme:",
+ "userrights-expiry-options": "1 dan:1 day,1 sedmica:1 week,1 mjesec:1 month,3 mjeseca:3 months,6 mjeseci:6 months,1 godina:1 year",
"userrights-invalid-expiry": "Vrijeme isticanja grupe \"$1\" nije ispravno.",
"userrights-expiry-in-past": "Vrijeme isticanja grupe \"$1\" je u prošlosti.",
"userrights-cannot-shorten-expiry": "Ne možete ubrzati istek članstva u grupi \"$1\". To mogu učiniti samo korisnici s dozvolom za dodavanje ili uklanjanje ove grupe.",
"action-changetags": "dodajte ili uklonite razne oznake na pojedinačnim verzijama i unosima u zapisnicima",
"action-deletechangetags": "brišete oznake iz baze podataka",
"action-purge": "preučitavanje ove stranice",
+ "action-apihighlimits": "korištenje viših ograničenja u API upitima",
+ "action-autoconfirmed": "izbjegavanje ograničenja stopa temeljenih na IP-u",
+ "action-bigdelete": "brisanje stranica sa velikom historijom",
+ "action-blockemail": "blokiranje korisnika da šalje e-mail",
+ "action-bot": "tretiranje kao automatski proces",
+ "action-editprotected": "uređivanje stranica zaštićenih kao \"{{int:protect-level-sysop}}\"",
+ "action-editsemiprotected": "uređivanje stranica zaštićenih kao \"{{int:protect-level-autoconfirmed}}\"",
+ "action-editinterface": "uređivanje korisničkog interfejsa",
+ "action-editusercss": "uređivanje CSS-datoteka drugih korisnika",
+ "action-edituserjson": "uređivanje JSON-datoteka drugih korisnika",
+ "action-edituserjs": "uređivanje JavaScript-datoteka drugih korisnika",
+ "action-editsitecss": "uređivanje CSS za cijelo wiki",
+ "action-editsitejson": "uređivanje JSONa za cijelo wiki",
+ "action-editsitejs": "uređivanje JavaScripta za cijelo wiki",
+ "action-editmyusercss": "uređivanje vlastitih CSS datoteka",
+ "action-editmyuserjson": "uređivanje vlastitih JSON datoteka",
+ "action-editmyuserjs": "uređivanje vlastitih JavaScript datoteka",
+ "action-viewsuppressed": "pregledaj izmjene skrivene od svih korisnika",
+ "action-hideuser": "blokiranje korisničkog imena, i njegovo sakrivanje od javnosti",
+ "action-ipblock-exempt": "zaobilaženje IP blokova, autoblokova i opsežnih blokova",
+ "action-unblockself": "deblokiraj samog sebe",
+ "action-noratelimit": "ne utječu na vremenska ograničenja uređivanja",
+ "action-reupload-own": "prezapis datoteke koju je postavio korisnik",
+ "action-nominornewtalk": "male izmjene na stranicama za razgovor ne uzrokuju obavještenje o novim porukama",
+ "action-markbotedits": "označavanje vraćenih izmjena kao izmjene bota",
+ "action-patrolmarks": "pregled oznaka patroliranja u spisku nedavnih izmjena",
+ "action-override-export-depth": "izvoz stranica uključujući povezane stranice do dubine od 5 linkova",
+ "action-suppressredirect": "ne se pravi preusmjerenje sa starog imena pri premještanju stranica",
"nchanges": "$1 {{PLURAL:$1|izmjena|izmjene|izmjena}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|izmjena od Vaše posljedne posjete}}",
"enhancedrc-history": "historija",
"delete-confirm": "Brisanje \"$1\"",
"delete-legend": "Obriši",
"historywarning": "<strong>Upozorenje</strong>: Stranica koju želite da obrišete ima historiju sa otprilike $1 {{PLURAL:$1|revizijom|revizije|revizija}}:",
- "historyaction-submit": "Prikaži",
+ "historyaction-submit": "Prikaži revizije",
"confirmdeletetext": "Upravo ćete obrisati stranicu sa svom njenom historijom.\nMolimo da potvrdite da ćete to učiniti, da razumijete posljedice te da to činite u skladu sa [[{{MediaWiki:Policy-url}}|pravilima]].",
"actioncomplete": "Radnja završena\n\nРадња завршена",
"actionfailed": "Akcija nije uspjela",
"deleting-backlinks-warning": "<strong>Upozorenje:</strong> Na stranicu koju želite izbrisati vode [[Special:WhatLinksHere/{{FULLPAGENAME}}|ostale stranice]] ili pak su uključene u nju.",
"deleting-subpages-warning": "<strong>Upozorenje:</strong> Stranica koju želite izbrisati ima [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|podstranicu|$1 podstranice|$1 podstranica|51=preko 50 podstranica}}]].",
"rollback": "Vrati izmjene",
+ "rollback-confirmation-yes": "Vraćanje",
+ "rollback-confirmation-no": "Otkaži",
"rollbacklink": "vrati",
"rollbacklinkcount": "vrati $1 {{PLURAL:$1|izmjenu|izmjene|izmjena}}",
"rollbacklinkcount-morethan": "vrati više od $1 {{PLURAL:$1|izmjene|izmjene|izmjena}}",
"revertpage-nouser": "Vraćene izmjene skrivenog korisnika na posljednju reviziju, koju je {{GENDER:$1|napravio|napravila}} [[User:$1|$1]]",
"rollback-success": "Vraćene su izmjene korisnika {{GENDER:$3|$1}};\nvraćeno na posljednju verziju koju je snimio {{GENDER:$4|$2}}.",
"sessionfailure-title": "Greška u sesiji",
- "sessionfailure": "Izgleda da postoji problem sa vašom sesijom; ova akcija je otkazana kao prevencija protiv napadanja sesija. Kliknite \"back\" (''nazad'') i osvježite stranicu sa koje ste došli, i opet pokušajte.",
+ "sessionfailure": "Izgleda da postoji problem sa vašom sesijom;\nova radnja je otkazana kao prevencija protiv napadanja sesija.\nMolimo ponovno pošaljite obrazac.",
"changecontentmodel": "Promijeni model sadržaja stranice",
"changecontentmodel-legend": "Promijeni model sadržaja",
"changecontentmodel-title-label": "Naslov stranice",
"mycontris": "Doprinosi",
"anoncontribs": "Doprinosi",
"contribsub2": "Za {{GENDER:$3|$1}} ($2)",
+ "contributions-subtitle": "Za {{GENDER:$3|$1}}",
"contributions-userdoesnotexist": "Korisnički račun \"$1\" nije registrovan.",
"negative-namespace-not-supported": "Nisu podržani imenski prostori s negativnim vrijednostima.",
"nocontribs": "Nisu nađene promjene koje zadovoljavaju ove uslove.",
"block": "Blokiraj korisnika",
"unblock": "Odblokiraj korisnika",
"blockip": "Blokiraj {{GENDER:$1|korisnika|korisnicu}}",
- "blockiptext": "Upotrebite donji upitnik da biste uklonili prava pisanja sa određene IP adrese ili korisničkog imena. \nOvo bi trebalo da bude urađeno samo da bi se spriječio vandalizam, i u skladu sa [[{{MediaWiki:Policy-url}}|smjernicama]]. \nUnesite konkretan razlog ispod (na primjer, navodeći koje konkretne stranice su vandalizovane).",
+ "blockiptext": "Upotrebite donji upitnik da biste uklonili prava pisanja sa određene IP adrese ili korisničkog imena. \nOvo bi trebalo da bude urađeno samo da bi se spriječio vandalizam, i u skladu sa [[{{MediaWiki:Policy-url}}|smjernicama]]. \nUnesite konkretan razlog ispod (na primjer, navodeći koje konkretne stranice su vandalizovane).\nMožete blokirati IP-opsege koristeći [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing sintaksu CIDR-a]; najveći dozvoljeni opseg za IPv4 je /$1, a za IPv6 /$2.",
"ipaddressorusername": "IP adresa ili korisničko ime:",
"ipbreason": "Razlog:",
"ipbreason-dropdown": "*Najčešći razlozi blokiranja\n**Unošenje netačnih informacija\n**Uklanjanje sadržaja stranica\n**Postavljanje spam vanjskih linkova\n**Ubacivanje gluposti/grafita\n**Osobni napadi (ili napadačko ponašanje)\n**Čarapare (zloupotreba više korisničkih računa)\n**Neprihvatljivo korisničko ime",
"confirmemail_subject": "{{SITENAME}} adresa e-pošte za potvrdu",
"confirmemail_body": "Neko, vjerovatno Vi, je sa IP adrese $1 registrovao nalog \"$2\" sa ovom adresom e-pošte na {{SITENAME}}.\n\nDa potvrdite da ovaj nalog stvarno pripada vama i da aktivirate mogućnost e-pošte na {{SITENAME}}, otvorite ovaj link u vašem pregledniku:\n\n$3\n\nAko ovo niste vi, pratite ovaj link da prekinete prijavu:\n$5\n\nOvaj kod za potvrdu će isteći u $4.",
"confirmemail_body_changed": "Neko, vjerovatno Vi, je sa IP adrese $1\nje promijenio adresu e-pošte računa \"$2\" na ovu adresu za {{SITENAME}}.\n\nDa potvrdite da ovaj nalog stvarno pripada Vama i da reaktivirate mogućnosti e-pošte na {{SITENAME}}, otvorite ovaj link u Vašem pregledniku:\n\n$3\n\nAko ovaj račun *ne* pripada Vama, pratite ovaj link da prekinete odobravanje adrese e-pošte:\n\n$5\n\nOvaj kod za potvrdu će isteći u $4.",
- "confirmemail_body_set": "Netko, vjerovatno Vi, sa IP adrese $1,\nje postavio e-mail adresu za račun \"$2\" na ovoj adresi za {{SITENAME}}.\n\nDa potvrdite kako ovaj račun uistinu pripada Vama i reaktivirate\ne-mail postavke na {{SITENAME}}, otvoriti ovaj link u vašem pregledniku:\n\n$3\n\nAko račun *ne* pripada Vama, pratite ovaj link\nkako bi poništili potvrdu e-mail adrese:\n\n$5\n\nOvaj kod za potvrdu će isteći u $4.",
+ "confirmemail_body_set": "Netko, vjerovatno Vi, sa IP adrese $1,\nje postavio e-mail adresu za račun \"$2\" na ovoj adresi za {{SITENAME}}.\n\nDa potvrdite kako ovaj račun stvarno pripada Vama i da uključite\nmogućnosti e-pošte na {{SITENAME}}, otvorite ovaj link u Vašem pregledniku:\n\n$3\n\nAko račun *ne* pripada Vama, pratite ovaj link\nkako bi poništili potvrdu:\n\n$5\n\nOvaj kod za potvrdu će isteći u $4.",
"confirmemail_invalidated": "Potvrda e-mail adrese otkazana",
"invalidateemail": "Odustani od e-mail potvrde",
"notificationemail_subject_changed": "Registrirana adresa e-pošte na projektu {{SITENAME}} je smijenjena",
"confirm-unwatch-top": "Izbrisati ovu stranicu sa Vašeg spiska praćenja?",
"confirm-rollback-button": "U redu",
"confirm-rollback-top": "Ukloniti uređivanja na ovoj stranici?",
+ "confirm-rollback-bottom": "Ova će radnja odmah poništiti izabrane promjene ove stranice.",
"confirm-mcrrestore-title": "Povrati reviziju",
"confirm-mcrundo-title": "Otkaži promjenu",
"mcrundofailed": "Otkazivanje nije uspjelo",
"tags-create-invalid-chars": "Nazivi oznaka ne smiju sadržavati zareze (<code>,</code>), uspravne (<code>|</code>) ni kose crte (<code>/</code>).",
"tags-create-invalid-title-chars": "Imena oznaka ne smiju sadržavati znakove što se ne mogu koristiti u naslovima stranica.",
"tags-create-already-exists": "Oznaka \"$1\" već postoji.",
+ "tags-create-warnings-above": "Pokušavajući stvoriti oznaku \"$1\" sam dogodio {{PLURAL:$2|sljedeće upozorenje|sljedeća upozorenja}}:",
+ "tags-create-warnings-below": "Želite li nastaviti stvaranje oznake?",
+ "tags-delete-title": "Izbriši oznaku",
+ "tags-delete-explanation-initial": "Brišete oznaku \"$1\" iz baze podataka.",
+ "tags-delete-explanation-in-use": "Bit će obrisana sa {{PLURAL:$2|jedne verzije ili unosa u zapisniku na kojim|svih $2 verzija i/ili unosa u zapisniku na kojima}} je primijenjena.",
+ "tags-delete-explanation-warning": "Ova radnja je <strong>nepovratna</strong> te se <strong>ne može poništiti</strong>. Ovo ne mogu uraditi čak ni administratori baze podataka. Zato, osigurajte da ovo stvarno je oznaka koju želite obrisati.",
+ "tags-delete-explanation-active": "<strong>Oznaka \"$1\" je i dalje aktivna te u budućnosti će se nastaviti primjenjivati.</strong> Kako biste ovo spriječili, otiđite na mjesto (ili mjesta) na kojima joj je postavljeno da se postavlja i onemogućite je tamo.",
+ "tags-delete-reason": "Razlog:",
+ "tags-delete-submit": "Nepovratno obriši ovu oznaku",
+ "tags-delete-not-allowed": "Oznake zadane dodatkom se ne mogu obrisati, osim ako to nije izričito dopušteno dodatkom.",
+ "tags-delete-not-found": "Oznaka \"$1\" ne postoji.",
+ "tags-delete-too-many-uses": "Oznaka \"$1\" primjenjuje se na više od {{PLURAL:$2|jedne revizije|$2 revizija}}, što znači da se ne može obrisati.",
+ "tags-delete-warnings-after-delete": "Oznaka \"$1\" je obrisana, ali naišao sam na {{PLURAL:$2|sljedeće upozorenje|sljedeća upozorenja}}:",
+ "tags-delete-no-permission": "Nemate dopuštenje da brišete oznake promjena.",
+ "tags-activate-title": "Aktiviraj oznaku",
+ "tags-activate-question": "Aktivirate oznaku \"$1\".",
+ "tags-activate-reason": "Razlog:",
+ "tags-activate-not-allowed": "Ne mogu aktivirati oznaku \"$1\".",
+ "tags-activate-not-found": "Oznaka \"$1\" ne postoji.",
+ "tags-activate-submit": "Aktiviraj",
+ "tags-deactivate-title": "Deaktiviraj oznaku",
+ "tags-deactivate-question": "Deaktivirate oznaku \"$1\".",
+ "tags-deactivate-reason": "Razlog:",
+ "tags-deactivate-not-allowed": "Ne mogu deaktivirati oznaku \"$1\".",
+ "tags-deactivate-submit": "Deaktiviraj",
+ "tags-apply-no-permission": "Nemate dopuštenje da stavljate oznake za promjene zajedno s izmjenama koje pravite.",
+ "tags-apply-blocked": "Ne možete zadavati oznake za promjenu dok {{GENDER:$1|ste}} blokirani.",
+ "tags-apply-not-allowed-one": "Nije dozvoljeno da se oznaka \"$1\" staje ručno.",
+ "tags-apply-not-allowed-multi": "Nije dozvoljeno da se {{PLURAL:$2|sljedeća oznaka staje ručno|sljedeće oznake staju ručno}}: $1",
+ "tags-update-no-permission": "Nemate dopuštenje da dodate ili uklonite oznake promjena sa zasebnih verzija ili zapisničkih unosa.",
+ "tags-update-blocked": "Ne možete dodavati niti uklanjati oznake za promjenu dok {{GENDER:$1|ste}} blokirani.",
+ "tags-update-add-not-allowed-one": "Nije dozvoljeno da se oznaka \"$1\" dodaje ručno.",
+ "tags-update-add-not-allowed-multi": "Nije dozvoljeno da se {{PLURAL:$2|sljedeća oznaka dodaje ručno|sljedeće oznake dodaju ručno}}: $1",
+ "tags-update-remove-not-allowed-one": "Nije dozvoljeno da se uklanja oznaka \"$1\".",
+ "tags-update-remove-not-allowed-multi": "Nije dozvoljeno da se {{PLURAL:$2|sljedeća oznaka uklanja ručno|sljedeće oznake uklanjaju ručno}}: $1",
+ "tags-edit-title": "Mijenjanje oznaka",
+ "tags-edit-manage-link": "Upravljaj oznakama",
+ "tags-edit-revision-selected": "{{PLURAL:$1|Odabrana izmjena|Odabrane izmjene}} [[:$2]]:",
+ "tags-edit-logentry-selected": "{{PLURAL:$1|Odabran unos registra|Odabrani unosi registra}}:",
+ "tags-edit-revision-legend": "Dodajte ili uklonite oznake sa {{PLURAL:$1|ove izmjene|svih $1 izmjena}}",
+ "tags-edit-logentry-legend": "Dodajte ili uklonite oznake sa {{PLURAL:$1|ovog zapisničkog unosa|svih $1 zapisničkih unosa}}",
+ "tags-edit-existing-tags": "Postojeće oznake:",
+ "tags-edit-existing-tags-none": "<em>Nema</em>",
+ "tags-edit-new-tags": "Nove oznake:",
+ "tags-edit-add": "Dodaj ove oznake:",
+ "tags-edit-remove": "Ukloni ove oznake:",
+ "tags-edit-remove-all-tags": "(ukloni sve oznake)",
+ "tags-edit-chosen-placeholder": "Odaberite neke oznake",
+ "tags-edit-chosen-no-results": "Nisam pronašao odgovarajuće oznake",
+ "tags-edit-reason": "Razlog:",
+ "tags-edit-revision-submit": "Primijeni izmjene {{PLURAL:$1|ovoj reviziji|svim $1 revizijama}}",
+ "tags-edit-logentry-submit": "Primijeni izmjene {{PLURAL:$1|ovom zapisničkom unosu|svim $1 zapisničkim unosima}}",
+ "tags-edit-success": "Izmjene su primijenjene.",
+ "tags-edit-failure": "Nisam mogao primijeniti izmjene:\n$1",
+ "tags-edit-nooldid-title": "Nevažeća odredišna revizija",
+ "tags-edit-nooldid-text": "Niste naveli odredišnu verziju na koju treba primijeniti izmjene, ili pak navedena verzija na postoji.",
+ "tags-edit-none-selected": "Odaberite bar jednu oznaku koju treba dodati ili ukloniti.",
"comparepages": "Usporedi stranice",
"compare-page1": "Stranica 1",
"compare-page2": "Stranica 2",
"suppressionlogtext": "Испод се налази списак брисања и блокирања који укључује садржај сакривен од администратора. Погледајте [[Special:BlockList|списак блокирања]] за списак текућих забрана и блокирања.",
"mergehistory": "Обједињавање историја странице",
"mergehistory-header": "Ова страница вам допушта да обједините историју измена неке изворне странице у новију страницу.\nУверите се да ће ова промена оставити непромењен садржај историје странице.",
- "mergehistory-box": "Обједини измене двеју страница:",
+ "mergehistory-box": "Обједињавање измена двеју страница:",
"mergehistory-from": "Изворна страница:",
"mergehistory-into": "Одредишна страница:",
"mergehistory-list": "Обједињива историја измена",
"tags-deactivate-question": "Деактивирате ознаку „$1“.",
"tags-deactivate-reason": "Разлог:",
"tags-deactivate-not-allowed": "Није могуће деактивирати ознаку „$1”.",
- "tags-deactivate-submit": "Ð\94екативирај",
+ "tags-deactivate-submit": "Ð\94еактивирај",
"tags-apply-no-permission": "Немате дозволу да примените ознаке промена заједно са својим променама.",
"tags-apply-blocked": "Не можете да примените ознаке промена заједно са вашим променама све док {{GENDER:$1|сте}} блокирани.",
"tags-update-no-permission": "Немате дозволу да додате или уклоните ознаке промена из појединачних измена или уноса у дневнику.",
"page_first": "перша",
"page_last": "остання",
"histlegend": "Вибір версії: позначте у кружечках версії для порівняння і натисніть «Enter» або кнопку внизу.<br />\nПояснення: <strong>({{int:cur}})</strong> = відмінності від поточної версії, <strong>({{int:last}})</strong> = відмінності від попередньої версії, <strong>{{int:minoreditletter}}</strong> = незначне редагування.",
- "history-fieldset-title": "Ð\9fоÑ\88Ñ\83к веÑ\80Ñ\81Ñ\96й",
+ "history-fieldset-title": "ФÑ\96лÑ\8cÑ\82Ñ\80Ñ\83ваÑ\82и веÑ\80Ñ\81Ñ\96Ñ\97",
"history-show-deleted": "Лише вилучені версії",
"histfirst": "найдавніші",
"histlast": "найновіші",
"historysize": "($1 {{PLURAL:$1|байт|байти|байтів}})",
- "historyempty": "(порожня)",
+ "historyempty": "порожня",
"history-feed-title": "Історія редагувань",
"history-feed-description": "Історія редагувань цієї сторінки в вікі",
"history-feed-item-nocomment": "$1 в $2",
"action-changetags": "додавання або вилучення будь-яких міток для певних версій сторінок або записів журналів",
"action-deletechangetags": "вилучення міток з бази даних",
"action-purge": "очищення кешу цієї сторінки",
+ "action-apihighlimits": "використання вищих лімітів у API-запитах",
+ "action-autoconfirmed": "без обмежень швидкості за IP",
+ "action-bigdelete": "вилучення сторінок з великою історією",
+ "action-blockemail": "блокування користувача від надсилання електронної пошти",
+ "action-bot": "вважатися автоматичним процесом",
+ "action-editprotected": "редагування сторінок з рівнем захисту «{{int:protect-level-sysop}}»",
+ "action-editsemiprotected": "редагування сторінок з рівнем захисту «{{int:protect-level-autoconfirmed}}»",
+ "action-editinterface": "редагування інтерфейсу користувача",
+ "action-editusercss": "редагування CSS-файлів інших користувачів",
+ "action-edituserjson": "редагування JSON-файлів інших користувачів",
+ "action-edituserjs": "редагування JavaScript-файлів інших користувачів",
+ "action-editsitecss": "редагування загального CSS",
+ "action-editsitejson": "редагування загального JSON",
+ "action-editsitejs": "редагування загального JavaScript",
+ "action-editmyusercss": "редагування власних CSS-файлів користувача",
+ "action-editmyuserjson": "редагування власних JSON-файлів користувача",
+ "action-editmyuserjs": "редагування власних JavaScript-файлів користувача",
+ "action-viewsuppressed": "перегляд змін, прихованих від усіх користувачів",
+ "action-hideuser": "блокування імені користувача і приховування його",
+ "action-ipblock-exempt": "уникнення IP-блокування, автоблокування і блокування діапазонів",
+ "action-unblockself": "розблоковування себе",
+ "action-noratelimit": "звільнення від обмежень швидкості",
+ "action-reupload-own": "перезаписування існуючих файлів, завантажених тим самим користувачем",
+ "action-nominornewtalk": "незначні редагування на сторінках обговорень користувачів не викликають попередження про нові повідомлення",
+ "action-markbotedits": "позначення відкинутих редагувань як редагування бота",
+ "action-patrolmarks": "перегляд позначок патрулювання у нових редагуваннях",
+ "action-override-export-depth": "експорт сторінок, включаючи пов'язані сторінки з глибиною до 5",
+ "action-suppressredirect": "нестворення перенаправлення зі старої назви на нову при перейменуванні сторінки",
"nchanges": "$1 {{PLURAL:$1|зміна|зміни|змін}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|зміна з останнього візиту|зміни з останнього візиту|змін з останнього візиту}}",
"enhancedrc-history": "історія",
"delete-confirm": "Вилучення «$1»",
"delete-legend": "Вилучення",
"historywarning": "<strong>Попередження:</strong> Сторінка, яку ви збираєтеся вилучити, має історію редагувань з приблизно $1 {{PLURAL:$1|1=версії|версій}}:",
- "historyaction-submit": "Показати",
+ "historyaction-submit": "Показати версії",
"confirmdeletetext": "Ви збираєтесь вилучити сторінку і всі її журнали редагувань з бази даних.\nБудь ласка, підтвердіть, що Ви бажаєте зробити це, повністю розумієте наслідки і що робите це у відповідності з [[{{MediaWiki:Policy-url}}|правилами]].",
"actioncomplete": "Дію виконано",
"actionfailed": "Виконати дію не вдалося",
"history-feed-description": "響哩個wiki嘅哩一頁嘅修訂歷史",
"history-feed-item-nocomment": "$1 響 $2",
"history-feed-empty": "要求嘅頁面並唔存在。\n佢可能響哩個 wiki 度刪除咗或者改咗名。\n試吓[[Special:Search|響哩個wiki度搵]]有關新頁面嘅資料。",
- "history-edit-tags": "編輯已經揀咗嘅分頁",
+ "history-edit-tags": "改揀咗嘅改動嘅標籤",
"rev-deleted-comment": "(編輯摘要已經移除咗)",
"rev-deleted-user": "(用戶名已經移除咗)",
"rev-deleted-event": "(日誌詳情已經移除咗)",
"page_first": "首页",
"page_last": "末页",
"histlegend": "差异选择:选中要对比的版本的单选按钮,按Enter键或下方的按钮。<br />说明:<strong>({{int:cur}})</strong>=与最后版本之间的差异,<strong>({{int:last}})</strong>=与上一版本之间的差异,<strong>{{int:minoreditletter}}</strong>=小编辑。",
- "history-fieldset-title": "搜索修订版本",
+ "history-fieldset-title": "筛选修订版本",
"history-show-deleted": "仅限修订版本删除",
"histfirst": "最旧",
"histlast": "最新",
"historysize": "($1字节)",
- "historyempty": "(空)",
+ "historyempty": "空",
"history-feed-title": "版本历史",
"history-feed-description": "本wiki的该页面的版本历史",
"history-feed-item-nocomment": "$2 $1",
"right-reupload-own": "覆盖自己上传的文件",
"right-reupload-shared": "本地覆盖共享文件库的文件",
"right-upload_by_url": "从URL上传文件",
- "right-purge": "æ\97 确认æ¸\85é\99¤é¡µé\9d¢ç¼\93å\98",
+ "right-purge": "清除页面缓存",
"right-autoconfirmed": "不受基于IP的速率限制",
"right-bot": "被视为自动程序",
"right-nominornewtalk": "不使小编辑在讨论页面引发新信息提示",
"action-changetags": "在个别修订和日志记录中添加和移除任意标签",
"action-deletechangetags": "从数据库删除标签",
"action-purge": "刷新此页面",
+ "action-apihighlimits": "在API查询中使用更高的上限",
+ "action-autoconfirmed": "不受基于IP的速率限制",
+ "action-bigdelete": "删除有大型历史的页面",
+ "action-blockemail": "阻止用户发送电子邮件",
+ "action-bot": "被视为自动程序",
+ "action-editprotected": "编辑保护级别为“{{int:protect-level-sysop}}”的页面",
+ "action-editsemiprotected": "编辑保护级别为“{{int:protect-level-autoconfirmed}}”的页面",
+ "action-editinterface": "编辑用户界面",
+ "action-editusercss": "编辑其他用户的CSS文件",
+ "action-edituserjson": "编辑其他用户的JSON文件",
+ "action-edituserjs": "编辑其他用户的JavaScript文件",
+ "action-editsitecss": "编辑全站CSS",
+ "action-editsitejson": "编辑全站JSON",
+ "action-editsitejs": "编辑全站JavaScript",
+ "action-editmyusercss": "编辑您的用户CSS文件",
+ "action-editmyuserjson": "编辑您的用户JSON文件",
+ "action-editmyuserjs": "编辑您的用户JavaScript文件",
+ "action-viewsuppressed": "查看被隐藏的任何用户的修订",
+ "action-hideuser": "封禁并隐藏用户名",
+ "action-ipblock-exempt": "绕过IP封禁、自动封禁和段封禁",
+ "action-unblockself": "自我解封",
+ "action-noratelimit": "不受速率限制影响",
+ "action-reupload-own": "覆盖自己上传的文件",
+ "action-nominornewtalk": "不使小编辑在讨论页面引发新信息提示",
+ "action-markbotedits": "标记回退编辑为机器人编辑",
+ "action-patrolmarks": "查看最近更改的巡查标记",
+ "action-override-export-depth": "导出页面,包括最多5层链接",
+ "action-suppressredirect": "移动页面时不创建来源页面的重定向",
"nchanges": "$1次更改",
"enhancedrc-since-last-visit": "{{PLURAL:$1|自上次访问}}有$1次",
"enhancedrc-history": "历史",
"delete-confirm": "删除“$1”",
"delete-legend": "删除",
"historywarning": "<strong>警告:</strong>您将要删除的页面有约$1次{{PLURAL:$1|修订}}的历史:",
- "historyaction-submit": "显示",
+ "historyaction-submit": "显示版本",
"confirmdeletetext": "您即将删除一个页面或图像以及其历史。请确定您要进行此项操作,并且了解其后果,同时您的行为符合[[{{MediaWiki:Policy-url}}|方针]]。",
"actioncomplete": "操作完成",
"actionfailed": "操作失败",
"page_first": "第一頁",
"page_last": "最後頁",
"histlegend": "比對選擇的版本差異:選擇要比對修訂版本的單選方塊並點選底部的按鈕進行比對。<br />\n符號說明:<strong>({{int:cur}})</strong> = 與最新的修訂版本比對,<strong>({{int:last}})</strong> = 與前一筆修訂版本比對,<strong>{{int:minoreditletter}}</strong> = 次要修訂。",
- "history-fieldset-title": "搜尋修訂",
+ "history-fieldset-title": "篩選修訂",
"history-show-deleted": "只顯示已刪除的修訂",
"histfirst": "最舊",
"histlast": "最新",
"rev-suppressed-text-view": "此頁面修訂已被 <strong>禁止顯示</strong>。\n您可繼續檢視修訂,可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。",
"rev-deleted-no-diff": "因頁面的其中一次修訂已被 <strong>刪除</strong>,您無法檢視差異。\n可至 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌] 取得詳細資訊。",
"rev-suppressed-no-diff": "因頁面的其中一次修訂已被 <strong>刪除</strong>,您無法檢視差異。",
- "rev-deleted-unhide-diff": "檢視差異的其中一個修訂已被 <strong>刪除</strong>。\n可至 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌] 取得詳細資料。\n若您要繼續,您仍可以 [$1 檢視此差異]。",
+ "rev-deleted-unhide-diff": "檢視差異的其中一個修訂已被 <strong>刪除</strong>。可至[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌]取得詳細資料。若您要繼續,您仍可以[$1 檢視此差異]。",
"rev-suppressed-unhide-diff": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。\n若您要繼續,您仍可以 [$1 檢視此差異]。",
"rev-deleted-diff-view": "檢視差異的其中一個修訂已被 <strong>刪除</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌] 取得詳細資訊。",
"rev-suppressed-diff-view": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。",
"right-suppressredirect": "移動頁面時不在原頁面位置建立重新導向頁面",
"right-upload": "上傳檔案",
"right-reupload": "覆蓋現有的檔案",
- "right-reupload-own": "覆蓋自己上傳的檔案",
+ "right-reupload-own": "覆蓋自己上傳的現有檔案",
"right-reupload-shared": "覆蓋共用媒體檔案庫於本地的檔案",
"right-upload_by_url": "使用 URL 上傳檔案",
"right-purge": "清除頁面的站台快取",
"right-block": "封鎖其他使用者的編輯權限",
"right-blockemail": "封鎖使用者傳送電子郵件",
"right-hideuser": "封鎖使用者名稱,避免公開顯示",
- "right-ipblock-exempt": "略過 IP 封鎖、自動封鎖及範圍封鎖檢查",
+ "right-ipblock-exempt": "略過IP封鎖、自動封鎖及範圍封鎖檢查",
"right-unblockself": "解除封鎖自己",
"right-protect": "更改保護層級及編輯被連鎖保護的頁面",
"right-editprotected": "編輯保護層級為「{{int:protect-level-sysop}}」的頁面",
"right-editmyprivateinfo": "編輯自己的隱私資料 (如:電子郵件地址及真實姓名)",
"right-editmyoptions": "編輯自己的偏好設定",
"right-rollback": "快速回退最後一位使用者對某一頁面的編輯",
- "right-markbotedits": "標示還原編輯為機械人編輯",
+ "right-markbotedits": "標示還原編輯為機器人編輯",
"right-noratelimit": "不受使用頻率限制",
"right-import": "由其他 Wiki 匯入頁面",
"right-importupload": "由檔案上傳匯入頁面",
"right-userrights": "編輯所有使用者的權限",
"right-userrights-interwiki": "編輯使用者在其它 Wiki 上的權限",
"right-siteadmin": "鎖定和解除鎖定資料庫",
- "right-override-export-depth": "匯出頁面包含連結內容,深度上限為 5 層",
+ "right-override-export-depth": "匯出頁面包含連結內容,深度上限為5層",
"right-sendemail": "傳送電子郵件聯絡其他使用者",
"right-managechangetags": "建立並自資料庫 (取消) 啟用 [[Special:Tags|標籤]]",
"right-applychangetags": "連同自己的變更一起套用[[Special:Tags|標籤]]",
"action-changetags": "加入與移除任何於各別修訂與日誌項目的標籤",
"action-deletechangetags": "從資料庫刪除標籤",
"action-purge": "清除此頁面",
+ "action-apihighlimits": "提高API查詢上限值",
+ "action-autoconfirmed": "不受基於IP的使用頻率限制",
+ "action-bigdelete": "刪除有大量歷史記錄的頁面",
+ "action-blockemail": "封鎖使用者傳送電子郵件",
+ "action-bot": "將其視為自動程序",
+ "action-editprotected": "編輯保護層級為「{{int:protect-level-sysop}}」的頁面",
+ "action-editsemiprotected": "編輯保護層級為「{{int:protect-level-autoconfirmed}}」的頁面",
+ "action-editinterface": "編輯使用者介面",
+ "action-editusercss": "編輯其他使用者的 CSS 檔",
+ "action-edituserjson": "編輯其他使用者的 JSON 檔",
+ "action-edituserjs": "編輯其他使用者的 JavaScript 檔",
+ "action-editsitecss": "編輯全站 CSS",
+ "action-editsitejson": "編輯全站 JSON",
+ "action-editsitejs": "編輯全站 JavaScript",
+ "action-editmyusercss": "編輯您自己的使用者 CSS 檔",
+ "action-editmyuserjson": "編輯您自己的使用者 JSON 檔",
+ "action-editmyuserjs": "編輯自己的 JavaScript 檔",
+ "action-viewsuppressed": "檢視所有使用者隱藏的修訂",
+ "action-hideuser": "封鎖使用者名稱,避免公開顯示",
+ "action-ipblock-exempt": "略過IP封鎖、自動封鎖及範圍封鎖檢查",
+ "action-unblockself": "解除封鎖自己",
+ "action-noratelimit": "不受使用頻率限制",
+ "action-reupload-own": "覆蓋自己上傳的現有檔案",
+ "action-nominornewtalk": "不顯示討論頁面中次要編輯的新訊息提示",
+ "action-markbotedits": "標示還原編輯為機器人編輯",
+ "action-patrolmarks": "檢視近期變更的巡查標記",
+ "action-override-export-depth": "匯出頁面包含連結內容,深度上限為5層",
+ "action-suppressredirect": "移動頁面時不在原頁面位置建立重新導向頁面",
"nchanges": "$1 次變更",
"enhancedrc-since-last-visit": "{{PLURAL:$1|自上次拜訪}}有$1次",
"enhancedrc-history": "歷史",
"delete-confirm": "刪除 \"$1\"",
"delete-legend": "刪除",
"historywarning": "<strong>警告:</strong>您正要刪除的頁面內含 $1 次{{PLURAL:$1|的修訂}}歷史:",
- "historyaction-submit": "顯示",
+ "historyaction-submit": "顯示修訂",
"confirmdeletetext": "您正要刪除一個頁面或圖片以及其所有歷史。請確定您要進行此操作,並了解其後果,同時您的行為符合[[{{MediaWiki:Policy-url}}|方針]]。",
"actioncomplete": "操作完成",
"actionfailed": "操作失敗",
"logentry-newusers-autocreate": "已自動{{GENDER:$2|建立}}使用者帳號 $1",
"logentry-protect-move_prot": "$1 {{GENDER:$2|已移動}}保護設定從 $4 至 $3",
"logentry-protect-unprotect": "$1 {{GENDER:$2|已移除}} $3 的保護",
- "logentry-protect-protect": "$1 {{GENDER:$2|pó-hō͘ liáu}} $3 $4",
+ "logentry-protect-protect": "$1 {{GENDER:$2|已保護}} $3 $4",
"logentry-protect-protect-cascade": "$1 {{GENDER:$2|已保護}} $3 $4 [連鎖]",
"logentry-protect-modify": "$1 {{GENDER:$2|已變更}} $3 的保護層級 $4",
"logentry-protect-modify-cascade": "$1 {{GENDER:$2|已變更}} $3 的保護層級 $4 [連鎖]",
$maintClass = false;
+// Some extensions rely on MW_INSTALL_PATH to find core files to include. Setting it here helps them
+// if they're included by a core script (like DatabaseUpdater) after Maintenance.php has already
+// been run.
+if ( strval( getenv( 'MW_INSTALL_PATH' ) ) === '' ) {
+ putenv( 'MW_INSTALL_PATH=' . realpath( __DIR__ . '/..' ) );
+}
+
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
* their own constructors
*/
public function __construct() {
- // Setup $IP, using MW_INSTALL_PATH if it exists
global $IP;
- $IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
- ? getenv( 'MW_INSTALL_PATH' )
- : realpath( __DIR__ . '/..' );
+ $IP = getenv( 'MW_INSTALL_PATH' );
$this->addDefaultParams();
register_shutdown_function( [ $this, 'outputChanneled' ], false );
}
/**
- * @var $child Maintenance
+ * @var Maintenance $child
*/
$child = new $maintClass();
$child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
if ( $wgDBservers ) {
/**
- * @var $wgDBservers array
+ * @var array $wgDBservers
*/
foreach ( $wgDBservers as $i => $server ) {
$wgDBservers[$i]['user'] = $wgDBuser;
$this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
$found = false;
foreach ( $wgLocalDatabases as $wikiID ) {
- /** @var $dbr Database */
+ /** @var Database $dbr */
$dbr = $this->getDB( DB_REPLICA, [], $wikiID );
foreach ( $protConds as $conds ) {
// Clean up spam on this wiki
$count = 0;
- /** @var $dbr Database */
+ /** @var Database $dbr */
$dbr = $this->getDB( DB_REPLICA );
foreach ( $protConds as $prot => $conds ) {
$res = $dbr->select(
<?php
/**
* Router for the php cli-server built-in webserver.
- * https://secure.php.net/manual/en/features.commandline.webserver.php
+ * https://www.php.net/manual/en/features.commandline.webserver.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
VER=5.6.32
TAR="php-$VER.tar.gz"
-PHPURL="https://secure.php.net/get/$TAR/from/this/mirror"
+PHPURL="https://www.php.net/get/$TAR/from/this/mirror"
cd "$DEV"
// Custom priorities
if ( $wgSitemapNamespacesPriorities !== false ) {
/**
- * @var $wgSitemapNamespacesPriorities array
+ * @var array $wgSitemapNamespacesPriorities
*/
foreach ( $wgSitemapNamespacesPriorities as $namespace => $priority ) {
$float = floatval( $priority );
);
foreach ( $res as $row ) {
/**
- * @var $flags int
+ * @var int $flags
*/
$flags = $row->old_flags;
$id = $row->old_id;
global $wgDBname;
if ( !function_exists( "gzdeflate" ) ) {
$this->fatalError( "You must enable zlib support in PHP to compress old revisions!\n" .
- "Please see https://secure.php.net/manual/en/ref.zlib.php\n" );
+ "Please see https://www.php.net/manual/en/ref.zlib.php\n" );
}
$type = $this->getOption( 'type', 'concat' );
* Dispatch a command to the next available replica DB.
* This may block until a replica DB finishes its work and becomes available.
*/
- function dispatch( /*...*/ ) {
- $args = func_get_args();
+ function dispatch( ...$args ) {
$pipes = $this->replicaPipes;
$x = [];
$y = [];
* Copy of CC standard stylesheet, plus tweaks for iframe usage
*/
+/* stylelint-disable selector-class-pattern */
+
body {
margin: 0;
background: #eee;
+/* stylelint-disable selector-class-pattern */
+
.env-check {
font-size: 90%;
margin: 1em 0 1em 2.5em;
@import 'mediawiki.mixins';
+/* stylelint-disable selector-class-pattern */
+
/* Table Sorting */
.client-js .sortable:not( .jquery-tablesorter ) > thead > :last-of-type > th:not( .unsortable ),
+/* stylelint-disable selector-class-pattern */
+
.tipsy {
padding: 5px;
position: absolute;
+/* stylelint-disable selector-class-pattern */
+
.jquery-confirmable-button {
/* Automatically flipped */
margin-left: 1ex;
/* suggestions plugin */
+/* stylelint-disable selector-class-pattern */
+
.suggestions {
overflow: hidden;
position: absolute;
/* Show/hide animation is incorrect if the table has a margin set. Extra
* ".wikitable" is needed in the selector for CSS specificity. */
+/* stylelint-disable-next-line selector-class-pattern */
.wikitable.preview-limit-report {
margin: 0;
}
* Styles for elements of the editing form.
*/
+/* stylelint-disable selector-class-pattern */
+
/*
* Add a bit of margin space between the preview and the toolbar.
* This replaces the ugly <p><br /></p> we used to insert into the page source
+/* stylelint-disable selector-class-pattern */
+
/* Styles for the JavaScript enhancements of the history page */
#pagehistory li.before input[ name='oldid' ],
* Basic styles for the edit revision history page 'HistoryAction.php'
*/
+/* stylelint-disable selector-class-pattern */
+
// Trigger only when collapsible & JS is available via `.mw-collapsed`.
#mw-history-search.mw-collapsed .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label {
margin-bottom: 0;
}
}
+/* stylelint-disable-next-line selector-class-pattern */
.redirect-in-category {
font-style: italic;
}
* File description page
*/
+/* stylelint-disable selector-class-pattern */
+
.mw-filepage-resolutioninfo {
font-size: smaller;
}
}
@media print {
+ /* stylelint-disable-next-line selector-class-pattern */
.mw_metadata .mw-metadata-show-hide-extended {
display: none;
}
@import 'mediawiki.mixins';
+/* stylelint-disable selector-class-pattern */
+
.postedit-container {
margin: 0 auto;
position: fixed;
+/* stylelint-disable-next-line selector-class-pattern */
.postedit {
top: 6em;
}
* Display neat icons on redirect pages.
*/
+/* stylelint-disable selector-class-pattern */
+
/* Hide, but keep accessible for screen-readers. */
.redirectMsg p {
overflow: hidden;
+/* stylelint-disable selector-class-pattern */
+
.apihelp-header {
clear: both;
margin-bottom: 0.1em;
+/* stylelint-disable selector-class-pattern */
+
.mw-special-ApiHelp h1.firstHeading {
display: none;
}
padding: 0.5em 1em;
}
+/* TODO: Remove this old class once the content caches have cleared */
+/* stylelint-disable-next-line selector-class-pattern */
.mw-json .value,
+.mw-json-value,
.mw-json-single-value {
background-color: #dcfae3;
font-family: monospace, monospace;
border-bottom: 1px solid #eee;
word-wrap: break-word;
+ /* stylelint-disable-next-line selector-class-pattern */
&.nr {
text-align: right;
}
+ /* stylelint-disable-next-line selector-class-pattern */
span.stats {
color: #727272;
}
cursor: pointer;
}
+ /* stylelint-disable-next-line selector-class-pattern */
&.current {
background-color: #dedede;
}
* Diff rendering
*/
+/* stylelint-disable selector-class-pattern */
+
.diff {
border: 0;
border-spacing: 4px;
+/* stylelint-disable selector-class-pattern */
/*!
* Diff rendering
*/
/* Styles for links to RSS/Atom feeds in sidebar */
+/* stylelint-disable-next-line selector-class-pattern */
a.feedlink {
/* SVG support using a transparent gradient to guarantee cross-browser
* compatibility (browsers able to understand gradient syntax support also SVG).
@import 'mediawiki.ui/variables';
+/* stylelint-disable selector-class-pattern */
+
// Increase the area of the button, so that the user can move the mouse cursor
// to the popup without the popup disappearing. (T157544)
.mediawiki-filewarning-anchor {
* Stylesheet for mediawiki.hlist module
* @author [[User:Edokter]]
*/
+
+/* stylelint-disable selector-class-pattern */
+
/* Generate interpuncts */
.hlist dt:after {
content: ':';
+/* stylelint-disable-next-line selector-class-pattern */
.hlist {
dl,
ol,
@import 'mediawiki.mixins';
+/* stylelint-disable selector-class-pattern */
+
// OOUIHTMLForm styles
@ooui-font-size-browser: 16; // assumed browser default of `16px`
@ooui-font-size-base: 0.875em; // equals `14px` at browser default of `16px`
content: '. .';
}
+/* stylelint-disable-next-line selector-class-pattern */
.comment--without-parentheses,
.mw-changeslist-links,
.mw-diff-bytes,
* Copyright Alexander Limi
*/
+/* stylelint-disable selector-class-pattern */
+
/**
* Hide all the elements irrelevant for printing
* Skins however can and should override.
* CologneBlue, the old pre-Monobook skins
*/
+/* stylelint-disable selector-class-pattern */
+
/* For clarity, explicitly state some recommendations from
* https://www.w3.org/TR/CSS21/sample.html to make sure the editsection links scale right
*/
* blocking CSS common to all pages.
*/
+/* stylelint-disable selector-class-pattern */
+
/* GENERAL CLASSES FOR DIRECTIONALITY SUPPORT */
/**
+/* stylelint-disable selector-class-pattern */
+
/* Galleries */
/* These display attributes look nonsensical, but are needed to support IE and FF2 */
/* Don't forget to update gallery.print.css */
+/* stylelint-disable selector-class-pattern */
li.gallerybox {
vertical-align: top;
display: inline-block;
* in MediaWiki (used e.g. on Special:ListFiles).
*/
+/* stylelint-disable selector-class-pattern */
+
@import 'mediawiki.mixins';
// TablePager uses `.mw-datatable` and is loaded in the right order by RL
@import 'mediawiki.ui/variables';
@import 'mw.rcfilters.mixins';
+/* stylelint-disable selector-class-pattern */
+
@rcfilters-spinner-size: 12px;
@rcfilters-head-min-height: 210px;
@rcfilters-head-margin-bottom: 20px;
+/* stylelint-disable selector-class-pattern */
+
/* Make sure the links are not underlined or colored, ever. */
/* There is already a :focus / :hover indication on the <div>. */
.suggestions a.mw-searchSuggest-link,
* (ie: the CSS classing built into the system), like the TOC.
*/
+/* stylelint-disable selector-class-pattern */
+
/* Table of Contents */
.toc,
.mw-warning,
* Icons and colors for external links.
*/
+/* stylelint-disable selector-class-pattern */
+
@import 'mediawiki.mixins';
.mw-parser-output a.external,
* Style Parsoid HTML+RDFa output consistent with wikitext from PHP parser.
*/
+/* stylelint-disable selector-class-pattern */
+
/*
* Auto-numbered external links
* Parsoid renders those as link without content, and lets CSS do the
* This style sheet is used by the Monobook and Vector skins.
*/
+/* stylelint-disable selector-class-pattern */
+
/* Links */
a {
text-decoration: none;
* they are outputted by the actual MonoBook/Vector code by convention.
*/
+/* stylelint-disable selector-class-pattern */
+
/* Categories */
.catlinks {
border: 1px solid #a2a9b1;
min-width: 6em;
}
+/* stylelint-disable-next-line selector-class-pattern */
.apihelp-deprecated {
font-weight: bold;
color: #d33;
}
+/* stylelint-disable-next-line selector-class-pattern */
.apihelp-deprecated-value .oo-ui-labelElement-label {
text-decoration: line-through;
}
}
/* Show/hide arrows in enhanced changeslist */
+/* stylelint-disable-next-line selector-class-pattern */
.mw-enhanced-rc .collapsible-expander {
float: none;
}
font-weight: bold;
}
+/* stylelint-disable-next-line selector-class-pattern */
span.changedby {
font-size: 95%;
}
@import 'mediawiki.ui/variables.less';
@import 'mediawiki.mixins';
+/* stylelint-disable selector-class-pattern */
+
.mw-searchresults-has-iw {
.iw-headline {
font-weight: bold;
/* Special:Search */
+/* stylelint-disable selector-class-pattern */
+
/*
* Fixes sister projects box moving down the extract
* of the first result (T18886).
margin-top: 6px;
}
+/* FIXME: These should be namespaced to mw-ext-confirmedit-fancycaptcha-, and really shouldn't be in core at all */
+/* stylelint-disable-next-line selector-class-pattern */
.fancycaptcha-captcha-container {
background-color: #f8f9fa;
margin-bottom: 15px;
}
/* Put a border around the fancycaptcha-image-container. */
+/* stylelint-disable-next-line selector-class-pattern */
.fancycaptcha-captcha-and-reload {
border: 1px solid #c8ccd1;
border-radius: 2px 2px 0 0;
background-color: #fff;
}
+/* stylelint-disable-next-line selector-class-pattern */
.fancycaptcha-captcha-container .mw-ui-input {
margin-top: -1px;
border-color: #c8ccd1;
}
/* Make the fancycaptcha-image-container full-width within its parent. */
+/* stylelint-disable-next-line selector-class-pattern */
.fancycaptcha-image-container {
width: 100%;
}
margin-bottom: 30px;
}
+/* stylelint-disable-next-line selector-class-pattern */
.mw-number-text.icon-edits {
/* @embed */
background: url( images/icon-edits.png ) no-repeat left center;
}
+/* stylelint-disable-next-line selector-class-pattern */
.mw-number-text.icon-pages {
/* @embed */
background: url( images/icon-pages.png ) no-repeat left center;
}
+/* stylelint-disable-next-line selector-class-pattern */
.mw-number-text.icon-contributors {
/* @embed */
background: url( images/icon-contributors.png ) no-repeat left center;
* Styles for Special:MovePage
*/
+/* stylelint-disable-next-line selector-class-pattern */
.movepage-wrapper {
width: 50em;
}
*/
/* Distinguish actual data from information about it being hidden visually. */
+/* stylelint-disable-next-line selector-class-pattern */
.prop-value-hidden {
font-style: italic;
}
*/
@import 'mediawiki.mixins';
+/* stylelint-disable selector-class-pattern */
+
/* Special:AllMessages */
/* Visually hide repeating text, but leave in for better form navigation on screen readers */
.mw-special-Allmessages .mw-htmlform-ooui .oo-ui-fieldsetLayout:first-child .oo-ui-fieldsetLayout-header {
/* This style is loaded on all media. */
/* Hide the content of the TOC when the checkbox is checked. */
+/* stylelint-disable-next-line selector-class-pattern */
.toctogglecheckbox:checked ~ ul {
display: none;
}
/* Hide the complete TOC on print when the TOC is hidden. */
+/* stylelint-disable-next-line selector-class-pattern */
.toctogglecheckbox:checked + .toctitle {
display: none;
}
/* This style adds a toggle button with internationalized message for the TOC. */
+/* stylelint-disable selector-class-pattern */
+
/* When the browser supports :checked then overwrite the style="display:none" and make the */
/* checkbox invisible on another way to allow to focus the checkbox with keyboard. */
:not( :checked ) > .toctogglecheckbox {
@import 'mediawiki.mixins';
@import 'mediawiki.ui/variables';
+/* stylelint-disable selector-class-pattern */
+
// Buttons
// Helper mixins
// Primary buttons mixin
@import 'mediawiki.mixins';
@import 'mediawiki.ui/variables';
+/* stylelint-disable selector-class-pattern */
+
// --------------------------------------------------------------------------
// Layouts
// --------------------------------------------------------------------------
* Utility function for execute()
*
* @ignore
- * @param {string} [media] Media attribute
* @param {string} url URL
+ * @param {string} [media] Media attribute
+ * @param {Node|null} [nextNode]
*/
- function addLink( media, url ) {
+ function addLink( url, media, nextNode ) {
var el = document.createElement( 'link' );
el.rel = 'stylesheet';
// see #addEmbeddedCSS, T33676, T43331, and T49277 for details.
el.href = url;
- if ( marker && marker.parentNode ) {
- marker.parentNode.insertBefore( el, marker );
+ if ( nextNode && nextNode.parentNode ) {
+ nextNode.parentNode.insertBefore( el, nextNode );
} else {
document.head.appendChild( el );
}
for ( i = 0; i < value.length; i++ ) {
if ( key === 'bc-url' ) {
// back-compat: { <media>: [url, ..] }
- addLink( media, value[ i ] );
+ addLink( value[ i ], media, marker );
} else if ( key === 'css' ) {
// { "css": [css, ..] }
addEmbeddedCSS( value[ i ], cssHandle() );
for ( media in value ) {
urls = value[ media ];
for ( i = 0; i < urls.length; i++ ) {
- addLink( media, urls[ i ] );
+ addLink( urls[ i ], media, marker );
}
}
}
* "text/javascript"; if no type is provided, text/javascript is assumed.
*/
load: function ( modules, type ) {
- var l;
-
- // Allow calling with a url or single dependency as a string
- if ( typeof modules === 'string' ) {
- // "https://example.org/x.js", "http://example.org/x.js", "//example.org/x.js", "/x.js"
- if ( /^(https?:)?\/?\//.test( modules ) ) {
- if ( type === 'text/css' ) {
- l = document.createElement( 'link' );
- l.rel = 'stylesheet';
- l.href = modules;
- document.head.appendChild( l );
- return;
- }
- if ( type === 'text/javascript' || type === undefined ) {
- addScript( modules );
- return;
- }
+ if ( typeof modules === 'string' && /^(https?:)?\/?\//.test( modules ) ) {
+ // Called with a url like so:
+ // - "https://example.org/x.js"
+ // - "http://example.org/x.js"
+ // - "//example.org/x.js"
+ // - "/x.js"
+ if ( type === 'text/css' ) {
+ addLink( modules );
+ } else if ( type === 'text/javascript' || type === undefined ) {
+ addScript( modules );
+ } else {
// Unknown type
throw new Error( 'type must be text/css or text/javascript, found ' + type );
}
- // Called with single module
- modules = [ modules ];
+ } else {
+ // One or more modules
+ modules = typeof modules === 'string' ? [ modules ] : modules;
+ // Resolve modules into flat list for internal queuing.
+ // This also filters out unknown modules and modules with
+ // unknown dependencies, allowing the rest to continue. (T36853)
+ enqueue( resolveStubbornly( modules ), undefined, undefined );
}
-
- // Resolve modules into flat list for internal queuing.
- // This also filters out unknown modules and modules with
- // unknown dependencies, allowing the rest to continue. (T36853)
- enqueue( resolveStubbornly( modules ), undefined, undefined );
},
/**
use Wikimedia\TestingAccessWrapper;
use WikiPage;
use WikitextContent;
+use DeferredUpdates;
/**
* @group Database
/**
* @param string|Title|WikiPage $page
+ * @param RevisionRecord|null $rec
+ * @param User|null $user
*
* @return DerivedPageDataUpdater
*/
- private function getDerivedPageDataUpdater( $page, RevisionRecord $rec = null ) {
+ private function getDerivedPageDataUpdater(
+ $page, RevisionRecord $rec = null, User $user = null
+ ) {
if ( is_string( $page ) || $page instanceof Title ) {
$page = $this->getPage( $page );
}
$page = TestingAccessWrapper::newFromObject( $page );
- return $page->getDerivedDataUpdater( null, $rec );
+ return $page->getDerivedDataUpdater( $user, $rec );
}
/**
* @param WikiPage $page
* @param string|Message|CommentStoreComment $summary
* @param null|string|Content $content
+ * @param User|null $user
*
* @return RevisionRecord|null
*/
- private function createRevision( WikiPage $page, $summary, $content = null ) {
- $user = $this->getTestUser()->getUser();
+ private function createRevision( WikiPage $page, $summary, $content = null, $user = null ) {
+ $user = $user ?: $this->getTestUser()->getUser();
$comment = CommentStoreComment::newUnsavedComment( $summary );
if ( $content === null || is_string( $content ) ) {
// TODO: test category membership update (with setRcWatchCategoryMembership())
}
+ /**
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doUpdates()
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doSecondaryDataUpdates()
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
+ */
+ public function testDoUpdatesCacheSaveDeferral_canonical() {
+ $page = $this->getPage( __METHOD__ );
+
+ // Case where user has canonical parser options
+ $content = [ 'main' => new WikitextContent( 'rev ID ver #1: {{REVISIONID}}' ) ];
+ $rev = $this->createRevision( $page, 'first', $content );
+ $pcache = MediaWikiServices::getInstance()->getParserCache();
+ $pcache->deleteOptionsKey( $page );
+
+ $this->db->startAtomic( __METHOD__ ); // let deferred updates queue up
+
+ $updater = $this->getDerivedPageDataUpdater( $page, $rev );
+ $updater->prepareUpdate( $rev, [] );
+ $updater->doUpdates();
+
+ $this->assertGreaterThan( 0, DeferredUpdates::pendingUpdatesCount(), 'Pending updates' );
+ $this->assertNotFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
+
+ $this->db->endAtomic( __METHOD__ ); // run deferred updates
+
+ $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No pending updates' );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doUpdates()
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doSecondaryDataUpdates()
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
+ */
+ public function testDoUpdatesCacheSaveDeferral_noncanonical() {
+ $page = $this->getPage( __METHOD__ );
+
+ // Case where user does not have canonical parser options
+ $user = $this->getMutableTestUser()->getUser();
+ $user->setOption(
+ 'thumbsize',
+ $user->getOption( 'thumbsize' ) + 1
+ );
+ $content = [ 'main' => new WikitextContent( 'rev ID ver #2: {{REVISIONID}}' ) ];
+ $rev = $this->createRevision( $page, 'first', $content, $user );
+ $pcache = MediaWikiServices::getInstance()->getParserCache();
+ $pcache->deleteOptionsKey( $page );
+
+ $this->db->startAtomic( __METHOD__ ); // let deferred updates queue up
+
+ $updater = $this->getDerivedPageDataUpdater( $page, $rev, $user );
+ $updater->prepareUpdate( $rev, [] );
+ $updater->doUpdates();
+
+ $this->assertGreaterThan( 1, DeferredUpdates::pendingUpdatesCount(), 'Pending updates' );
+ $this->assertFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
+
+ $this->db->endAtomic( __METHOD__ ); // run deferred updates
+
+ $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No pending updates' );
+ $this->assertNotFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
+ }
+
/**
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
*/
->getMock();
$mock->expects( $this->exactly( $insertCalls ) )
->method( 'insert' )
- ->willReturnCallback( function () {
- return call_user_func_array( [ $this->db, 'insert' ], func_get_args() );
+ ->willReturnCallback( function ( ...$args ) {
+ return call_user_func_array( [ $this->db, 'insert' ], $args );
} );
$mock->expects( $this->exactly( $selectCalls ) )
->method( 'select' )
- ->willReturnCallback( function () {
- return call_user_func_array( [ $this->db, 'select' ], func_get_args() );
+ ->willReturnCallback( function ( ...$args ) {
+ return call_user_func_array( [ $this->db, 'select' ], $args );
} );
$mock->expects( $this->exactly( $insertCalls ) )
->method( 'affectedRows' )
- ->willReturnCallback( function () {
- return call_user_func_array( [ $this->db, 'affectedRows' ], func_get_args() );
+ ->willReturnCallback( function ( ...$args ) {
+ return call_user_func_array( [ $this->db, 'affectedRows' ], $args );
} );
$mock->expects( $this->any() )
->method( 'insertId' )
- ->willReturnCallback( function () {
- return call_user_func_array( [ $this->db, 'insertId' ], func_get_args() );
+ ->willReturnCallback( function ( ...$args ) {
+ return call_user_func_array( [ $this->db, 'insertId' ], $args );
} );
return $mock;
}
--- /dev/null
+<?php
+
+namespace MediaWiki\Edit;
+
+use ParserOutput;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Edit\PreparedEdit
+ */
+class PreparedEditTest extends MediaWikiTestCase {
+ function testCallback() {
+ $output = new ParserOutput();
+ $edit = new PreparedEdit();
+ $edit->parserOutputCallback = function () {
+ return new ParserOutput();
+ };
+
+ $this->assertEquals( $output, $edit->getOutput() );
+ $this->assertEquals( $output, $edit->output );
+ }
+}
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
+use Wikimedia\TestingAccessWrapper;
/**
* @group Database
$this->setContentLang( 'en' );
}
+ protected function tearDown() {
+ // For testNewMainPage
+ MessageCache::destroyInstance();
+ parent::tearDown();
+ }
+
/**
* @covers Title::legalChars
*/
$firstValue->equals( $secondValue )
);
}
+
+ /**
+ * @covers Title::newMainPage
+ */
+ public function testNewMainPage() {
+ $msgCache = TestingAccessWrapper::newFromClass( MessageCache::class );
+ $msgCache->instance = $this->createMock( MessageCache::class );
+ $msgCache->instance->method( 'get' )->willReturn( 'Foresheet' );
+ $msgCache->instance->method( 'transform' )->willReturn( 'Foresheet' );
+
+ $this->assertSame(
+ 'Foresheet',
+ Title::newMainPage()->getText()
+ );
+ }
+
+ /**
+ * @covers Title::newMainPage
+ */
+ public function testNewMainPageWithLocal() {
+ $local = $this->createMock( MessageLocalizer::class );
+ $local->method( 'msg' )->willReturn( new RawMessage( 'Prime Article' ) );
+
+ $this->assertSame(
+ 'Prime Article',
+ Title::newMainPage( $local )->getText()
+ );
+ }
}
/**
* Merges all requests parameter + expected values into one
- * @param array $v,... List of arrays, each of which contains exactly two
+ * @param array ...$arrays List of arrays, each of which contains exactly two
* @return array
*/
- protected function merge( /*...*/ ) {
+ protected function merge( ...$arrays ) {
$request = [];
$expected = [];
- foreach ( func_get_args() as $v ) {
- list( $req, $exp ) = $this->validateRequestExpectedPair( $v );
+ foreach ( $arrays as $array ) {
+ list( $req, $exp ) = $this->validateRequestExpectedPair( $array );
$request = array_merge_recursive( $request, $req );
$this->mergeExpected( $expected, $exp );
}
* @dataProvider provideLoadFromSubmission
*/
public function testLoadFromSubmission(
- array $args, array $data, $expectState /* $hiddenPref, $enableEmail */
+ array $args, array $data, $expectState, $hiddenPref = null, $enableEmail = null
) {
- list( $args, $data, $expectState, $hiddenPref, $enableEmail ) = func_get_args();
$this->setMwGlobals( 'wgHiddenPrefs', $hiddenPref );
$this->setMwGlobals( 'wgEnableEmail', $enableEmail );
parent::testLoadFromSubmission( $args, $data, $expectState );
*/
private static $pageName = 'CategoryMembershipChangeTestPage';
- public static function newForCategorizationCallback() {
- self::$lastNotifyArgs = func_get_args();
+ public static function newForCategorizationCallback( ...$args ) {
+ self::$lastNotifyArgs = $args;
self::$notifyCallCounter += 1;
return self::$mockRecentChange;
}
$customContentHandler->expects( $this->any() )
->method( 'createDifferenceEngine' )
->willReturn( $customDifferenceEngine );
- /** @var $customContentHandler ContentHandler */
+ /** @var ContentHandler $customContentHandler */
$slotDiffRenderer = $customContentHandler->getSlotDiffRenderer( RequestContext::getMain() );
$this->assertInstanceOf( DifferenceEngineSlotDiffRenderer::class, $slotDiffRenderer );
$this->assertSame(
$customContentHandler2->expects( $this->any() )
->method( 'getSlotDiffRendererInternal' )
->willReturn( $customSlotDiffRenderer );
- /** @var $customContentHandler2 ContentHandler */
+ /** @var ContentHandler $customContentHandler2 */
$slotDiffRenderer = $customContentHandler2->getSlotDiffRenderer( RequestContext::getMain() );
$this->assertSame( $customSlotDiffRenderer, $slotDiffRenderer );
}
$customContentHandler->expects( $this->any() )
->method( 'createDifferenceEngine' )
->willReturn( $customDifferenceEngine );
- /** @var $customContentHandler ContentHandler */
+ /** @var ContentHandler $customContentHandler */
$customSlotDiffRenderer = $this->getMockBuilder( SlotDiffRenderer::class )
->disableOriginalConstructor()
$customContentHandler2->expects( $this->any() )
->method( 'getSlotDiffRendererInternal' )
->willReturn( $customSlotDiffRenderer );
- /** @var $customContentHandler2 ContentHandler */
+ /** @var ContentHandler $customContentHandler2 */
$customSlotDiffRenderer2 = $this->getMockBuilder( SlotDiffRenderer::class )
->disableOriginalConstructor()
],
[
(object)[ 'foo' ],
- '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr>' .
+ '<table class="mw-json"><tbody><tr><th>0</th><td class="mw-json-value">"foo"</td></tr>' .
'</tbody></table>'
],
[
(object)[ 'foo', 'bar' ],
- '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"foo"</td></tr>' .
- '<tr><th>1</th><td class="value">"bar"</td></tr></tbody></table>'
+ '<table class="mw-json"><tbody><tr><th>0</th><td class="mw-json-value">"foo"</td></tr>' .
+ '<tr><th>1</th><td class="mw-json-value">"bar"</td></tr></tbody></table>'
],
[
(object)[ 'baz' => 'foo', 'bar' ],
- '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">"foo"</td></tr>' .
- '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>'
+ '<table class="mw-json"><tbody><tr><th>baz</th><td class="mw-json-value">"foo"</td></tr>' .
+ '<tr><th>0</th><td class="mw-json-value">"bar"</td></tr></tbody></table>'
],
[
(object)[ 'baz' => 1000, 'bar' ],
- '<table class="mw-json"><tbody><tr><th>baz</th><td class="value">1000</td></tr>' .
- '<tr><th>0</th><td class="value">"bar"</td></tr></tbody></table>'
+ '<table class="mw-json"><tbody><tr><th>baz</th><td class="mw-json-value">1000</td></tr>' .
+ '<tr><th>0</th><td class="mw-json-value">"bar"</td></tr></tbody></table>'
],
[
(object)[ '<script>alert("evil!")</script>' ],
- '<table class="mw-json"><tbody><tr><th>0</th><td class="value">"' .
+ '<table class="mw-json"><tbody><tr><th>0</th><td class="mw-json-value">"' .
'<script>alert("evil!")</script>"' .
'</td></tr></tbody></table>',
],
} );
/**
- * @var $mockEngine SearchEngine
+ * @var SearchEngine $mockEngine
*/
$fields = $handler->getFieldsForSearchIndex( $mockEngine );
$mappedFields = [];
foreach ( $fields as $name => $field ) {
$this->assertInstanceOf( SearchIndexField::class, $field );
/**
- * @var $field SearchIndexField
+ * @var SearchIndexField $field
*/
$mappedFields[$name] = $field->getMapping( $mockEngine );
}
$customContentHandler->expects( $this->any() )
->method( 'createDifferenceEngine' )
->willReturn( $customDifferenceEngine );
- /** @var $customContentHandler ContentHandler */
+ /** @var ContentHandler $customContentHandler */
$customContent = $this->getMockBuilder( Content::class )
->setMethods( [ 'getContentHandler' ] )
->getMockForAbstractClass();
$customContent->expects( $this->any() )
->method( 'getContentHandler' )
->willReturn( $customContentHandler );
- /** @var $customContent Content */
+ /** @var Content $customContent */
$customContent2 = clone $customContent;
$slotDiffRenderer = $customContentHandler->getSlotDiffRenderer( RequestContext::getMain() );
/**
* Constant values are from PHP 5.3.28 using cURL 7.24.0
- * @see https://secure.php.net/manual/en/curl.constants.php
+ * @see https://www.php.net/manual/en/curl.constants.php
*
* All constant values are present so that developers don’t need to remember
* to add them if added at a later date. The commented out constants were
if ( !Http::$httpEngine ) {
Http::$httpEngine = 'guzzle';
} elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
- throw new DomainException( __METHOD__ . ': curl (https://secure.php.net/curl) is not ' .
+ throw new DomainException( __METHOD__ . ': curl (https://www.php.net/curl) is not ' .
'installed, but Http::$httpEngine is set to "curl"' );
}
if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
throw new DomainException( __METHOD__ .
': allow_url_fopen needs to be enabled for pure PHP HTTP requests to work. '
- . 'If possible, curl should be used instead. See https://secure.php.net/curl.' );
+ . 'If possible, curl should be used instead. See https://www.php.net/curl.' );
}
return new PhpHttpRequestTester( $url, $options, $caller );
* @covers ArrayUtils::arrayDiffAssocRecursive
* @dataProvider provideArrayDiffAssocRecursive
*/
- function testArrayDiffAssocRecursive( $expected ) {
- $args = func_get_args();
- array_shift( $args );
+ function testArrayDiffAssocRecursive( $expected, ...$args ) {
$this->assertEquals( call_user_func_array(
'ArrayUtils::arrayDiffAssocRecursive', $args
), $expected );
protected function tearDown() {
foreach ( $this->pagesToDelete as $p ) {
- /* @var $p WikiPage */
+ /* @var WikiPage $p */
try {
if ( $p->exists() ) {
ScopedCallback::consume( $reset );
}
+ public function testMatchesForCacheKey() {
+ $cOpts = ParserOptions::newCanonical( null, 'en' );
+
+ $uOpts = ParserOptions::newFromAnon();
+ $this->assertTrue( $cOpts->matchesForCacheKey( $uOpts ) );
+
+ $user = new User();
+ $uOpts = ParserOptions::newFromUser( $user );
+ $this->assertTrue( $cOpts->matchesForCacheKey( $uOpts ) );
+
+ $user = new User();
+ $user->setOption( 'thumbsize', 251 );
+ $uOpts = ParserOptions::newFromUser( $user );
+ $this->assertFalse( $cOpts->matchesForCacheKey( $uOpts ) );
+
+ $user = new User();
+ $user->setOption( 'stubthreshold', 800 );
+ $uOpts = ParserOptions::newFromUser( $user );
+ $this->assertFalse( $cOpts->matchesForCacheKey( $uOpts ) );
+
+ $user = new User();
+ $uOpts = ParserOptions::newFromUserAndLang( $user, Language::factory( 'zh' ) );
+ $this->assertFalse( $cOpts->matchesForCacheKey( $uOpts ) );
+ }
+
public function testAllCacheVaryingOptions() {
$this->setTemporaryHook( 'ParserOptionsRegister', null );
$this->assertSame( [
// That call will die if the contructor is not public, unless we use disableOriginalConstructor(),
// in which case we could not test the constructor.
abstract class PoolCounterAbstractMock extends PoolCounter {
- public function __construct() {
- call_user_func_array( 'parent::__construct', func_get_args() );
+ public function __construct( ...$args ) {
+ call_user_func_array( 'parent::__construct', $args );
}
}
$testUser = $this->getTestUser();
$form = $this->getPreferencesFactory()->getForm( $testUser->getUser(), $this->context );
- $this->assertInstanceOf( PreferencesFormLegacy::class, $form );
+ $this->assertInstanceOf( PreferencesFormOOUI::class, $form );
$this->assertCount( 5, $form->getPreferenceSections() );
}
$configMock = new HashConfig( [
'HiddenPrefs' => []
] );
- $form = $this->getMockBuilder( PreferencesFormLegacy::class )
+ $form = $this->getMockBuilder( PreferencesFormOOUI::class )
->disableOriginalConstructor()
->getMock();
* @dataProvider provideType
*/
public function testType( $given, $expected ) {
- $checker = new VersionChecker( '1.0.0', '7.0.0', [ 'phpLoadedExtension' ] );
+ $checker = new VersionChecker(
+ '1.0.0',
+ '7.0.0',
+ [ 'phpLoadedExtension' ],
+ [
+ 'presentAbility' => true,
+ 'presentAbilityWithMessage' => true,
+ 'missingAbility' => false,
+ 'missingAbilityWithMessage' => false,
+ ],
+ [
+ 'presentAbilityWithMessage' => 'Present.',
+ 'missingAbilityWithMessage' => 'Missing.',
+ ]
+ );
$checker->setLoadedExtensionsAndSkins( [
'FakeDependency' => [
'version' => '1.0.0',
],
],
],
+ [
+ [
+ 'platform' => [
+ 'ability-presentAbility' => true,
+ ],
+ ],
+ [],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-presentAbilityWithMessage' => true,
+ ],
+ ],
+ [],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-presentAbility' => false,
+ ],
+ ],
+ [],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-presentAbilityWithMessage' => false,
+ ],
+ ],
+ [],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-missingAbility' => true,
+ ],
+ ],
+ [
+ [
+ 'missing' => 'missingAbility',
+ 'type' => 'missing-ability',
+ 'msg' => 'FakeExtension requires "missingAbility" ability',
+ ],
+ ],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-missingAbilityWithMessage' => true,
+ ],
+ ],
+ [
+ [
+ 'missing' => 'missingAbilityWithMessage',
+ 'type' => 'missing-ability',
+ // phpcs:ignore Generic.Files.LineLength.TooLong
+ 'msg' => 'FakeExtension requires "missingAbilityWithMessage" ability: Missing.',
+ ],
+ ],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-missingAbility' => false,
+ ],
+ ],
+ [],
+ ],
+ [
+ [
+ 'platform' => [
+ 'ability-missingAbilityWithMessage' => false,
+ ],
+ ],
+ [],
+ ],
];
}
],
'phpLoadedExtension',
],
+ [
+ [
+ 'FakeExtension' => [
+ 'platform' => [
+ 'ability-invalidAbility' => true,
+ ],
+ ],
+ ],
+ 'ability-invalidAbility',
+ ],
+ [
+ [
+ 'FakeExtension' => [
+ 'platform' => [
+ 'presentAbility' => true,
+ ],
+ ],
+ ],
+ 'presentAbility',
+ ],
[
[
'FakeExtension' => [
* @dataProvider provideInvalidDependency
*/
public function testInvalidDependency( $depencency, $type ) {
- $checker = new VersionChecker( '1.0.0', '7.0.0', [ 'phpLoadedExtension' ] );
+ $checker = new VersionChecker(
+ '1.0.0',
+ '7.0.0',
+ [ 'phpLoadedExtension' ],
+ [
+ 'presentAbility' => true,
+ 'missingAbility' => false,
+ ]
+ );
$this->setExpectedException(
UnexpectedValueException::class,
"Dependency type $type unknown in FakeExtension"
],
] );
}
+
+ /**
+ * @dataProvider provideInvalidAbilityType
+ */
+ public function testInvalidAbilityType( $value ) {
+ $checker = new VersionChecker( '1.0.0', '7.0.0', [], [ 'presentAbility' => true ] );
+ $this->setExpectedException(
+ UnexpectedValueException::class,
+ 'Only booleans are allowed to to indicate the presence of abilities in FakeExtension'
+ );
+ $checker->checkArray( [
+ 'FakeExtension' => [
+ 'platform' => [
+ 'ability-presentAbility' => $value,
+ ],
+ ],
+ ] );
+ }
+
+ public function provideInvalidAbilityType() {
+ return [
+ [ null ],
+ [ 1 ],
+ [ '1' ],
+ ];
+ }
+
}
*/
public function testSearchIndexFields() {
/**
- * @var $mockEngine SearchEngine
+ * @var SearchEngine $mockEngine
*/
$mockEngine = $this->getMockBuilder( SearchEngine::class )
->setMethods( [ 'makeSearchFieldMapping' ] )->getMock();
} ) );
$mock->expects( $this->any() )
->method( 'isAllowedAny' )
- ->will( $this->returnCallback( function () use ( $notAllowedAction ) {
- $actions = func_get_args();
+ ->will( $this->returnCallback( function ( ...$actions ) use ( $notAllowedAction ) {
return !in_array( $notAllowedAction, $actions );
} ) );
->getMock();
$mock->expects( $this->any() )
->method( 'makeKey' )
- ->will( $this->returnCallback( function () {
- return implode( ':', func_get_args() );
+ ->will( $this->returnCallback( function ( ...$args ) {
+ return implode( ':', $args );
} ) );
return $mock;
}
);
}
+ /**
+ * Test, if for all rights an action- message exist,
+ * which is used on Special:ListGroupRights as help text
+ * Extensions and core
+ *
+ * @coversNothing
+ */
+ public function testAllActionsWithMessages() {
+ $this->checkMessagesExist( 'action-' );
+ }
+
/**
* Test, if for all rights a right- message exist,
* which is used on Special:ListGroupRights as help text
* @coversNothing
*/
public function testAllRightsWithMessage() {
+ $this->checkMessagesExist( 'right-' );
+ }
+
+ /**
+ * @param string $prefix
+ */
+ private function checkMessagesExist( $prefix ) {
// Getting all user rights, for core: User::$mCoreRights, for extensions: $wgAvailableRights
$allRights = User::getAllRights();
$allMessageKeys = Language::getMessageKeysFor( 'en' );
- $rightsWithMessage = [];
+ $messagesToCheck = [];
foreach ( $allMessageKeys as $message ) {
// === 0: must be at beginning of string (position 0)
- if ( strpos( $message, 'right-' ) === 0 ) {
- $rightsWithMessage[] = substr( $message, strlen( 'right-' ) );
+ if ( strpos( $message, $prefix ) === 0 ) {
+ $messagesToCheck[] = substr( $message, strlen( $prefix ) );
}
}
$missing = array_diff(
$allRights,
- $rightsWithMessage
+ $messagesToCheck
);
$this->assertEquals(
[],
$missing,
- 'Each user rights (core/extensions) has a corresponding right- message.'
+ "Each user right (core/extensions) has a corresponding $prefix message."
);
}
}
$testsName = $extensionName . '__' . basename( $fileName, '.txt' );
$parserTestClassName = ucfirst( $testsName );
- // Official spec for class names: https://secure.php.net/manual/en/language.oop5.basic.php
+ // Official spec for class names: https://www.php.net/manual/en/language.oop5.basic.php
// Prepend 'ParserTest_' to be paranoid about it not starting with a number
$parserTestClassName = 'ParserTest_' .
preg_replace( '/[^a-zA-Z0-9_\x7f-\xff]/', '_', $parserTestClassName );
QUnit.test( '.implement( package files )', function ( assert ) {
var done = assert.async(),
- initJsRan = false;
+ initJsRan = false,
+ counter = 41;
mw.loader.implement(
'test.implement.packageFiles',
{
files: {
'resources/src/foo/data/hello.json': { hello: 'world' },
'resources/src/foo/foo.js': function ( require, module ) {
- window.mwTestFooJsCounter = window.mwTestFooJsCounter || 41;
- window.mwTestFooJsCounter++;
- module.exports = { answer: window.mwTestFooJsCounter };
+ counter++;
+ module.exports = { answer: counter };
},
'resources/src/bar/bar.js': function ( require, module ) {
var core = require( './core.js' );
assert.strictEqual( HistoryPage.rollbackConfirmableNo.getText(), 'Cancel' );
} );
- it( 'should offer a way to cancel rollbacks', function () {
+ it.skip( 'should offer a way to cancel rollbacks', function () {
HistoryPage.rollback.click();
browser.pause( 300 );
assert.strictEqual( HistoryPage.heading.getText(), 'Revision history of "' + name + '"' );
} );
- it( 'should perform rollbacks after confirming intention', function () {
+ it.skip( 'should perform rollbacks after confirming intention', function () {
HistoryPage.rollback.click();
browser.pause( 300 );