Merge "registration: Support 'ServiceWiringFiles' in extension.json"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 17 Sep 2016 22:45:18 +0000 (22:45 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 17 Sep 2016 22:45:18 +0000 (22:45 +0000)
1689 files changed:
RELEASE-NOTES-1.28
autoload.php
composer.json
docs/hooks.txt
docs/uidesign/mediawiki.action.history.diff.html [deleted file]
docs/uidesign/mediawiki.diff.html [new file with mode: 0644]
includes/Block.php
includes/Category.php
includes/CategoryFinder.php
includes/CategoryViewer.php
includes/DefaultSettings.php
includes/Defines.php
includes/DummyLinker.php
includes/EditPage.php
includes/FauxRequest.php
includes/GlobalFunctions.php
includes/HistoryBlob.php
includes/Hooks.php
includes/HttpFunctions.php
includes/LinkFilter.php
includes/Linker.php
includes/MWTimestamp.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MediaWikiServices.php
includes/MergeHistory.php
includes/Message.php
includes/OutputPage.php
includes/PageProps.php
includes/PathRouter.php
includes/Pingback.php
includes/Preferences.php
includes/PrefixSearch.php
includes/Revision.php
includes/RevisionList.php
includes/ServiceWiring.php
includes/Setup.php
includes/SiteStats.php
includes/Status.php
includes/TemplatesOnThisPageFormatter.php [new file with mode: 0644]
includes/Title.php
includes/WatchedItemQueryService.php
includes/WatchedItemStore.php
includes/WebRequest.php
includes/actions/HistoryAction.php
includes/actions/InfoAction.php
includes/actions/PurgeAction.php
includes/actions/RevertAction.php
includes/actions/RollbackAction.php
includes/actions/ViewAction.php
includes/api/ApiAuthManagerHelper.php
includes/api/ApiBase.php
includes/api/ApiEditPage.php
includes/api/ApiErrorFormatter.php
includes/api/ApiExpandTemplates.php
includes/api/ApiLogin.php
includes/api/ApiMain.php
includes/api/ApiPageSet.php
includes/api/ApiParamInfo.php
includes/api/ApiPurge.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAuthManagerInfo.php
includes/api/ApiQueryBacklinksprop.php
includes/api/ApiQueryBlocks.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryTags.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiResult.php
includes/api/ApiStashEdit.php
includes/api/ApiTag.php
includes/api/ApiUpload.php
includes/api/i18n/cs.json
includes/api/i18n/de.json
includes/api/i18n/diq.json
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/lij.json [new file with mode: 0644]
includes/api/i18n/mr.json
includes/api/i18n/oc.json
includes/api/i18n/pl.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sv.json
includes/api/i18n/uk.json
includes/api/i18n/zh-hans.json
includes/auth/AuthManager.php
includes/auth/AuthenticationProvider.php
includes/auth/AuthenticationRequest.php
includes/auth/AuthenticationResponse.php
includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
includes/auth/EmailNotificationSecondaryAuthenticationProvider.php
includes/auth/PreAuthenticationProvider.php
includes/auth/PrimaryAuthenticationProvider.php
includes/auth/ResetPasswordSecondaryAuthenticationProvider.php
includes/auth/SecondaryAuthenticationProvider.php
includes/auth/TemporaryPasswordPrimaryAuthenticationProvider.php
includes/cache/BacklinkCache.php
includes/cache/GenderCache.php
includes/cache/HTMLFileCache.php
includes/cache/LinkBatch.php
includes/cache/LinkCache.php
includes/cache/MessageBlobStore.php
includes/cache/MessageCache.php
includes/cache/ObjectFileCache.php [deleted file]
includes/cache/UserCache.php
includes/cache/localisation/LCStoreDB.php
includes/changes/CategoryMembershipChange.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/content/ContentHandler.php
includes/content/JsonContentHandler.php
includes/content/WikiTextStructure.php
includes/content/WikitextContentHandler.php
includes/dao/DBAccessObjectUtils.php
includes/dao/IDBAccessObject.php
includes/db/ChronologyProtector.php [deleted file]
includes/db/CloneDatabase.php
includes/db/DBConnRef.php [deleted file]
includes/db/Database.php [deleted file]
includes/db/DatabaseError.php [deleted file]
includes/db/DatabaseMssql.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseOracle.php
includes/db/DatabasePostgres.php
includes/db/DatabaseSqlite.php
includes/db/DatabaseUtility.php [deleted file]
includes/db/IDatabase.php [deleted file]
includes/db/loadbalancer/LBFactory.php [deleted file]
includes/db/loadbalancer/LBFactoryFake.php [deleted file]
includes/db/loadbalancer/LBFactoryMW.php [new file with mode: 0644]
includes/db/loadbalancer/LBFactoryMulti.php
includes/db/loadbalancer/LBFactorySimple.php
includes/db/loadbalancer/LBFactorySingle.php
includes/db/loadbalancer/LoadBalancer.php [deleted file]
includes/db/loadbalancer/LoadMonitor.php [deleted file]
includes/db/loadbalancer/LoadMonitorMySQL.php [deleted file]
includes/debug/MWDebug.php
includes/debug/logger/LegacySpi.php
includes/debug/logger/MonologSpi.php
includes/debug/logger/NullSpi.php
includes/deferred/AtomicSectionUpdate.php
includes/deferred/AutoCommitUpdate.php
includes/deferred/DataUpdate.php
includes/deferred/DeferredUpdates.php
includes/deferred/LinksDeletionUpdate.php
includes/deferred/LinksUpdate.php
includes/deferred/MWCallableUpdate.php
includes/deferred/SiteStatsUpdate.php
includes/deferred/SqlDataUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWException.php
includes/exception/MWExceptionHandler.php
includes/exception/MWExceptionRenderer.php [new file with mode: 0644]
includes/exception/TimestampException.php [deleted file]
includes/exception/UserNotLoggedIn.php
includes/externalstore/ExternalStoreDB.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendStore.php
includes/filebackend/lockmanager/LockManager.php
includes/filebackend/lockmanager/MemcLockManager.php
includes/filerepo/FileBackendDBRepoWrapper.php
includes/filerepo/FileRepo.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/ForeignDBViaLBRepo.php
includes/filerepo/LocalRepo.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/LocalFile.php
includes/htmlform/HTMLForm.php
includes/htmlform/OOUIHTMLForm.php
includes/htmlform/fields/HTMLRadioField.php
includes/import/WikiImporter.php
includes/installer/DatabaseInstaller.php
includes/installer/DatabaseUpdater.php
includes/installer/Installer.php
includes/installer/LocalSettingsGenerator.php
includes/installer/MysqlUpdater.php
includes/installer/i18n/ast.json
includes/installer/i18n/bn.json
includes/installer/i18n/ckb.json
includes/installer/i18n/de.json
includes/installer/i18n/diq.json
includes/installer/i18n/fr.json
includes/installer/i18n/ia.json
includes/installer/i18n/lij.json [new file with mode: 0644]
includes/installer/i18n/ne.json
includes/installer/i18n/tcy.json
includes/installer/i18n/vi.json
includes/interwiki/ClassicInterwikiLookup.php
includes/interwiki/InterwikiLookup.php
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueGroup.php
includes/jobqueue/JobRunner.php
includes/jobqueue/jobs/CategoryMembershipChangeJob.php
includes/jobqueue/jobs/DeleteLinksJob.php
includes/jobqueue/jobs/NullJob.php
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/jobqueue/utils/PurgeJobUtils.php
includes/libs/MapCacheLRU.php
includes/libs/SamplingStatsdClient.php
includes/libs/StatusValue.php
includes/libs/WaitConditionLoop.php [new file with mode: 0644]
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/IExpiringStore.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/RESTBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/objectcache/WinCacheBagOStuff.php
includes/libs/rdbms/TransactionProfiler.php [new file with mode: 0644]
includes/libs/rdbms/chronologyprotector/ChronologyProtector.php [new file with mode: 0644]
includes/libs/rdbms/database/DBConnRef.php [new file with mode: 0644]
includes/libs/rdbms/database/Database.php [new file with mode: 0644]
includes/libs/rdbms/database/DatabaseBase.php [new file with mode: 0644]
includes/libs/rdbms/database/DatabaseDomain.php [new file with mode: 0644]
includes/libs/rdbms/database/IDatabase.php [new file with mode: 0644]
includes/libs/rdbms/database/position/DBMasterPos.php [new file with mode: 0644]
includes/libs/rdbms/database/position/MySQLMasterPos.php [new file with mode: 0644]
includes/libs/rdbms/database/resultwrapper/FakeResultWrapper.php [new file with mode: 0644]
includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php [new file with mode: 0644]
includes/libs/rdbms/database/resultwrapper/ResultWrapper.php [new file with mode: 0644]
includes/libs/rdbms/defines.php [new file with mode: 0644]
includes/libs/rdbms/encasing/Blob.php [new file with mode: 0644]
includes/libs/rdbms/encasing/LikeMatch.php [new file with mode: 0644]
includes/libs/rdbms/encasing/MssqlBlob.php [new file with mode: 0644]
includes/libs/rdbms/encasing/PostgresBlob.php [new file with mode: 0644]
includes/libs/rdbms/exception/DBError.php [new file with mode: 0644]
includes/libs/rdbms/field/Field.php [new file with mode: 0644]
includes/libs/rdbms/field/MssqlField.php [new file with mode: 0644]
includes/libs/rdbms/field/MySQLField.php [new file with mode: 0644]
includes/libs/rdbms/field/ORAField.php [new file with mode: 0644]
includes/libs/rdbms/field/SQLiteField.php [new file with mode: 0644]
includes/libs/rdbms/lbfactory/LBFactory.php [new file with mode: 0644]
includes/libs/rdbms/loadbalancer/ILoadBalancer.php [new file with mode: 0644]
includes/libs/rdbms/loadbalancer/LoadBalancer.php [new file with mode: 0644]
includes/libs/rdbms/loadmonitor/ILoadMonitor.php [new file with mode: 0644]
includes/libs/rdbms/loadmonitor/LoadMonitor.php [new file with mode: 0644]
includes/libs/rdbms/loadmonitor/LoadMonitorMySQL.php [new file with mode: 0644]
includes/libs/rdbms/loadmonitor/LoadMonitorNull.php [new file with mode: 0644]
includes/libs/time/ConvertableTimestamp.php [new file with mode: 0644]
includes/libs/time/TimestampException.php [new file with mode: 0644]
includes/libs/time/defines.php [new file with mode: 0644]
includes/logging/LogEntry.php
includes/logging/LogPager.php
includes/objectcache/MemcachedPeclBagOStuff.php [deleted file]
includes/objectcache/ObjectCache.php
includes/objectcache/RedisBagOStuff.php
includes/objectcache/SqlBagOStuff.php
includes/page/Article.php
includes/page/CategoryPage.php
includes/page/ImageHistoryList.php
includes/page/ImagePage.php
includes/page/WikiFilePage.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/pager/ReverseChronologicalPager.php
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/parser/ParserCache.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/poolcounter/PoolCounterRedis.php
includes/profiler/TransactionProfiler.php [deleted file]
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderImageModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionList.php
includes/revisiondelete/RevisionDeleter.php
includes/search/AugmentPageProps.php [new file with mode: 0644]
includes/search/PerRowAugmentor.php [new file with mode: 0644]
includes/search/ResultAugmentor.php [new file with mode: 0644]
includes/search/ResultSetAugmentor.php [new file with mode: 0644]
includes/search/SearchDatabase.php
includes/search/SearchEngine.php
includes/search/SearchEngineFactory.php
includes/search/SearchHighlighter.php
includes/search/SearchIndexField.php
includes/search/SearchMySQL.php
includes/search/SearchNearMatchResultSet.php
includes/search/SearchResult.php
includes/search/SearchResultSet.php
includes/search/SqlSearchResultSet.php
includes/session/Session.php
includes/session/SessionBackend.php
includes/session/SessionInfo.php
includes/session/SessionManager.php
includes/session/SessionManagerInterface.php
includes/session/SessionProvider.php
includes/session/Token.php
includes/site/DBSiteStore.php
includes/skins/BaseTemplate.php
includes/skins/Skin.php
includes/skins/SkinTemplate.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/QueryPage.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialBotPasswords.php
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialDoubleRedirects.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialExport.php
includes/specials/SpecialLinkSearch.php
includes/specials/SpecialMediaStatistics.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialPageLanguage.php
includes/specials/SpecialPagesWithProp.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialProtectedpages.php
includes/specials/SpecialRandomInCategory.php
includes/specials/SpecialRandompage.php
includes/specials/SpecialRandomrootpage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialSearch.php
includes/specials/SpecialTags.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUploadStash.php
includes/specials/SpecialUserrights.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
includes/specials/SpecialWhatlinkshere.php
includes/specials/SpecialWithoutinterwiki.php
includes/specials/pagers/AllMessagesTablePager.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/specials/pagers/ImageListPager.php
includes/specials/pagers/MergeHistoryPager.php
includes/specials/pagers/NewFilesPager.php
includes/specials/pagers/UsersPager.php
includes/upload/UploadBase.php
includes/upload/UploadStash.php
includes/user/BotPassword.php
includes/user/CentralIdLookup.php
includes/user/LocalIdLookup.php
includes/user/PasswordReset.php
includes/user/User.php
includes/user/UserArray.php
includes/user/UserNamePrefixSearch.php
includes/user/UserRightsProxy.php
includes/utils/BatchRowUpdate.php
includes/utils/RowUpdateGenerator.php
languages/Language.php
languages/data/ZhConversion.php
languages/i18n/aeb-arab.json
languages/i18n/an.json
languages/i18n/ang.json
languages/i18n/ar.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dty.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/ga.json
languages/i18n/gl.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ilo.json
languages/i18n/io.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/kiu.json
languages/i18n/kk-cyrl.json
languages/i18n/ko.json
languages/i18n/la.json
languages/i18n/lb.json
languages/i18n/lij.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/mai.json
languages/i18n/mk.json
languages/i18n/mr.json
languages/i18n/my.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/oc.json
languages/i18n/or.json
languages/i18n/pl.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/qu.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/ta.json
languages/i18n/tcy.json
languages/i18n/te.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vi.json
languages/i18n/vo.json
languages/i18n/wa.json
languages/i18n/yi.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesLzh.php
languages/messages/MessagesUr.php
maintenance/Maintenance.php
maintenance/Makefile
maintenance/archives/patch-pl-tl-il-nonunique.sql [new file with mode: 0644]
maintenance/archives/patch-pl-tl-il-unique.sql [deleted file]
maintenance/archives/patch-user_rights.sql
maintenance/backup.inc
maintenance/benchmarks/benchmarkParse.php
maintenance/checkBadRedirects.php
maintenance/checkImages.php
maintenance/checkLess.php
maintenance/checkUsernames.php
maintenance/cleanupSpam.php
maintenance/cleanupTable.inc
maintenance/cleanupTitles.php
maintenance/clearInterwikiCache.php
maintenance/compareParserCache.php
maintenance/deleteDefaultMessages.php
maintenance/doMaintenance.php
maintenance/dumpLinks.php
maintenance/dumpTextPass.php
maintenance/dumpUploads.php
maintenance/fetchText.php
maintenance/fixDefaultJsonContentPages.php
maintenance/fixDoubleRedirects.php
maintenance/fixUserRegistration.php
maintenance/generateSitemap.php
maintenance/getReplicaServer.php [new file with mode: 0644]
maintenance/getSlaveServer.php
maintenance/importImages.php
maintenance/initEditCount.php
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/language/zhtable/tradphrases_exclude.manual
maintenance/namespaceDupes.php
maintenance/populateFilearchiveSha1.php
maintenance/populateRevisionLength.php
maintenance/purgeChangedFiles.php
maintenance/purgeChangedPages.php
maintenance/purgeList.php
maintenance/rebuildFileCache.php
maintenance/rebuildImages.php
maintenance/rebuildall.php
maintenance/refreshFileHeaders.php
maintenance/refreshLinks.php
maintenance/removeInvalidEmails.php
maintenance/removeUnusedAccounts.php
maintenance/resetUserTokens.php
maintenance/rollbackEdits.php
maintenance/runBatchedQuery.php
maintenance/runJobs.php
maintenance/showSiteStats.php
maintenance/sql.php
maintenance/storage/checkStorage.php
maintenance/storage/compressOld.php
maintenance/storage/dumpRev.php
maintenance/storage/fixBug20757.php
maintenance/storage/moveToExternal.php
maintenance/storage/orphanStats.php
maintenance/storage/recompressTracked.php
maintenance/storage/resolveStubs.php
maintenance/storage/storageTypeStats.php
maintenance/storage/testCompression.php
maintenance/storage/trackBlobs.php
maintenance/tidyUpBug37714.php
maintenance/updateArticleCount.php
maintenance/updateCollation.php
maintenance/updateSpecialPages.php
maintenance/userOptions.inc
resources/Resources.php
resources/lib/moment/locale/af.js
resources/lib/moment/locale/ar-ma.js
resources/lib/moment/locale/ar-sa.js
resources/lib/moment/locale/ar.js
resources/lib/moment/locale/az.js
resources/lib/moment/locale/be.js
resources/lib/moment/locale/bg.js
resources/lib/moment/locale/bn.js
resources/lib/moment/locale/bo.js
resources/lib/moment/locale/br.js
resources/lib/moment/locale/bs.js
resources/lib/moment/locale/ca.js
resources/lib/moment/locale/cs.js
resources/lib/moment/locale/cv.js
resources/lib/moment/locale/cy.js
resources/lib/moment/locale/da.js
resources/lib/moment/locale/de-at.js
resources/lib/moment/locale/de.js
resources/lib/moment/locale/el.js
resources/lib/moment/locale/en-au.js
resources/lib/moment/locale/en-ca.js
resources/lib/moment/locale/en-gb.js
resources/lib/moment/locale/eo.js
resources/lib/moment/locale/es.js
resources/lib/moment/locale/et.js
resources/lib/moment/locale/eu.js
resources/lib/moment/locale/fa.js
resources/lib/moment/locale/fi.js
resources/lib/moment/locale/fo.js
resources/lib/moment/locale/fr-ca.js
resources/lib/moment/locale/fr.js
resources/lib/moment/locale/gl.js
resources/lib/moment/locale/he.js
resources/lib/moment/locale/hi.js
resources/lib/moment/locale/hr.js
resources/lib/moment/locale/hu.js
resources/lib/moment/locale/hy-am.js
resources/lib/moment/locale/id.js
resources/lib/moment/locale/is.js
resources/lib/moment/locale/it.js
resources/lib/moment/locale/ja.js
resources/lib/moment/locale/ka.js
resources/lib/moment/locale/km.js
resources/lib/moment/locale/ko.js
resources/lib/moment/locale/lb.js
resources/lib/moment/locale/lt.js
resources/lib/moment/locale/lv.js
resources/lib/moment/locale/mk.js
resources/lib/moment/locale/ml.js
resources/lib/moment/locale/mr.js
resources/lib/moment/locale/ms-my.js
resources/lib/moment/locale/my.js
resources/lib/moment/locale/nb.js
resources/lib/moment/locale/ne.js
resources/lib/moment/locale/nl.js
resources/lib/moment/locale/nn.js
resources/lib/moment/locale/pl.js
resources/lib/moment/locale/pt-br.js
resources/lib/moment/locale/pt.js
resources/lib/moment/locale/ro.js
resources/lib/moment/locale/ru.js
resources/lib/moment/locale/sk.js
resources/lib/moment/locale/sl.js
resources/lib/moment/locale/sq.js
resources/lib/moment/locale/sr-cyrl.js
resources/lib/moment/locale/sr.js
resources/lib/moment/locale/sv.js
resources/lib/moment/locale/ta.js
resources/lib/moment/locale/th.js
resources/lib/moment/locale/tl-ph.js
resources/lib/moment/locale/tr.js
resources/lib/moment/locale/tzm-latn.js
resources/lib/moment/locale/tzm.js
resources/lib/moment/locale/uk.js
resources/lib/moment/locale/uz.js
resources/lib/moment/locale/vi.js
resources/lib/moment/locale/zh-cn.js
resources/lib/moment/locale/zh-tw.js
resources/lib/oojs-ui/i18n/bho.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/jv.json
resources/lib/oojs-ui/i18n/kn.json
resources/lib/oojs-ui/i18n/oc.json
resources/lib/oojs-ui/i18n/shn.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/te.json
resources/lib/oojs-ui/i18n/ur.json [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/info.png
resources/lib/oojs-ui/themes/apex/images/icons/listBullet-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr-invert.png
resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/printer-rtl-invert.png
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/table-insert-row-after.png
resources/lib/oojs-ui/themes/apex/images/icons/trash.png
resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-rtl.png
resources/lib/oojs-ui/themes/mediawiki/icons-alerts.json
resources/lib/oojs-ui/themes/mediawiki/icons-content.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-advanced.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-core.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-list.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-styling.json
resources/lib/oojs-ui/themes/mediawiki/icons-interactions.json
resources/lib/oojs-ui/themes/mediawiki/icons-layout.json
resources/lib/oojs-ui/themes/mediawiki/icons-location.json
resources/lib/oojs-ui/themes/mediawiki/icons-media.json
resources/lib/oojs-ui/themes/mediawiki/icons-moderation.json
resources/lib/oojs-ui/themes/mediawiki/icons-movement.json
resources/lib/oojs-ui/themes/mediawiki/icons-user.json
resources/lib/oojs-ui/themes/mediawiki/icons-wikimedia.json
resources/lib/oojs-ui/themes/mediawiki/icons.json
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/history-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/info-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/info-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/info.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-warning.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-destructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-progressive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/window-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/indicators.json
resources/lib/phpjs-sha1/LICENSE.txt [deleted file]
resources/src/mediawiki.action/mediawiki.action.history.diff.css [deleted file]
resources/src/mediawiki.action/mediawiki.action.history.diff.print.css [deleted file]
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.less/mediawiki.ui/variables.less
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.movePage.js
resources/src/mediawiki.ui/components/forms.less
resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js
resources/src/mediawiki/ForeignApi.js
resources/src/mediawiki/api.js
resources/src/mediawiki/api/messages.js
resources/src/mediawiki/api/options.js
resources/src/mediawiki/api/rollback.js
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.diff.styles.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.diff.styles.print.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.notification.hideForPrint.css [deleted file]
resources/src/mediawiki/mediawiki.notification.print.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.toc.js
resources/src/mediawiki/mediawiki.user.js
resources/src/mediawiki/mediawiki.util.js
resources/src/mediawiki/page/gallery-print.css [deleted file]
resources/src/mediawiki/page/gallery.css
resources/src/mediawiki/page/gallery.print.css [new file with mode: 0644]
resources/src/mediawiki/page/rollback.js
tests/TestsAutoLoader.php [deleted file]
tests/browser/environments.yml
tests/common/TestSetup.php [new file with mode: 0644]
tests/common/TestsAutoLoader.php [new file with mode: 0644]
tests/parser/DbTestPreviewer.php [new file with mode: 0644]
tests/parser/DbTestRecorder.php [new file with mode: 0644]
tests/parser/DjVuSupport.php [new file with mode: 0644]
tests/parser/MultiTestRecorder.php [new file with mode: 0644]
tests/parser/ParserTestParserHook.php [new file with mode: 0644]
tests/parser/ParserTestPrinter.php [new file with mode: 0644]
tests/parser/ParserTestResult.php
tests/parser/ParserTestResultNormalizer.php [new file with mode: 0644]
tests/parser/ParserTestRunner.php [new file with mode: 0644]
tests/parser/PhpunitTestRecorder.php [new file with mode: 0644]
tests/parser/README
tests/parser/TestFileReader.php [new file with mode: 0644]
tests/parser/TestRecorder.php [new file with mode: 0644]
tests/parser/TidySupport.php [new file with mode: 0644]
tests/parser/fuzzTest.php [new file with mode: 0644]
tests/parser/parserTest.inc [deleted file]
tests/parser/parserTests.php [new file with mode: 0644]
tests/parser/parserTests.txt
tests/parser/parserTestsParserHook.php [deleted file]
tests/parserTests.php [deleted file]
tests/phpunit/Makefile
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/FauxRequestTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MWTimestampTest.php
tests/phpunit/includes/PrefixSearchTest.php
tests/phpunit/includes/StatusTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/WebRequestTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/ApiErrorFormatterTest.php
tests/phpunit/includes/api/ApiLoginTest.php
tests/phpunit/includes/api/ApiPageSetTest.php
tests/phpunit/includes/api/ApiQueryAllPagesTest.php
tests/phpunit/includes/api/MockApi.php
tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php
tests/phpunit/includes/api/query/ApiQueryTest.php
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/auth/AuthenticationResponseTest.php
tests/phpunit/includes/content/JsonContentHandlerTest.php [new file with mode: 0644]
tests/phpunit/includes/content/WikitextStructureTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/DatabaseSQLTest.php
tests/phpunit/includes/db/DatabaseTest.php
tests/phpunit/includes/db/DatabaseTestHelper.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/deferred/DeferredUpdatesTest.php
tests/phpunit/includes/deferred/LinksUpdateTest.php
tests/phpunit/includes/filerepo/file/FileTest.php
tests/phpunit/includes/libs/SamplingStatsdClientTest.php
tests/phpunit/includes/libs/WaitConditionLoopTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/time/ConvertableTimestampTest.php [new file with mode: 0644]
tests/phpunit/includes/linker/LinkRendererTest.php
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/phpunit/includes/page/WikiPageTest.php
tests/phpunit/includes/pager/ReverseChronologicalPagerTest.php [new file with mode: 0644]
tests/phpunit/includes/parser/MediaWikiParserTest.php [deleted file]
tests/phpunit/includes/parser/NewParserTest.php [deleted file]
tests/phpunit/includes/parser/ParserIntegrationTest.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/DerivativeResourceLoaderContextTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php
tests/phpunit/includes/search/SearchEngineTest.php
tests/phpunit/includes/user/BotPasswordTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/utils/FileContentsHasherTest.php
tests/phpunit/includes/utils/MWCryptHashTest.php
tests/phpunit/phpunit.php
tests/phpunit/structure/ContentHandlerSanityTest.php [new file with mode: 0644]
tests/phpunit/suite.xml
tests/phpunit/suites/CoreParserTestSuite.php [new file with mode: 0644]
tests/phpunit/suites/ExtensionsParserTestSuite.php
tests/phpunit/suites/ParserTestFileSuite.php [new file with mode: 0644]
tests/phpunit/suites/ParserTestTopLevelSuite.php [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
tests/testHelpers.inc [deleted file]

index 1463c6d..c3a91c4 100644 (file)
@@ -26,9 +26,13 @@ production.
   https://www.mediawiki.org/beacon with basic information about the local
   MediaWiki installation. This data includes, for example, the type of system,
   PHP version, and chosen database backend. This behavior is off by default.
-* When $wgEditButtonPublishNotSave is true, MediaWiki will label the button to
-  store-to-database-and-show-to-others as "Publish page"/"Publish changes"; if
-  false, the default, they will be "Save page"/"Save changes".
+* When $EditSubmitButtonLabelPublish is true, MediaWiki will label the button
+  to store-to-database-and-show-to-others as "Publish page"/"Publish changes";
+  if false, the default, they will be "Save page"/"Save changes".
+* The 'editcontentmodel' permission is now granted to all logged-in users ('user').
+  instead of just administrators ('sysop'). Documentation for this feature is
+  available at <https://www.mediawiki.org/wiki/Help:ChangeContentModel>.
+* $wgRevisionCacheExpiry is now set to one week by default instead of being disabled.
 
 === New features in 1.28 ===
 * User::isBot() method for checking if an account is a bot role account.
@@ -45,6 +49,17 @@ production.
   to 'uca-default-u-kn' or 'uca-<langcode>-u-kn'. If you can't use UCA collations,
   a 'numeric' collation is also available. If migrating from another
   collation, you will need to run the updateCollation.php maintenance script.
+* Two new codes have been added to #time parser function: "xit" for days in current
+  month, and "xiz" for days passed in the year, both in Iranian calendar.
+* mw.Api has a new option, useUS, to use U+001F (Unit Separator) when
+  appropriate for sending multi-valued parameters. This defaults to true when
+  the mw.Api instance seems to be for the local wiki.
+* After a client performs an action which alters a database that has replica databases,
+  MediaWiki will wait for the replica databases to synchronize with the master database
+  while it renders the HTML output. However, if the output is a redirect to another wiki
+  on the wiki farm with a different domain, MediaWiki will instead alter the redirect
+  URL to include a ?cpPosTime parameter that triggers the database synchronization when
+  the URL is followed by the client. The same-domain case uses a new cpPosTime cookie.
 
 === External library changes in 1.28 ===
 
@@ -82,6 +97,17 @@ production.
   action=createaccount, action=linkaccount, and action=changeauthenticationdata
   in the query string is now deprecated and outputs a warning. They should be
   submitted in the POST body instead.
+* (T141960) Multi-valued parameters may now be separated using U+001F (Unit Separator)
+  instead of the pipe character. This will be useful if some of the multiple
+  values need to contain pipes, e.g. for action=options.
+* The API will now warn if input is not NFC-normalized Unicode or if it
+  contains invalid characters.
+* The 'normalized' list output by action=query and other modules that use
+  ApiPageSet may contain entries where the 'from' value is percent-encoded as
+  the raw value cannot be represented in a valid API response. These are
+  indicated by a 'fromencoded' boolean alongside the existing 'from' parameter.
+* (T28680) action=paraminfo can now return info about all submodules of a
+  module without listing them all explicitly.
 
 === Action API internal changes in 1.28 ===
 * Added a new hook, 'ApiMakeParserOptions', to allow extensions to better
@@ -112,10 +138,13 @@ changes to languages because of Phabricator reports.
   login and visiting the login page while already logged in.
 * ResourceLoader::makeLoaderURL() was removed (deprecated since 1.24).
 * $.fn.liveAndTestAtStart was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyPrefix was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyRegexp was removed (deprecated since 1.24).
 * Linker::link() and Linker::linkKnown() were deprecated; please instead use
   MediaWiki\Linker\LinkRenderer. In addition, the LinkBegin and LinkEnd hooks
   were replaced by HtmlPageLinkRendererBegin and HtmlPageLinkRendererEnd
   respectively. See docs/hooks.txt for the specific changes needed for those hooks.
+* Linker::formatSize() was deprecated. Use Language::formatSize() directly.
 * Aliases for Linker methods, deprecated since 1.21, were removed from Skin:
   * Skin::commentBlock() (use Linker::commentBlock() instead)
   * Skin::generateRollback() (use Linker::generateRollback() instead)
@@ -137,6 +166,12 @@ changes to languages because of Phabricator reports.
 * OOjs UI PHP widgets constructed with the `'infusable' => true` config option
   will no longer be automatically infused. You should call `OO.ui.infuse()`
   on them yourself from your JavaScript code.
+* parserTests.php has moved to tests/parser/parserTests.php
+* The command line options specific to parser tests have been removed from
+  phpunit.php: --regex and --keep-uploads. Instead of --regex, use --filter.
+  Instead of --keep-uploads, use the same option to parserTests.php, but you
+  must specify a directory with --upload-dir.
+* The 'jquery.arrowSteps' ResourceLoader module is now deprecated.
 
 == Compatibility ==
 
index 39102fd..035c152 100644 (file)
@@ -153,6 +153,7 @@ $wgAutoloadLocalClasses = [
        'AtomFeed' => __DIR__ . '/includes/Feed.php',
        'AtomicSectionUpdate' => __DIR__ . '/includes/deferred/AtomicSectionUpdate.php',
        'AttachLatest' => __DIR__ . '/maintenance/attachLatest.php',
+       'AugmentPageProps' => __DIR__ . '/includes/search/AugmentPageProps.php',
        'AuthManagerSpecialPage' => __DIR__ . '/includes/specialpage/AuthManagerSpecialPage.php',
        'AuthPlugin' => __DIR__ . '/includes/AuthPlugin.php',
        'AuthPluginUser' => __DIR__ . '/includes/AuthPlugin.php',
@@ -189,7 +190,7 @@ $wgAutoloadLocalClasses = [
        'BitmapHandler' => __DIR__ . '/includes/media/Bitmap.php',
        'BitmapHandler_ClientOnly' => __DIR__ . '/includes/media/Bitmap_ClientOnly.php',
        'BitmapMetadataHandler' => __DIR__ . '/includes/media/BitmapMetadataHandler.php',
-       'Blob' => __DIR__ . '/includes/db/DatabaseUtility.php',
+       'Blob' => __DIR__ . '/includes/libs/rdbms/encasing/Blob.php',
        'Block' => __DIR__ . '/includes/Block.php',
        'BlockLevelPass' => __DIR__ . '/includes/parser/BlockLevelPass.php',
        'BlockListPager' => __DIR__ . '/includes/specials/pagers/BlockListPager.php',
@@ -241,7 +242,7 @@ $wgAutoloadLocalClasses = [
        'CheckStorage' => __DIR__ . '/maintenance/storage/checkStorage.php',
        'CheckSyntax' => __DIR__ . '/maintenance/checkSyntax.php',
        'CheckUsernames' => __DIR__ . '/maintenance/checkUsernames.php',
-       'ChronologyProtector' => __DIR__ . '/includes/db/ChronologyProtector.php',
+       'ChronologyProtector' => __DIR__ . '/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php',
        'ClassCollector' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
        'CleanupAncientTables' => __DIR__ . '/maintenance/cleanupAncientTables.php',
        'CleanupBlocks' => __DIR__ . '/maintenance/cleanupBlocks.php',
@@ -280,6 +281,7 @@ $wgAutoloadLocalClasses = [
        'ConvertExtensionToRegistration' => __DIR__ . '/maintenance/convertExtensionToRegistration.php',
        'ConvertLinks' => __DIR__ . '/maintenance/convertLinks.php',
        'ConvertUserOptions' => __DIR__ . '/maintenance/convertUserOptions.php',
+       'ConvertableTimestamp' => __DIR__ . '/includes/libs/time/ConvertableTimestamp.php',
        'ConverterRule' => __DIR__ . '/languages/ConverterRule.php',
        'Cookie' => __DIR__ . '/includes/libs/Cookie.php',
        'CookieJar' => __DIR__ . '/includes/libs/CookieJar.php',
@@ -297,24 +299,26 @@ $wgAutoloadLocalClasses = [
        'CsvStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'CurlHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'DBAccessBase' => __DIR__ . '/includes/dao/DBAccessBase.php',
-       'DBAccessError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
+       'DBAccessError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
        'DBAccessObjectUtils' => __DIR__ . '/includes/dao/DBAccessObjectUtils.php',
-       'DBConnRef' => __DIR__ . '/includes/db/DBConnRef.php',
-       'DBConnectionError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBExpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
+       'DBConnRef' => __DIR__ . '/includes/libs/rdbms/database/DBConnRef.php',
+       'DBConnectionError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBExpectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
        'DBFileJournal' => __DIR__ . '/includes/filebackend/filejournal/DBFileJournal.php',
        'DBLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
-       'DBMasterPos' => __DIR__ . '/includes/db/DatabaseUtility.php',
-       'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBReadOnlyError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBReplicationWaitError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
+       'DBMasterPos' => __DIR__ . '/includes/libs/rdbms/database/position/DBMasterPos.php',
+       'DBQueryError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBReadOnlyError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBReplicationWaitError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
-       'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
+       'DBTransactionError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBTransactionSizeError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
+       'DBUnexpectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
        'DataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
-       'Database' => __DIR__ . '/includes/db/Database.php',
-       'DatabaseBase' => __DIR__ . '/includes/db/Database.php',
+       'Database' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
+       'DatabaseBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseBase.php',
+       'DatabaseDomain' => __DIR__ . '/includes/libs/rdbms/database/DatabaseDomain.php',
        'DatabaseInstaller' => __DIR__ . '/includes/installer/DatabaseInstaller.php',
        'DatabaseLag' => __DIR__ . '/maintenance/lag.php',
        'DatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
@@ -439,7 +443,7 @@ $wgAutoloadLocalClasses = [
        'FakeAuthTemplate' => __DIR__ . '/includes/specialpage/LoginSignupSpecialPage.php',
        'FakeConverter' => __DIR__ . '/languages/FakeConverter.php',
        'FakeMaintenance' => __DIR__ . '/maintenance/Maintenance.php',
-       'FakeResultWrapper' => __DIR__ . '/includes/db/DatabaseUtility.php',
+       'FakeResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/FakeResultWrapper.php',
        'FatalError' => __DIR__ . '/includes/exception/FatalError.php',
        'FauxRequest' => __DIR__ . '/includes/FauxRequest.php',
        'FauxResponse' => __DIR__ . '/includes/WebResponse.php',
@@ -447,7 +451,7 @@ $wgAutoloadLocalClasses = [
        'FeedUtils' => __DIR__ . '/includes/FeedUtils.php',
        'FetchText' => __DIR__ . '/maintenance/fetchText.php',
        'FewestrevisionsPage' => __DIR__ . '/includes/specials/SpecialFewestrevisions.php',
-       'Field' => __DIR__ . '/includes/db/DatabaseUtility.php',
+       'Field' => __DIR__ . '/includes/libs/rdbms/field/Field.php',
        'File' => __DIR__ . '/includes/filerepo/file/File.php',
        'FileAwareNodeVisitor' => __DIR__ . '/maintenance/findDeprecated.php',
        'FileBackend' => __DIR__ . '/includes/filebackend/FileBackend.php',
@@ -509,7 +513,7 @@ $wgAutoloadLocalClasses = [
        'GenericArrayObject' => __DIR__ . '/includes/libs/GenericArrayObject.php',
        'GetConfiguration' => __DIR__ . '/maintenance/getConfiguration.php',
        'GetLagTimes' => __DIR__ . '/maintenance/getLagTimes.php',
-       'GetSlaveServer' => __DIR__ . '/maintenance/getSlaveServer.php',
+       'GetSlaveServer' => __DIR__ . '/maintenance/getReplicaServer.php',
        'GetTextMaint' => __DIR__ . '/maintenance/getText.php',
        'GitInfo' => __DIR__ . '/includes/GitInfo.php',
        'GlobalDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
@@ -575,11 +579,13 @@ $wgAutoloadLocalClasses = [
        'ICacheHelper' => __DIR__ . '/includes/cache/CacheHelper.php',
        'IContextSource' => __DIR__ . '/includes/context/IContextSource.php',
        'IDBAccessObject' => __DIR__ . '/includes/dao/IDBAccessObject.php',
-       'IDatabase' => __DIR__ . '/includes/db/IDatabase.php',
+       'IDatabase' => __DIR__ . '/includes/libs/rdbms/database/IDatabase.php',
        'IEContentAnalyzer' => __DIR__ . '/includes/libs/IEContentAnalyzer.php',
        'IEUrlExtension' => __DIR__ . '/includes/libs/IEUrlExtension.php',
        'IExpiringStore' => __DIR__ . '/includes/libs/objectcache/IExpiringStore.php',
        'IJobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
+       'ILoadBalancer' => __DIR__ . '/includes/libs/rdbms/loadbalancer/ILoadBalancer.php',
+       'ILoadMonitor' => __DIR__ . '/includes/libs/rdbms/loadmonitor/ILoadMonitor.php',
        'IP' => __DIR__ . '/includes/utils/IP.php',
        'IPSet' => __DIR__ . '/includes/compat/IPSetCompat.php',
        'IPTC' => __DIR__ . '/includes/media/IPTC.php',
@@ -651,8 +657,8 @@ $wgAutoloadLocalClasses = [
        'JsonContentHandler' => __DIR__ . '/includes/content/JsonContentHandler.php',
        'KkConverter' => __DIR__ . '/languages/classes/LanguageKk.php',
        'KuConverter' => __DIR__ . '/languages/classes/LanguageKu.php',
-       'LBFactory' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
-       'LBFactoryFake' => __DIR__ . '/includes/db/loadbalancer/LBFactoryFake.php',
+       'LBFactory' => __DIR__ . '/includes/libs/rdbms/lbfactory/LBFactory.php',
+       'LBFactoryMW' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMW.php',
        'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php',
        'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php',
        'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
@@ -713,7 +719,7 @@ $wgAutoloadLocalClasses = [
        'LegacyLogFormatter' => __DIR__ . '/includes/logging/LogFormatter.php',
        'License' => __DIR__ . '/includes/Licenses.php',
        'Licenses' => __DIR__ . '/includes/Licenses.php',
-       'LikeMatch' => __DIR__ . '/includes/db/DatabaseUtility.php',
+       'LikeMatch' => __DIR__ . '/includes/libs/rdbms/encasing/LikeMatch.php',
        'LinkBatch' => __DIR__ . '/includes/cache/LinkBatch.php',
        'LinkCache' => __DIR__ . '/includes/cache/LinkCache.php',
        'LinkFilter' => __DIR__ . '/includes/LinkFilter.php',
@@ -726,11 +732,11 @@ $wgAutoloadLocalClasses = [
        'ListToggle' => __DIR__ . '/includes/ListToggle.php',
        'ListVariants' => __DIR__ . '/maintenance/language/listVariants.php',
        'ListredirectsPage' => __DIR__ . '/includes/specials/SpecialListredirects.php',
-       'LoadBalancer' => __DIR__ . '/includes/db/loadbalancer/LoadBalancer.php',
+       'LoadBalancer' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancer.php',
        'LoadBalancerSingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
-       'LoadMonitor' => __DIR__ . '/includes/db/loadbalancer/LoadMonitor.php',
-       'LoadMonitorMySQL' => __DIR__ . '/includes/db/loadbalancer/LoadMonitorMySQL.php',
-       'LoadMonitorNull' => __DIR__ . '/includes/db/loadbalancer/LoadMonitor.php',
+       'LoadMonitor' => __DIR__ . '/includes/libs/rdbms/loadmonitor/LoadMonitor.php',
+       'LoadMonitorMySQL' => __DIR__ . '/includes/libs/rdbms/loadmonitor/LoadMonitorMySQL.php',
+       'LoadMonitorNull' => __DIR__ . '/includes/libs/rdbms/loadmonitor/LoadMonitorNull.php',
        'LocalFile' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
        'LocalFileDeleteBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
        'LocalFileLockError' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
@@ -766,6 +772,7 @@ $wgAutoloadLocalClasses = [
        'MWDocGen' => __DIR__ . '/maintenance/mwdocgen.php',
        'MWException' => __DIR__ . '/includes/exception/MWException.php',
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
+       'MWExceptionRenderer' => __DIR__ . '/includes/exception/MWExceptionRenderer.php',
        'MWGrants' => __DIR__ . '/includes/utils/MWGrants.php',
        'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'MWMemcached' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
@@ -910,7 +917,7 @@ $wgAutoloadLocalClasses = [
        'MemcLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MemcLockManager.php',
        'MemcachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedBagOStuff.php',
        'MemcachedClient' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
-       'MemcachedPeclBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedPeclBagOStuff.php',
+       'MemcachedPeclBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedPeclBagOStuff.php',
        'MemcachedPhpBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedPhpBagOStuff.php',
        'MemoizedCallable' => __DIR__ . '/includes/libs/MemoizedCallable.php',
        'MemoryFileBackend' => __DIR__ . '/includes/filebackend/MemoryFileBackend.php',
@@ -940,10 +947,10 @@ $wgAutoloadLocalClasses = [
        'MoveLogFormatter' => __DIR__ . '/includes/logging/MoveLogFormatter.php',
        'MovePage' => __DIR__ . '/includes/MovePage.php',
        'MovePageForm' => __DIR__ . '/includes/specials/SpecialMovepage.php',
-       'MssqlBlob' => __DIR__ . '/includes/db/DatabaseMssql.php',
-       'MssqlField' => __DIR__ . '/includes/db/DatabaseMssql.php',
+       'MssqlBlob' => __DIR__ . '/includes/libs/rdbms/encasing/MssqlBlob.php',
+       'MssqlField' => __DIR__ . '/includes/libs/rdbms/field/MssqlField.php',
        'MssqlInstaller' => __DIR__ . '/includes/installer/MssqlInstaller.php',
-       'MssqlResultWrapper' => __DIR__ . '/includes/db/DatabaseMssql.php',
+       'MssqlResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php',
        'MssqlUpdater' => __DIR__ . '/includes/installer/MssqlUpdater.php',
        'MultiConfig' => __DIR__ . '/includes/config/MultiConfig.php',
        'MultiHttpClient' => __DIR__ . '/includes/libs/MultiHttpClient.php',
@@ -951,8 +958,8 @@ $wgAutoloadLocalClasses = [
        'MutableConfig' => __DIR__ . '/includes/config/MutableConfig.php',
        'MutableContext' => __DIR__ . '/includes/context/MutableContext.php',
        'MwSql' => __DIR__ . '/maintenance/sql.php',
-       'MySQLField' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
-       'MySQLMasterPos' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
+       'MySQLField' => __DIR__ . '/includes/libs/rdbms/field/MySQLField.php',
+       'MySQLMasterPos' => __DIR__ . '/includes/libs/rdbms/database/position/MySQLMasterPos.php',
        'MySqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MySqlLockManager.php',
        'MysqlInstaller' => __DIR__ . '/includes/installer/MysqlInstaller.php',
        'MysqlUpdater' => __DIR__ . '/includes/installer/MysqlUpdater.php',
@@ -977,11 +984,10 @@ $wgAutoloadLocalClasses = [
        'NullStatsdDataFactory' => __DIR__ . '/includes/libs/stats/NullStatsdDataFactory.php',
        'NumericUppercaseCollation' => __DIR__ . '/includes/collation/NumericUppercaseCollation.php',
        'OOUIHTMLForm' => __DIR__ . '/includes/htmlform/OOUIHTMLForm.php',
-       'ORAField' => __DIR__ . '/includes/db/DatabaseOracle.php',
+       'ORAField' => __DIR__ . '/includes/libs/rdbms/field/ORAField.php',
        'ORAResult' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
        'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php',
-       'ObjectFileCache' => __DIR__ . '/includes/cache/ObjectFileCache.php',
        'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
        'OldLocalFile' => __DIR__ . '/includes/filerepo/file/OldLocalFile.php',
        'OracleInstaller' => __DIR__ . '/includes/installer/OracleInstaller.php',
@@ -1041,6 +1047,7 @@ $wgAutoloadLocalClasses = [
        'PatrolLog' => __DIR__ . '/includes/logging/PatrolLog.php',
        'PatrolLogFormatter' => __DIR__ . '/includes/logging/PatrolLogFormatter.php',
        'Pbkdf2Password' => __DIR__ . '/includes/password/Pbkdf2Password.php',
+       'PerRowAugmentor' => __DIR__ . '/includes/search/PerRowAugmentor.php',
        'PermissionsError' => __DIR__ . '/includes/exception/PermissionsError.php',
        'PhpHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php',
@@ -1063,7 +1070,7 @@ $wgAutoloadLocalClasses = [
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
        'PopulateRevisionSha1' => __DIR__ . '/maintenance/populateRevisionSha1.php',
        'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/PostgreSqlLockManager.php',
-       'PostgresBlob' => __DIR__ . '/includes/db/DatabasePostgres.php',
+       'PostgresBlob' => __DIR__ . '/includes/libs/rdbms/encasing/PostgresBlob.php',
        'PostgresField' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php',
        'PostgresUpdater' => __DIR__ . '/includes/installer/PostgresUpdater.php',
@@ -1180,7 +1187,9 @@ $wgAutoloadLocalClasses = [
        'ResourceLoaderUserTokensModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserTokensModule.php',
        'ResourceLoaderWikiModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderWikiModule.php',
        'RestbaseVirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/RestbaseVirtualRESTService.php',
-       'ResultWrapper' => __DIR__ . '/includes/db/DatabaseUtility.php',
+       'ResultAugmentor' => __DIR__ . '/includes/search/ResultAugmentor.php',
+       'ResultSetAugmentor' => __DIR__ . '/includes/search/ResultSetAugmentor.php',
+       'ResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php',
        'RevDelArchiveItem' => __DIR__ . '/includes/revisiondelete/RevDelArchiveItem.php',
        'RevDelArchiveList' => __DIR__ . '/includes/revisiondelete/RevDelArchiveList.php',
        'RevDelArchivedFileItem' => __DIR__ . '/includes/revisiondelete/RevDelArchivedFileItem.php',
@@ -1212,7 +1221,7 @@ $wgAutoloadLocalClasses = [
        'RowUpdateGenerator' => __DIR__ . '/includes/utils/RowUpdateGenerator.php',
        'RunJobs' => __DIR__ . '/maintenance/runJobs.php',
        'RunningStat' => __DIR__ . '/includes/compat/RunningStatCompat.php',
-       'SQLiteField' => __DIR__ . '/includes/db/DatabaseSqlite.php',
+       'SQLiteField' => __DIR__ . '/includes/libs/rdbms/field/SQLiteField.php',
        'SVGMetadataExtractor' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
        'SVGReader' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
        'SamplingStatsdClient' => __DIR__ . '/includes/libs/SamplingStatsdClient.php',
@@ -1396,6 +1405,7 @@ $wgAutoloadLocalClasses = [
        'TempFSFile' => __DIR__ . '/includes/filebackend/TempFSFile.php',
        'TempFileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php',
        'TemplateParser' => __DIR__ . '/includes/TemplateParser.php',
+       'TemplatesOnThisPageFormatter' => __DIR__ . '/includes/TemplatesOnThisPageFormatter.php',
        'TestFileOpPerformance' => __DIR__ . '/maintenance/fileOpPerfTest.php',
        'TextContent' => __DIR__ . '/includes/content/TextContent.php',
        'TextContentHandler' => __DIR__ . '/includes/content/TextContentHandler.php',
@@ -1407,7 +1417,7 @@ $wgAutoloadLocalClasses = [
        'ThumbnailRenderJob' => __DIR__ . '/includes/jobqueue/jobs/ThumbnailRenderJob.php',
        'TidyUpBug37714' => __DIR__ . '/maintenance/tidyUpBug37714.php',
        'TiffHandler' => __DIR__ . '/includes/media/Tiff.php',
-       'TimestampException' => __DIR__ . '/includes/exception/TimestampException.php',
+       'TimestampException' => __DIR__ . '/includes/libs/time/TimestampException.php',
        'Timing' => __DIR__ . '/includes/libs/Timing.php',
        'Title' => __DIR__ . '/includes/Title.php',
        'TitleArray' => __DIR__ . '/includes/TitleArray.php',
@@ -1419,7 +1429,7 @@ $wgAutoloadLocalClasses = [
        'TitleValue' => __DIR__ . '/includes/title/TitleValue.php',
        'TrackBlobs' => __DIR__ . '/maintenance/storage/trackBlobs.php',
        'TraditionalImageGallery' => __DIR__ . '/includes/gallery/TraditionalImageGallery.php',
-       'TransactionProfiler' => __DIR__ . '/includes/profiler/TransactionProfiler.php',
+       'TransactionProfiler' => __DIR__ . '/includes/libs/rdbms/TransactionProfiler.php',
        'TransformParameterError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
        'TransformTooBigImageAreaError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
        'TransformationalImageHandler' => __DIR__ . '/includes/media/TransformationalImageHandler.php',
@@ -1498,6 +1508,7 @@ $wgAutoloadLocalClasses = [
        'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
        'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
+       'WaitConditionLoop' => __DIR__ . '/includes/libs/WaitConditionLoop.php',
        'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
index 2243b7c..eedaa4e 100644 (file)
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.17.8",
+               "oojs/oojs-ui": "0.17.9",
                "oyejorge/less.php": "1.7.0.10",
                "php": ">=5.5.9",
                "psr/log": "1.0.0",
index a0ee8bd..ae0770b 100644 (file)
@@ -596,7 +596,7 @@ $outputPage: OutputPage that can be used to append the output.
 &$user: the user that deleted the article
 $reason: the reason the article was deleted
 $id: id of the article that was deleted
-$content: the Content of the deleted page
+$content: the Content of the deleted page (or null, when deleting a broken page)
 $logEntry: the ManualLogEntry used to record the deletion
 $archivedRevisionCount: the number of revisions archived during the deletion
 
@@ -1937,8 +1937,8 @@ LinkRenderer, before processing starts.  Return false to skip default
 processing and return $ret.
 $linkRenderer: the LinkRenderer object
 $target: the LinkTarget that the link is pointing to
-&$html: the contents that the <a> tag should have (raw HTML); null means
-  "default".
+&$text: the contents that the <a> tag should have; either a plain, unescaped
+  string or a HtmlArmor object; null means "default".
 &$customAttribs: the HTML attributes that the <a> tag should have, in
   associative array form, with keys and values unescaped.  Should be merged
   with default values, with a value of false meaning to suppress the
@@ -1955,7 +1955,8 @@ return false, $ret will be returned.
 $linkRenderer: the LinkRenderer object
 $target: the LinkTarget object that the link is pointing to
 $isKnown: boolean indicating whether the page is known or not
-&$html: the final (raw HTML) contents of the <a> tag, after processing.
+&$text: the contents that the <a> tag should have; either a plain, unescaped
+  string or a HtmlArmor object.
 &$attribs: the final HTML attributes of the <a> tag, after processing, in
   associative array form.
 &$ret: the value to return if your hook returns false.
@@ -2698,6 +2699,13 @@ $page: WikiPage that is being indexed
 $output: ParserOutput that is produced from the page
 $engine: SearchEngine for which the indexing is intended
 
+'SearchResultsAugment': Allows extension to add its code to the list of search
+result augmentors.
+&$setAugmentors: List of whole-set augmentor objects, must implement ResultSetAugmentor
+&$rowAugmentors: List of per-row augmentor objects, must implement ResultAugmentor.
+Note that lists should be in the format name => object and the names in both lists should
+be distinct.
+
 'SecondaryDataUpdates': Allows modification of the list of DataUpdates to
 perform when page content is modified. Currently called by
 AbstractContent::getSecondaryDataUpdates.
@@ -3649,6 +3657,16 @@ $userId: User id of the current user
 $userText: User name of the current user
 &$items: Array of user tool links as HTML fragments
 
+'UsersPagerDoBatchLookups': Called in UsersPager::doBatchLookups() to give
+extensions providing user group data from an alternate source a chance to add
+their data into the cache array so that things like global user groups are
+displayed correctly in Special:ListUsers.
+$dbr: Read-only database handle
+$userIds: Array of user IDs whose groups we should look up
+&$cache: Array of user ID -> internal user group name (e.g. 'sysop') mappings
+&$groups: Array of group name -> bool true mappings for members of a given user
+group
+
 'ValidateExtendedMetadataCache': Called to validate the cached metadata in
 FormatMetadata::getExtendedMeta (return false means cache will be
 invalidated and GetExtendedMetadata hook called again).
@@ -3721,7 +3739,8 @@ a page is deleted. Called in WikiPage::getDeletionUpdates(). Note that updates
 specific to a content model should be provided by the respective Content's
 getDeletionUpdates() method.
 $page: the WikiPage
-$content: the Content to generate updates for
+$content: the Content to generate updates for (or null, if the Content could not be loaded
+due to an error)
 &$updates: the array of DataUpdate objects. Hook function may want to add to it.
 
 'XmlDumpWriterOpenPage': Called at the end of XmlDumpWriter::openPage, to allow
diff --git a/docs/uidesign/mediawiki.action.history.diff.html b/docs/uidesign/mediawiki.action.history.diff.html
deleted file mode 100644 (file)
index 615558f..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-<!DOCTYPE html>
-<html lang="en" dir="ltr">
-<head>
-       <meta charset="utf-8">
-       <link rel="stylesheet" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">
-       <link rel="stylesheet" media="print" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">
-</head>
-<body>
-
-<p>This show various styles for our diff action. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">resources/src/mediawiki.action/mediawiki.action.history.diff.css</a></code>.</p>
-<p>This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
-<p>Try it out in print mode, too. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">resources/src/mediawiki.action/mediawiki.action.history.diff.print.css</a></code>.</p>
-
-<p>Practical example copied from MediaWiki's HTML output:</p>
-
-<table class="diff diff-contentalign-left">
-       <colgroup><col class="diff-marker">
-       <col class="diff-content">
-       <col class="diff-marker">
-       <col class="diff-content">
-       </colgroup>
-<tbody>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Lorem ipsum dolor sit amet<del class="diffchange diffchange-inline">, consectetur adipisicing elit</del>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"></td>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim<del class="diffchange diffchange-inline"> id est laborum</del>.</div></td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Excepteur sint occaecat cupidatat non proident, sunt<ins class="diffchange diffchange-inline"> reprehenderit in voluptate</ins> in culpa qui officia deserunt mollit anim.</div></td>
-</tr>
-<tr>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"></td>
-</tr>
-<tr>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
-</tr>
-</tbody></table>
-
-<p>Below are some basic lines being applied one or two classes. Mainly for debugging purposes.</p>
-
-<table class="diff">
-       <tr><th>Diff</th></tr>
-
-       <tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
-       <tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
-       <tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
-
-       <tr><th>Same as above with a <code>&lt;ins&gt;</code> or <code>&lt;del&gt;</code> child element having the <code>diffchange</code> class:</th></tr>
-
-       <tr><td class="diffchange">Diffchange</td></tr>
-       <tr><td class="diff-addedline"><ins class="diffchange">Added line + diffchange</ins></td></tr>
-       <tr><td class="diff-deletedline"><del class="diffchange">Deleted line + diffchange</del></td></tr>
-</table>
-
-</body>
-</html>
diff --git a/docs/uidesign/mediawiki.diff.html b/docs/uidesign/mediawiki.diff.html
new file mode 100644 (file)
index 0000000..0372595
--- /dev/null
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+       <meta charset="utf-8">
+       <link rel="stylesheet" href="../../resources/src/mediawiki/mediawiki.diff.styles.css">
+       <link rel="stylesheet" media="print" href="../../resources/src/mediawiki/mediawiki.diff.styles.print.css">
+</head>
+<body>
+
+<p>This show various styles for our diff action. Style sheet: <code><a href="../../resources/src/mediawiki/mediawiki.diff.styles.css">resources/src/mediawiki/mediawiki.diff.styles.css</a></code>.</p>
+<p>This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
+<p>Try it out in print mode, too. Style sheet: <code><a href="../../resources/src/mediawiki/mediawiki.diff.styles.print.css">resources/src/mediawiki/mediawiki.diff.styles.print.css</a></code>.</p>
+
+<p>Practical example copied from MediaWiki's HTML output:</p>
+
+<table class="diff diff-contentalign-left">
+       <colgroup><col class="diff-marker">
+       <col class="diff-content">
+       <col class="diff-marker">
+       <col class="diff-content">
+       </colgroup>
+<tbody>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Lorem ipsum dolor sit amet<del class="diffchange diffchange-inline">, consectetur adipisicing elit</del>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim<del class="diffchange diffchange-inline"> id est laborum</del>.</div></td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Excepteur sint occaecat cupidatat non proident, sunt<ins class="diffchange diffchange-inline"> reprehenderit in voluptate</ins> in culpa qui officia deserunt mollit anim.</div></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+</tr>
+</tbody></table>
+
+<p>Below are some basic lines being applied one or two classes. Mainly for debugging purposes.</p>
+
+<table class="diff">
+       <tr><th>Diff</th></tr>
+
+       <tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
+       <tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
+       <tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
+
+       <tr><th>Same as above with a <code>&lt;ins&gt;</code> or <code>&lt;del&gt;</code> child element having the <code>diffchange</code> class:</th></tr>
+
+       <tr><td class="diffchange">Diffchange</td></tr>
+       <tr><td class="diff-addedline"><ins class="diffchange">Added line + diffchange</ins></td></tr>
+       <tr><td class="diff-deletedline"><del class="diffchange">Deleted line + diffchange</del></td></tr>
+</table>
+
+</body>
+</html>
index 79b31bb..19ba0a2 100644 (file)
@@ -145,7 +145,7 @@ class Block {
 
                $this->mReason = $options['reason'];
                $this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
-               $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $options['expiry'] );
+               $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] );
 
                # Boolean settings
                $this->mAuto = (bool)$options['auto'];
@@ -168,7 +168,7 @@ class Block {
         * @return Block|null
         */
        public static function newFromID( $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->selectRow(
                        'ipblocks',
                        self::selectFields(),
@@ -242,7 +242,7 @@ class Block {
         * @return bool Whether a relevant block was found
         */
        protected function newLoad( $vagueTarget = null ) {
-               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_REPLICA );
 
                if ( $this->type !== null ) {
                        $conds = [
@@ -351,7 +351,7 @@ class Block {
                # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
                # so we can improve performance by filtering on a LIKE clause
                $chunk = self::getIpFragment( $start );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $like = $dbr->buildLike( $chunk, $dbr->anyString() );
 
                # Fairly hard to make a malicious SQL statement out of hex characters,
@@ -405,7 +405,7 @@ class Block {
                $this->mParentBlockId = $row->ipb_parent_block_id;
 
                // I wish I didn't have to do this
-               $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $row->ipb_expiry );
+               $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $row->ipb_expiry );
 
                $this->isHardblock( !$row->ipb_anon_only );
                $this->isAutoblocking( $row->ipb_enable_autoblock );
@@ -569,7 +569,7 @@ class Block {
         */
        protected function getDatabaseArray( $db = null ) {
                if ( !$db ) {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $expiry = $db->encodeExpiry( $this->mExpiry );
 
@@ -653,7 +653,7 @@ class Block {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
                $conds = [ 'rc_user_text' => (string)$block->getTarget() ];
@@ -1110,7 +1110,7 @@ class Block {
         * @param array $ipChain List of IPs (strings), usually retrieved from the
         *         X-Forwarded-For header of the request
         * @param bool $isAnon Exclude anonymous-only blocks if false
-        * @param bool $fromMaster Whether to query the master or slave database
+        * @param bool $fromMaster Whether to query the master or replica DB
         * @return array Array of Blocks
         * @since 1.22
         */
@@ -1146,7 +1146,7 @@ class Block {
                if ( $fromMaster ) {
                        $db = wfGetDB( DB_MASTER );
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $conds = $db->makeList( $conds, LIST_OR );
                if ( !$isAnon ) {
index 531e0be..1ebd605 100644 (file)
@@ -60,7 +60,7 @@ class Category {
                        return true;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow(
                        'category',
                        [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
@@ -264,7 +264,7 @@ class Category {
         */
        public function getMembers( $limit = false, $offset = '' ) {
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $conds = [ 'cl_to' => $this->getName(), 'cl_from = page_id' ];
                $options = [ 'ORDER BY' => 'cl_sortkey' ];
index 3d5e6c5..4efa2ff 100644 (file)
@@ -64,7 +64,7 @@ class CategoryFinder {
        /** @var string "AND" or "OR" */
        protected $mode;
 
-       /** @var IDatabase Read-DB slave */
+       /** @var IDatabase Read-DB replica DB */
        protected $dbr;
 
        /**
@@ -96,7 +96,7 @@ class CategoryFinder {
         * @return array Array of page_ids (those given to seed() that match the conditions)
         */
        public function run() {
-               $this->dbr = wfGetDB( DB_SLAVE );
+               $this->dbr = wfGetDB( DB_REPLICA );
                while ( count( $this->next ) > 0 ) {
                        $this->scanNextLayer();
                }
index 490f548..a8e988f 100644 (file)
@@ -286,7 +286,7 @@ class CategoryViewer extends ContextSource {
        }
 
        function doCategoryQuery() {
-               $dbr = wfGetDB( DB_SLAVE, 'category' );
+               $dbr = wfGetDB( DB_REPLICA, 'category' );
 
                $this->nextPage = [
                        'page' => null,
index b7fbbb7..135c3e5 100644 (file)
@@ -1835,13 +1835,6 @@ $wgDBmwschema = null;
  */
 $wgSQLiteDataDir = '';
 
-/**
- * Make all database connections secretly go to localhost. Fool the load balancer
- * thinking there is an arbitrarily large cluster of servers to connect to.
- * Useful for debugging.
- */
-$wgAllDBsAreLocalhost = false;
-
 /**
  * Shared database for multiple wikis. Commonly used for storing a user table
  * for single sign-on. The server for this database must be the same as for the
@@ -1895,7 +1888,7 @@ $wgSharedSchema = false;
  *   - password:    DB password
  *   - type:        DB type
  *
- *   - load:        Ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0.
+ *   - load:        Ratio of DB_REPLICA load, must be >=0, the sum of all loads must be >0.
  *                  If this is zero for any given server, no normal query traffic will be
  *                  sent to it. It will be excluded from lag checks in maintenance scripts.
  *                  The only way it can receive traffic is if groupLoads is used.
@@ -1904,7 +1897,7 @@ $wgSharedSchema = false;
  *                  to several groups, the most specific group defined here is used.
  *
  *   - flags:       bit field
- *                  - DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
+ *                  - DBO_DEFAULT -- turns on DBO_TRX only if "cliMode" is off (recommended)
  *                  - DBO_DEBUG -- equivalent of $wgDebugDumpSql
  *                  - DBO_TRX -- wrap entire request in a transaction
  *                  - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
@@ -1913,8 +1906,11 @@ $wgSharedSchema = false;
  *                  - DBO_COMPRESS -- uses internal compression in database connections,
  *                                    if available
  *
- *   - max lag:     (optional) Maximum replication lag before a slave will taken out of rotation
+ *   - max lag:     (optional) Maximum replication lag before a replica DB goes out of rotation
  *   - is static:   (optional) Set to true if the dataset is static and no replication is used.
+ *   - cliMode:     (optional) Connection handles will not assume that requests are short-lived
+ *                  nor that INSERT..SELECT can be rewritten into a buffered SELECT and INSERT.
+ *                  [Default: uses value of $wgCommandLineMode]
  *
  *   These and any other user-defined properties will be assigned to the mLBInfo member
  *   variable of the Database object.
@@ -1924,15 +1920,15 @@ $wgSharedSchema = false;
  * perhaps in some command-line scripts).
  *
  * The first server listed in this array (with key 0) will be the master. The
- * rest of the servers will be slaves. To prevent writes to your slaves due to
+ * rest of the servers will be replica DBs. To prevent writes to your replica DBs due to
  * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
- * slaves in my.cnf. You can set read_only mode at runtime using:
+ * replica DBs in my.cnf. You can set read_only mode at runtime using:
  *
  * @code
  *     SET @@read_only=1;
  * @endcode
  *
- * Since the effect of writing to a slave is so damaging and difficult to clean
+ * Since the effect of writing to a replica DB is so damaging and difficult to clean
  * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
  * our masters, and then set read_only=0 on masters at runtime.
  */
@@ -2120,7 +2116,7 @@ $wgDefaultExternalStore = false;
  *
  * Set to 0 to disable, or number of seconds before cache expiry.
  */
-$wgRevisionCacheExpiry = 0;
+$wgRevisionCacheExpiry = 86400 * 7;
 
 /** @} */ # end text storage }
 
@@ -2278,7 +2274,8 @@ $wgObjectCaches = [
                        'class' => 'SqlBagOStuff',
                        'args'  => [ [ 'slaveOnly' => false ] ]
                ],
-               'loggroup'  => 'SQLBagOStuff'
+               'loggroup'  => 'SQLBagOStuff',
+               'reportDupes' => false
        ],
 
        'apc' => [ 'class' => 'APCBagOStuff', 'reportDupes' => false ],
@@ -2657,7 +2654,7 @@ $wgInternalServer = false;
 $wgSquidMaxage = 18000;
 
 /**
- * Cache timeout for the CDN when DB slave lag is high
+ * Cache timeout for the CDN when DB replica DB lag is high
  * @see $wgSquidMaxage
  * @since 1.27
  */
@@ -2667,7 +2664,7 @@ $wgCdnMaxageLagged = 30;
  * If set, any SquidPurge call on a URL or URLs will send a second purge no less than
  * this many seconds later via the job queue. This requires delayed job support.
  * This should be safely higher than the 'max lag' value in $wgLBFactoryConf, so that
- * slave lag does not cause page to be stuck in stales states in CDN.
+ * replica DB lag does not cause page to be stuck in stales states in CDN.
  *
  * This also fixes race conditions in two-tiered CDN setups (e.g. cdn2 => cdn1 => MediaWiki).
  * If a purge for a URL reaches cdn2 before cdn1 and a request reaches cdn2 for that URL,
@@ -3200,7 +3197,7 @@ $wgUseMediaWikiUIEverywhere = false;
  *
  * @since 1.28
  */
-$wgEditButtonPublishNotSave = false;
+$wgEditSubmitButtonLabelPublish = false;
 
 /**
  * Permit other namespaces in addition to the w3.org default.
@@ -3756,10 +3753,8 @@ $wgResourceLoaderLESSImportPaths = [
 /**
  * Whether ResourceLoader should attempt to persist modules in localStorage on
  * browsers that support the Web Storage API.
- *
- * @since 1.23 - Client-side module persistence is experimental. Exercise care.
  */
-$wgResourceLoaderStorageEnabled = false;
+$wgResourceLoaderStorageEnabled = true;
 
 /**
  * Cache version for client-side ResourceLoader module storage. You can trigger
@@ -4351,6 +4346,18 @@ $wgEnableScaryTranscluding = false;
  */
 $wgTranscludeCacheExpiry = 3600;
 
+/**
+ * Enable the magic links feature of automatically turning ISBN xxx,
+ * PMID xxx, RFC xxx into links
+ *
+ * @since 1.28
+ */
+$wgEnableMagicLinks = [
+       'ISBN' => true,
+       'PMID' => true,
+       'RFC' => true
+];
+
 /** @} */ # end of parser settings }
 
 /************************************************************************//**
@@ -5075,6 +5082,7 @@ $wgGroupPermissions['user']['purge'] = true;
 $wgGroupPermissions['user']['sendemail'] = true;
 $wgGroupPermissions['user']['applychangetags'] = true;
 $wgGroupPermissions['user']['changetags'] = true;
+$wgGroupPermissions['user']['editcontentmodel'] = true;
 
 // Implicit group for accounts that pass $wgAutoConfirmAge
 $wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -5105,7 +5113,6 @@ $wgGroupPermissions['sysop']['undelete'] = true;
 $wgGroupPermissions['sysop']['editinterface'] = true;
 $wgGroupPermissions['sysop']['editusercss'] = true;
 $wgGroupPermissions['sysop']['edituserjs'] = true;
-$wgGroupPermissions['sysop']['editcontentmodel'] = true;
 $wgGroupPermissions['sysop']['import'] = true;
 $wgGroupPermissions['sysop']['importupload'] = true;
 $wgGroupPermissions['sysop']['move'] = true;
@@ -5579,6 +5586,11 @@ $wgRateLimits = [
                'ip' => [ 8, 60 ],
                'newbie' => [ 8, 60 ],
        ],
+       // Changing the content model of a page
+       'editcontentmodel' => [
+               'newbie' => [ 2, 120 ],
+               'user' => [ 8, 60 ],
+       ],
 ];
 
 /**
@@ -5965,7 +5977,7 @@ $wgTrxProfilerLimits = [
        'POST' => [
                'readQueryTime' => 5,
                'writeQueryTime' => 1,
-               'maxAffected' => 500
+               'maxAffected' => 1000
        ],
        'POST-nonwrite' => [
                'masterConns' => 0,
@@ -5976,7 +5988,7 @@ $wgTrxProfilerLimits = [
        'PostSend' => [
                'readQueryTime' => 5,
                'writeQueryTime' => 1,
-               'maxAffected' => 500
+               'maxAffected' => 1000
        ],
        // Background job runner
        'JobRunner' => [
@@ -6161,6 +6173,14 @@ $wgStatsdServer = false;
  */
 $wgStatsdMetricPrefix = 'MediaWiki';
 
+/**
+ * Sampling rate for statsd metrics as an associative array of patterns and rates.
+ * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+ * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+ * @since 1.28
+ */
+$wgStatsdSamplingRates = [];
+
 /**
  * InfoAction retrieves a list of transclusion links (both to and from).
  * This number puts a limit on that query in the case of highly transcluded
@@ -7210,8 +7230,8 @@ $wgJobTypesExcludedFromDefaultQueue = [ 'AssembleUploadChunks', 'PublishStashedF
 $wgJobBackoffThrottling = [];
 
 /**
- * Make job runners commit changes for slave-lag prone jobs one job at a time.
- * This is useful if there are many job workers that race on slave lag checks.
+ * Make job runners commit changes for replica DB-lag prone jobs one job at a time.
+ * This is useful if there are many job workers that race on replica DB lag checks.
  * If set, jobs taking this many seconds of DB write time have serialized commits.
  *
  * Note that affected jobs may have worse lock contention. Also, if they affect
@@ -7833,7 +7853,7 @@ $wgAPIMaxResultSize = 8388608;
 $wgAPIMaxUncachedDiffs = 1;
 
 /**
- * Maximum amount of DB lag on a majority of DB slaves to tolerate
+ * Maximum amount of DB lag on a majority of DB replica DBs to tolerate
  * before forcing bots to retry any write requests via API errors.
  * This should be lower than the 'max lag' value in $wgLBFactoryConf.
  */
@@ -8265,7 +8285,7 @@ $wgPageLanguageUseDB = false;
  * Global configuration variable for Virtual REST Services.
  *
  * Use the 'path' key to define automatically mounted services. The value for this
- * key is a map of path prefixes to service configuration. The later is an array of:
+ * key is a map of path prefixes to service configuration. The latter is an array of:
  *   - class : the fully qualified class name
  *   - options : map of arguments to the class constructor
  * Such services will be available to handle queries under their path from the VRS
index fe5083e..077f39a 100644 (file)
  * @defgroup Constants MediaWiki constants
  */
 
-/**@{
- * Database related constants
- */
-define( 'DBO_DEBUG', 1 );
-define( 'DBO_NOBUFFER', 2 );
-define( 'DBO_IGNORE', 4 );
-define( 'DBO_TRX', 8 ); // automatically start transaction on first query
-define( 'DBO_DEFAULT', 16 );
-define( 'DBO_PERSISTENT', 32 );
-define( 'DBO_SYSDBA', 64 ); // for oracle maintenance
-define( 'DBO_DDLMODE', 128 ); // when using schema files: mostly for Oracle
-define( 'DBO_SSL', 256 );
-define( 'DBO_COMPRESS', 512 );
-/**@}*/
-
-/**@{
- * Valid database indexes
- * Operation-based indexes
- */
-define( 'DB_SLAVE', -1 );     # Read from the slave (or only server)
-define( 'DB_MASTER', -2 );    # Write to master (or only server)
-/**@}*/
-
 # Obsolete aliases
-define( 'DB_READ', -1 );
-define( 'DB_WRITE', -2 );
+define( 'DB_SLAVE', -1 );
 
 /**@{
  * Virtual namespaces; don't appear in the page database
@@ -186,16 +162,10 @@ define( 'EDIT_AUTOSUMMARY', 64 );
 define( 'EDIT_INTERNAL', 128 );
 /**@}*/
 
-/**@{
- * Flags for Database::makeList()
- * These are also available as Database class constants
+/**
+ * Database related
  */
-define( 'LIST_COMMA', 0 );
-define( 'LIST_AND', 1 );
-define( 'LIST_SET', 2 );
-define( 'LIST_NAMES', 3 );
-define( 'LIST_OR', 4 );
-/**@}*/
+require_once __DIR__ . '/libs/rdbms/defines.php';
 
 /**
  * Unicode and normalisation related
index ba24799..fc94a63 100644 (file)
@@ -453,12 +453,17 @@ class DummyLinker {
                );
        }
 
+       /**
+        * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
+        */
        public function formatTemplates(
                $templates,
                $preview = false,
                $section = false,
                $more = null
        ) {
+               wfDeprecated( __METHOD__, '1.28' );
+
                return Linker::formatTemplates(
                        $templates,
                        $preview,
@@ -471,7 +476,12 @@ class DummyLinker {
                return Linker::formatHiddenCategories( $hiddencats );
        }
 
+       /**
+        * @deprecated since 1.28, use Language::formatSize() directly
+        */
        public function formatSize( $size ) {
+               wfDeprecated( __METHOD__, '1.28' );
+
                return Linker::formatSize( $size );
        }
 
index 0da7e6e..606b4cd 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * The edit page/HTML interface (split from Article)
@@ -507,6 +508,7 @@ class EditPage {
         * the newly-edited page.
         */
        function edit() {
+               global $wgOut, $wgRequest, $wgUser;
                // Allow extensions to modify/prevent this form or submission
                if ( !Hooks::run( 'AlternateEdit', [ $this ] ) ) {
                        return;
@@ -514,15 +516,13 @@ class EditPage {
 
                wfDebug( __METHOD__ . ": enter\n" );
 
-               $request = $this->context->getRequest();
-               $out = $this->context->getOutput();
                // If they used redlink=1 and the page exists, redirect to the main article
-               if ( $request->getBool( 'redlink' ) && $this->mTitle->exists() ) {
-                       $out->redirect( $this->mTitle->getFullURL() );
+               if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) {
+                       $wgOut->redirect( $this->mTitle->getFullURL() );
                        return;
                }
 
-               $this->importFormData( $request );
+               $this->importFormData( $wgRequest );
                $this->firsttime = false;
 
                if ( wfReadOnly() && $this->save ) {
@@ -551,7 +551,7 @@ class EditPage {
                        wfDebug( __METHOD__ . ": User can't edit\n" );
                        // Auto-block user's IP if the account was "hard" blocked
                        if ( !wfReadOnly() ) {
-                               $user = $this->context->getUser();
+                               $user = $wgUser;
                                DeferredUpdates::addCallableUpdate( function () use ( $user ) {
                                        $user->spreadAnyEditBlock();
                                } );
@@ -563,16 +563,29 @@ class EditPage {
 
                $revision = $this->mArticle->getRevisionFetched();
                // Disallow editing revisions with content models different from the current one
-               if ( $revision && $revision->getContentModel() !== $this->contentModel ) {
-                       $this->displayViewSourcePage(
-                               $this->getContentObject(),
-                               wfMessage(
-                                       'contentmodelediterror',
-                                       $revision->getContentModel(),
-                                       $this->contentModel
-                               )->plain()
-                       );
-                       return;
+               // Undo edits being an exception in order to allow reverting content model changes.
+               if ( $revision
+                       && $revision->getContentModel() !== $this->contentModel
+               ) {
+                       $prevRev = null;
+                       if ( $this->undidRev ) {
+                               $undidRevObj = Revision::newFromId( $this->undidRev );
+                               $prevRev = $undidRevObj ? $undidRevObj->getPrevious() : null;
+                       }
+                       if ( !$this->undidRev
+                               || !$prevRev
+                               || $prevRev->getContentModel() !== $this->contentModel
+                       ) {
+                               $this->displayViewSourcePage(
+                                       $this->getContentObject(),
+                                       $this->context->msg(
+                                               'contentmodelediterror',
+                                               $revision->getContentModel(),
+                                               $this->contentModel
+                                       )->plain()
+                               );
+                               return;
+                       }
                }
 
                $this->isConflict = false;
@@ -625,14 +638,15 @@ class EditPage {
         * @return array
         */
        protected function getEditPermissionErrors( $rigor = 'secure' ) {
-               $user = $this->context->getUser();
-               $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $user, $rigor );
+               global $wgUser;
+
+               $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser, $rigor );
                # Can this title be created?
                if ( !$this->mTitle->exists() ) {
                        $permErrors = array_merge(
                                $permErrors,
                                wfArrayDiff2(
-                                       $this->mTitle->getUserPermissionsErrors( 'create', $user, $rigor ),
+                                       $this->mTitle->getUserPermissionsErrors( 'create', $wgUser, $rigor ),
                                        $permErrors
                                )
                        );
@@ -665,12 +679,13 @@ class EditPage {
         * @throws PermissionsError
         */
        protected function displayPermissionsError( array $permErrors ) {
-               $out = $this->context->getOutput();
-               if ( $this->context->getRequest()->getBool( 'redlink' ) ) {
+               global $wgRequest, $wgOut;
+
+               if ( $wgRequest->getBool( 'redlink' ) ) {
                        // The edit page was reached via a red link.
                        // Redirect to the article page and let them click the edit tab if
                        // they really want a permission error.
-                       $out->redirect( $this->mTitle->getFullURL() );
+                       $wgOut->redirect( $this->mTitle->getFullURL() );
                        return;
                }
 
@@ -685,7 +700,7 @@ class EditPage {
 
                $this->displayViewSourcePage(
                        $content,
-                       $out->formatPermissionsErrorMessage( $permErrors, 'edit' )
+                       $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' )
                );
        }
 
@@ -695,28 +710,29 @@ class EditPage {
         * @param string $errorMessage additional wikitext error message to display
         */
        protected function displayViewSourcePage( Content $content, $errorMessage = '' ) {
-               $out = $this->context->getOutput();
-               Hooks::run( 'EditPage::showReadOnlyForm:initial', [ $this, &$out ] );
+               global $wgOut;
 
-               $out->setRobotPolicy( 'noindex,nofollow' );
-               $out->setPageTitle( wfMessage(
+               Hooks::run( 'EditPage::showReadOnlyForm:initial', [ $this, &$wgOut ] );
+
+               $wgOut->setRobotPolicy( 'noindex,nofollow' );
+               $wgOut->setPageTitle( $this->context->msg(
                        'viewsource-title',
                        $this->getContextTitle()->getPrefixedText()
                ) );
-               $out->addBacklinkSubtitle( $this->getContextTitle() );
-               $out->addHTML( $this->editFormPageTop );
-               $out->addHTML( $this->editFormTextTop );
+               $wgOut->addBacklinkSubtitle( $this->getContextTitle() );
+               $wgOut->addHTML( $this->editFormPageTop );
+               $wgOut->addHTML( $this->editFormTextTop );
 
                if ( $errorMessage !== '' ) {
-                       $out->addWikiText( $errorMessage );
-                       $out->addHTML( "<hr />\n" );
+                       $wgOut->addWikiText( $errorMessage );
+                       $wgOut->addHTML( "<hr />\n" );
                }
 
                # If the user made changes, preserve them when showing the markup
                # (This happens when a user is blocked during edit, for instance)
                if ( !$this->firsttime ) {
                        $text = $this->textbox1;
-                       $out->addWikiMsg( 'viewyourtext' );
+                       $wgOut->addWikiMsg( 'viewyourtext' );
                } else {
                        try {
                                $text = $this->toEditText( $content );
@@ -725,21 +741,20 @@ class EditPage {
                                # (e.g. for an old revision with a different model)
                                $text = $content->serialize();
                        }
-                       $out->addWikiMsg( 'viewsourcetext' );
+                       $wgOut->addWikiMsg( 'viewsourcetext' );
                }
 
-               $out->addHTML( $this->editFormTextBeforeContent );
+               $wgOut->addHTML( $this->editFormTextBeforeContent );
                $this->showTextbox( $text, 'wpTextbox1', [ 'readonly' ] );
-               $out->addHTML( $this->editFormTextAfterContent );
+               $wgOut->addHTML( $this->editFormTextAfterContent );
 
-               $out->addHTML( Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
-                       Linker::formatTemplates( $this->getTemplates() ) ) );
+               $wgOut->addHTML( $this->makeTemplatesOnThisPageList( $this->getTemplates() ) );
 
-               $out->addModules( 'mediawiki.action.edit.collapsibleFooter' );
+               $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
 
-               $out->addHTML( $this->editFormTextBottom );
+               $wgOut->addHTML( $this->editFormTextBottom );
                if ( $this->mTitle->exists() ) {
-                       $out->returnToMain( null, $this->mTitle );
+                       $wgOut->returnToMain( null, $this->mTitle );
                }
        }
 
@@ -749,19 +764,18 @@ class EditPage {
         * @return bool
         */
        protected function previewOnOpen() {
-               global $wgPreviewOnOpenNamespaces;
-               $request = $this->context->getRequest();
-               if ( $request->getVal( 'preview' ) == 'yes' ) {
+               global $wgRequest, $wgUser, $wgPreviewOnOpenNamespaces;
+               if ( $wgRequest->getVal( 'preview' ) == 'yes' ) {
                        // Explicit override from request
                        return true;
-               } elseif ( $request->getVal( 'preview' ) == 'no' ) {
+               } elseif ( $wgRequest->getVal( 'preview' ) == 'no' ) {
                        // Explicit override from request
                        return false;
                } elseif ( $this->section == 'new' ) {
                        // Nothing *to* preview for new sections
                        return false;
-               } elseif ( ( $request->getVal( 'preload' ) !== null || $this->mTitle->exists() )
-                       && $this->context->getUser()->getOption( 'previewonfirst' )
+               } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() )
+                       && $wgUser->getOption( 'previewonfirst' )
                ) {
                        // Standard preference behavior
                        return true;
@@ -814,7 +828,7 @@ class EditPage {
         * @throws ErrorPageError
         */
        function importFormData( &$request ) {
-               global $wgContLang;
+               global $wgContLang, $wgUser;
 
                # Section edit can come from either the form or a link
                $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
@@ -926,14 +940,13 @@ class EditPage {
                        $this->watchthis = $request->getCheck( 'wpWatchthis' );
 
                        # Don't force edit summaries when a user is editing their own user or talk page
-                       $user = $this->context->getUser();
                        if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK )
-                               && $this->mTitle->getText() == $user->getName()
+                               && $this->mTitle->getText() == $wgUser->getName()
                        ) {
                                $this->allowBlankSummary = true;
                        } else {
                                $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' )
-                                       || !$user->getOption( 'forceeditsummary' );
+                                       || !$wgUser->getOption( 'forceeditsummary' );
                        }
 
                        $this->autoSumm = $request->getText( 'wpAutoSummary' );
@@ -995,9 +1008,17 @@ class EditPage {
                // May be overridden by revision.
                $this->contentFormat = $request->getText( 'format', $this->contentFormat );
 
-               if ( !ContentHandler::getForModelID( $this->contentModel )
-                       ->isSupportedFormat( $this->contentFormat )
-               ) {
+               try {
+                       $handler = ContentHandler::getForModelID( $this->contentModel );
+               } catch ( MWUnknownContentModelException $e ) {
+                       throw new ErrorPageError(
+                               'editpage-invalidcontentmodel-title',
+                               'editpage-invalidcontentmodel-text',
+                               [ $this->contentModel ]
+                       );
+               }
+
+               if ( !$handler->isSupportedFormat( $this->contentFormat ) ) {
                        throw new ErrorPageError(
                                'editpage-notsupportedcontentformat-title',
                                'editpage-notsupportedcontentformat-text',
@@ -1039,6 +1060,7 @@ class EditPage {
         * @return bool If the requested section is valid
         */
        function initialiseForm() {
+               global $wgUser;
                $this->edittime = $this->page->getTimestamp();
                $this->editRevId = $this->page->getLatest();
 
@@ -1047,21 +1069,20 @@ class EditPage {
                        return false;
                }
                $this->textbox1 = $this->toEditText( $content );
-               $user = $this->context->getUser();
 
                // activate checkboxes if user wants them to be always active
                # Sort out the "watch" checkbox
-               if ( $user->getOption( 'watchdefault' ) ) {
+               if ( $wgUser->getOption( 'watchdefault' ) ) {
                        # Watch all edits
                        $this->watchthis = true;
-               } elseif ( $user->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
+               } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
                        # Watch creations
                        $this->watchthis = true;
-               } elseif ( $user->isWatched( $this->mTitle ) ) {
+               } elseif ( $wgUser->isWatched( $this->mTitle ) ) {
                        # Already watched
                        $this->watchthis = true;
                }
-               if ( $user->getOption( 'minordefault' ) && !$this->isNew ) {
+               if ( $wgUser->getOption( 'minordefault' ) && !$this->isNew ) {
                        $this->minoredit = true;
                }
                if ( $this->textbox1 === false ) {
@@ -1078,11 +1099,9 @@ class EditPage {
         * @since 1.21
         */
        protected function getContentObject( $def_content = null ) {
-               global $wgContLang;
+               global $wgOut, $wgRequest, $wgUser, $wgContLang;
 
                $content = false;
-               $request = $this->context->getRequest();
-               $user = $this->context->getUser();
 
                // For message page not locally set, use the i18n message.
                // For other non-existent articles, use preload text if any.
@@ -1095,10 +1114,10 @@ class EditPage {
                        }
                        if ( $content === false ) {
                                # If requested, preload some text.
-                               $preload = $request->getVal( 'preload',
+                               $preload = $wgRequest->getVal( 'preload',
                                        // Custom preload text for new sections
                                        $this->section === 'new' ? 'MediaWiki:addsection-preload' : '' );
-                               $params = $request->getArray( 'preloadparams', [] );
+                               $params = $wgRequest->getArray( 'preloadparams', [] );
 
                                $content = $this->getPreloadedContent( $preload, $params );
                        }
@@ -1106,15 +1125,15 @@ class EditPage {
                } else {
                        if ( $this->section != '' ) {
                                // Get section edit text (returns $def_text for invalid sections)
-                               $orig = $this->getOriginalContent( $user );
+                               $orig = $this->getOriginalContent( $wgUser );
                                $content = $orig ? $orig->getSection( $this->section ) : null;
 
                                if ( !$content ) {
                                        $content = $def_content;
                                }
                        } else {
-                               $undoafter = $request->getInt( 'undoafter' );
-                               $undo = $request->getInt( 'undo' );
+                               $undoafter = $wgRequest->getInt( 'undoafter' );
+                               $undo = $wgRequest->getInt( 'undo' );
 
                                if ( $undo > 0 && $undoafter > 0 ) {
                                        $undorev = Revision::newFromId( $undo );
@@ -1134,8 +1153,16 @@ class EditPage {
                                                        $undoMsg = 'failure';
                                                } else {
                                                        $oldContent = $this->page->getContent( Revision::RAW );
-                                                       $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
-                                                       $newContent = $content->preSaveTransform( $this->mTitle, $user, $popts );
+                                                       $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+                                                       $newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
+                                                       if ( $newContent->getModel() !== $oldContent->getModel() ) {
+                                                               // The undo may change content
+                                                               // model if its reverting the top
+                                                               // edit. This can result in
+                                                               // mismatched content model/format.
+                                                               $this->contentModel = $newContent->getModel();
+                                                               $this->contentFormat = $oldrev->getContentFormat();
+                                                       }
 
                                                        if ( $newContent->equals( $oldContent ) ) {
                                                                # Tell the user that the undo results in no change,
@@ -1151,12 +1178,12 @@ class EditPage {
                                                                if ( $firstrev && $firstrev->getId() == $undo ) {
                                                                        $userText = $undorev->getUserText();
                                                                        if ( $userText === '' ) {
-                                                                               $undoSummary = wfMessage(
+                                                                               $undoSummary = $this->context->msg(
                                                                                        'undo-summary-username-hidden',
                                                                                        $undo
                                                                                )->inContentLanguage()->text();
                                                                        } else {
-                                                                               $undoSummary = wfMessage(
+                                                                               $undoSummary = $this->context->msg(
                                                                                        'undo-summary',
                                                                                        $undo,
                                                                                        $userText
@@ -1165,7 +1192,7 @@ class EditPage {
                                                                        if ( $this->summary === '' ) {
                                                                                $this->summary = $undoSummary;
                                                                        } else {
-                                                                               $this->summary = $undoSummary . wfMessage( 'colon-separator' )
+                                                                               $this->summary = $undoSummary . $this->context->msg( 'colon-separator' )
                                                                                        ->inContentLanguage()->text() . $this->summary;
                                                                        }
                                                                        $this->undidRev = $undo;
@@ -1182,13 +1209,12 @@ class EditPage {
 
                                        // Messages: undo-success, undo-failure, undo-norev, undo-nochange
                                        $class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
-                                       $this->editFormPageTop .= $this->context->getOutput()->parse(
-                                               "<div class=\"{$class}\">" .
-                                               wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
+                                       $this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
+                                               $this->context->msg( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
                                }
 
                                if ( $content === false ) {
-                                       $content = $this->getOriginalContent( $user );
+                                       $content = $this->getOriginalContent( $wgUser );
                                }
                        }
                }
@@ -1267,9 +1293,11 @@ class EditPage {
                        $handler = ContentHandler::getForModelID( $this->contentModel );
 
                        return $handler->makeEmptyContent();
-               } else {
+               } elseif ( !$this->undidRev ) {
                        // Content models should always be the same since we error
-                       // out if they are different before this point.
+                       // out if they are different before this point (in ->edit()).
+                       // The exception being, during an undo, the current revision might
+                       // differ from the prior revision.
                        $logger = LoggerFactory::getInstance( 'editpage' );
                        if ( $this->contentModel !== $rev->getContentModel() ) {
                                $logger->warning( "Overriding content model from current edit {prev} to {new}", [
@@ -1293,9 +1321,8 @@ class EditPage {
                                ] );
                                $this->contentFormat = $rev->getContentFormat();
                        }
-
-                       return $content;
                }
+               return $content;
        }
 
        /**
@@ -1385,10 +1412,10 @@ class EditPage {
         * @private
         */
        function tokenOk( &$request ) {
+               global $wgUser;
                $token = $request->getVal( 'wpEditToken' );
-               $user = $this->context->getUser();
-               $this->mTokenOk = $user->matchEditToken( $token );
-               $this->mTokenOkExceptSuffix = $user->matchEditTokenNoSuffix( $token );
+               $this->mTokenOk = $wgUser->matchEditToken( $token );
+               $this->mTokenOkExceptSuffix = $wgUser->matchEditTokenNoSuffix( $token );
                return $this->mTokenOk;
        }
 
@@ -1419,7 +1446,7 @@ class EditPage {
                        $val = 'restored';
                }
 
-               $response = $this->context->getRequest()->response();
+               $response = RequestContext::getMain()->getRequest()->response();
                $response->setCookie( $postEditKey, $val, time() + self::POST_EDIT_COOKIE_DURATION, [
                        'httpOnly' => false,
                ] );
@@ -1432,8 +1459,10 @@ class EditPage {
         * @return Status The resulting status object.
         */
        public function attemptSave( &$resultDetails = false ) {
+               global $wgUser;
+
                # Allow bots to exempt some edits from bot flagging
-               $bot = $this->context->getUser()->isAllowed( 'bot' ) && $this->bot;
+               $bot = $wgUser->isAllowed( 'bot' ) && $this->bot;
                $status = $this->internalAttemptSave( $resultDetails, $bot );
 
                Hooks::run( 'EditPage::attemptSave:after', [ $this, $status, $resultDetails ] );
@@ -1451,6 +1480,8 @@ class EditPage {
         * @return bool False, if output is done, true if rest of the form should be displayed
         */
        private function handleStatus( Status $status, $resultDetails ) {
+               global $wgUser, $wgOut;
+
                /**
                 * @todo FIXME: once the interface for internalAttemptSave() is made
                 *   nicer, this should use the message in $status
@@ -1464,11 +1495,9 @@ class EditPage {
                        }
                }
 
-               $out = $this->context->getOutput();
-
                // "wpExtraQueryRedirect" is a hidden input to modify
                // after save URL and is not used by actual edit form
-               $request = $this->context->getRequest();
+               $request = RequestContext::getMain()->getRequest();
                $extraQueryRedirect = $request->getVal( 'wpExtraQueryRedirect' );
 
                switch ( $status->value ) {
@@ -1489,7 +1518,7 @@ class EditPage {
 
                        case self::AS_CANNOT_USE_CUSTOM_MODEL:
                        case self::AS_PARSE_ERROR:
-                               $out->addWikiText( '<div class="error">' . "\n" . $status->getWikiText() . '</div>' );
+                               $wgOut->addWikiText( '<div class="error">' . "\n" . $status->getWikiText() . '</div>' );
                                return true;
 
                        case self::AS_SUCCESS_NEW_ARTICLE:
@@ -1502,7 +1531,7 @@ class EditPage {
                                        }
                                }
                                $anchor = isset( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : '';
-                               $out->redirect( $this->mTitle->getFullURL( $query ) . $anchor );
+                               $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $anchor );
                                return false;
 
                        case self::AS_SUCCESS_UPDATE:
@@ -1530,7 +1559,7 @@ class EditPage {
                                        }
                                }
 
-                               $out->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
+                               $wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
                                return false;
 
                        case self::AS_SPAM_ERROR:
@@ -1538,7 +1567,7 @@ class EditPage {
                                return false;
 
                        case self::AS_BLOCKED_PAGE_FOR_USER:
-                               throw new UserBlockedError( $this->context->getUser()->getBlock() );
+                               throw new UserBlockedError( $wgUser->getBlock() );
 
                        case self::AS_IMAGE_REDIRECT_ANON:
                        case self::AS_IMAGE_REDIRECT_LOGGED:
@@ -1599,7 +1628,7 @@ class EditPage {
 
                // Run new style post-section-merge edit filter
                if ( !Hooks::run( 'EditFilterMergedContent',
-                               [ $this->context, $content, $status, $this->summary,
+                               [ $this->mArticle->getContext(), $content, $status, $this->summary,
                                $user, $this->minoredit ] )
                ) {
                        # Error messages etc. could be handled within the hook...
@@ -1645,7 +1674,7 @@ class EditPage {
                        // passed.
                        if ( $this->summary === '' ) {
                                $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
-                               return wfMessage( 'newsectionsummary' )
+                               return $this->context->msg( 'newsectionsummary' )
                                        ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
                        }
                } elseif ( $this->summary !== '' ) {
@@ -1653,7 +1682,7 @@ class EditPage {
                        # This is a new section, so create a link to the new section
                        # in the revision summary.
                        $cleanSummary = $wgParser->stripSectionName( $this->summary );
-                       return wfMessage( 'newsectionsummary' )
+                       return $this->context->msg( 'newsectionsummary' )
                                ->rawParams( $cleanSummary )->inContentLanguage()->text();
                }
                return $this->summary;
@@ -1684,11 +1713,10 @@ class EditPage {
         * time.
         */
        function internalAttemptSave( &$result, $bot = false ) {
-               global $wgParser, $wgMaxArticleSize, $wgContentHandlerUseDB;
+               global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
+               global $wgContentHandlerUseDB;
 
                $status = Status::newGood();
-               $user = $this->context->getUser();
-               $request = $this->context->getRequest();
 
                if ( !Hooks::run( 'EditPage::attemptSave', [ $this ] ) ) {
                        wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
@@ -1697,11 +1725,11 @@ class EditPage {
                        return $status;
                }
 
-               $spam = $request->getText( 'wpAntispam' );
+               $spam = $wgRequest->getText( 'wpAntispam' );
                if ( $spam !== '' ) {
                        wfDebugLog(
                                'SimpleAntiSpam',
-                               $user->getName() .
+                               $wgUser->getName() .
                                ' editing "' .
                                $this->mTitle->getPrefixedText() .
                                '" submitted bogus field "' .
@@ -1730,9 +1758,9 @@ class EditPage {
                # Check image redirect
                if ( $this->mTitle->getNamespace() == NS_FILE &&
                        $textbox_content->isRedirect() &&
-                       !$user->isAllowed( 'upload' )
+                       !$wgUser->isAllowed( 'upload' )
                ) {
-                               $code = $user->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
+                               $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
                                $status->setResult( false, $code );
 
                                return $status;
@@ -1757,7 +1785,7 @@ class EditPage {
                }
                if ( $match !== false ) {
                        $result['spam'] = $match;
-                       $ip = $request->getIP();
+                       $ip = $wgRequest->getIP();
                        $pdbk = $this->mTitle->getPrefixedDBkey();
                        $match = str_replace( "\n", '', $match );
                        wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
@@ -1780,10 +1808,10 @@ class EditPage {
                        return $status;
                }
 
-               if ( $user->isBlockedFrom( $this->mTitle, false ) ) {
+               if ( $wgUser->isBlockedFrom( $this->mTitle, false ) ) {
                        // Auto-block user's IP if the account was "hard" blocked
                        if ( !wfReadOnly() ) {
-                               $user->spreadAnyEditBlock();
+                               $wgUser->spreadAnyEditBlock();
                        }
                        # Check block state against master, thus 'false'.
                        $status->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
@@ -1798,8 +1826,8 @@ class EditPage {
                        return $status;
                }
 
-               if ( !$user->isAllowed( 'edit' ) ) {
-                       if ( $user->isAnon() ) {
+               if ( !$wgUser->isAllowed( 'edit' ) ) {
+                       if ( $wgUser->isAnon() ) {
                                $status->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
                                return $status;
                        } else {
@@ -1815,7 +1843,7 @@ class EditPage {
                                $status->fatal( 'editpage-cannot-use-custom-model' );
                                $status->value = self::AS_CANNOT_USE_CUSTOM_MODEL;
                                return $status;
-                       } elseif ( !$user->isAllowed( 'editcontentmodel' ) ) {
+                       } elseif ( !$wgUser->isAllowed( 'editcontentmodel' ) ) {
                                $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
                                return $status;
 
@@ -1826,7 +1854,7 @@ class EditPage {
 
                if ( $this->changeTags ) {
                        $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange(
-                               $this->changeTags, $user );
+                               $this->changeTags, $wgUser );
                        if ( !$changeTagsStatus->isOK() ) {
                                $changeTagsStatus->value = self::AS_CHANGE_TAG_ERROR;
                                return $changeTagsStatus;
@@ -1838,7 +1866,9 @@ class EditPage {
                        $status->value = self::AS_READ_ONLY_PAGE;
                        return $status;
                }
-               if ( $user->pingLimiter() || $user->pingLimiter( 'linkpurge', 0 ) ) {
+               if ( $wgUser->pingLimiter() || $wgUser->pingLimiter( 'linkpurge', 0 )
+                       || ( $changingContentModel && $wgUser->pingLimiter( 'editcontentmodel' ) )
+               ) {
                        $status->fatal( 'actionthrottledtext' );
                        $status->value = self::AS_RATE_LIMITED;
                        return $status;
@@ -1858,7 +1888,7 @@ class EditPage {
 
                if ( $new ) {
                        // Late check for create permission, just in case *PARANOIA*
-                       if ( !$this->mTitle->userCan( 'create', $user ) ) {
+                       if ( !$this->mTitle->userCan( 'create', $wgUser ) ) {
                                $status->fatal( 'nocreatetext' );
                                $status->value = self::AS_NO_CREATE_PERMISSION;
                                wfDebug( __METHOD__ . ": no create permission\n" );
@@ -1882,7 +1912,7 @@ class EditPage {
                                return $status;
                        }
 
-                       if ( !$this->runPostMergeFilters( $textbox_content, $status, $user ) ) {
+                       if ( !$this->runPostMergeFilters( $textbox_content, $status, $wgUser ) ) {
                                return $status;
                        }
 
@@ -1918,7 +1948,7 @@ class EditPage {
                        ) {
                                $this->isConflict = true;
                                if ( $this->section == 'new' ) {
-                                       if ( $this->page->getUserText() == $user->getName() &&
+                                       if ( $this->page->getUserText() == $wgUser->getName() &&
                                                $this->page->getComment() == $this->newSectionSummary()
                                        ) {
                                                // Probably a duplicate submission of a new comment.
@@ -1934,7 +1964,7 @@ class EditPage {
                                } elseif ( $this->section == ''
                                        && Revision::userWasLastToEdit(
                                                DB_MASTER, $this->mTitle->getArticleID(),
-                                               $user->getId(), $this->edittime
+                                               $wgUser->getId(), $this->edittime
                                        )
                                ) {
                                        # Suppress edit conflict with self, except for section edits where merging is required.
@@ -2004,7 +2034,7 @@ class EditPage {
                                return $status;
                        }
 
-                       if ( !$this->runPostMergeFilters( $content, $status, $user ) ) {
+                       if ( !$this->runPostMergeFilters( $content, $status, $wgUser ) ) {
                                return $status;
                        }
 
@@ -2025,7 +2055,7 @@ class EditPage {
                                        return $status;
                                }
                        } elseif ( !$this->allowBlankSummary
-                               && !$content->equals( $this->getOriginalContent( $user ) )
+                               && !$content->equals( $this->getOriginalContent( $wgUser ) )
                                && !$content->isRedirect()
                                && md5( $this->summary ) == $this->autoSumm
                        ) {
@@ -2095,7 +2125,7 @@ class EditPage {
                        $this->summary,
                        $flags,
                        false,
-                       $user,
+                       $wgUser,
                        $content->getDefaultFormat(),
                        $this->changeTags
                );
@@ -2118,7 +2148,7 @@ class EditPage {
                $result['nullEdit'] = $doEditStatus->hasMessage( 'edit-no-change' );
                if ( $result['nullEdit'] ) {
                        // We don't know if it was a null edit until now, so increment here
-                       $user->pingLimiter( 'linkpurge' );
+                       $wgUser->pingLimiter( 'linkpurge' );
                }
                $result['redirect'] = $content->isRedirect();
 
@@ -2127,7 +2157,7 @@ class EditPage {
                // If the content model changed, add a log entry
                if ( $changingContentModel ) {
                        $this->addContentModelChangeLogEntry(
-                               $user,
+                               $wgUser,
                                $new ? false : $oldContentModel,
                                $this->contentModel,
                                $this->summary
@@ -2161,12 +2191,13 @@ class EditPage {
         * Register the change of watch status
         */
        protected function updateWatchlist() {
-               $user = $this->context->getUser();
+               global $wgUser;
 
-               if ( !$user->isLoggedIn() ) {
+               if ( !$wgUser->isLoggedIn() ) {
                        return;
                }
 
+               $user = $wgUser;
                $title = $this->mTitle;
                $watch = $this->watchthis;
                // Do this in its own transaction to reduce contention...
@@ -2281,32 +2312,29 @@ class EditPage {
        }
 
        function setHeaders() {
-               global $wgAjaxEditStash;
-
-               $out = $this->context->getOutput();
-               $user = $this->context->getUser();
+               global $wgOut, $wgUser, $wgAjaxEditStash;
 
-               $out->addModules( 'mediawiki.action.edit' );
-               $out->addModuleStyles( 'mediawiki.action.edit.styles' );
+               $wgOut->addModules( 'mediawiki.action.edit' );
+               $wgOut->addModuleStyles( 'mediawiki.action.edit.styles' );
 
-               if ( $user->getOption( 'showtoolbar' ) ) {
+               if ( $wgUser->getOption( 'showtoolbar' ) ) {
                        // The addition of default buttons is handled by getEditToolbar() which
                        // has its own dependency on this module. The call here ensures the module
                        // is loaded in time (it has position "top") for other modules to register
                        // buttons (e.g. extensions, gadgets, user scripts).
-                       $out->addModules( 'mediawiki.toolbar' );
+                       $wgOut->addModules( 'mediawiki.toolbar' );
                }
 
-               if ( $user->getOption( 'uselivepreview' ) ) {
-                       $out->addModules( 'mediawiki.action.edit.preview' );
+               if ( $wgUser->getOption( 'uselivepreview' ) ) {
+                       $wgOut->addModules( 'mediawiki.action.edit.preview' );
                }
 
-               if ( $user->getOption( 'useeditwarning' ) ) {
-                       $out->addModules( 'mediawiki.action.edit.editWarning' );
+               if ( $wgUser->getOption( 'useeditwarning' ) ) {
+                       $wgOut->addModules( 'mediawiki.action.edit.editWarning' );
                }
 
                # Enabled article-related sidebar, toplinks, etc.
-               $out->setArticleRelated( true );
+               $wgOut->setArticleRelated( true );
 
                $contextTitle = $this->getContextTitle();
                if ( $this->isConflict ) {
@@ -2329,10 +2357,10 @@ class EditPage {
                if ( $displayTitle === false ) {
                        $displayTitle = $contextTitle->getPrefixedText();
                }
-               $out->setPageTitle( wfMessage( $msg, $displayTitle ) );
+               $wgOut->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
                # Transmit the name of the message to JavaScript for live preview
                # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
-               $out->addJsConfigVars( [
+               $wgOut->addJsConfigVars( [
                        'wgEditMessage' => $msg,
                        'wgAjaxEditStash' => $wgAjaxEditStash,
                ] );
@@ -2342,16 +2370,16 @@ class EditPage {
         * Show all applicable editing introductions
         */
        protected function showIntro() {
+               global $wgOut, $wgUser;
                if ( $this->suppressIntro ) {
                        return;
                }
 
-               $out = $this->context->getOutput();
                $namespace = $this->mTitle->getNamespace();
 
                if ( $namespace == NS_MEDIAWIKI ) {
                        # Show a warning if editing an interface message
-                       $out->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' );
+                       $wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' );
                        # If this is a default message (but not css or js),
                        # show a hint that it is translatable on translatewiki.net
                        if ( !$this->mTitle->hasContentModel( CONTENT_MODEL_CSS )
@@ -2359,7 +2387,7 @@ class EditPage {
                        ) {
                                $defaultMessageText = $this->mTitle->getDefaultMessageText();
                                if ( $defaultMessageText !== false ) {
-                                       $out->wrapWikiMsg( "<div class='mw-translateinterface'>\n$1\n</div>",
+                                       $wgOut->wrapWikiMsg( "<div class='mw-translateinterface'>\n$1\n</div>",
                                                'translateinterface' );
                                }
                        }
@@ -2371,11 +2399,11 @@ class EditPage {
                                # there must be a description url to show a hint to shared repo
                                if ( $descUrl ) {
                                        if ( !$this->mTitle->exists() ) {
-                                               $out->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", [
+                                               $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", [
                                                                        'sharedupload-desc-create', $file->getRepo()->getDisplayName(), $descUrl
                                                ] );
                                        } else {
-                                               $out->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", [
+                                               $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", [
                                                                        'sharedupload-desc-edit', $file->getRepo()->getDisplayName(), $descUrl
                                                ] );
                                        }
@@ -2391,12 +2419,12 @@ class EditPage {
                        $ip = User::isIP( $username );
                        $block = Block::newFromTarget( $user, $user );
                        if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
-                               $out->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
+                               $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
                                        [ 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ] );
                        } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
                                # Show log extract if the user is currently blocked
                                LogEventsList::showLogExtract(
-                                       $out,
+                                       $wgOut,
                                        'block',
                                        MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(),
                                        '',
@@ -2414,10 +2442,10 @@ class EditPage {
                # Try to add a custom edit intro, or use the standard one if this is not possible.
                if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
                        $helpLink = wfExpandUrl( Skin::makeInternalOrExternalUrl(
-                               wfMessage( 'helppage' )->inContentLanguage()->text()
+                               $this->context->msg( 'helppage' )->inContentLanguage()->text()
                        ) );
-                       if ( $this->context->getUser()->isLoggedIn() ) {
-                               $out->wrapWikiMsg(
+                       if ( $wgUser->isLoggedIn() ) {
+                               $wgOut->wrapWikiMsg(
                                        // Suppress the external link icon, consider the help url an internal one
                                        "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>",
                                        [
@@ -2426,7 +2454,7 @@ class EditPage {
                                        ]
                                );
                        } else {
-                               $out->wrapWikiMsg(
+                               $wgOut->wrapWikiMsg(
                                        // Suppress the external link icon, consider the help url an internal one
                                        "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>",
                                        [
@@ -2438,7 +2466,7 @@ class EditPage {
                }
                # Give a notice if the user is editing a deleted/moved page...
                if ( !$this->mTitle->exists() ) {
-                       LogEventsList::showLogExtract( $out, [ 'delete', 'move' ], $this->mTitle,
+                       LogEventsList::showLogExtract( $wgOut, [ 'delete', 'move' ], $this->mTitle,
                                '',
                                [
                                        'lim' => 10,
@@ -2459,8 +2487,9 @@ class EditPage {
                if ( $this->editintro ) {
                        $title = Title::newFromText( $this->editintro );
                        if ( $title instanceof Title && $title->exists() && $title->userCan( 'read' ) ) {
+                               global $wgOut;
                                // Added using template syntax, to take <noinclude>'s into account.
-                               $this->context->getOutput()->addWikiTextTitleTidy(
+                               $wgOut->addWikiTextTitleTidy(
                                        '<div class="mw-editintro">{{:' . $title->getFullText() . '}}</div>',
                                        $this->mTitle
                                );
@@ -2494,8 +2523,7 @@ class EditPage {
                }
 
                if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
-                       throw new MWException( 'This content model is not supported: '
-                               . ContentHandler::getLocalizedName( $content->getModel() ) );
+                       throw new MWException( 'This content model is not supported: ' . $content->getModel() );
                }
 
                return $content->serialize( $this->contentFormat );
@@ -2526,8 +2554,7 @@ class EditPage {
                        $this->contentModel, $this->contentFormat );
 
                if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
-                       throw new MWException( 'This content model is not supported: '
-                               . ContentHandler::getLocalizedName( $content->getModel() ) );
+                       throw new MWException( 'This content model is not supported: ' . $content->getModel() );
                }
 
                return $content;
@@ -2542,6 +2569,8 @@ class EditPage {
         * use the EditPage::showEditForm:fields hook instead.
         */
        function showEditForm( $formCallback = null ) {
+               global $wgOut, $wgUser;
+
                # need to parse the preview early so that we know which templates are used,
                # otherwise users with "show preview after edit box" will get a blank list
                # we parse this near the beginning so that setHeaders can do the title
@@ -2551,8 +2580,7 @@ class EditPage {
                        $previewOutput = $this->getPreviewText();
                }
 
-               $out = $this->context->getOutput();
-               Hooks::run( 'EditPage::showEditForm:initial', [ &$this, &$out ] );
+               Hooks::run( 'EditPage::showEditForm:initial', [ &$this, &$wgOut ] );
 
                $this->setHeaders();
 
@@ -2560,14 +2588,13 @@ class EditPage {
                        return;
                }
 
-               $out->addHTML( $this->editFormPageTop );
+               $wgOut->addHTML( $this->editFormPageTop );
 
-               $user = $this->context->getUser();
-               if ( $user->getOption( 'previewontop' ) ) {
+               if ( $wgUser->getOption( 'previewontop' ) ) {
                        $this->displayPreviewArea( $previewOutput, true );
                }
 
-               $out->addHTML( $this->editFormTextTop );
+               $wgOut->addHTML( $this->editFormTextTop );
 
                $showToolbar = true;
                if ( $this->wasDeletedSinceLastEdit() ) {
@@ -2576,14 +2603,14 @@ class EditPage {
                                // Add an confirmation checkbox and explanation.
                                $showToolbar = false;
                        } else {
-                               $out->wrapWikiMsg( "<div class='error mw-deleted-while-editing'>\n$1\n</div>",
+                               $wgOut->wrapWikiMsg( "<div class='error mw-deleted-while-editing'>\n$1\n</div>",
                                        'deletedwhileediting' );
                        }
                }
 
                // @todo add EditForm plugin interface and use it here!
                //       search for textarea1 and textares2, and allow EditForm to override all uses.
-               $out->addHTML( Html::openElement(
+               $wgOut->addHTML( Html::openElement(
                        'form',
                        [
                                'id' => self::EDITFORM_ID,
@@ -2596,16 +2623,16 @@ class EditPage {
 
                if ( is_callable( $formCallback ) ) {
                        wfWarn( 'The $formCallback parameter to ' . __METHOD__ . 'is deprecated' );
-                       call_user_func_array( $formCallback, [ &$out ] );
+                       call_user_func_array( $formCallback, [ &$wgOut ] );
                }
 
                // Add an empty field to trip up spambots
-               $out->addHTML(
+               $wgOut->addHTML(
                        Xml::openElement( 'div', [ 'id' => 'antispam-container', 'style' => 'display: none;' ] )
                        . Html::rawElement(
                                'label',
                                [ 'for' => 'wpAntispam' ],
-                               wfMessage( 'simpleantispam-label' )->parse()
+                               $this->context->msg( 'simpleantispam-label' )->parse()
                        )
                        . Xml::element(
                                'input',
@@ -2619,7 +2646,7 @@ class EditPage {
                        . Xml::closeElement( 'div' )
                );
 
-               Hooks::run( 'EditPage::showEditForm:fields', [ &$this, &$out ] );
+               Hooks::run( 'EditPage::showEditForm:fields', [ &$this, &$wgOut ] );
 
                // Put these up at the top to ensure they aren't lost on early form submission
                $this->showFormBeforeText();
@@ -2633,10 +2660,10 @@ class EditPage {
                        $key = $comment === ''
                                ? 'confirmrecreate-noreason'
                                : 'confirmrecreate';
-                       $out->addHTML(
+                       $wgOut->addHTML(
                                '<div class="mw-confirm-recreate">' .
-                                       wfMessage( $key, $username, "<nowiki>$comment</nowiki>" )->parse() .
-                               Xml::checkLabel( wfMessage( 'recreate' )->text(), 'wpRecreate', 'wpRecreate', false,
+                                       $this->context->msg( $key, $username, "<nowiki>$comment</nowiki>" )->parse() .
+                               Xml::checkLabel( $this->context->msg( 'recreate' )->text(), 'wpRecreate', 'wpRecreate', false,
                                        [ 'title' => Linker::titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' ]
                                ) .
                                '</div>'
@@ -2645,7 +2672,7 @@ class EditPage {
 
                # When the summary is hidden, also hide them on preview/show changes
                if ( $this->nosummary ) {
-                       $out->addHTML( Html::hidden( 'nosummary', true ) );
+                       $wgOut->addHTML( Html::hidden( 'nosummary', true ) );
                }
 
                # If a blank edit summary was previously provided, and the appropriate
@@ -2656,15 +2683,15 @@ class EditPage {
                # For a bit more sophisticated detection of blank summaries, hash the
                # automatic one and pass that in the hidden field wpAutoSummary.
                if ( $this->missingSummary || ( $this->section == 'new' && $this->nosummary ) ) {
-                       $out->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) );
+                       $wgOut->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) );
                }
 
                if ( $this->undidRev ) {
-                       $out->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
+                       $wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
                }
 
                if ( $this->selfRedirect ) {
-                       $out->addHTML( Html::hidden( 'wpIgnoreSelfRedirect', true ) );
+                       $wgOut->addHTML( Html::hidden( 'wpIgnoreSelfRedirect', true ) );
                }
 
                if ( $this->hasPresetSummary ) {
@@ -2675,27 +2702,27 @@ class EditPage {
                }
 
                $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
-               $out->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
+               $wgOut->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
 
-               $out->addHTML( Html::hidden( 'oldid', $this->oldid ) );
-               $out->addHTML( Html::hidden( 'parentRevId', $this->getParentRevId() ) );
+               $wgOut->addHTML( Html::hidden( 'oldid', $this->oldid ) );
+               $wgOut->addHTML( Html::hidden( 'parentRevId', $this->getParentRevId() ) );
 
-               $out->addHTML( Html::hidden( 'format', $this->contentFormat ) );
-               $out->addHTML( Html::hidden( 'model', $this->contentModel ) );
+               $wgOut->addHTML( Html::hidden( 'format', $this->contentFormat ) );
+               $wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) );
 
                if ( $this->section == 'new' ) {
                        $this->showSummaryInput( true, $this->summary );
-                       $out->addHTML( $this->getSummaryPreview( true, $this->summary ) );
+                       $wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) );
                }
 
-               $out->addHTML( $this->editFormTextBeforeContent );
+               $wgOut->addHTML( $this->editFormTextBeforeContent );
 
-               if ( !$this->isCssJsSubpage && $showToolbar && $user->getOption( 'showtoolbar' ) ) {
-                       $out->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
+               if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
+                       $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
                }
 
                if ( $this->blankArticle ) {
-                       $out->addHTML( Html::hidden( 'wpIgnoreBlankArticle', true ) );
+                       $wgOut->addHTML( Html::hidden( 'wpIgnoreBlankArticle', true ) );
                }
 
                if ( $this->isConflict ) {
@@ -2713,7 +2740,7 @@ class EditPage {
                        $this->showContentForm();
                }
 
-               $out->addHTML( $this->editFormTextAfterContent );
+               $wgOut->addHTML( $this->editFormTextAfterContent );
 
                $this->showStandardInputs();
 
@@ -2723,32 +2750,31 @@ class EditPage {
 
                $this->showEditTools();
 
-               $out->addHTML( $this->editFormTextAfterTools . "\n" );
+               $wgOut->addHTML( $this->editFormTextAfterTools . "\n" );
 
-               $out->addHTML( Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
-                       Linker::formatTemplates( $this->getTemplates(), $this->preview, $this->section != '' ) ) );
+               $wgOut->addHTML( $this->makeTemplatesOnThisPageList( $this->getTemplates() ) );
 
-               $out->addHTML( Html::rawElement( 'div', [ 'class' => 'hiddencats' ],
+               $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'hiddencats' ],
                        Linker::formatHiddenCategories( $this->page->getHiddenCategories() ) ) );
 
                if ( $this->mParserOutput ) {
-                       $out->setLimitReportData( $this->mParserOutput->getLimitReportData() );
+                       $wgOut->setLimitReportData( $this->mParserOutput->getLimitReportData() );
                }
 
-               $out->addModules( 'mediawiki.action.edit.collapsibleFooter' );
+               $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
 
                if ( $this->isConflict ) {
                        try {
                                $this->showConflict();
                        } catch ( MWContentSerializationException $ex ) {
                                // this can't really happen, but be nice if it does.
-                               $msg = wfMessage(
+                               $msg = $this->context->msg(
                                        'content-failed-to-parse',
                                        $this->contentModel,
                                        $this->contentFormat,
                                        $ex->getMessage()
                                );
-                               $out->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
+                               $wgOut->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
                        }
                }
 
@@ -2762,19 +2788,45 @@ class EditPage {
                } else {
                        $mode = 'text';
                }
-               $out->addHTML( Html::hidden( 'mode', $mode, [ 'id' => 'mw-edit-mode' ] ) );
+               $wgOut->addHTML( Html::hidden( 'mode', $mode, [ 'id' => 'mw-edit-mode' ] ) );
 
                // Marker for detecting truncated form data.  This must be the last
                // parameter sent in order to be of use, so do not move me.
-               $out->addHTML( Html::hidden( 'wpUltimateParam', true ) );
-               $out->addHTML( $this->editFormTextBottom . "\n</form>\n" );
+               $wgOut->addHTML( Html::hidden( 'wpUltimateParam', true ) );
+               $wgOut->addHTML( $this->editFormTextBottom . "\n</form>\n" );
 
-               if ( !$user->getOption( 'previewontop' ) ) {
+               if ( !$wgUser->getOption( 'previewontop' ) ) {
                        $this->displayPreviewArea( $previewOutput, false );
                }
 
        }
 
+       /**
+        * Wrapper around TemplatesOnThisPageFormatter to make
+        * a "templates on this page" list.
+        *
+        * @param Title[] $templates
+        * @return string HTML
+        */
+       protected function makeTemplatesOnThisPageList( array $templates ) {
+               $templateListFormatter = new TemplatesOnThisPageFormatter(
+                       $this->context, MediaWikiServices::getInstance()->getLinkRenderer()
+               );
+
+               // preview if preview, else section if section, else false
+               $type = false;
+               if ( $this->preview ) {
+                       $type = 'preview';
+               } elseif ( $this->section != '' ) {
+                       $type = 'section';
+               }
+
+               return Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
+                       $templateListFormatter->format( $templates, $type )
+               );
+
+       }
+
        /**
         * Extract the section title from current section text, if any.
         *
@@ -2795,23 +2847,21 @@ class EditPage {
         * @return bool
         */
        protected function showHeader() {
-               global $wgMaxArticleSize, $wgAllowUserCss, $wgAllowUserJs;
-
-               $out = $this->context->getOutput();
-               $user = $this->context->getUser();
+               global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang;
+               global $wgAllowUserCss, $wgAllowUserJs;
 
                if ( $this->mTitle->isTalkPage() ) {
-                       $out->addWikiMsg( 'talkpagetext' );
+                       $wgOut->addWikiMsg( 'talkpagetext' );
                }
 
                // Add edit notices
                $editNotices = $this->mTitle->getEditNotices( $this->oldid );
                if ( count( $editNotices ) ) {
-                       $out->addHTML( implode( "\n", $editNotices ) );
+                       $wgOut->addHTML( implode( "\n", $editNotices ) );
                } else {
-                       $msg = wfMessage( 'editnotice-notext' );
+                       $msg = $this->context->msg( 'editnotice-notext' );
                        if ( !$msg->isDisabled() ) {
-                               $out->addHTML(
+                               $wgOut->addHTML(
                                        '<div class="mw-editnotice-notext">'
                                        . $msg->parseAsBlock()
                                        . '</div>'
@@ -2820,14 +2870,14 @@ class EditPage {
                }
 
                if ( $this->isConflict ) {
-                       $out->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
+                       $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
                        $this->editRevId = $this->page->getLatest();
                } else {
                        if ( $this->section != '' && !$this->isSectionEditSupported() ) {
                                // We use $this->section to much before this and getVal('wgSection') directly in other places
                                // at this point we can't reset $this->section to '' to fallback to non-section editing.
                                // Someone is welcome to try refactoring though
-                               $out->showErrorPage( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
+                               $wgOut->showErrorPage( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
                                return false;
                        }
 
@@ -2841,31 +2891,31 @@ class EditPage {
                        }
 
                        if ( $this->missingComment ) {
-                               $out->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1\n</div>", 'missingcommenttext' );
+                               $wgOut->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1\n</div>", 'missingcommenttext' );
                        }
 
                        if ( $this->missingSummary && $this->section != 'new' ) {
-                               $out->wrapWikiMsg( "<div id='mw-missingsummary'>\n$1\n</div>", 'missingsummary' );
+                               $wgOut->wrapWikiMsg( "<div id='mw-missingsummary'>\n$1\n</div>", 'missingsummary' );
                        }
 
                        if ( $this->missingSummary && $this->section == 'new' ) {
-                               $out->wrapWikiMsg( "<div id='mw-missingcommentheader'>\n$1\n</div>", 'missingcommentheader' );
+                               $wgOut->wrapWikiMsg( "<div id='mw-missingcommentheader'>\n$1\n</div>", 'missingcommentheader' );
                        }
 
                        if ( $this->blankArticle ) {
-                               $out->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
+                               $wgOut->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
                        }
 
                        if ( $this->selfRedirect ) {
-                               $out->wrapWikiMsg( "<div id='mw-selfredirect'>\n$1\n</div>", 'selfredirect' );
+                               $wgOut->wrapWikiMsg( "<div id='mw-selfredirect'>\n$1\n</div>", 'selfredirect' );
                        }
 
                        if ( $this->hookError !== '' ) {
-                               $out->addWikiText( $this->hookError );
+                               $wgOut->addWikiText( $this->hookError );
                        }
 
                        if ( !$this->checkUnicodeCompliantBrowser() ) {
-                               $out->addWikiMsg( 'nonunicodebrowser' );
+                               $wgOut->addWikiMsg( 'nonunicodebrowser' );
                        }
 
                        if ( $this->section != 'new' ) {
@@ -2873,13 +2923,13 @@ class EditPage {
                                if ( $revision ) {
                                        // Let sysop know that this will make private content public if saved
 
-                                       if ( !$revision->userCan( Revision::DELETED_TEXT, $user ) ) {
-                                               $out->wrapWikiMsg(
+                                       if ( !$revision->userCan( Revision::DELETED_TEXT, $wgUser ) ) {
+                                               $wgOut->wrapWikiMsg(
                                                        "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                                        'rev-deleted-text-permission'
                                                );
                                        } elseif ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
-                                               $out->wrapWikiMsg(
+                                               $wgOut->wrapWikiMsg(
                                                        "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                                        'rev-deleted-text-view'
                                                );
@@ -2887,25 +2937,25 @@ class EditPage {
 
                                        if ( !$revision->isCurrent() ) {
                                                $this->mArticle->setOldSubtitle( $revision->getId() );
-                                               $out->addWikiMsg( 'editingold' );
+                                               $wgOut->addWikiMsg( 'editingold' );
                                        }
                                } elseif ( $this->mTitle->exists() ) {
                                        // Something went wrong
 
-                                       $out->wrapWikiMsg( "<div class='errorbox'>\n$1\n</div>\n",
+                                       $wgOut->wrapWikiMsg( "<div class='errorbox'>\n$1\n</div>\n",
                                                [ 'missing-revision', $this->oldid ] );
                                }
                        }
                }
 
                if ( wfReadOnly() ) {
-                       $out->wrapWikiMsg(
+                       $wgOut->wrapWikiMsg(
                                "<div id=\"mw-read-only-warning\">\n$1\n</div>",
                                [ 'readonlywarning', wfReadOnlyReason() ]
                        );
-               } elseif ( $user->isAnon() ) {
+               } elseif ( $wgUser->isAnon() ) {
                        if ( $this->formtype != 'preview' ) {
-                               $out->wrapWikiMsg(
+                               $wgOut->wrapWikiMsg(
                                        "<div id='mw-anon-edit-warning' class='warningbox'>\n$1\n</div>",
                                        [ 'anoneditwarning',
                                                // Log-in link
@@ -2919,7 +2969,7 @@ class EditPage {
                                        ]
                                );
                        } else {
-                               $out->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
+                               $wgOut->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
                                        'anonpreviewwarning'
                                );
                        }
@@ -2927,25 +2977,25 @@ class EditPage {
                        if ( $this->isCssJsSubpage ) {
                                # Check the skin exists
                                if ( $this->isWrongCaseCssJsPage ) {
-                                       $out->wrapWikiMsg(
+                                       $wgOut->wrapWikiMsg(
                                                "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>",
                                                [ 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ]
                                        );
                                }
-                               if ( $this->getTitle()->isSubpageOf( $user->getUserPage() ) ) {
-                                       $out->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
+                               if ( $this->getTitle()->isSubpageOf( $wgUser->getUserPage() ) ) {
+                                       $wgOut->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
                                                $this->isCssSubpage ? 'usercssispublic' : 'userjsispublic'
                                        );
                                        if ( $this->formtype !== 'preview' ) {
                                                if ( $this->isCssSubpage && $wgAllowUserCss ) {
-                                                       $out->wrapWikiMsg(
+                                                       $wgOut->wrapWikiMsg(
                                                                "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
                                                                [ 'usercssyoucanpreview' ]
                                                        );
                                                }
 
                                                if ( $this->isJsSubpage && $wgAllowUserJs ) {
-                                                       $out->wrapWikiMsg(
+                                                       $wgOut->wrapWikiMsg(
                                                                "<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
                                                                [ 'userjsyoucanpreview' ]
                                                        );
@@ -2965,7 +3015,7 @@ class EditPage {
                                # Then it must be protected based on static groups (regular)
                                $noticeMsg = 'protectedpagewarning';
                        }
-                       LogEventsList::showLogExtract( $out, 'protect', $this->mTitle, '',
+                       LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle, '',
                                [ 'lim' => 1, 'msgKey' => [ $noticeMsg ] ] );
                }
                if ( $this->mTitle->isCascadeProtected() ) {
@@ -2981,10 +3031,10 @@ class EditPage {
                                }
                        }
                        $notice .= '</div>';
-                       $out->wrapWikiMsg( $notice, [ 'cascadeprotectedwarning', $cascadeSourcesCount ] );
+                       $wgOut->wrapWikiMsg( $notice, [ 'cascadeprotectedwarning', $cascadeSourcesCount ] );
                }
                if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) {
-                       LogEventsList::showLogExtract( $out, 'protect', $this->mTitle, '',
+                       LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle, '',
                                [ 'lim' => 1,
                                        'showIfEmpty' => false,
                                        'msgKey' => [ 'titleprotectedwarning' ],
@@ -2995,21 +3045,20 @@ class EditPage {
                        $this->contentLength = strlen( $this->textbox1 );
                }
 
-               $lang = $this->context->getLanguage();
                if ( $this->tooBig || $this->contentLength > $wgMaxArticleSize * 1024 ) {
-                       $out->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
+                       $wgOut->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
                                [
                                        'longpageerror',
-                                       $lang->formatNum( round( $this->contentLength / 1024, 3 ) ),
-                                       $lang->formatNum( $wgMaxArticleSize )
+                                       $wgLang->formatNum( round( $this->contentLength / 1024, 3 ) ),
+                                       $wgLang->formatNum( $wgMaxArticleSize )
                                ]
                        );
                } else {
-                       if ( !wfMessage( 'longpage-hint' )->isDisabled() ) {
-                               $out->wrapWikiMsg( "<div id='mw-edit-longpage-hint'>\n$1\n</div>",
+                       if ( !$this->context->msg( 'longpage-hint' )->isDisabled() ) {
+                               $wgOut->wrapWikiMsg( "<div id='mw-edit-longpage-hint'>\n$1\n</div>",
                                        [
                                                'longpage-hint',
-                                               $lang->formatSize( strlen( $this->textbox1 ) ),
+                                               $wgLang->formatSize( strlen( $this->textbox1 ) ),
                                                strlen( $this->textbox1 )
                                        ]
                                );
@@ -3026,14 +3075,14 @@ class EditPage {
         * subclasses may reorganize the form.
         * Note that you do not need to worry about the label's for=, it will be
         * inferred by the id given to the input. You can remove them both by
-        * passing array( 'id' => false ) to $userInputAttrs.
+        * passing [ 'id' => false ] to $userInputAttrs.
         *
         * @param string $summary The value of the summary input
         * @param string $labelText The html to place inside the label
         * @param array $inputAttrs Array of attrs to use on the input
         * @param array $spanLabelAttrs Array of attrs to use on the span inside the label
         *
-        * @return array An array in the format array( $label, $input )
+        * @return array An array in the format [ $label, $input ]
         */
        function getSummaryInput( $summary = "", $labelText = null,
                $inputAttrs = null, $spanLabelAttrs = null
@@ -3074,6 +3123,7 @@ class EditPage {
         * @param string $summary The text of the summary to display
         */
        protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
+               global $wgOut;
                # Add a class if 'missingsummary' is triggered to allow styling of the summary line
                $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
                if ( $isSubjectPreview ) {
@@ -3085,14 +3135,14 @@ class EditPage {
                                return;
                        }
                }
-               $labelText = wfMessage( $isSubjectPreview ? 'subject' : 'summary' )->parse();
+               $labelText = $this->context->msg( $isSubjectPreview ? 'subject' : 'summary' )->parse();
                list( $label, $input ) = $this->getSummaryInput(
                        $summary,
                        $labelText,
                        [ 'class' => $summaryClass ],
                        []
                );
-               $this->context->getOutput()->addHTML( "{$label} {$input}" );
+               $wgOut->addHTML( "{$label} {$input}" );
        }
 
        /**
@@ -3112,21 +3162,22 @@ class EditPage {
                global $wgParser;
 
                if ( $isSubjectPreview ) {
-                       $summary = wfMessage( 'newsectionsummary' )->rawParams( $wgParser->stripSectionName( $summary ) )
+                       $summary = $this->context->msg( 'newsectionsummary' )
+                               ->rawParams( $wgParser->stripSectionName( $summary ) )
                                ->inContentLanguage()->text();
                }
 
                $message = $isSubjectPreview ? 'subject-preview' : 'summary-preview';
 
-               $summary = wfMessage( $message )->parse()
+               $summary = $this->context->msg( $message )->parse()
                        . Linker::commentBlock( $summary, $this->mTitle, $isSubjectPreview );
                return Xml::tags( 'div', [ 'class' => 'mw-summary-preview' ], $summary );
        }
 
        protected function showFormBeforeText() {
+               global $wgOut;
                $section = htmlspecialchars( $this->section );
-               $out = $this->context->getOutput();
-               $out->addHTML( <<<HTML
+               $wgOut->addHTML( <<<HTML
 <input type='hidden' value="{$section}" name="wpSection"/>
 <input type='hidden' value="{$this->starttime}" name="wpStarttime" />
 <input type='hidden' value="{$this->edittime}" name="wpEdittime" />
@@ -3136,11 +3187,12 @@ class EditPage {
 HTML
                );
                if ( !$this->checkUnicodeCompliantBrowser() ) {
-                       $out->addHTML( Html::hidden( 'safemode', '1' ) );
+                       $wgOut->addHTML( Html::hidden( 'safemode', '1' ) );
                }
        }
 
        protected function showFormAfterText() {
+               global $wgOut, $wgUser;
                /**
                 * To make it harder for someone to slip a user a page
                 * which submits an edit form to the wiki without their
@@ -3153,10 +3205,7 @@ HTML
                 * include the constant suffix to prevent editing from
                 * broken text-mangling proxies.
                 */
-               $token = $this->context->getUser()->getEditToken();
-               $this->context->getOutput()->addHTML(
-                       "\n" . Html::hidden( "wpEditToken", $token ) . "\n"
-               );
+               $wgOut->addHTML( "\n" . Html::hidden( "wpEditToken", $wgUser->getEditToken() ) . "\n" );
        }
 
        /**
@@ -3226,6 +3275,8 @@ HTML
        }
 
        protected function showTextbox( $text, $name, $customAttribs = [] ) {
+               global $wgOut, $wgUser;
+
                $wikitext = $this->safeUnicodeOutput( $text );
                if ( strval( $wikitext ) !== '' ) {
                        // Ensure there's a newline at the end, otherwise adding lines
@@ -3235,12 +3286,11 @@ HTML
                        $wikitext .= "\n";
                }
 
-               $user = $this->context->getUser();
                $attribs = $customAttribs + [
                        'accesskey' => ',',
                        'id' => $name,
-                       'cols' => $user->getIntOption( 'cols' ),
-                       'rows' => $user->getIntOption( 'rows' ),
+                       'cols' => $wgUser->getIntOption( 'cols' ),
+                       'rows' => $wgUser->getIntOption( 'rows' ),
                        // Avoid PHP notices when appending preferences
                        // (appending allows customAttribs['style'] to still work).
                        'style' => ''
@@ -3250,10 +3300,11 @@ HTML
                $attribs['lang'] = $pageLang->getHtmlCode();
                $attribs['dir'] = $pageLang->getDir();
 
-               $this->context->getOutput()->addHTML( Html::textarea( $name, $wikitext, $attribs ) );
+               $wgOut->addHTML( Html::textarea( $name, $wikitext, $attribs ) );
        }
 
        protected function displayPreviewArea( $previewOutput, $isOnTop = false ) {
+               global $wgOut;
                $classes = [];
                if ( $isOnTop ) {
                        $classes[] = 'ontop';
@@ -3265,8 +3316,7 @@ HTML
                        $attribs['style'] = 'display: none;';
                }
 
-               $out = $this->context->getOutput();
-               $out->addHTML( Xml::openElement( 'div', $attribs ) );
+               $wgOut->addHTML( Xml::openElement( 'div', $attribs ) );
 
                if ( $this->formtype == 'preview' ) {
                        $this->showPreview( $previewOutput );
@@ -3275,22 +3325,22 @@ HTML
                        $pageViewLang = $this->mTitle->getPageViewLanguage();
                        $attribs = [ 'lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(),
                                'class' => 'mw-content-' . $pageViewLang->getDir() ];
-                       $out->addHTML( Html::rawElement( 'div', $attribs ) );
+                       $wgOut->addHTML( Html::rawElement( 'div', $attribs ) );
                }
 
-               $out->addHTML( '</div>' );
+               $wgOut->addHTML( '</div>' );
 
                if ( $this->formtype == 'diff' ) {
                        try {
                                $this->showDiff();
                        } catch ( MWContentSerializationException $ex ) {
-                               $msg = wfMessage(
+                               $msg = $this->context->msg(
                                        'content-failed-to-parse',
                                        $this->contentModel,
                                        $this->contentFormat,
                                        $ex->getMessage()
                                );
-                               $out->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
+                               $wgOut->addWikiText( '<div class="error">' . $msg->text() . '</div>' );
                        }
                }
        }
@@ -3302,14 +3352,14 @@ HTML
         * @param string $text The HTML to be output for the preview.
         */
        protected function showPreview( $text ) {
+               global $wgOut;
                if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
                        $this->mArticle->openShowCategory();
                }
-               $out = $this->context->getOutput();
                # This hook seems slightly odd here, but makes things more
                # consistent for extensions.
-               Hooks::run( 'OutputPageBeforeHTML', [ &$out, &$text ] );
-               $out->addHTML( $text );
+               Hooks::run( 'OutputPageBeforeHTML', [ &$wgOut, &$text ] );
+               $wgOut->addHTML( $text );
                if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
                        $this->mArticle->closeShowCategory();
                }
@@ -3323,7 +3373,7 @@ HTML
         * save and then make a comparison.
         */
        function showDiff() {
-               global $wgContLang;
+               global $wgUser, $wgContLang, $wgOut;
 
                $oldtitlemsg = 'currentrev';
                # if message does not exist, show diff against the preloaded default
@@ -3354,14 +3404,13 @@ HTML
                        ContentHandler::runLegacyHooks( 'EditPageGetDiffText', [ $this, &$newContent ] );
                        Hooks::run( 'EditPageGetDiffContent', [ $this, &$newContent ] );
 
-                       $user = $this->context->getUser();
-                       $popts = ParserOptions::newFromUserAndLang( $user, $wgContLang );
-                       $newContent = $newContent->preSaveTransform( $this->mTitle, $user, $popts );
+                       $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+                       $newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts );
                }
 
                if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) {
-                       $oldtitle = wfMessage( $oldtitlemsg )->parse();
-                       $newtitle = wfMessage( 'yourtext' )->parse();
+                       $oldtitle = $this->context->msg( $oldtitlemsg )->parse();
+                       $newtitle = $this->context->msg( 'yourtext' )->parse();
 
                        if ( !$oldContent ) {
                                $oldContent = $newContent->getContentHandler()->makeEmptyContent();
@@ -3380,7 +3429,7 @@ HTML
                        $difftext = '';
                }
 
-               $this->context->getOutput()->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' );
+               $wgOut->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' );
        }
 
        /**
@@ -3388,8 +3437,9 @@ HTML
         */
        protected function showHeaderCopyrightWarning() {
                $msg = 'editpage-head-copy-warn';
-               if ( !wfMessage( $msg )->isDisabled() ) {
-                       $this->context->getOutput()->wrapWikiMsg( "<div class='editpage-head-copywarn'>\n$1\n</div>",
+               if ( !$this->context->msg( $msg )->isDisabled() ) {
+                       global $wgOut;
+                       $wgOut->wrapWikiMsg( "<div class='editpage-head-copywarn'>\n$1\n</div>",
                                'editpage-head-copy-warn' );
                }
        }
@@ -3405,17 +3455,18 @@ HTML
        protected function showTosSummary() {
                $msg = 'editpage-tos-summary';
                Hooks::run( 'EditPageTosSummary', [ $this->mTitle, &$msg ] );
-               if ( !wfMessage( $msg )->isDisabled() ) {
-                       $out = $this->context->getOutput();
-                       $out->addHTML( '<div class="mw-tos-summary">' );
-                       $out->addWikiMsg( $msg );
-                       $out->addHTML( '</div>' );
+               if ( !$this->context->msg( $msg )->isDisabled() ) {
+                       global $wgOut;
+                       $wgOut->addHTML( '<div class="mw-tos-summary">' );
+                       $wgOut->addWikiMsg( $msg );
+                       $wgOut->addHTML( '</div>' );
                }
        }
 
        protected function showEditTools() {
-               $this->context->getOutput()->addHTML( '<div class="mw-editTools">' .
-                       wfMessage( 'edittools' )->inContentLanguage()->parse() .
+               global $wgOut;
+               $wgOut->addHTML( '<div class="mw-editTools">' .
+                       $this->context->msg( 'edittools' )->inContentLanguage()->parse() .
                        '</div>' );
        }
 
@@ -3450,7 +3501,7 @@ HTML
                Hooks::run( 'EditPageCopyrightWarning', [ $title, &$copywarnMsg ] );
 
                return "<div id=\"editpage-copywarn\">\n" .
-                       call_user_func_array( 'wfMessage', $copywarnMsg )->$format() . "\n</div>";
+                       call_user_func_array( 'wfMessage', $copywarnMsg )->title( $title )->$format() . "\n</div>";
        }
 
        /**
@@ -3474,50 +3525,50 @@ HTML
        }
 
        protected function showStandardInputs( &$tabindex = 2 ) {
-               $out = $this->context->getOutput();
-               $out->addHTML( "<div class='editOptions'>\n" );
+               global $wgOut;
+               $wgOut->addHTML( "<div class='editOptions'>\n" );
 
                if ( $this->section != 'new' ) {
                        $this->showSummaryInput( false, $this->summary );
-                       $out->addHTML( $this->getSummaryPreview( false, $this->summary ) );
+                       $wgOut->addHTML( $this->getSummaryPreview( false, $this->summary ) );
                }
 
                $checkboxes = $this->getCheckboxes( $tabindex,
                        [ 'minor' => $this->minoredit, 'watch' => $this->watchthis ] );
-               $out->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" );
+               $wgOut->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" );
 
                // Show copyright warning.
-               $out->addWikiText( $this->getCopywarn() );
-               $out->addHTML( $this->editFormTextAfterWarn );
+               $wgOut->addWikiText( $this->getCopywarn() );
+               $wgOut->addHTML( $this->editFormTextAfterWarn );
 
-               $out->addHTML( "<div class='editButtons'>\n" );
-               $out->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" );
+               $wgOut->addHTML( "<div class='editButtons'>\n" );
+               $wgOut->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" );
 
                $cancel = $this->getCancelLink();
                if ( $cancel !== '' ) {
                        $cancel .= Html::element( 'span',
                                [ 'class' => 'mw-editButtons-pipe-separator' ],
-                               wfMessage( 'pipe-separator' )->text() );
+                               $this->context->msg( 'pipe-separator' )->text() );
                }
 
-               $message = wfMessage( 'edithelppage' )->inContentLanguage()->text();
+               $message = $this->context->msg( 'edithelppage' )->inContentLanguage()->text();
                $edithelpurl = Skin::makeInternalOrExternalUrl( $message );
                $attrs = [
                        'target' => 'helpwindow',
                        'href' => $edithelpurl,
                ];
-               $edithelp = Html::linkButton( wfMessage( 'edithelp' )->text(),
+               $edithelp = Html::linkButton( $this->context->msg( 'edithelp' )->text(),
                        $attrs, [ 'mw-ui-quiet' ] ) .
-                       wfMessage( 'word-separator' )->escaped() .
-                       wfMessage( 'newwindow' )->parse();
+                       $this->context->msg( 'word-separator' )->escaped() .
+                       $this->context->msg( 'newwindow' )->parse();
 
-               $out->addHTML( "        <span class='cancelLink'>{$cancel}</span>\n" );
-               $out->addHTML( "        <span class='editHelp'>{$edithelp}</span>\n" );
-               $out->addHTML( "</div><!-- editButtons -->\n" );
+               $wgOut->addHTML( "      <span class='cancelLink'>{$cancel}</span>\n" );
+               $wgOut->addHTML( "      <span class='editHelp'>{$edithelp}</span>\n" );
+               $wgOut->addHTML( "</div><!-- editButtons -->\n" );
 
-               Hooks::run( 'EditPage::showStandardInputs:options', [ $this, $out, &$tabindex ] );
+               Hooks::run( 'EditPage::showStandardInputs:options', [ $this, $wgOut, &$tabindex ] );
 
-               $out->addHTML( "</div><!-- editOptions -->\n" );
+               $wgOut->addHTML( "</div><!-- editOptions -->\n" );
        }
 
        /**
@@ -3525,9 +3576,10 @@ HTML
         * If you want to use another entry point to this function, be careful.
         */
        protected function showConflict() {
-               $out = $this->context->getOutput();
-               if ( Hooks::run( 'EditPageBeforeConflictDiff', [ &$this, &$out ] ) ) {
-                       $stats = $this->context->getStats();
+               global $wgOut;
+
+               if ( Hooks::run( 'EditPageBeforeConflictDiff', [ &$this, &$wgOut ] ) ) {
+                       $stats = $wgOut->getContext()->getStats();
                        $stats->increment( 'edit.failures.conflict' );
                        // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
                        if (
@@ -3537,7 +3589,7 @@ HTML
                                $stats->increment( 'edit.failures.conflict.byNamespaceId.' . $this->mTitle->getNamespace() );
                        }
 
-                       $out->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
+                       $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
 
                        $content1 = $this->toEditContent( $this->textbox1 );
                        $content2 = $this->toEditContent( $this->textbox2 );
@@ -3546,11 +3598,11 @@ HTML
                        $de = $handler->createDifferenceEngine( $this->mArticle->getContext() );
                        $de->setContent( $content2, $content1 );
                        $de->showDiff(
-                               wfMessage( 'yourtext' )->parse(),
-                               wfMessage( 'storedversion' )->text()
+                               $this->context->msg( 'yourtext' )->parse(),
+                               $this->context->msg( 'storedversion' )->text()
                        );
 
-                       $out->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
+                       $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
                        $this->showTextbox2();
                }
        }
@@ -3569,7 +3621,7 @@ HTML
 
                return Linker::linkKnown(
                        $this->getContextTitle(),
-                       wfMessage( 'cancel' )->parse(),
+                       $this->context->msg( 'cancel' )->parse(),
                        Html::buttonAttributes( $attrs, [ 'mw-ui-quiet' ] ),
                        $cancelParams
                );
@@ -3619,7 +3671,7 @@ HTML
         * @return bool|stdClass
         */
        protected function getLastDelete() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $data = $dbr->selectRow(
                        [ 'logging', 'user' ],
                        [
@@ -3646,11 +3698,11 @@ HTML
                // Quick paranoid permission checks...
                if ( is_object( $data ) ) {
                        if ( $data->log_deleted & LogPage::DELETED_USER ) {
-                               $data->user_name = wfMessage( 'rev-deleted-user' )->escaped();
+                               $data->user_name = $this->context->msg( 'rev-deleted-user' )->escaped();
                        }
 
                        if ( $data->log_deleted & LogPage::DELETED_COMMENT ) {
-                               $data->log_comment = wfMessage( 'rev-deleted-comment' )->escaped();
+                               $data->log_comment = $this->context->msg( 'rev-deleted-comment' )->escaped();
                        }
                }
 
@@ -3663,10 +3715,10 @@ HTML
         * @return string
         */
        function getPreviewText() {
-               global $wgRawHtml, $wgAllowUserCss, $wgAllowUserJs;
+               global $wgOut, $wgRawHtml, $wgLang;
+               global $wgAllowUserCss, $wgAllowUserJs;
 
-               $stats = $this->context->getStats();
-               $out = $this->context->getOutput();
+               $stats = $wgOut->getContext()->getStats();
 
                if ( $wgRawHtml && !$this->mTokenOk ) {
                        // Could be an offsite preview attempt. This is very unsafe if
@@ -3676,8 +3728,9 @@ HTML
                                // Do not put big scary notice, if previewing the empty
                                // string, which happens when you initially edit
                                // a category page, due to automatic preview-on-open.
-                               $parsedNote = $out->parse( "<div class='previewnote'>" .
-                                       wfMessage( 'session_fail_preview_html' )->text() . "</div>", true, /* interface */true );
+                               $parsedNote = $wgOut->parse( "<div class='previewnote'>" .
+                                       $this->context->msg( 'session_fail_preview_html' )->text() . "</div>",
+                                       true, /* interface */true );
                        }
                        $stats->increment( 'edit.failures.session_loss' );
                        return $parsedNote;
@@ -3698,23 +3751,23 @@ HTML
 
                        # provide a anchor link to the editform
                        $continueEditing = '<span class="mw-continue-editing">' .
-                               '[[#' . self::EDITFORM_ID . '|' . $this->context->getLanguage()->getArrow() . ' ' .
-                               wfMessage( 'continue-editing' )->text() . ']]</span>';
+                               '[[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' .
+                               $this->context->msg( 'continue-editing' )->text() . ']]</span>';
                        if ( $this->mTriedSave && !$this->mTokenOk ) {
                                if ( $this->mTokenOkExceptSuffix ) {
-                                       $note = wfMessage( 'token_suffix_mismatch' )->plain();
+                                       $note = $this->context->msg( 'token_suffix_mismatch' )->plain();
                                        $stats->increment( 'edit.failures.bad_token' );
                                } else {
-                                       $note = wfMessage( 'session_fail_preview' )->plain();
+                                       $note = $this->context->msg( 'session_fail_preview' )->plain();
                                        $stats->increment( 'edit.failures.session_loss' );
                                }
                        } elseif ( $this->incompleteForm ) {
-                               $note = wfMessage( 'edit_form_incomplete' )->plain();
+                               $note = $this->context->msg( 'edit_form_incomplete' )->plain();
                                if ( $this->mTriedSave ) {
                                        $stats->increment( 'edit.failures.incomplete_form' );
                                }
                        } else {
-                               $note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
+                               $note = $this->context->msg( 'previewnote' )->plain() . ' ' . $continueEditing;
                        }
 
                        # don't parse non-wikitext pages, show message about preview
@@ -3745,7 +3798,7 @@ HTML
                                # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
                                if ( $level && $format ) {
                                        $note = "<div id='mw-{$level}{$format}preview'>" .
-                                               wfMessage( "{$level}{$format}preview" )->text() .
+                                               $this->context->msg( "{$level}{$format}preview" )->text() .
                                                ' ' . $continueEditing . "</div>";
                                }
                        }
@@ -3764,14 +3817,14 @@ HTML
                        $parserOutput = $parserResult['parserOutput'];
                        $previewHTML = $parserResult['html'];
                        $this->mParserOutput = $parserOutput;
-                       $out->addParserOutputMetadata( $parserOutput );
+                       $wgOut->addParserOutputMetadata( $parserOutput );
 
                        if ( count( $parserOutput->getWarnings() ) ) {
                                $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
                        }
 
                } catch ( MWContentSerializationException $ex ) {
-                       $m = wfMessage(
+                       $m = $this->context->msg(
                                'content-failed-to-parse',
                                $this->contentModel,
                                $this->contentFormat,
@@ -3783,14 +3836,14 @@ HTML
 
                if ( $this->isConflict ) {
                        $conflict = '<h2 id="mw-previewconflict">'
-                               . wfMessage( 'previewconflict' )->escaped() . "</h2>\n";
+                               . $this->context->msg( 'previewconflict' )->escaped() . "</h2>\n";
                } else {
                        $conflict = '<hr />';
                }
 
                $previewhead = "<div class='previewnote'>\n" .
-                       '<h2 id="mw-previewheader">' . wfMessage( 'preview' )->escaped() . "</h2>" .
-                       $out->parse( $note, true, /* interface */true ) . $conflict . "</div>\n";
+                       '<h2 id="mw-previewheader">' . $this->context->msg( 'preview' )->escaped() . "</h2>" .
+                       $wgOut->parse( $note, true, /* interface */true ) . $conflict . "</div>\n";
 
                $pageViewLang = $this->mTitle->getPageViewLanguage();
                $attribs = [ 'lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(),
@@ -3805,7 +3858,7 @@ HTML
         * @return ParserOptions
         */
        protected function getPreviewParserOptions() {
-               $parserOptions = $this->page->makeParserOptions( $this->context );
+               $parserOptions = $this->page->makeParserOptions( $this->mArticle->getContext() );
                $parserOptions->setIsPreview( true );
                $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
                $parserOptions->enableLimitReport();
@@ -3822,11 +3875,11 @@ HTML
         *   - html: The HTML to be displayed
         */
        protected function doPreviewParse( Content $content ) {
-               $user = $this->context->getUser();
+               global $wgUser;
                $parserOptions = $this->getPreviewParserOptions();
-               $pstContent = $content->preSaveTransform( $this->mTitle, $user, $parserOptions );
+               $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
                $scopedCallback = $parserOptions->setupFakeRevision(
-                       $this->mTitle, $pstContent, $user );
+                       $this->mTitle, $pstContent, $wgUser );
                $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
                ScopedCallback::consume( $scopedCallback );
                $parserOutput->setEditSectionTokens( false ); // no section edit links
@@ -4002,19 +4055,18 @@ HTML
         * @return array
         */
        public function getCheckboxes( &$tabindex, $checked ) {
-               global $wgUseMediaWikiUIEverywhere;
+               global $wgUser, $wgUseMediaWikiUIEverywhere;
 
                $checkboxes = [];
-               $user = $this->context->getUser();
 
                // don't show the minor edit checkbox if it's a new page or section
                if ( !$this->isNew ) {
                        $checkboxes['minor'] = '';
-                       $minorLabel = wfMessage( 'minoredit' )->parse();
-                       if ( $user->isAllowed( 'minoredit' ) ) {
+                       $minorLabel = $this->context->msg( 'minoredit' )->parse();
+                       if ( $wgUser->isAllowed( 'minoredit' ) ) {
                                $attribs = [
                                        'tabindex' => ++$tabindex,
-                                       'accesskey' => wfMessage( 'accesskey-minoredit' )->text(),
+                                       'accesskey' => $this->context->msg( 'accesskey-minoredit' )->text(),
                                        'id' => 'wpMinoredit',
                                ];
                                $minorEditHtml =
@@ -4033,12 +4085,12 @@ HTML
                        }
                }
 
-               $watchLabel = wfMessage( 'watchthis' )->parse();
+               $watchLabel = $this->context->msg( 'watchthis' )->parse();
                $checkboxes['watch'] = '';
-               if ( $user->isLoggedIn() ) {
+               if ( $wgUser->isLoggedIn() ) {
                        $attribs = [
                                'tabindex' => ++$tabindex,
-                               'accesskey' => wfMessage( 'accesskey-watch' )->text(),
+                               'accesskey' => $this->context->msg( 'accesskey-watch' )->text(),
                                'id' => 'wpWatchthis',
                        ];
                        $watchThisHtml =
@@ -4069,14 +4121,16 @@ HTML
        public function getEditButtons( &$tabindex ) {
                $buttons = [];
 
-               $labelAsPublish = $this->mArticle->getContext()->getConfig()->get( 'EditButtonPublishNotSave' );
+               $labelAsPublish =
+                       $this->mArticle->getContext()->getConfig()->get( 'EditSubmitButtonLabelPublish' );
+
                // Can't use $this->isNew as that's also true if we're adding a new section to an extant page
                if ( $labelAsPublish ) {
                        $buttonLabelKey = !$this->mTitle->exists() ? 'publishpage' : 'publishchanges';
                } else {
                        $buttonLabelKey = !$this->mTitle->exists() ? 'savearticle' : 'savechanges';
                }
-               $buttonLabel = wfMessage( $buttonLabelKey )->text();
+               $buttonLabel = $this->context->msg( $buttonLabelKey )->text();
                $attribs = [
                        'id' => 'wpSave',
                        'name' => 'wpSave',
@@ -4090,7 +4144,7 @@ HTML
                        'name' => 'wpPreview',
                        'tabindex' => $tabindex,
                ] + Linker::tooltipAndAccesskeyAttribs( 'preview' );
-               $buttons['preview'] = Html::submitButton( wfMessage( 'showpreview' )->text(),
+               $buttons['preview'] = Html::submitButton( $this->context->msg( 'showpreview' )->text(),
                        $attribs );
                $buttons['live'] = '';
 
@@ -4099,7 +4153,7 @@ HTML
                        'name' => 'wpDiff',
                        'tabindex' => ++$tabindex,
                ] + Linker::tooltipAndAccesskeyAttribs( 'diff' );
-               $buttons['diff'] = Html::submitButton( wfMessage( 'showdiff' )->text(),
+               $buttons['diff'] = Html::submitButton( $this->context->msg( 'showdiff' )->text(),
                        $attribs );
 
                Hooks::run( 'EditPageBeforeEditButtons', [ &$this, &$buttons, &$tabindex ] );
@@ -4111,14 +4165,15 @@ HTML
         * they have attempted to edit a nonexistent section.
         */
        function noSuchSectionPage() {
-               $out = $this->context->getOutput();
-               $out->prepareErrorPage( wfMessage( 'nosuchsectiontitle' ) );
+               global $wgOut;
+
+               $wgOut->prepareErrorPage( $this->context->msg( 'nosuchsectiontitle' ) );
 
-               $res = wfMessage( 'nosuchsectiontext', $this->section )->parseAsBlock();
+               $res = $this->context->msg( 'nosuchsectiontext', $this->section )->parseAsBlock();
                Hooks::run( 'EditPageNoSuchSection', [ &$this, &$res ] );
-               $out->addHTML( $res );
+               $wgOut->addHTML( $res );
 
-               $out->returnToMain( false, $this->mTitle );
+               $wgOut->returnToMain( false, $this->mTitle );
        }
 
        /**
@@ -4127,28 +4182,28 @@ HTML
         * @param string|array|bool $match Text (or array of texts) which triggered one or more filters
         */
        public function spamPageWithContent( $match = false ) {
+               global $wgOut, $wgLang;
                $this->textbox2 = $this->textbox1;
 
                if ( is_array( $match ) ) {
-                       $match = $this->context->getLanguage()->listToText( $match );
+                       $match = $wgLang->listToText( $match );
                }
-               $out = $this->context->getOutput();
-               $out->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) );
+               $wgOut->prepareErrorPage( $this->context->msg( 'spamprotectiontitle' ) );
 
-               $out->addHTML( '<div id="spamprotected">' );
-               $out->addWikiMsg( 'spamprotectiontext' );
+               $wgOut->addHTML( '<div id="spamprotected">' );
+               $wgOut->addWikiMsg( 'spamprotectiontext' );
                if ( $match ) {
-                       $out->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
+                       $wgOut->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
                }
-               $out->addHTML( '</div>' );
+               $wgOut->addHTML( '</div>' );
 
-               $out->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
+               $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
                $this->showDiff();
 
-               $out->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
+               $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
                $this->showTextbox2();
 
-               $out->addReturnTo( $this->getContextTitle(), [ 'action' => 'edit' ] );
+               $wgOut->addReturnTo( $this->getContextTitle(), [ 'action' => 'edit' ] );
        }
 
        /**
@@ -4158,9 +4213,9 @@ HTML
         * @return bool
         */
        private function checkUnicodeCompliantBrowser() {
-               global $wgBrowserBlackList;
+               global $wgBrowserBlackList, $wgRequest;
 
-               $currentbrowser = $this->context->getRequest()->getHeader( 'User-Agent' );
+               $currentbrowser = $wgRequest->getHeader( 'User-Agent' );
                if ( $currentbrowser === false ) {
                        // No User-Agent header sent? Trust it by default...
                        return true;
index 158c852..3b2283b 100644 (file)
@@ -226,6 +226,7 @@ class FauxRequest extends WebRequest {
        }
 
        /**
+        * @codeCoverageIgnore
         * @param array $extWhitelist
         * @return bool
         */
@@ -234,6 +235,7 @@ class FauxRequest extends WebRequest {
        }
 
        /**
+        * @codeCoverageIgnore
         * @return string
         */
        protected function getRawIP() {
index 0d66908..90bba53 100644 (file)
@@ -358,7 +358,7 @@ function wfRandomString( $length = 32 ) {
  *
  * ;:@$!*(),/~
  *
- * However, IIS7 redirects fail when the url contains a colon (Bug 22709),
+ * However, IIS7 redirects fail when the url contains a colon (see T24709),
  * so no fancy : for IIS7.
  *
  * %2F in the page titles seems to fatally break for some reason.
@@ -617,7 +617,7 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
  * This is the basic structure used (brackets contain keys for $urlParts):
  * [scheme][delimiter][user]:[pass]@[host]:[port][path]?[query]#[fragment]
  *
- * @todo Need to integrate this into wfExpandUrl (bug 32168)
+ * @todo Need to integrate this into wfExpandUrl (see T34168)
  *
  * @since 1.19
  * @param array $urlParts URL parts, as output from wfParseUrl
@@ -670,7 +670,7 @@ function wfAssembleUrl( $urlParts ) {
  * '/a/./b/../c/' becomes '/a/c/'.  For details on the algorithm, please see
  * RFC3986 section 5.2.4.
  *
- * @todo Need to integrate this into wfExpandUrl (bug 32168)
+ * @todo Need to integrate this into wfExpandUrl (see T34168)
  *
  * @param string $urlPath URL path, potentially containing dot-segments
  * @return string URL path with all dot-segments removed
@@ -850,11 +850,11 @@ function wfParseUrl( $url ) {
                return false;
        }
 
-       /* Provide an empty host for eg. file:/// urls (see bug 28627) */
+       /* Provide an empty host for eg. file:/// urls (see T30627) */
        if ( !isset( $bits['host'] ) ) {
                $bits['host'] = '';
 
-               // bug 45069
+               // See T47069
                if ( isset( $bits['path'] ) ) {
                        /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
                        if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
@@ -1195,6 +1195,7 @@ function wfLogProfilingData() {
                        $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
                        $statsdSender = new SocketSender( $statsdHost, $statsdPort );
                        $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
+                       $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
                        $statsdClient->send( $context->getStats()->getBuffer() );
                } catch ( Exception $ex ) {
                        MWExceptionHandler::logException( $ex );
@@ -1277,7 +1278,7 @@ function wfReadOnly() {
  * Check if the site is in read-only mode and return the message if so
  *
  * This checks wfConfiguredReadOnlyReason() and the main load balancer
- * for slave lag. This may result in DB_SLAVE connection being made.
+ * for replica DB lag. This may result in DB connection being made.
  *
  * @return string|bool String when in read-only mode; false otherwise
  */
@@ -1664,6 +1665,7 @@ function wfClientAcceptsGzip( $force = false ) {
  * @return string
  */
 function wfEscapeWikiText( $text ) {
+       global $wgEnableMagicLinks;
        static $repl = null, $repl2 = null;
        if ( $repl === null ) {
                $repl = [
@@ -1681,8 +1683,9 @@ function wfEscapeWikiText( $text ) {
                        '__' => '_&#95;', '://' => '&#58;//',
                ];
 
+               $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
                // We have to catch everything "\s" matches in PCRE
-               foreach ( [ 'ISBN', 'RFC', 'PMID' ] as $magic ) {
+               foreach ( $magicLinks as $magic ) {
                        $repl["$magic "] = "$magic&#32;";
                        $repl["$magic\t"] = "$magic&#9;";
                        $repl["$magic\r"] = "$magic&#13;";
@@ -1989,56 +1992,7 @@ function wfRestoreWarnings() {
 
 # Autodetect, convert and provide timestamps of various types
 
-/**
- * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC
- */
-define( 'TS_UNIX', 0 );
-
-/**
- * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
- */
-define( 'TS_MW', 1 );
-
-/**
- * MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
- */
-define( 'TS_DB', 2 );
-
-/**
- * RFC 2822 format, for E-mail and HTTP headers
- */
-define( 'TS_RFC2822', 3 );
-
-/**
- * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z
- *
- * This is used by Special:Export
- */
-define( 'TS_ISO_8601', 4 );
-
-/**
- * An Exif timestamp (YYYY:MM:DD HH:MM:SS)
- *
- * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
- *       DateTime tag and page 36 for the DateTimeOriginal and
- *       DateTimeDigitized tags.
- */
-define( 'TS_EXIF', 5 );
-
-/**
- * Oracle format time.
- */
-define( 'TS_ORACLE', 6 );
-
-/**
- * Postgres format time.
- */
-define( 'TS_POSTGRES', 7 );
-
-/**
- * ISO 8601 basic format with no timezone: 19860209T200000Z.  This is used by ResourceLoader
- */
-define( 'TS_ISO_8601_BASIC', 9 );
+require_once __DIR__ . '/libs/time/defines.php';
 
 /**
  * Get a timestamp string in one of various formats
@@ -2305,7 +2259,7 @@ function wfEscapeShellArg( /*...*/ ) {
                        // Refs:
                        //  * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
                        //  * http://technet.microsoft.com/en-us/library/cc723564.aspx
-                       //  * Bug #13518
+                       //  * T15518
                        //  * CR r63214
                        // Double the backslashes before any double quotes. Escape the double quotes.
                        // @codingStandardsIgnoreEnd
@@ -3124,7 +3078,7 @@ function wfSplitWikiID( $wiki ) {
  * Get a Database object.
  *
  * @param int $db Index of the connection to get. May be DB_MASTER for the
- *            master (for write queries), DB_SLAVE for potentially lagged read
+ *            master (for write queries), DB_REPLICA for potentially lagged read
  *            queries, or an integer >= 0 for a particular server.
  *
  * @param string|string[] $groups Query groups. An array of group names that this query
@@ -3133,7 +3087,7 @@ function wfSplitWikiID( $wiki ) {
  *
  * @param string|bool $wiki The wiki ID, or false for the current wiki
  *
- * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request
+ * Note: multiple calls to wfGetDB(DB_REPLICA) during the course of one request
  * will always return the same object, unless the underlying connection or load
  * balancer is manually destroyed.
  *
@@ -3278,10 +3232,10 @@ function wfGetNull() {
 }
 
 /**
- * Waits for the slaves to catch up to the master position
+ * Waits for the replica DBs to catch up to the master position
  *
  * Use this when updating very large numbers of rows, as in maintenance scripts,
- * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
  *
  * By default this waits on the main DB cluster of the current wiki.
  * If $cluster is set to "*" it will wait on all DB clusters, including
index 8673125..b17a2f5 100644 (file)
@@ -245,7 +245,7 @@ class HistoryBlobStub {
                if ( isset( self::$blobCache[$this->mOldId] ) ) {
                        $obj = self::$blobCache[$this->mOldId];
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow(
                                'text',
                                [ 'old_flags', 'old_text' ],
@@ -336,7 +336,7 @@ class HistoryBlobCurStub {
         * @return string|bool
         */
        function getText() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'cur', [ 'cur_text' ], [ 'cur_id' => $this->mCurId ] );
                if ( !$row ) {
                        return false;
index b6c194c..511781d 100644 (file)
@@ -64,7 +64,7 @@ class Hooks {
         * @throws MWException If not in testing mode.
         */
        public static function clear( $name ) {
-               if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+               if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MW_PARSER_TEST' ) ) {
                        throw new MWException( 'Cannot reset hooks in operation.' );
                }
 
index 54b057a..2ca5e1b 100644 (file)
@@ -599,7 +599,7 @@ class MWHttpRequest {
         * Returns the value of the given response header.
         *
         * @param string $header
-        * @return string
+        * @return string|null
         */
        public function getResponseHeader( $header ) {
                if ( !$this->respHeaders ) {
index 802bfbe..7b3d72b 100644 (file)
@@ -89,10 +89,10 @@ class LinkFilter {
         *
         * @param string $filterEntry Domainparts
         * @param string $protocol Protocol (default http://)
-        * @return array Array to be passed to Database::buildLike() or false on error
+        * @return array|bool Array to be passed to Database::buildLike() or false on error
         */
        public static function makeLikeArray( $filterEntry, $protocol = 'http://' ) {
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
 
                $target = $protocol . $filterEntry;
                $bits = wfParseUrl( $target );
index d5507b7..8682991 100644 (file)
@@ -428,47 +428,43 @@ class Linker {
                        return self::link( $title );
                }
 
-               // Shortcuts
-               $fp =& $frameParams;
-               $hp =& $handlerParams;
-
                // Clean up parameters
-               $page = isset( $hp['page'] ) ? $hp['page'] : false;
-               if ( !isset( $fp['align'] ) ) {
-                       $fp['align'] = '';
+               $page = isset( $handlerParams['page'] ) ? $handlerParams['page'] : false;
+               if ( !isset( $frameParams['align'] ) ) {
+                       $frameParams['align'] = '';
                }
-               if ( !isset( $fp['alt'] ) ) {
-                       $fp['alt'] = '';
+               if ( !isset( $frameParams['alt'] ) ) {
+                       $frameParams['alt'] = '';
                }
-               if ( !isset( $fp['title'] ) ) {
-                       $fp['title'] = '';
+               if ( !isset( $frameParams['title'] ) ) {
+                       $frameParams['title'] = '';
                }
-               if ( !isset( $fp['class'] ) ) {
-                       $fp['class'] = '';
+               if ( !isset( $frameParams['class'] ) ) {
+                       $frameParams['class'] = '';
                }
 
                $prefix = $postfix = '';
 
-               if ( 'center' == $fp['align'] ) {
+               if ( 'center' == $frameParams['align'] ) {
                        $prefix = '<div class="center">';
                        $postfix = '</div>';
-                       $fp['align'] = 'none';
+                       $frameParams['align'] = 'none';
                }
-               if ( $file && !isset( $hp['width'] ) ) {
-                       if ( isset( $hp['height'] ) && $file->isVectorized() ) {
+               if ( $file && !isset( $handlerParams['width'] ) ) {
+                       if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
                                // If its a vector image, and user only specifies height
                                // we don't want it to be limited by its "normal" width.
                                global $wgSVGMaxSize;
-                               $hp['width'] = $wgSVGMaxSize;
+                               $handlerParams['width'] = $wgSVGMaxSize;
                        } else {
-                               $hp['width'] = $file->getWidth( $page );
+                               $handlerParams['width'] = $file->getWidth( $page );
                        }
 
-                       if ( isset( $fp['thumbnail'] )
-                               || isset( $fp['manualthumb'] )
-                               || isset( $fp['framed'] )
-                               || isset( $fp['frameless'] )
-                               || !$hp['width']
+                       if ( isset( $frameParams['thumbnail'] )
+                               || isset( $frameParams['manualthumb'] )
+                               || isset( $frameParams['framed'] )
+                               || isset( $frameParams['frameless'] )
+                               || !$handlerParams['width']
                        ) {
                                global $wgThumbLimits, $wgThumbUpright;
 
@@ -477,73 +473,77 @@ class Linker {
                                }
 
                                // Reduce width for upright images when parameter 'upright' is used
-                               if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
-                                       $fp['upright'] = $wgThumbUpright;
+                               if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
+                                       $frameParams['upright'] = $wgThumbUpright;
                                }
 
                                // For caching health: If width scaled down due to upright
                                // parameter, round to full __0 pixel to avoid the creation of a
                                // lot of odd thumbs.
-                               $prefWidth = isset( $fp['upright'] ) ?
-                                       round( $wgThumbLimits[$widthOption] * $fp['upright'], -1 ) :
+                               $prefWidth = isset( $frameParams['upright'] ) ?
+                                       round( $wgThumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
                                        $wgThumbLimits[$widthOption];
 
                                // Use width which is smaller: real image width or user preference width
                                // Unless image is scalable vector.
-                               if ( !isset( $hp['height'] ) && ( $hp['width'] <= 0 ||
-                                               $prefWidth < $hp['width'] || $file->isVectorized() ) ) {
-                                       $hp['width'] = $prefWidth;
+                               if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
+                                               $prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
+                                       $handlerParams['width'] = $prefWidth;
                                }
                        }
                }
 
-               if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
+               if ( isset( $frameParams['thumbnail'] ) || isset( $frameParams['manualthumb'] )
+                       || isset( $frameParams['framed'] )
+               ) {
                        # Create a thumbnail. Alignment depends on the writing direction of
                        # the page content language (right-aligned for LTR languages,
                        # left-aligned for RTL languages)
                        # If a thumbnail width has not been provided, it is set
                        # to the default user option as specified in Language*.php
-                       if ( $fp['align'] == '' ) {
-                               $fp['align'] = $parser->getTargetLanguage()->alignEnd();
+                       if ( $frameParams['align'] == '' ) {
+                               $frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
                        }
-                       return $prefix . self::makeThumbLink2( $title, $file, $fp, $hp, $time, $query ) . $postfix;
+                       return $prefix .
+                               self::makeThumbLink2( $title, $file, $frameParams, $handlerParams, $time, $query ) .
+                               $postfix;
                }
 
-               if ( $file && isset( $fp['frameless'] ) ) {
+               if ( $file && isset( $frameParams['frameless'] ) ) {
                        $srcWidth = $file->getWidth( $page );
                        # For "frameless" option: do not present an image bigger than the
                        # source (for bitmap-style images). This is the same behavior as the
                        # "thumb" option does it already.
-                       if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
-                               $hp['width'] = $srcWidth;
+                       if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
+                               $handlerParams['width'] = $srcWidth;
                        }
                }
 
-               if ( $file && isset( $hp['width'] ) ) {
+               if ( $file && isset( $handlerParams['width'] ) ) {
                        # Create a resized image, without the additional thumbnail features
-                       $thumb = $file->transform( $hp );
+                       $thumb = $file->transform( $handlerParams );
                } else {
                        $thumb = false;
                }
 
                if ( !$thumb ) {
-                       $s = self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
+                       $s = self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
                } else {
-                       self::processResponsiveImages( $file, $thumb, $hp );
+                       self::processResponsiveImages( $file, $thumb, $handlerParams );
                        $params = [
-                               'alt' => $fp['alt'],
-                               'title' => $fp['title'],
-                               'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false,
-                               'img-class' => $fp['class'] ];
-                       if ( isset( $fp['border'] ) ) {
+                               'alt' => $frameParams['alt'],
+                               'title' => $frameParams['title'],
+                               'valign' => isset( $frameParams['valign'] ) ? $frameParams['valign'] : false,
+                               'img-class' => $frameParams['class'] ];
+                       if ( isset( $frameParams['border'] ) ) {
                                $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
                        }
-                       $params = self::getImageLinkMTOParams( $fp, $query, $parser ) + $params;
+                       $params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
 
                        $s = $thumb->toHtml( $params );
                }
-               if ( $fp['align'] != '' ) {
-                       $s = "<div class=\"float{$fp['align']}\">{$s}</div>";
+               if ( $frameParams['align'] != '' ) {
+                       $s = "<div class=\"float{$frameParams['align']}\">{$s}</div>";
                }
                return str_replace( "\n", ' ', $prefix . $s . $postfix );
        }
@@ -626,65 +626,61 @@ class Linker {
        ) {
                $exists = $file && $file->exists();
 
-               # Shortcuts
-               $fp =& $frameParams;
-               $hp =& $handlerParams;
-
-               $page = isset( $hp['page'] ) ? $hp['page'] : false;
-               if ( !isset( $fp['align'] ) ) {
-                       $fp['align'] = 'right';
+               $page = isset( $handlerParams['page'] ) ? $handlerParams['page'] : false;
+               if ( !isset( $frameParams['align'] ) ) {
+                       $frameParams['align'] = 'right';
                }
-               if ( !isset( $fp['alt'] ) ) {
-                       $fp['alt'] = '';
+               if ( !isset( $frameParams['alt'] ) ) {
+                       $frameParams['alt'] = '';
                }
-               if ( !isset( $fp['title'] ) ) {
-                       $fp['title'] = '';
+               if ( !isset( $frameParams['title'] ) ) {
+                       $frameParams['title'] = '';
                }
-               if ( !isset( $fp['caption'] ) ) {
-                       $fp['caption'] = '';
+               if ( !isset( $frameParams['caption'] ) ) {
+                       $frameParams['caption'] = '';
                }
 
-               if ( empty( $hp['width'] ) ) {
+               if ( empty( $handlerParams['width'] ) ) {
                        // Reduce width for upright images when parameter 'upright' is used
-                       $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
+                       $handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
                }
                $thumb = false;
                $noscale = false;
                $manualthumb = false;
 
                if ( !$exists ) {
-                       $outerWidth = $hp['width'] + 2;
+                       $outerWidth = $handlerParams['width'] + 2;
                } else {
-                       if ( isset( $fp['manualthumb'] ) ) {
+                       if ( isset( $frameParams['manualthumb'] ) ) {
                                # Use manually specified thumbnail
-                               $manual_title = Title::makeTitleSafe( NS_FILE, $fp['manualthumb'] );
+                               $manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
                                if ( $manual_title ) {
                                        $manual_img = wfFindFile( $manual_title );
                                        if ( $manual_img ) {
-                                               $thumb = $manual_img->getUnscaledThumb( $hp );
+                                               $thumb = $manual_img->getUnscaledThumb( $handlerParams );
                                                $manualthumb = true;
                                        } else {
                                                $exists = false;
                                        }
                                }
-                       } elseif ( isset( $fp['framed'] ) ) {
+                       } elseif ( isset( $frameParams['framed'] ) ) {
                                // Use image dimensions, don't scale
-                               $thumb = $file->getUnscaledThumb( $hp );
+                               $thumb = $file->getUnscaledThumb( $handlerParams );
                                $noscale = true;
                        } else {
                                # Do not present an image bigger than the source, for bitmap-style images
                                # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
                                $srcWidth = $file->getWidth( $page );
-                               if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
-                                       $hp['width'] = $srcWidth;
+                               if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
+                                       $handlerParams['width'] = $srcWidth;
                                }
-                               $thumb = $file->transform( $hp );
+                               $thumb = $file->transform( $handlerParams );
                        }
 
                        if ( $thumb ) {
                                $outerWidth = $thumb->getWidth() + 2;
                        } else {
-                               $outerWidth = $hp['width'] + 2;
+                               $outerWidth = $handlerParams['width'] + 2;
                        }
                }
 
@@ -696,35 +692,35 @@ class Linker {
                        $url = wfAppendQuery( $url, [ 'page' => $page ] );
                }
                if ( $manualthumb
-                       && !isset( $fp['link-title'] )
-                       && !isset( $fp['link-url'] )
-                       && !isset( $fp['no-link'] ) ) {
-                       $fp['link-url'] = $url;
+                       && !isset( $frameParams['link-title'] )
+                       && !isset( $frameParams['link-url'] )
+                       && !isset( $frameParams['no-link'] ) ) {
+                       $frameParams['link-url'] = $url;
                }
 
-               $s = "<div class=\"thumb t{$fp['align']}\">"
+               $s = "<div class=\"thumb t{$frameParams['align']}\">"
                        . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
 
                if ( !$exists ) {
-                       $s .= self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
+                       $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
                        $zoomIcon = '';
                } elseif ( !$thumb ) {
                        $s .= wfMessage( 'thumbnail_error', '' )->escaped();
                        $zoomIcon = '';
                } else {
                        if ( !$noscale && !$manualthumb ) {
-                               self::processResponsiveImages( $file, $thumb, $hp );
+                               self::processResponsiveImages( $file, $thumb, $handlerParams );
                        }
                        $params = [
-                               'alt' => $fp['alt'],
-                               'title' => $fp['title'],
-                               'img-class' => ( isset( $fp['class'] ) && $fp['class'] !== ''
-                                       ? $fp['class'] . ' '
+                               'alt' => $frameParams['alt'],
+                               'title' => $frameParams['title'],
+                               'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
+                                       ? $frameParams['class'] . ' '
                                        : '' ) . 'thumbimage'
                        ];
-                       $params = self::getImageLinkMTOParams( $fp, $query ) + $params;
+                       $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
                        $s .= $thumb->toHtml( $params );
-                       if ( isset( $fp['framed'] ) ) {
+                       if ( isset( $frameParams['framed'] ) ) {
                                $zoomIcon = "";
                        } else {
                                $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
@@ -735,7 +731,7 @@ class Linker {
                                                "" ) );
                        }
                }
-               $s .= '  <div class="thumbcaption">' . $zoomIcon . $fp['caption'] . "</div></div></div>";
+               $s .= '  <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
                return str_replace( "\n", ' ', $s );
        }
 
@@ -997,9 +993,10 @@ class Linker {
                        $page = Title::makeTitle( NS_USER, $userName );
                }
 
+               // Wrap the output with <bdi> tags for directionality isolation
                return self::link(
                        $page,
-                       htmlspecialchars( $altUserName !== false ? $altUserName : $userName ),
+                       '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>',
                        [ 'class' => $classes ]
                );
        }
@@ -1807,7 +1804,7 @@ class Linker {
                        return null;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Up to the value of $wgShowRollbackEditCount revisions are counted
                $res = $dbr->select(
@@ -1922,6 +1919,8 @@ class Linker {
        }
 
        /**
+        * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
+        *
         * Returns HTML for the "templates used on this page" list.
         *
         * Make an HTML list of templates, and then add a "More..." link at
@@ -1940,87 +1939,24 @@ class Linker {
        public static function formatTemplates( $templates, $preview = false,
                $section = false, $more = null
        ) {
-               global $wgLang;
-
-               $outText = '';
-               if ( count( $templates ) > 0 ) {
-                       # Do a batch existence check
-                       $batch = new LinkBatch;
-                       foreach ( $templates as $title ) {
-                               $batch->addObj( $title );
-                       }
-                       $batch->execute();
-
-                       # Construct the HTML
-                       $outText = '<div class="mw-templatesUsedExplanation">';
-                       if ( $preview ) {
-                               $outText .= wfMessage( 'templatesusedpreview' )->numParams( count( $templates ) )
-                                       ->parseAsBlock();
-                       } elseif ( $section ) {
-                               $outText .= wfMessage( 'templatesusedsection' )->numParams( count( $templates ) )
-                                       ->parseAsBlock();
-                       } else {
-                               $outText .= wfMessage( 'templatesused' )->numParams( count( $templates ) )
-                                       ->parseAsBlock();
-                       }
-                       $outText .= "</div><ul>\n";
-
-                       usort( $templates, 'Title::compare' );
-                       foreach ( $templates as $titleObj ) {
-                               $protected = '';
-                               $restrictions = $titleObj->getRestrictions( 'edit' );
-                               if ( $restrictions ) {
-                                       // Check backwards-compatible messages
-                                       $msg = null;
-                                       if ( $restrictions === [ 'sysop' ] ) {
-                                               $msg = wfMessage( 'template-protected' );
-                                       } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
-                                               $msg = wfMessage( 'template-semiprotected' );
-                                       }
-                                       if ( $msg && !$msg->isDisabled() ) {
-                                               $protected = $msg->parse();
-                                       } else {
-                                               // Construct the message from restriction-level-*
-                                               // e.g. restriction-level-sysop, restriction-level-autoconfirmed
-                                               $msgs = [];
-                                               foreach ( $restrictions as $r ) {
-                                                       $msgs[] = wfMessage( "restriction-level-$r" )->parse();
-                                               }
-                                               $protected = wfMessage( 'parentheses' )
-                                                       ->rawParams( $wgLang->commaList( $msgs ) )->escaped();
-                                       }
-                               }
-                               if ( $titleObj->quickUserCan( 'edit' ) ) {
-                                       $editLink = self::link(
-                                               $titleObj,
-                                               wfMessage( 'editlink' )->escaped(),
-                                               [],
-                                               [ 'action' => 'edit' ]
-                                       );
-                               } else {
-                                       $editLink = self::link(
-                                               $titleObj,
-                                               wfMessage( 'viewsourcelink' )->escaped(),
-                                               [],
-                                               [ 'action' => 'edit' ]
-                                       );
-                               }
-                               $outText .= '<li>' . self::link( $titleObj )
-                                       . wfMessage( 'word-separator' )->escaped()
-                                       . wfMessage( 'parentheses' )->rawParams( $editLink )->escaped()
-                                       . wfMessage( 'word-separator' )->escaped()
-                                       . $protected . '</li>';
-                       }
+               wfDeprecated( __METHOD__, '1.28' );
 
-                       if ( $more instanceof Title ) {
-                               $outText .= '<li>' . self::link( $more, wfMessage( 'moredotdotdot' ) ) . '</li>';
-                       } elseif ( $more ) {
-                               $outText .= "<li>$more</li>";
-                       }
+               $type = false;
+               if ( $preview ) {
+                       $type = 'preview';
+               } elseif ( $section ) {
+                       $type = 'section';
+               }
 
-                       $outText .= '</ul>';
+               if ( $more instanceof Message ) {
+                       $more = $more->toString();
                }
-               return $outText;
+
+               $formatter = new TemplatesOnThisPageFormatter(
+                       RequestContext::getMain(),
+                       MediaWikiServices::getInstance()->getLinkRenderer()
+               );
+               return $formatter->format( $templates, $type, $more );
        }
 
        /**
@@ -2052,6 +1988,8 @@ class Linker {
        }
 
        /**
+        * @deprecated since 1.28, use Language::formatSize() directly
+        *
         * Format a size in bytes for output, using an appropriate
         * unit (B, KB, MB or GB) according to the magnitude in question
         *
@@ -2060,6 +1998,8 @@ class Linker {
         * @return string
         */
        public static function formatSize( $size ) {
+               wfDeprecated( __METHOD__, '1.28' );
+
                global $wgLang;
                return htmlspecialchars( $wgLang->formatSize( $size ) );
        }
index defdc96..201e9b6 100644 (file)
  *
  * @since 1.20
  */
-class MWTimestamp {
+class MWTimestamp extends ConvertableTimestamp {
        /**
-        * Standard gmdate() formats for the different timestamp types.
-        */
-       private static $formats = [
-               TS_UNIX => 'U',
-               TS_MW => 'YmdHis',
-               TS_DB => 'Y-m-d H:i:s',
-               TS_ISO_8601 => 'Y-m-d\TH:i:s\Z',
-               TS_ISO_8601_BASIC => 'Ymd\THis\Z',
-               TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness
-               TS_RFC2822 => 'D, d M Y H:i:s',
-               TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500
-               TS_POSTGRES => 'Y-m-d H:i:s',
-       ];
-
-       /**
-        * The actual timestamp being wrapped (DateTime object).
-        * @var DateTime
-        */
-       public $timestamp;
-
-       /**
-        * Make a new timestamp and set it to the specified time,
-        * or the current time if unspecified.
-        *
-        * @since 1.20
-        *
-        * @param bool|string|int|float|DateTime $timestamp Timestamp to set, or false for current time
-        */
-       public function __construct( $timestamp = false ) {
-               if ( $timestamp instanceof DateTime ) {
-                       $this->timestamp = $timestamp;
-               } else {
-                       $this->setTimestamp( $timestamp );
-               }
-       }
-
-       /**
-        * Set the timestamp to the specified time, or the current time if unspecified.
-        *
-        * Parse the given timestamp into either a DateTime object or a Unix timestamp,
-        * and then store it.
-        *
-        * @since 1.20
-        *
-        * @param string|bool $ts Timestamp to store, or false for now
-        * @throws TimestampException
-        */
-       public function setTimestamp( $ts = false ) {
-               $m = [];
-               $da = [];
-               $strtime = '';
-
-               // We want to catch 0, '', null... but not date strings starting with a letter.
-               if ( !$ts || $ts === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ) {
-                       $uts = time();
-                       $strtime = "@$uts";
-               } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
-                       # TS_DB
-               } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
-                       # TS_EXIF
-               } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) {
-                       # TS_MW
-               } elseif ( preg_match( '/^(-?\d{1,13})(\.\d+)?$/D', $ts, $m ) ) {
-                       # TS_UNIX
-                       $strtime = "@{$m[1]}"; // http://php.net/manual/en/datetime.formats.compound.php
-               } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) {
-                       # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
-                       $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
-                                       str_replace( '+00:00', 'UTC', $ts ) );
-               } elseif ( preg_match(
-                       '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z?$/',
-                       $ts,
-                       $da
-               ) ) {
-                       # TS_ISO_8601
-               } elseif ( preg_match(
-                       '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z?$/',
-                       $ts,
-                       $da
-               ) ) {
-                       # TS_ISO_8601_BASIC
-               } elseif ( preg_match(
-                       '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/',
-                       $ts,
-                       $da
-               ) ) {
-                       # TS_POSTGRES
-               } elseif ( preg_match(
-                       '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/',
-                       $ts,
-                       $da
-               ) ) {
-                       # TS_POSTGRES
-               } elseif ( preg_match(
-                       # Day of week
-                       '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' .
-                       # dd Mon yyyy
-                       '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' .
-                       # hh:mm:ss
-                       '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S',
-                       $ts
-               ) ) {
-                       # TS_RFC2822, accepting a trailing comment.
-                       # See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171
-                       # The regex is a superset of rfc2822 for readability
-                       $strtime = strtok( $ts, ';' );
-               } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) {
-                       # TS_RFC850
-                       $strtime = $ts;
-               } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) {
-                       # asctime
-                       $strtime = $ts;
-               } else {
-                       throw new TimestampException( __METHOD__ . ": Invalid timestamp - $ts" );
-               }
-
-               if ( !$strtime ) {
-                       $da = array_map( 'intval', $da );
-                       $da[0] = "%04d-%02d-%02dT%02d:%02d:%02d.00+00:00";
-                       $strtime = call_user_func_array( "sprintf", $da );
-               }
-
-               try {
-                       $final = new DateTime( $strtime, new DateTimeZone( 'GMT' ) );
-               } catch ( Exception $e ) {
-                       throw new TimestampException( __METHOD__ . ': Invalid timestamp format.', $e->getCode(), $e );
-               }
-
-               if ( $final === false ) {
-                       throw new TimestampException( __METHOD__ . ': Invalid timestamp format.' );
-               }
-               $this->timestamp = $final;
-       }
-
-       /**
-        * Get the timestamp represented by this object in a certain form.
-        *
-        * Convert the internal timestamp to the specified format and then
-        * return it.
-        *
-        * @since 1.20
+        * Get a timestamp instance in GMT
         *
-        * @param int $style Constant Output format for timestamp
-        * @throws TimestampException
-        * @return string The formatted timestamp
+        * @param bool|string $ts Timestamp to set, or false for current time
+        * @return MWTimestamp The instance
         */
-       public function getTimestamp( $style = TS_UNIX ) {
-               if ( !isset( self::$formats[$style] ) ) {
-                       throw new TimestampException( __METHOD__ . ': Illegal timestamp output type.' );
-               }
-
-               $output = $this->timestamp->format( self::$formats[$style] );
-
-               if ( ( $style == TS_RFC2822 ) || ( $style == TS_POSTGRES ) ) {
-                       $output .= ' GMT';
-               }
-
-               if ( $style == TS_MW && strlen( $output ) !== 14 ) {
-                       throw new TimestampException( __METHOD__ . ': The timestamp cannot be represented in ' .
-                               'the specified format' );
-               }
-
-               return $output;
+       public static function getInstance( $ts = false ) {
+               return new static( $ts );
        }
 
        /**
@@ -329,52 +173,6 @@ class MWTimestamp {
                return $ts;
        }
 
-       /**
-        * @since 1.20
-        *
-        * @return string
-        */
-       public function __toString() {
-               return $this->getTimestamp();
-       }
-
-       /**
-        * Calculate the difference between two MWTimestamp objects.
-        *
-        * @since 1.22
-        * @param MWTimestamp $relativeTo Base time to calculate difference from
-        * @return DateInterval|bool The DateInterval object representing the
-        *   difference between the two dates or false on failure
-        */
-       public function diff( MWTimestamp $relativeTo ) {
-               return $this->timestamp->diff( $relativeTo->timestamp );
-       }
-
-       /**
-        * Set the timezone of this timestamp to the specified timezone.
-        *
-        * @since 1.22
-        * @param string $timezone Timezone to set
-        * @throws TimestampException
-        */
-       public function setTimezone( $timezone ) {
-               try {
-                       $this->timestamp->setTimezone( new DateTimeZone( $timezone ) );
-               } catch ( Exception $e ) {
-                       throw new TimestampException( __METHOD__ . ': Invalid timezone.', $e->getCode(), $e );
-               }
-       }
-
-       /**
-        * Get the timezone of this timestamp.
-        *
-        * @since 1.22
-        * @return DateTimeZone The timezone
-        */
-       public function getTimezone() {
-               return $this->timestamp->getTimezone();
-       }
-
        /**
         * Get the localized timezone message, if available.
         *
@@ -395,17 +193,6 @@ class MWTimestamp {
                }
        }
 
-       /**
-        * Format the timestamp in a given format.
-        *
-        * @since 1.22
-        * @param string $format Pattern to format in
-        * @return string The formatted timestamp
-        */
-       public function format( $format ) {
-               return $this->timestamp->format( $format );
-       }
-
        /**
         * Get a timestamp instance in the server local timezone ($wgLocaltimezone)
         *
@@ -419,15 +206,4 @@ class MWTimestamp {
                $timestamp->setTimezone( $wgLocaltimezone );
                return $timestamp;
        }
-
-       /**
-        * Get a timestamp instance in GMT
-        *
-        * @since 1.22
-        * @param bool|string $ts Timestamp to set, or false for current time
-        * @return MWTimestamp The instance
-        */
-       public static function getInstance( $ts = false ) {
-               return new self( $ts );
-       }
 }
index 13f706d..391e05a 100644 (file)
  *
  * @par Example:
  * @code
- * $magicWords = array();
+ * $magicWords = [];
  *
- * $magicWords['en'] = array(
- *     'magicwordkey' => array( 0, 'case_insensitive_magic_word' ),
- *     'magicwordkey2' => array( 1, 'CASE_sensitive_magic_word2' ),
- * );
+ * $magicWords['en'] = [
+ *     'magicwordkey' => [ 0, 'case_insensitive_magic_word' ],
+ *     'magicwordkey2' => [ 1, 'CASE_sensitive_magic_word2' ],
+ * ];
  * @endcode
  *
  * For magic words which are also Parser variables, add a MagicWordwgVariableIDs
index 2a00900..2fce08c 100644 (file)
@@ -286,16 +286,6 @@ class MediaWiki {
                                // may still be a wikipage redirect to another article or URL.
                                $article = $this->initializeArticle();
                                if ( is_object( $article ) ) {
-                                       $url = $request->getFullRequestURL(); // requested URL
-                                       if (
-                                               $request->getMethod() === 'GET' &&
-                                               $url === $article->getTitle()->getCanonicalURL() &&
-                                               $article->checkTouched() &&
-                                               $output->checkLastModified( $article->getTouched() )
-                                       ) {
-                                               wfDebug( __METHOD__ . ": done 304\n" );
-                                               return;
-                                       }
                                        $this->performAction( $article, $requestTitle );
                                } elseif ( is_string( $article ) ) {
                                        $output->redirect( $article );
@@ -527,6 +517,7 @@ class MediaWiki {
         */
        public function run() {
                try {
+                       $this->setDBProfilingAgent();
                        try {
                                $this->main();
                        } catch ( ErrorPageError $e ) {
@@ -537,18 +528,46 @@ class MediaWiki {
                                $e->report(); // display the GUI error
                        }
                } catch ( Exception $e ) {
+                       $context = $this->context;
+                       if (
+                               $e instanceof DBConnectionError &&
+                               $context->hasTitle() &&
+                               $context->getTitle()->canExist() &&
+                               $context->getRequest()->getVal( 'action', 'view' ) === 'view' &&
+                               HTMLFileCache::useFileCache( $this->context, HTMLFileCache::MODE_OUTAGE )
+                       ) {
+                               // Try to use any (even stale) file during outages...
+                               $cache = new HTMLFileCache( $context->getTitle(), 'view' );
+                               if ( $cache->isCached() ) {
+                                       $cache->loadFromFileCache( $context, HTMLFileCache::MODE_OUTAGE );
+                                       print MWExceptionRenderer::getHTML( $e );
+                                       exit;
+                               }
+
+                       }
+
                        MWExceptionHandler::handleException( $e );
                }
 
                $this->doPostOutputShutdown( 'normal' );
        }
 
+       private function setDBProfilingAgent() {
+               $services = MediaWikiServices::getInstance();
+               // Add a comment for easy SHOW PROCESSLIST interpretation
+               $name = $this->context->getUser()->getName();
+               $services->getDBLoadBalancerFactory()->setAgentName(
+                       mb_strlen( $name ) > 15 ? mb_substr( $name, 0, 15 ) . '...' : $name
+               );
+       }
+
        /**
         * @see MediaWiki::preOutputCommit()
+        * @param callable $postCommitWork [default: null]
         * @since 1.26
         */
-       public function doPreOutputCommit() {
-               self::preOutputCommit( $this->context );
+       public function doPreOutputCommit( callable $postCommitWork = null ) {
+               self::preOutputCommit( $this->context, $postCommitWork );
        }
 
        /**
@@ -556,44 +575,91 @@ class MediaWiki {
         * the user can receive a response (in case commit fails)
         *
         * @param IContextSource $context
+        * @param callable $postCommitWork [default: null]
         * @since 1.27
         */
-       public static function preOutputCommit( IContextSource $context ) {
+       public static function preOutputCommit(
+               IContextSource $context, callable $postCommitWork = null
+       ) {
                // Either all DBs should commit or none
                ignore_user_abort( true );
 
                $config = $context->getConfig();
+               $request = $context->getRequest();
+               $output = $context->getOutput();
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
 
-               $factory = wfGetLBFactory();
                // Commit all changes
-               $factory->commitMasterChanges(
+               $lbFactory->commitMasterChanges(
                        __METHOD__,
                        // Abort if any transaction was too big
                        [ 'maxWriteDuration' => $config->get( 'MaxUserDBWriteDuration' ) ]
                );
-               // Record ChronologyProtector positions
-               $factory->shutdown();
-               wfDebug( __METHOD__ . ': all transactions committed' );
+               wfDebug( __METHOD__ . ': primary transaction round committed' );
 
+               // Run updates that need to block the user or affect output (this is the last chance)
                DeferredUpdates::doUpdates( 'enqueue', DeferredUpdates::PRESEND );
                wfDebug( __METHOD__ . ': pre-send deferred updates completed' );
 
+               // Decide when clients block on ChronologyProtector DB position writes
+               $urlDomainDistance = (
+                       $request->wasPosted() &&
+                       $output->getRedirect() &&
+                       $lbFactory->hasOrMadeRecentMasterChanges( INF )
+               ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false;
+
+               if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) {
+                       // OutputPage::output() will be fast; $postCommitWork will not be useful for
+                       // masking the latency of syncing DB positions accross all datacenters synchronously.
+                       // Instead, make use of the RTT time of the client follow redirects.
+                       $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC;
+                       $cpPosTime = microtime( true );
+                       // Client's next request should see 1+ positions with this DBMasterPos::asOf() time
+                       if ( $urlDomainDistance === 'local' ) {
+                               // Client will stay on this domain, so set an unobtrusive cookie
+                               $expires = time() + ChronologyProtector::POSITION_TTL;
+                               $options = [ 'prefix' => '' ];
+                               $request->response()->setCookie( 'cpPosTime', $cpPosTime, $expires, $options );
+                       } else {
+                               // Cookies may not work across wiki domains, so use a URL parameter
+                               $safeUrl = $lbFactory->appendPreShutdownTimeAsQuery(
+                                       $output->getRedirect(),
+                                       $cpPosTime
+                               );
+                               $output->redirect( $safeUrl );
+                       }
+               } else {
+                       // OutputPage::output() is fairly slow; run it in $postCommitWork to mask
+                       // the latency of syncing DB positions accross all datacenters synchronously
+                       $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC;
+                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) ) {
+                               $cpPosTime = microtime( true );
+                               // Set a cookie in case the DB position store cannot sync accross datacenters.
+                               // This will at least cover the common case of the user staying on the domain.
+                               $expires = time() + ChronologyProtector::POSITION_TTL;
+                               $options = [ 'prefix' => '' ];
+                               $request->response()->setCookie( 'cpPosTime', $cpPosTime, $expires, $options );
+                       }
+               }
+               // Record ChronologyProtector positions for DBs affected in this request at this point
+               $lbFactory->shutdown( $flags, $postCommitWork );
+               wfDebug( __METHOD__ . ': LBFactory shutdown completed' );
+
                // Set a cookie to tell all CDN edge nodes to "stick" the user to the DC that handles this
                // POST request (e.g. the "master" data center). Also have the user briefly bypass CDN so
                // ChronologyProtector works for cacheable URLs.
-               $request = $context->getRequest();
-               if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
+               if ( $request->wasPosted() && $lbFactory->hasOrMadeRecentMasterChanges() ) {
                        $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
                        $options = [ 'prefix' => '' ];
                        $request->response()->setCookie( 'UseDC', 'master', $expires, $options );
                        $request->response()->setCookie( 'UseCDNCache', 'false', $expires, $options );
                }
 
-               // Avoid letting a few seconds of slave lag cause a month of stale data. This logic is
+               // Avoid letting a few seconds of replica DB lag cause a month of stale data. This logic is
                // also intimately related to the value of $wgCdnReboundPurgeDelay.
-               if ( $factory->laggedSlaveUsed() ) {
+               if ( $lbFactory->laggedReplicaUsed() ) {
                        $maxAge = $config->get( 'CdnMaxageLagged' );
-                       $context->getOutput()->lowerCdnMaxage( $maxAge );
+                       $output->lowerCdnMaxage( $maxAge );
                        $request->response()->header( "X-Database-Lagged: true" );
                        wfDebugLog( 'replication', "Lagged DB used; CDN cache TTL limited to $maxAge seconds" );
                }
@@ -601,11 +667,46 @@ class MediaWiki {
                // Avoid long-term cache pollution due to message cache rebuild timeouts (T133069)
                if ( MessageCache::singleton()->isDisabled() ) {
                        $maxAge = $config->get( 'CdnMaxageSubstitute' );
-                       $context->getOutput()->lowerCdnMaxage( $maxAge );
+                       $output->lowerCdnMaxage( $maxAge );
                        $request->response()->header( "X-Response-Substitute: true" );
                }
        }
 
+       /**
+        * @param string $url
+        * @param IContextSource $context
+        * @return string|bool Either "local" or "remote" if in the farm, false otherwise
+        */
+       private function getUrlDomainDistance( $url, IContextSource $context ) {
+               static $relevantKeys = [ 'host' => true, 'port' => true ];
+
+               $infoCandidate = wfParseUrl( $url );
+               if ( $infoCandidate === false ) {
+                       return false;
+               }
+
+               $infoCandidate = array_intersect_key( $infoCandidate, $relevantKeys );
+               $clusterHosts = array_merge(
+                       // Local wiki host (the most common case)
+                       [ $context->getConfig()->get( 'CanonicalServer' ) ],
+                       // Any local/remote wiki virtual hosts for this wiki farm
+                       $context->getConfig()->get( 'LocalVirtualHosts' )
+               );
+
+               foreach ( $clusterHosts as $i => $clusterHost ) {
+                       $parseUrl = wfParseUrl( $clusterHost );
+                       if ( !$parseUrl ) {
+                               continue;
+                       }
+                       $infoHost = array_intersect_key( $parseUrl, $relevantKeys );
+                       if ( $infoCandidate === $infoHost ) {
+                               return ( $i === 0 ) ? 'local' : 'remote';
+                       }
+               }
+
+               return false;
+       }
+
        /**
         * This function does work that can be done *after* the
         * user gets the HTTP response so they don't block on it
@@ -623,10 +724,9 @@ class MediaWiki {
                // Show visible profiling data if enabled (which cannot be post-send)
                Profiler::instance()->logDataPageOutputOnly();
 
-               $that = $this;
-               $callback = function () use ( $that, $mode ) {
+               $callback = function () use ( $mode ) {
                        try {
-                               $that->restInPeace( $mode );
+                               $this->restInPeace( $mode );
                        } catch ( Exception $e ) {
                                MWExceptionHandler::handleException( $e );
                        }
@@ -641,7 +741,7 @@ class MediaWiki {
                                fastcgi_finish_request();
                        } else {
                                // Either all DB and deferred updates should happen or none.
-                               // The later should not be cancelled due to client disconnect.
+                               // The latter should not be cancelled due to client disconnect.
                                ignore_user_abort( true );
                        }
 
@@ -652,6 +752,7 @@ class MediaWiki {
        private function main() {
                global $wgTitle;
 
+               $output = $this->context->getOutput();
                $request = $this->context->getRequest();
 
                // Send Ajax requests to the Ajax dispatcher.
@@ -665,6 +766,7 @@ class MediaWiki {
 
                        $dispatcher = new AjaxDispatcher( $this->config );
                        $dispatcher->performAction( $this->context->getUser() );
+
                        return;
                }
 
@@ -726,45 +828,55 @@ class MediaWiki {
                                // Setup dummy Title, otherwise OutputPage::redirect will fail
                                $title = Title::newFromText( 'REDIR', NS_MAIN );
                                $this->context->setTitle( $title );
-                               $output = $this->context->getOutput();
                                // Since we only do this redir to change proto, always send a vary header
                                $output->addVaryHeader( 'X-Forwarded-Proto' );
                                $output->redirect( $redirUrl );
                                $output->output();
+
                                return;
                        }
                }
 
-               if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) {
-                       if ( HTMLFileCache::useFileCache( $this->context ) ) {
-                               // Try low-level file cache hit
-                               $cache = new HTMLFileCache( $title, $action );
-                               if ( $cache->isCacheGood( /* Assume up to date */ ) ) {
-                                       // Check incoming headers to see if client has this cached
-                                       $timestamp = $cache->cacheTimestamp();
-                                       if ( !$this->context->getOutput()->checkLastModified( $timestamp ) ) {
-                                               $cache->loadFromFileCache( $this->context );
-                                       }
-                                       // Do any stats increment/watchlist stuff
-                                       // Assume we're viewing the latest revision (this should always be the case with file cache)
-                                       $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
-                                       // Tell OutputPage that output is taken care of
-                                       $this->context->getOutput()->disable();
-                                       return;
+               if ( $title->canExist() && HTMLFileCache::useFileCache( $this->context ) ) {
+                       // Try low-level file cache hit
+                       $cache = new HTMLFileCache( $title, $action );
+                       if ( $cache->isCacheGood( /* Assume up to date */ ) ) {
+                               // Check incoming headers to see if client has this cached
+                               $timestamp = $cache->cacheTimestamp();
+                               if ( !$output->checkLastModified( $timestamp ) ) {
+                                       $cache->loadFromFileCache( $this->context );
                                }
+                               // Do any stats increment/watchlist stuff, assuming user is viewing the
+                               // latest revision (which should always be the case for file cache)
+                               $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
+                               // Tell OutputPage that output is taken care of
+                               $output->disable();
+
+                               return;
                        }
                }
 
                // Actually do the work of the request and build up any output
                $this->performRequest();
 
+               // GUI-ify and stash the page output in MediaWiki::doPreOutputCommit() while
+               // ChronologyProtector synchronizes DB positions or slaves accross all datacenters.
+               $buffer = null;
+               $outputWork = function () use ( $output, &$buffer ) {
+                       if ( $buffer === null ) {
+                               $buffer = $output->output( true );
+                       }
+
+                       return $buffer;
+               };
+
                // Now commit any transactions, so that unreported errors after
                // output() don't roll back the whole DB transaction and so that
                // we avoid having both success and error text in the response
-               $this->doPreOutputCommit();
+               $this->doPreOutputCommit( $outputWork );
 
-               // Output everything!
-               $this->context->getOutput()->output();
+               // Now send the actual output
+               print $outputWork();
        }
 
        /**
@@ -772,9 +884,9 @@ class MediaWiki {
         * @param string $mode Use 'fast' to always skip job running
         */
        public function restInPeace( $mode = 'fast' ) {
-               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                // Assure deferred updates are not in the main transaction
-               $factory->commitMasterChanges( __METHOD__ );
+               $lbFactory->commitMasterChanges( __METHOD__ );
 
                // Loosen DB query expectations since the HTTP client is unblocked
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
@@ -800,8 +912,8 @@ class MediaWiki {
                wfLogProfilingData();
 
                // Commit and close up!
-               $factory->commitMasterChanges( __METHOD__ );
-               $factory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $lbFactory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
 
                wfDebug( "Request ended normally\n" );
        }
@@ -831,16 +943,18 @@ class MediaWiki {
 
                $runJobsLogger = LoggerFactory::getInstance( 'runJobs' );
 
+               // Fall back to running the job(s) while the user waits if needed
                if ( !$this->config->get( 'RunJobsAsync' ) ) {
-                       // Fall back to running the job here while the user waits
                        $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
+                       $runner->run( [ 'maxJobs' => $n ] );
                        return;
                }
 
+               // Do not send request if there are probably no jobs
                try {
-                       if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
-                               return; // do not send request if there are probably no jobs
+                       $group = JobQueueGroup::singleton();
+                       if ( !$group->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
+                               return;
                        }
                } catch ( JobQueueError $e ) {
                        MWExceptionHandler::logException( $e );
@@ -854,8 +968,7 @@ class MediaWiki {
 
                $errno = $errstr = null;
                $info = wfParseUrl( $this->config->get( 'CanonicalServer' ) );
-               MediaWiki\suppressWarnings();
-               $host = $info['host'];
+               $host = $info ? $info['host'] : null;
                $port = 80;
                if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
                        $host = "tls://" . $host;
@@ -864,48 +977,60 @@ class MediaWiki {
                if ( isset( $info['port'] ) ) {
                        $port = $info['port'];
                }
-               $sock = fsockopen(
+
+               MediaWiki\suppressWarnings();
+               $sock = $host ? fsockopen(
                        $host,
                        $port,
                        $errno,
                        $errstr,
-                       // If it takes more than 100ms to connect to ourselves there
-                       // is a problem elsewhere.
-                       0.1
-               );
+                       // If it takes more than 100ms to connect to ourselves there is a problem...
+                       0.100
+               ) : false;
                MediaWiki\restoreWarnings();
-               if ( !$sock ) {
+
+               $invokedWithSuccess = true;
+               if ( $sock ) {
+                       $special = SpecialPageFactory::getPage( 'RunJobs' );
+                       $url = $special->getPageTitle()->getCanonicalURL( $query );
+                       $req = (
+                               "POST $url HTTP/1.1\r\n" .
+                               "Host: {$info['host']}\r\n" .
+                               "Connection: Close\r\n" .
+                               "Content-Length: 0\r\n\r\n"
+                       );
+
+                       $runJobsLogger->info( "Running $n job(s) via '$url'" );
+                       // Send a cron API request to be performed in the background.
+                       // Give up if this takes too long to send (which should be rare).
+                       stream_set_timeout( $sock, 2 );
+                       $bytes = fwrite( $sock, $req );
+                       if ( $bytes !== strlen( $req ) ) {
+                               $invokedWithSuccess = false;
+                               $runJobsLogger->error( "Failed to start cron API (socket write error)" );
+                       } else {
+                               // Do not wait for the response (the script should handle client aborts).
+                               // Make sure that we don't close before that script reaches ignore_user_abort().
+                               $start = microtime( true );
+                               $status = fgets( $sock );
+                               $sec = microtime( true ) - $start;
+                               if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
+                                       $invokedWithSuccess = false;
+                                       $runJobsLogger->error( "Failed to start cron API: received '$status' ($sec)" );
+                               }
+                       }
+                       fclose( $sock );
+               } else {
+                       $invokedWithSuccess = false;
                        $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
-                       // Fall back to running the job here while the user waits
-                       $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
-                       return;
                }
 
-               $special = SpecialPageFactory::getPage( 'RunJobs' );
-               $url = $special->getPageTitle()->getCanonicalURL( $query );
-               $req = (
-                       "POST $url HTTP/1.1\r\n" .
-                       "Host: {$info['host']}\r\n" .
-                       "Connection: Close\r\n" .
-                       "Content-Length: 0\r\n\r\n"
-               );
+               // Fall back to running the job(s) while the user waits if needed
+               if ( !$invokedWithSuccess ) {
+                       $runJobsLogger->warning( "Jobs switched to blocking; Special:RunJobs disabled" );
 
-               $runJobsLogger->info( "Running $n job(s) via '$url'" );
-               // Send a cron API request to be performed in the background.
-               // Give up if this takes too long to send (which should be rare).
-               stream_set_timeout( $sock, 2 );
-               $bytes = fwrite( $sock, $req );
-               if ( $bytes !== strlen( $req ) ) {
-                       $runJobsLogger->error( "Failed to start cron API (socket write error)" );
-               } else {
-                       // Do not wait for the response (the script should handle client aborts).
-                       // Make sure that we don't close before that script reaches ignore_user_abort().
-                       $status = fgets( $sock );
-                       if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
-                               $runJobsLogger->error( "Failed to start cron API: received '$status'" );
-                       }
+                       $runner = new JobRunner( $runJobsLogger );
+                       $runner->run( [ 'maxJobs'  => $n ] );
                }
-               fclose( $sock );
        }
 }
index 49891df..f621867 100644 (file)
@@ -16,6 +16,7 @@ use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Services\SalvageableService;
 use MediaWiki\Services\ServiceContainer;
+use MediaWiki\Services\NoSuchServiceException;
 use MWException;
 use ObjectCache;
 use SearchEngine;
@@ -198,7 +199,14 @@ class MediaWikiServices extends ServiceContainer {
         */
        private function salvage( self $other ) {
                foreach ( $this->getServiceNames() as $name ) {
-                       $oldService = $other->peekService( $name );
+                       // The service could be new in the new instance and not registered in the
+                       // other instance (e.g. an extension that was loaded after the instantiation of
+                       // the other instance. Skip this service in this case. See T143974
+                       try {
+                               $oldService = $other->peekService( $name );
+                       } catch ( NoSuchServiceException $e ) {
+                               continue;
+                       }
 
                        if ( $oldService instanceof SalvageableService ) {
                                /** @var SalvageableService $newService */
index 441fe9e..dd1fd37 100644 (file)
@@ -33,7 +33,7 @@
  */
 class MergeHistory {
 
-       /** @const int Maximum number of revisions that can be merged at once (avoid too much slave lag) */
+       /** @const int Maximum number of revisions that can be merged at once */
        const REVISION_LIMIT = 5000;
 
        /** @var Title Page from which history will be merged */
index 2c979de..c2c954a 100644 (file)
  *
  * @code
  *     // old style:
- *     wfMsgExt( 'key', array( 'parseinline' ), 'apple' );
+ *     wfMsgExt( 'key', [ 'parseinline' ], 'apple' );
  *     // new style:
  *     wfMessage( 'key', 'apple' )->parse();
  * @endcode
  * Places where HTML cannot be used. {{-transformation is done.
  * @code
  *     // old style:
- *     wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' );
+ *     wfMsgExt( 'key', [ 'parsemag' ], 'apple', 'pear' );
  *     // new style:
  *     wfMessage( 'key', 'apple', 'pear' )->text();
  * @endcode
index 77dbde7..ba14b99 100644 (file)
@@ -1265,7 +1265,7 @@ class OutputPage extends ContextSource {
                $lb->setArray( $arr );
 
                # Fetch existence plus the hiddencat property
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $fields = array_merge(
                        LinkCache::getSelectFields(),
                        [ 'page_namespace', 'page_title', 'pp_value' ]
@@ -2214,10 +2214,16 @@ class OutputPage extends ContextSource {
        /**
         * Finally, all the text has been munged and accumulated into
         * the object, let's actually output it:
+        *
+        * @param bool $return Set to true to get the result as a string rather than sending it
+        * @return string|null
+        * @throws Exception
+        * @throws FatalError
+        * @throws MWException
         */
-       public function output() {
+       public function output( $return = false ) {
                if ( $this->mDoNothing ) {
-                       return;
+                       return $return ? '' : null;
                }
 
                $response = $this->getRequest()->response();
@@ -2253,7 +2259,7 @@ class OutputPage extends ContextSource {
                                }
                        }
 
-                       return;
+                       return $return ? '' : null;
                } elseif ( $this->mStatusCode ) {
                        $response->statusHeader( $this->mStatusCode );
                }
@@ -2262,7 +2268,7 @@ class OutputPage extends ContextSource {
                ob_start();
 
                $response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' );
-               $response->header( 'Content-language: ' . $config->get( 'LanguageCode' ) );
+               $response->header( 'Content-language: ' . $config->get( 'ContLang' )->getHtmlCode() );
 
                // Avoid Internet Explorer "compatibility view" in IE 8-10, so that
                // jQuery etc. can work correctly.
@@ -2303,7 +2309,6 @@ class OutputPage extends ContextSource {
                        // Hook that allows last minute changes to the output page, e.g.
                        // adding of CSS or Javascript by extensions.
                        Hooks::run( 'BeforePageDisplay', [ &$this, &$sk ] );
-                       $this->getSkin()->setupSkinUserCss( $this );
 
                        try {
                                $sk->outputPage();
@@ -2323,8 +2328,12 @@ class OutputPage extends ContextSource {
 
                $this->sendCacheControl();
 
-               ob_end_flush();
-
+               if ( $return ) {
+                       return ob_get_clean();
+               } else {
+                       ob_end_flush();
+                       return null;
+               }
        }
 
        /**
@@ -2536,7 +2545,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Show a warning about slave lag
+        * Show a warning about replica DB lag
         *
         * If the lag is higher than $wgSlaveLagCritical seconds,
         * then the warning is a bit more obvious. If the lag is
@@ -2547,6 +2556,7 @@ class OutputPage extends ContextSource {
        public function showLagWarning( $lag ) {
                $config = $this->getConfig();
                if ( $lag >= $config->get( 'SlaveLagWarning' ) ) {
+                       $lag = floor( $lag ); // floor to avoid nano seconds to display
                        $message = $lag < $config->get( 'SlaveLagCritical' )
                                ? 'lag-warn-normal'
                                : 'lag-warn-high';
@@ -2674,20 +2684,34 @@ class OutputPage extends ContextSource {
                                'user.styles',
                                'user.cssprefs',
                        ] );
+                       $this->getSkin()->setupSkinUserCss( $this );
 
                        // Prepare exempt modules for buildExemptModules()
                        $exemptGroups = [ 'site' => [], 'noscript' => [], 'private' => [], 'user' => [] ];
                        $exemptStates = [];
-                       $moduleStyles = array_filter( $this->getModuleStyles( /*filter*/ true ),
+                       $moduleStyles = $this->getModuleStyles( /*filter*/ true );
+
+                       // Batch preload getTitleInfo for isKnownEmpty() calls below
+                       $exemptModules = array_filter( $moduleStyles,
+                               function ( $name ) use ( $rl, &$exemptGroups ) {
+                                       $module = $rl->getModule( $name );
+                                       return $module && isset( $exemptGroups[ $module->getGroup() ] );
+                               }
+                       );
+                       ResourceLoaderWikiModule::preloadTitleInfo(
+                               $context, wfGetDB( DB_REPLICA ), $exemptModules );
+
+                       // Filter out modules handled by buildExemptModules()
+                       $moduleStyles = array_filter( $moduleStyles,
                                function ( $name ) use ( $rl, $context, &$exemptGroups, &$exemptStates ) {
                                        $module = $rl->getModule( $name );
                                        if ( $module ) {
-                                               $group = $module->getGroup();
                                                if ( $name === 'user.styles' && $this->isUserCssPreview() ) {
                                                        $exemptStates[$name] = 'ready';
                                                        // Special case in buildExemptModules()
                                                        return false;
                                                }
+                                               $group = $module->getGroup();
                                                if ( isset( $exemptGroups[$group] ) ) {
                                                        $exemptStates[$name] = 'ready';
                                                        if ( !$module->isKnownEmpty( $context ) ) {
@@ -2702,12 +2726,17 @@ class OutputPage extends ContextSource {
                        );
                        $this->rlExemptStyleModules = $exemptGroups;
 
-                       // Manually handled by getBottomScripts()
-                       $userModule = $rl->getModule( 'user' );
-                       $userState = $userModule->isKnownEmpty( $context ) && !$this->isUserJsPreview()
-                               ? 'ready'
-                               : 'loading';
-                       $this->rlUserModuleState = $exemptStates['user'] = $userState;
+                       $isUserModuleFiltered = !$this->filterModules( [ 'user' ] );
+                       // If this page filters out 'user', makeResourceLoaderLink will drop it.
+                       // Avoid indefinite "loading" state or untrue "ready" state (T145368).
+                       if ( !$isUserModuleFiltered ) {
+                               // Manually handled by getBottomScripts()
+                               $userModule = $rl->getModule( 'user' );
+                               $userState = $userModule->isKnownEmpty( $context ) && !$this->isUserJsPreview()
+                                       ? 'ready'
+                                       : 'loading';
+                               $this->rlUserModuleState = $exemptStates['user'] = $userState;
+                       }
 
                        $rlClient = new ResourceLoaderClientHtml( $context, $this->getTarget() );
                        $rlClient->setConfig( $this->getJSVars() );
index 5579ba5..ed06935 100644 (file)
@@ -140,7 +140,7 @@ class PageProps {
                }
 
                if ( $queryIDs ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->select(
                                'page_props',
                                [
@@ -198,7 +198,7 @@ class PageProps {
                }
 
                if ( $queryIDs != [] ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->select(
                                'page_props',
                                [
index 005c341..049b32f 100644 (file)
  *
  * $router->add( "/wiki/$1" );
  *   - Matches /wiki/Foo style urls and extracts the title
- * $router->add( array( 'edit' => "/edit/$key" ), array( 'action' => '$key' ) );
+ * $router->add( [ 'edit' => "/edit/$key" ], [ 'action' => '$key' ] );
  *   - Matches /edit/Foo style urls and sets action=edit
  * $router->add( '/$2/$1',
- *   array( 'variant' => '$2' ),
- *   array( '$2' => array( 'zh-hant', 'zh-hans' )
+ *   [ 'variant' => '$2' ],
+ *   [ '$2' => [ 'zh-hant', 'zh-hans' ] ]
  * );
  *   - Matches /zh-hant/Foo or /zh-hans/Foo
- * $router->addStrict( "/foo/Bar", array( 'title' => 'Baz' ) );
+ * $router->addStrict( "/foo/Bar", [ 'title' => 'Baz' ] );
  *   - Matches /foo/Bar explicitly and uses "Baz" as the title
- * $router->add( '/help/$1', array( 'title' => 'Help:$1' ) );
+ * $router->add( '/help/$1', [ 'title' => 'Help:$1' ] );
  *   - Matches /help/Foo with "Help:Foo" as the title
- * $router->add( '/$1', array( 'foo' => array( 'value' => 'bar$2' ) );
+ * $router->add( '/$1', [ 'foo' => [ 'value' => 'bar$2' ] ] );
  *   - Matches /Foo and sets 'foo' to 'bar$2' without $2 being replaced
- * $router->add( '/$1', array( 'data:foo' => 'bar' ), array( 'callback' => 'functionname' ) );
+ * $router->add( '/$1', [ 'data:foo' => 'bar' ], [ 'callback' => 'functionname' ] );
  *   - Matches /Foo, adds the key 'foo' with the value 'bar' to the data array
  *     and calls functionname( &$matches, $data );
  *
@@ -56,7 +56,7 @@
  *   - The default behavior is equivalent to `array( 'title' => '$1' )`,
  *     if you don't want the title parameter you can explicitly use `array( 'title' => false )`
  *   - You can specify a value that won't have replacements in it
- *     using `'foo' => array( 'value' => 'bar' );`
+ *     using `'foo' => [ 'value' => 'bar' ];`
  *
  * Options:
  *   - The option keys $1, $2, etc... can be specified to restrict the possible values
index 0039d2b..bd1b2a2 100644 (file)
@@ -72,7 +72,7 @@ class Pingback {
         * @return bool
         */
        private function checkIfSent() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $sent = $dbr->selectField(
                        'updatelog', '1', [ 'ul_key' => $this->key ], __METHOD__ );
                return $sent !== false;
@@ -168,7 +168,7 @@ class Pingback {
         */
        private function getOrCreatePingbackId() {
                if ( !$this->id ) {
-                       $id = wfGetDB( DB_SLAVE )->selectField(
+                       $id = wfGetDB( DB_REPLICA )->selectField(
                                'updatelog', 'ul_value', [ 'ul_key' => 'PingBack' ] );
 
                        if ( $id == false ) {
index 70addfc..9f8c06b 100644 (file)
@@ -1494,7 +1494,7 @@ class Preferences {
 
                        $context = $form->getContext();
                        // Set session data for the success message
-                       $context->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
+                       $context->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
 
                        $context->getOutput()->redirect( $url );
                }
@@ -1594,7 +1594,7 @@ class PreferencesForm extends HTMLForm {
         * Get extra parameters for the query string when redirecting after
         * successful save.
         *
-        * @return array()
+        * @return array
         */
        public function getExtraSuccessRedirectParameters() {
                return [];
index 04c68ca..98bc885 100644 (file)
@@ -57,35 +57,55 @@ abstract class PrefixSearch {
                if ( $search == '' ) {
                        return []; // Return empty result
                }
-               $namespaces = $this->validateNamespaces( $namespaces );
-
-               // Find a Title which is not an interwiki and is in NS_MAIN
-               $title = Title::newFromText( $search );
-               if ( $title && !$title->isExternal() ) {
-                       $ns = [ $title->getNamespace() ];
-                       $search = $title->getText();
-                       if ( $ns[0] == NS_MAIN ) {
-                               $ns = $namespaces; // no explicit prefix, use default namespaces
-                               Hooks::run( 'PrefixSearchExtractNamespace', [ &$ns, &$search ] );
-                       }
-                       return $this->searchBackend( $ns, $search, $limit, $offset );
-               }
 
-               // Is this a namespace prefix?
-               $title = Title::newFromText( $search . 'Dummy' );
-               if ( $title && $title->getText() == 'Dummy'
-                       && $title->getNamespace() != NS_MAIN
-                       && !$title->isExternal() )
-               {
-                       $namespaces = [ $title->getNamespace() ];
-                       $search = '';
+               $hasNamespace = $this->extractNamespace( $search );
+               if ( $hasNamespace ) {
+                       list( $namespace, $search ) = $hasNamespace;
+                       $namespaces = [ $namespace ];
                } else {
+                       $namespaces = $this->validateNamespaces( $namespaces );
                        Hooks::run( 'PrefixSearchExtractNamespace', [ &$namespaces, &$search ] );
                }
 
                return $this->searchBackend( $namespaces, $search, $limit, $offset );
        }
 
+       /**
+        * Figure out if given input contains an explicit namespace.
+        *
+        * @param string $input
+        * @return false|array Array of namespace and remaining text, or false if no namespace given.
+        */
+       protected function extractNamespace( $input ) {
+               if ( strpos( $input, ':' ) === false ) {
+                       return false;
+               }
+
+               // Namespace prefix only
+               $title = Title::newFromText( $input . 'Dummy' );
+               if (
+                       $title &&
+                       $title->getText() === 'Dummy' &&
+                       !$title->inNamespace( NS_MAIN ) &&
+                       !$title->isExternal()
+               ) {
+                       return [ $title->getNamespace(), '' ];
+               }
+
+               // Namespace prefix with additional input
+               $title = Title::newFromText( $input );
+               if (
+                       $title &&
+                       !$title->inNamespace( NS_MAIN ) &&
+                       !$title->isExternal()
+               ) {
+                       // getText provides correct capitalization
+                       return [ $title->getNamespace(), $title->getText() ];
+               }
+
+               return false;
+       }
+
        /**
         * Do a prefix search for all possible variants of the prefix
         * @param string $search
@@ -254,43 +274,60 @@ abstract class PrefixSearch {
         * be automatically capitalized by Title::secureAndSpit()
         * later on depending on $wgCapitalLinks)
         *
-        * @param array $namespaces Namespaces to search in
+        * @param array|null $namespaces Namespaces to search in
         * @param string $search Term
         * @param int $limit Max number of items to return
         * @param int $offset Number of items to skip
-        * @return array Array of Title objects
+        * @return Title[] Array of Title objects
         */
        public function defaultSearchBackend( $namespaces, $search, $limit, $offset ) {
-               $ns = array_shift( $namespaces ); // support only one namespace
-               if ( is_null( $ns ) || in_array( NS_MAIN, $namespaces ) ) {
-                       $ns = NS_MAIN; // if searching on many always default to main
+               // Backwards compatability with old code. Default to NS_MAIN if no namespaces provided.
+               if ( $namespaces === null ) {
+                       $namespaces = [];
+               }
+               if ( !$namespaces ) {
+                       $namespaces[] = NS_MAIN;
                }
 
-               if ( $ns == NS_SPECIAL ) {
-                       return $this->specialSearch( $search, $limit, $offset );
+               // Construct suitable prefix for each namespace. They differ in cases where
+               // some namespaces always capitalize and some don't.
+               $prefixes = [];
+               foreach ( $namespaces as $namespace ) {
+                       // For now, if special is included, ignore the other namespaces
+                       if ( $namespace == NS_SPECIAL ) {
+                               return $this->specialSearch( $search, $limit, $offset );
+                       }
+
+                       $title = Title::makeTitleSafe( $namespace, $search );
+                       // Why does the prefix default to empty?
+                       $prefix = $title ? $title->getDBkey() : '';
+                       $prefixes[$prefix][] = $namespace;
                }
 
-               $t = Title::newFromText( $search, $ns );
-               $prefix = $t ? $t->getDBkey() : '';
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'page',
-                       [ 'page_id', 'page_namespace', 'page_title' ],
-                       [
-                               'page_namespace' => $ns,
-                               'page_title ' . $dbr->buildLike( $prefix, $dbr->anyString() )
-                       ],
-                       __METHOD__,
-                       [
-                               'LIMIT' => $limit,
-                               'ORDER BY' => 'page_title',
-                               'OFFSET' => $offset
-                       ]
-               );
-               $srchres = [];
-               foreach ( $res as $row ) {
-                       $srchres[] = Title::newFromRow( $row );
+               $dbr = wfGetDB( DB_REPLICA );
+               // Often there is only one prefix that applies to all requested namespaces,
+               // but sometimes there are two if some namespaces do not always capitalize.
+               $conds = [];
+               foreach ( $prefixes as $prefix => $namespaces ) {
+                       $condition = [
+                               'page_namespace' => $namespaces,
+                               'page_title' . $dbr->buildLike( $prefix, $dbr->anyString() ),
+                       ];
+                       $conds[] = $dbr->makeList( $condition, LIST_AND );
                }
-               return $srchres;
+
+               $table = 'page';
+               $fields = [ 'page_id', 'page_namespace', 'page_title' ];
+               $conds = $dbr->makeList( $conds, LIST_OR );
+               $options = [
+                       'LIMIT' => $limit,
+                       'ORDER BY' => [ 'page_title', 'page_namespace' ],
+                       'OFFSET' => $offset
+               ];
+
+               $res = $dbr->select( $table, $fields, $conds, __METHOD__, $options );
+
+               return iterator_to_array( TitleArray::newFromResult( $res ) );
        }
 
        /**
index eda8989..6acc528 100644 (file)
@@ -25,52 +25,60 @@ use MediaWiki\Linker\LinkTarget;
  * @todo document
  */
 class Revision implements IDBAccessObject {
+       /** @var int|null */
        protected $mId;
-
-       /**
-        * @var int|null
-        */
+       /** @var int|null */
        protected $mPage;
+       /** @var string */
        protected $mUserText;
+       /** @var string */
        protected $mOrigUserText;
+       /** @var int */
        protected $mUser;
+       /** @var bool */
        protected $mMinorEdit;
+       /** @var string */
        protected $mTimestamp;
+       /** @var int */
        protected $mDeleted;
+       /** @var int */
        protected $mSize;
+       /** @var string */
        protected $mSha1;
+       /** @var int */
        protected $mParentId;
+       /** @var string */
        protected $mComment;
+       /** @var string */
        protected $mText;
+       /** @var int */
        protected $mTextId;
+       /** @var int */
+       protected $mUnpatrolled;
 
-       /**
-        * @var stdClass|null
-        */
+       /** @var stdClass|null */
        protected $mTextRow;
 
-       /**
-        * @var null|Title
-        */
+       /**  @var null|Title */
        protected $mTitle;
+       /** @var bool */
        protected $mCurrent;
+       /** @var string */
        protected $mContentModel;
+       /** @var string */
        protected $mContentFormat;
 
-       /**
-        * @var Content|null|bool
-        */
+       /** @var Content|null|bool */
        protected $mContent;
-
-       /**
-        * @var null|ContentHandler
-        */
+       /** @var null|ContentHandler */
        protected $mContentHandler;
 
-       /**
-        * @var int
-        */
+       /** @var int */
        protected $mQueryFlags = 0;
+       /** @var bool Used for cached values to reload user text and rev_deleted */
+       protected $mRefreshMutableFields = false;
+       /** @var string Wiki ID; false means the current wiki */
+       protected $mWiki = false;
 
        // Revision deletion constants
        const DELETED_TEXT = 1;
@@ -84,6 +92,8 @@ class Revision implements IDBAccessObject {
        const FOR_THIS_USER = 2;
        const RAW = 3;
 
+       const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count
+
        /**
         * Load a page revision from a given revision ID number.
         * Returns null if no such revision can be found.
@@ -126,7 +136,7 @@ class Revision implements IDBAccessObject {
                } else {
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id=page_latest';
-                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                        return self::loadFromConds( $db, $conds, $flags );
                }
        }
@@ -153,7 +163,7 @@ class Revision implements IDBAccessObject {
                } else {
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id = page_latest';
-                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                        return self::loadFromConds( $db, $conds, $flags );
                }
        }
@@ -301,14 +311,14 @@ class Revision implements IDBAccessObject {
         * Given a set of conditions, fetch a revision
         *
         * This method is used then a revision ID is qualified and
-        * will incorporate some basic slave/master fallback logic
+        * will incorporate some basic replica DB/master fallback logic
         *
         * @param array $conditions
         * @param int $flags (optional)
         * @return Revision|null
         */
        private static function newFromConds( $conditions, $flags = 0 ) {
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
 
                $rev = self::loadFromConds( $db, $conditions, $flags );
                // Make sure new pending/committed revision are visibile later on
@@ -340,16 +350,15 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        private static function loadFromConds( $db, $conditions, $flags = 0 ) {
-               $res = self::fetchFromConds( $db, $conditions, $flags );
-               if ( $res ) {
-                       $row = $res->fetchObject();
-                       if ( $row ) {
-                               $ret = new Revision( $row );
-                               return $ret;
-                       }
+               $row = self::fetchFromConds( $db, $conditions, $flags );
+               if ( $row ) {
+                       $rev = new Revision( $row );
+                       $rev->mWiki = $db->getWikiID();
+
+                       return $rev;
                }
-               $ret = null;
-               return $ret;
+
+               return null;
        }
 
        /**
@@ -357,18 +366,21 @@ class Revision implements IDBAccessObject {
         * fetch all of a given page's revisions in turn.
         * Each row can be fed to the constructor to get objects.
         *
-        * @param Title $title
+        * @param LinkTarget $title
         * @return ResultWrapper
+        * @deprecated Since 1.28
         */
-       public static function fetchRevision( $title ) {
-               return self::fetchFromConds(
-                       wfGetDB( DB_SLAVE ),
+       public static function fetchRevision( LinkTarget $title ) {
+               $row = self::fetchFromConds(
+                       wfGetDB( DB_REPLICA ),
                        [
                                'rev_id=page_latest',
                                'page_namespace' => $title->getNamespace(),
                                'page_title' => $title->getDBkey()
                        ]
                );
+
+               return new FakeResultWrapper( $row ? [ $row ] : [] );
        }
 
        /**
@@ -379,7 +391,7 @@ class Revision implements IDBAccessObject {
         * @param IDatabase $db
         * @param array $conditions
         * @param int $flags (optional)
-        * @return ResultWrapper
+        * @return stdClass
         */
        private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
                $fields = array_merge(
@@ -387,11 +399,11 @@ class Revision implements IDBAccessObject {
                        self::selectPageFields(),
                        self::selectUserFields()
                );
-               $options = [ 'LIMIT' => 1 ];
+               $options = [];
                if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
                        $options[] = 'FOR UPDATE';
                }
-               return $db->select(
+               return $db->selectRow(
                        [ 'revision', 'page', 'user' ],
                        $fields,
                        $conditions,
@@ -793,20 +805,24 @@ class Revision implements IDBAccessObject {
                }
                // rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
                if ( $this->mId !== null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
                        $row = $dbr->selectRow(
                                [ 'page', 'revision' ],
                                self::selectPageFields(),
-                               [ 'page_id=rev_page',
-                                       'rev_id' => $this->mId ],
-                               __METHOD__ );
+                               [ 'page_id=rev_page', 'rev_id' => $this->mId ],
+                               __METHOD__
+                       );
                        if ( $row ) {
+                               // @TODO: better foreign title handling
                                $this->mTitle = Title::newFromRow( $row );
                        }
                }
 
-               if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
-                       $this->mTitle = Title::newFromID( $this->mPage );
+               if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) {
+                       // Loading by ID is best, though not possible for foreign titles
+                       if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
+                               $this->mTitle = Title::newFromID( $this->mPage );
+                       }
                }
 
                return $this->mTitle;
@@ -878,6 +894,8 @@ class Revision implements IDBAccessObject {
         * @return string
         */
        public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
+               $this->loadMutableFields();
+
                if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
                        return '';
                } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
@@ -973,7 +991,7 @@ class Revision implements IDBAccessObject {
         * @return RecentChange|null
         */
        public function getRecentChange( $flags = 0 ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
 
@@ -994,7 +1012,14 @@ class Revision implements IDBAccessObject {
         * @return bool
         */
        public function isDeleted( $field ) {
-               return ( $this->mDeleted & $field ) == $field;
+               if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
+                       // Current revisions of pages cannot have the content hidden. Skipping this
+                       // check is very useful for Parser as it fetches templates using newKnownCurrent().
+                       // Calling getVisibility() in that case triggers a verification database query.
+                       return false; // no need to check
+               }
+
+               return ( $this->getVisibility() & $field ) == $field;
        }
 
        /**
@@ -1003,6 +1028,8 @@ class Revision implements IDBAccessObject {
         * @return int
         */
        public function getVisibility() {
+               $this->loadMutableFields();
+
                return (int)$this->mDeleted;
        }
 
@@ -1054,13 +1081,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Fetch original serialized data without regard for view restrictions
+        * Get original serialized data (without checking view restrictions)
         *
         * @since 1.21
         * @return string
         */
        public function getSerializedData() {
                if ( $this->mText === null ) {
+                       // Revision is immutable. Load on demand.
                        $this->mText = $this->loadText();
                }
 
@@ -1078,17 +1106,14 @@ class Revision implements IDBAccessObject {
         */
        protected function getContentInternal() {
                if ( $this->mContent === null ) {
-                       // Revision is immutable. Load on demand:
-                       if ( $this->mText === null ) {
-                               $this->mText = $this->loadText();
-                       }
+                       $text = $this->getSerializedData();
 
-                       if ( $this->mText !== null && $this->mText !== false ) {
+                       if ( $text !== null && $text !== false ) {
                                // Unserialize content
                                $handler = $this->getContentHandler();
                                $format = $this->getContentFormat();
 
-                               $this->mContent = $handler->unserializeContent( $this->mText, $format );
+                               $this->mContent = $handler->unserializeContent( $text, $format );
                        }
                }
 
@@ -1551,29 +1576,30 @@ class Revision implements IDBAccessObject {
         *
         * @return string|bool The revision's text, or false on failure
         */
-       protected function loadText() {
-               // Caching may be beneficial for massive use of external storage
+       private function loadText() {
                global $wgRevisionCacheExpiry;
-               static $processCache = null;
 
-               if ( !$processCache ) {
-                       $processCache = new MapCacheLRU( 10 );
+               $cache = ObjectCache::getMainWANInstance();
+               if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
+                       // Do not cache RDBMs blobs in...the RDBMs store
+                       $ttl = $cache::TTL_UNCACHEABLE;
+               } else {
+                       $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
                }
 
-               $cache = ObjectCache::getMainWANInstance();
-               $textId = $this->getTextId();
-               $key = wfMemcKey( 'revisiontext', 'textid', $textId );
+               // No negative caching; negative hits on text rows may be due to corrupted replica DBs
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
+                       $ttl,
+                       function () {
+                               return $this->fetchText();
+                       },
+                       [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
+       }
 
-               if ( $wgRevisionCacheExpiry ) {
-                       if ( $processCache->has( $key ) ) {
-                               return $processCache->get( $key );
-                       }
-                       $text = $cache->get( $key );
-                       if ( is_string( $text ) ) {
-                               $processCache->set( $key, $text );
-                               return $text;
-                       }
-               }
+       private function fetchText() {
+               $textId = $this->getTextId();
 
                // If we kept data for lazy extraction, use it now...
                if ( $this->mTextRow !== null ) {
@@ -1583,25 +1609,38 @@ class Revision implements IDBAccessObject {
                        $row = null;
                }
 
+               // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables
+               // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases.
+               $flags = $this->mQueryFlags;
+               $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST )
+                       ? self::READ_LATEST_IMMUTABLE
+                       : 0;
+
+               list( $index, $options, $fallbackIndex, $fallbackOptions ) =
+                       DBAccessObjectUtils::getDBOptions( $flags );
+
                if ( !$row ) {
-                       // Text data is immutable; check slaves first.
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $row = $dbr->selectRow( 'text',
+                       // Text data is immutable; check replica DBs first.
+                       $row = wfGetDB( $index )->selectRow(
+                               'text',
                                [ 'old_text', 'old_flags' ],
                                [ 'old_id' => $textId ],
-                               __METHOD__ );
+                               __METHOD__,
+                               $options
+                       );
                }
 
-               // Fallback to the master in case of slave lag. Also use FOR UPDATE if it was
-               // used to fetch this revision to avoid missing the row due to REPEATABLE-READ.
-               $forUpdate = ( $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING );
-               if ( !$row && ( $forUpdate || wfGetLB()->getServerCount() > 1 ) ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $row = $dbw->selectRow( 'text',
+               // Fallback to DB_MASTER in some cases if the row was not found
+               if ( !$row && $fallbackIndex !== null ) {
+                       // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row
+                       // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided.
+                       $row = wfGetDB( $fallbackIndex )->selectRow(
+                               'text',
                                [ 'old_text', 'old_flags' ],
                                [ 'old_id' => $textId ],
                                __METHOD__,
-                               $forUpdate ? [ 'FOR UPDATE' ] : [] );
+                               $fallbackOptions
+                       );
                }
 
                if ( !$row ) {
@@ -1613,13 +1652,7 @@ class Revision implements IDBAccessObject {
                        wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
                }
 
-               # No negative caching -- negative hits on text rows may be due to corrupted slave servers
-               if ( $wgRevisionCacheExpiry && $text !== false ) {
-                       $processCache->set( $key, $text );
-                       $cache->set( $key, $text, $wgRevisionCacheExpiry );
-               }
-
-               return $text;
+               return is_string( $text ) ? $text : false;
        }
 
        /**
@@ -1706,7 +1739,7 @@ class Revision implements IDBAccessObject {
         * @return bool
         */
        public function userCan( $field, User $user = null ) {
-               return self::userCanBitfield( $this->mDeleted, $field, $user );
+               return self::userCanBitfield( $this->getVisibility(), $field, $user );
        }
 
        /**
@@ -1767,7 +1800,7 @@ class Revision implements IDBAccessObject {
        static function getTimestampFromId( $title, $id, $flags = 0 ) {
                $db = ( $flags & self::READ_LATEST )
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
                // Casting fix for databases that can't take '' for rev_id
                if ( $id == '' ) {
                        $id = 0;
@@ -1850,4 +1883,60 @@ class Revision implements IDBAccessObject {
                }
                return true;
        }
+
+       /**
+        * Load a revision based on a known page ID and current revision ID from the DB
+        *
+        * This method allows for the use of caching, though accessing anything that normally
+        * requires permission checks (aside from the text) will trigger a small DB lookup.
+        * The title will also be lazy loaded, though setTitle() can be used to preload it.
+        *
+        * @param IDatabase $db
+        * @param int $pageId Page ID
+        * @param int $revId Known current revision of this page
+        * @return Revision|bool Returns false if missing
+        * @since 1.28
+        */
+       public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) {
+               $cache = ObjectCache::getMainWANInstance();
+               return $cache->getWithSetCallback(
+                       // Page/rev IDs passed in from DB to reflect history merges
+                       $cache->makeGlobalKey( 'revision', $db->getWikiID(), $pageId, $revId ),
+                       $cache::TTL_WEEK,
+                       function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
+                               $setOpts += Database::getCacheSetOptions( $db );
+
+                               $rev = Revision::loadFromPageId( $db, $pageId, $revId );
+                               // Reflect revision deletion and user renames
+                               if ( $rev ) {
+                                       $rev->mTitle = null; // mutable; lazy-load
+                                       $rev->mRefreshMutableFields = true;
+                               }
+
+                               return $rev ?: false; // don't cache negatives
+                       }
+               );
+       }
+
+       /**
+        * For cached revisions, make sure the user name and rev_deleted is up-to-date
+        */
+       private function loadMutableFields() {
+               if ( !$this->mRefreshMutableFields ) {
+                       return; // not needed
+               }
+
+               $this->mRefreshMutableFields = false;
+               $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
+               $row = $dbr->selectRow(
+                       [ 'revision', 'user' ],
+                       [ 'rev_deleted', 'user_name' ],
+                       [ 'rev_id' => $this->mId, 'user_id = rev_user' ],
+                       __METHOD__
+               );
+               if ( $row ) { // update values
+                       $this->mDeleted = (int)$row->rev_deleted;
+                       $this->mUserText = $row->user_name;
+               }
+       }
 }
index 811870c..fb444bd 100644 (file)
@@ -81,7 +81,7 @@ abstract class RevisionListBase extends ContextSource implements Iterator {
         */
        public function reset() {
                if ( !$this->res ) {
-                       $this->res = $this->doQuery( wfGetDB( DB_SLAVE ) );
+                       $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
                } else {
                        $this->res->rewind();
                }
index 33569e6..4ab412e 100644 (file)
@@ -45,7 +45,7 @@ return [
        'DBLoadBalancerFactory' => function( MediaWikiServices $services ) {
                $config = $services->getMainConfig()->get( 'LBFactoryConf' );
 
-               $class = LBFactory::getLBFactoryClass( $config );
+               $class = LBFactoryMW::getLBFactoryClass( $config );
                if ( !isset( $config['readOnlyReason'] ) ) {
                        // TODO: replace the global wfConfiguredReadOnlyReason() with a service.
                        $config['readOnlyReason'] = wfConfiguredReadOnlyReason();
@@ -166,7 +166,8 @@ return [
 
        'LinkCache' => function( MediaWikiServices $services ) {
                return new LinkCache(
-                       $services->getTitleFormatter()
+                       $services->getTitleFormatter(),
+                       ObjectCache::getMainWANInstance()
                );
        },
 
index cbe4e2e..ddf5b89 100644 (file)
@@ -504,6 +504,19 @@ if ( !class_exists( 'AutoLoader' ) ) {
 // Reset the global service locator, so any services that have already been created will be
 // re-created while taking into account any custom settings and extensions.
 MediaWikiServices::resetGlobalInstance( new GlobalVarConfig(), 'quick' );
+// Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections)
+if ( $wgSharedDB && $wgSharedTables ) {
+       MediaWikiServices::getInstance()->getDBLoadBalancer()->setTableAliases(
+               array_fill_keys(
+                       $wgSharedTables,
+                       [
+                               'dbname' => $wgSharedDB,
+                               'schema' => $wgSharedSchema,
+                               'prefix' => $wgSharedPrefix
+                       ]
+               )
+       );
+}
 
 // Define a constant that indicates that the bootstrapping of the service locator
 // is complete.
@@ -638,6 +651,9 @@ date_default_timezone_set( $wgLocaltimezone );
 if ( is_null( $wgLocalTZoffset ) ) {
        $wgLocalTZoffset = date( 'Z' ) / 60;
 }
+// The part after the System| is ignored, but rest of MW fills it
+// out as the local offset.
+$wgDefaultUserOptions['timecorrection'] = "System|$wgLocalTZoffset";
 
 if ( !$wgDBerrorLogTZ ) {
        $wgDBerrorLogTZ = $wgLocaltimezone;
@@ -671,7 +687,7 @@ $parserMemc = wfGetParserCacheStorage();
 
 wfDebugLog( 'caches',
        'cluster: ' . get_class( $wgMemc ) .
-       ', WAN: ' . $wgMainWANCache .
+       ', WAN: ' . ( $wgMainWANCache === CACHE_NONE ? 'CACHE_NONE' : $wgMainWANCache ) .
        ', stash: ' . $wgMainStash .
        ', message: ' . get_class( $messageMemc ) .
        ', parser: ' . get_class( $parserMemc ) .
index 604ab93..ff7875c 100644 (file)
@@ -59,7 +59,7 @@ class SiteStats {
                        # Update schema
                        $u = new SiteStatsUpdate( 0, 0, 0 );
                        $u->doUpdate();
-                       self::$row = self::doLoad( wfGetDB( DB_SLAVE ) );
+                       self::$row = self::doLoad( wfGetDB( DB_REPLICA ) );
                }
 
                self::$loaded = true;
@@ -71,12 +71,12 @@ class SiteStats {
        static function loadAndLazyInit() {
                global $wgMiserMode;
 
-               wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
-               $row = self::doLoad( wfGetDB( DB_SLAVE ) );
+               wfDebug( __METHOD__ . ": reading site_stats from replica DB\n" );
+               $row = self::doLoad( wfGetDB( DB_REPLICA ) );
 
                if ( !self::isSane( $row ) ) {
                        // Might have just been initialized during this request? Underflow?
-                       wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
+                       wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
                }
 
@@ -87,7 +87,7 @@ class SiteStats {
                        // clean schema with mwdumper.
                        wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
 
-                       SiteStatsInit::doAllAndCommit( wfGetDB( DB_SLAVE ) );
+                       SiteStatsInit::doAllAndCommit( wfGetDB( DB_REPLICA ) );
 
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
                }
@@ -186,7 +186,7 @@ class SiteStats {
                        wfMemcKey( 'SiteStats', 'groupcounts', $group ),
                        $cache::TTL_HOUR,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -229,7 +229,7 @@ class SiteStats {
         */
        static function pagesInNs( $ns ) {
                if ( !isset( self::$pageCount[$ns] ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        self::$pageCount[$ns] = (int)$dbr->selectField(
                                'page',
                                'COUNT(*)',
@@ -296,7 +296,7 @@ class SiteStatsInit {
                } elseif ( $database ) {
                        $this->db = wfGetDB( DB_MASTER );
                } else {
-                       $this->db = wfGetDB( DB_SLAVE, 'vslow' );
+                       $this->db = wfGetDB( DB_REPLICA, 'vslow' );
                }
        }
 
index e578873..a6348b7 100644 (file)
@@ -105,6 +105,26 @@ class Status {
                return new self( $sv );
        }
 
+       /**
+        * Splits this Status object into two new Status objects, one which contains only
+        * the error messages, and one that contains the warnings, only. The returned array is
+        * defined as:
+        * array(
+        *      0 => object(Status) # the Status with error messages, only
+        *      1 => object(Status) # The Status with warning messages, only
+        * )
+        *
+        * @return array
+        */
+       public function splitByErrorType() {
+               list( $errorsOnlyStatusValue, $warningsOnlyStatusValue ) = $this->sv->splitByErrorType();
+               $errorsOnlyStatus = new Status( $errorsOnlyStatusValue );
+               $warningsOnlyStatus = new Status( $warningsOnlyStatusValue );
+               $errorsOnlyStatus->cleanCallback = $warningsOnlyStatus->cleanCallback = $this->cleanCallback;
+
+               return [ $errorsOnlyStatus, $warningsOnlyStatus ];
+       }
+
        /**
         * Change operation result
         *
@@ -314,6 +334,7 @@ class Status {
 
        /**
         * Return the message for a single error.
+        *
         * @param mixed $error With an array & two values keyed by
         * 'message' and 'params', use those keys-value pairs.
         * Otherwise, if its an array, just use the first value as the
diff --git a/includes/TemplatesOnThisPageFormatter.php b/includes/TemplatesOnThisPageFormatter.php
new file mode 100644 (file)
index 0000000..c0ae374
--- /dev/null
@@ -0,0 +1,183 @@
+<?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
+ */
+
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\Linker\LinkTarget;
+
+/**
+ * Handles formatting for the "templates used on this page"
+ * lists. Formerly known as Linker::formatTemplates()
+ *
+ * @since 1.28
+ */
+class TemplatesOnThisPageFormatter {
+
+       /**
+        * @var IContextSource
+        */
+       private $context;
+
+       /**
+        * @var LinkRenderer
+        */
+       private $linkRenderer;
+
+       /**
+        * @param IContextSource $context
+        * @param LinkRenderer $linkRenderer
+        */
+       public function __construct( IContextSource $context, LinkRenderer $linkRenderer ) {
+               $this->context = $context;
+               $this->linkRenderer = $linkRenderer;
+       }
+
+       /**
+        * Make an HTML list of templates, and then add a "More..." link at
+        * the bottom. If $more is null, do not add a "More..." link. If $more
+        * is a LinkTarget, make a link to that title and use it. If $more is a string,
+        * directly paste it in as the link (escaping needs to be done manually).
+        *
+        * @param LinkTarget[] $templates
+        * @param string|bool $type 'preview' if a preview, 'section' if a section edit, false if neither
+        * @param LinkTarget|string|null $more An escaped link for "More..." of the templates
+        * @return string HTML output
+        */
+       public function format( array $templates, $type = false, $more = null ) {
+               if ( !$templates ) {
+                       // No templates
+                       return '';
+               }
+
+               # Do a batch existence check
+               $batch = new LinkBatch;
+               foreach ( $templates as $title ) {
+                       $batch->addObj( $title );
+               }
+               $batch->execute();
+
+               # Construct the HTML
+               $outText = '<div class="mw-templatesUsedExplanation">';
+               $count = count( $templates );
+               if ( $type === 'preview' ) {
+                       $outText .= $this->context->msg( 'templatesusedpreview' )->numParams( $count )
+                               ->parseAsBlock();
+               } elseif ( $type === 'section' ) {
+                       $outText .= $this->context->msg( 'templatesusedsection' )->numParams( $count )
+                               ->parseAsBlock();
+               } else {
+                       $outText .= $this->context->msg( 'templatesused' )->numParams( $count )
+                               ->parseAsBlock();
+               }
+               $outText .= "</div><ul>\n";
+
+               usort( $templates, 'Title::compare' );
+               foreach ( $templates as $template ) {
+                       $outText .= $this->formatTemplate( $template );
+               }
+
+               if ( $more instanceof LinkTarget ) {
+                       $outText .= Html::rawElement( 'li', $this->linkRenderer->makeLink(
+                               $more, $this->context->msg( 'moredotdotdot' )->text() ) );
+               } elseif ( $more ) {
+                       // Documented as should already be escaped
+                       $outText .= Html::rawElement( 'li', $more );
+               }
+
+               $outText .= '</ul>';
+               return $outText;
+       }
+
+       /**
+        * Builds an <li> item for an individual template
+        *
+        * @param LinkTarget $target
+        * @return string
+        */
+       private function formatTemplate( LinkTarget $target ) {
+               // TODO Would be nice if we didn't have to use Title here
+               $titleObj = Title::newFromLinkTarget( $target );
+               $protected = $this->getRestrictionsText( $titleObj->getRestrictions( 'edit' ) );
+               $editLink = $this->buildEditLink( $titleObj );
+               return '<li>' . $this->linkRenderer->makeLink( $target )
+                       . $this->context->msg( 'word-separator' )->escaped()
+                       . $this->context->msg( 'parentheses' )->rawParams( $editLink )->escaped()
+                       . $this->context->msg( 'word-separator' )->escaped()
+                       . $protected . '</li>';
+       }
+
+       /**
+        * If the page is protected, get the relevant text
+        * for those restrictions
+        *
+        * @param array $restrictions
+        * @return string
+        */
+       private function getRestrictionsText( array $restrictions ) {
+               $protected = '';
+               if ( !$restrictions ) {
+                       return $protected;
+               }
+
+               // Check backwards-compatible messages
+               $msg = null;
+               if ( $restrictions === [ 'sysop' ] ) {
+                       $msg = $this->context->msg( 'template-protected' );
+               } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
+                       $msg = $this->context->msg( 'template-semiprotected' );
+               }
+               if ( $msg && !$msg->isDisabled() ) {
+                       $protected = $msg->parse();
+               } else {
+                       // Construct the message from restriction-level-*
+                       // e.g. restriction-level-sysop, restriction-level-autoconfirmed
+                       $msgs = [];
+                       foreach ( $restrictions as $r ) {
+                               $msgs[] = $this->context->msg( "restriction-level-$r" )->parse();
+                       }
+                       $protected = $this->context->msg( 'parentheses' )
+                               ->rawParams( $this->context->getLanguage()->commaList( $msgs ) )->escaped();
+               }
+
+               return $protected;
+       }
+
+       /**
+        * Return a link to the edit page, with the text
+        * saying "view source" if the user can't edit the page
+        *
+        * @param Title $titleObj
+        * @return string
+        */
+       private function buildEditLink( Title $titleObj ) {
+               if ( $titleObj->quickUserCan( 'edit', $this->context->getUser() ) ) {
+                       $linkMsg = 'editlink';
+               } else {
+                       $linkMsg = 'viewsourcelink';
+               }
+
+               return $this->linkRenderer->makeLink(
+                       $titleObj,
+                       $this->context->msg( $linkMsg )->text(),
+                       [],
+                       [ 'action' => 'edit' ]
+               );
+       }
+
+}
index 2021e0a..6d9ddd6 100644 (file)
@@ -394,7 +394,7 @@ class Title implements LinkTarget {
         * @return Title|null The new object, or null on an error
         */
        public static function newFromID( $id, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $row = $db->selectRow(
                        'page',
                        self::getSelectFields(),
@@ -419,7 +419,7 @@ class Title implements LinkTarget {
                if ( !count( $ids ) ) {
                        return [];
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $res = $dbr->select(
                        'page',
@@ -561,7 +561,7 @@ class Title implements LinkTarget {
         * @return Title|null An object representing the article, or null if no such article was found
         */
        public static function nameOf( $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $s = $dbr->selectRow(
                        'page',
@@ -751,12 +751,12 @@ class Title implements LinkTarget {
        /**
         * Callback for usort() to do title sorts by (namespace, title)
         *
-        * @param Title $a
-        * @param Title $b
+        * @param LinkTarget $a
+        * @param LinkTarget $b
         *
         * @return int Result of string comparison, or namespace comparison
         */
-       public static function compare( $a, $b ) {
+       public static function compare( LinkTarget $a, LinkTarget $b ) {
                if ( $a->getNamespace() == $b->getNamespace() ) {
                        return strcmp( $a->getText(), $b->getText() );
                } else {
@@ -1886,8 +1886,8 @@ class Title implements LinkTarget {
         * @param string $action Action that permission needs to be checked for
         * @param User $user User to check
         * @param string $rigor One of (quick,full,secure)
-        *   - quick  : does cheap permission checks from slaves (usable for GUI creation)
-        *   - full   : does cheap and expensive checks possibly from a slave
+        *   - quick  : does cheap permission checks from replica DBs (usable for GUI creation)
+        *   - full   : does cheap and expensive checks possibly from a replica DB
         *   - secure : does cheap and expensive checks, using the master as needed
         * @param array $ignoreErrors Array of Strings Set this to a list of message keys
         *   whose corresponding errors may be ignored.
@@ -2416,8 +2416,8 @@ class Title implements LinkTarget {
         * @param string $action Action that permission needs to be checked for
         * @param User $user User to check
         * @param string $rigor One of (quick,full,secure)
-        *   - quick  : does cheap permission checks from slaves (usable for GUI creation)
-        *   - full   : does cheap and expensive checks possibly from a slave
+        *   - quick  : does cheap permission checks from replica DBs (usable for GUI creation)
+        *   - full   : does cheap and expensive checks possibly from a replica DB
         *   - secure : does cheap and expensive checks, using the master as needed
         * @param bool $short Set this to true to stop after the first permission error.
         * @return array Array of arrays of the arguments to wfMessage to explain permissions problems.
@@ -2540,7 +2540,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mTitleProtection === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $res = $dbr->select(
                                'protected_titles',
                                [
@@ -2708,7 +2708,7 @@ class Title implements LinkTarget {
                        return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $this->getNamespace() == NS_FILE ) {
                        $tables = [ 'imagelinks', 'page_restrictions' ];
@@ -2848,23 +2848,6 @@ class Title implements LinkTarget {
                return $this->mCascadeRestriction;
        }
 
-       /**
-        * Loads a string into mRestrictions array
-        *
-        * @param ResultWrapper $res Resource restrictions as an SQL result.
-        * @param string $oldFashionedRestrictions Comma-separated list of page
-        *        restrictions from page table (pre 1.10)
-        */
-       private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) {
-               $rows = [];
-
-               foreach ( $res as $row ) {
-                       $rows[] = $row;
-               }
-
-               $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
-       }
-
        /**
         * Compiles list of active page restrictions from both page table (pre 1.10)
         * and page_restrictions table for this existing page.
@@ -2875,7 +2858,7 @@ class Title implements LinkTarget {
         *   restrictions from page table (pre 1.10)
         */
        public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $restrictionTypes = $this->getRestrictionTypes();
 
@@ -2948,36 +2931,53 @@ class Title implements LinkTarget {
         *   restrictions from page table (pre 1.10)
         */
        public function loadRestrictions( $oldFashionedRestrictions = null ) {
-               if ( !$this->mRestrictionsLoaded ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       if ( $this->exists() ) {
-                               $res = $dbr->select(
-                                       'page_restrictions',
-                                       [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
-                                       [ 'pr_page' => $this->getArticleID() ],
-                                       __METHOD__
-                               );
+               if ( $this->mRestrictionsLoaded ) {
+                       return;
+               }
 
-                               $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions );
-                       } else {
-                               $title_protection = $this->getTitleProtection();
-
-                               if ( $title_protection ) {
-                                       $now = wfTimestampNow();
-                                       $expiry = $dbr->decodeExpiry( $title_protection['expiry'] );
-
-                                       if ( !$expiry || $expiry > $now ) {
-                                               // Apply the restrictions
-                                               $this->mRestrictionsExpiry['create'] = $expiry;
-                                               $this->mRestrictions['create'] = explode( ',', trim( $title_protection['permission'] ) );
-                                       } else { // Get rid of the old restrictions
-                                               $this->mTitleProtection = false;
-                                       }
-                               } else {
-                                       $this->mRestrictionsExpiry['create'] = 'infinity';
+               $id = $this->getArticleID();
+               if ( $id ) {
+                       $cache = ObjectCache::getMainWANInstance();
+                       $rows = $cache->getWithSetCallback(
+                               // Page protections always leave a new null revision
+                               $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
+                               $cache::TTL_DAY,
+                               function ( $curValue, &$ttl, array &$setOpts ) {
+                                       $dbr = wfGetDB( DB_REPLICA );
+
+                                       $setOpts += Database::getCacheSetOptions( $dbr );
+
+                                       return iterator_to_array(
+                                               $dbr->select(
+                                                       'page_restrictions',
+                                                       [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
+                                                       [ 'pr_page' => $this->getArticleID() ],
+                                                       __METHOD__
+                                               )
+                                       );
                                }
-                               $this->mRestrictionsLoaded = true;
+                       );
+
+                       $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
+               } else {
+                       $title_protection = $this->getTitleProtection();
+
+                       if ( $title_protection ) {
+                               $now = wfTimestampNow();
+                               $expiry = wfGetDB( DB_REPLICA )->decodeExpiry( $title_protection['expiry'] );
+
+                               if ( !$expiry || $expiry > $now ) {
+                                       // Apply the restrictions
+                                       $this->mRestrictionsExpiry['create'] = $expiry;
+                                       $this->mRestrictions['create'] =
+                                               explode( ',', trim( $title_protection['permission'] ) );
+                               } else { // Get rid of the old restrictions
+                                       $this->mTitleProtection = false;
+                               }
+                       } else {
+                               $this->mRestrictionsExpiry['create'] = 'infinity';
                        }
+                       $this->mRestrictionsLoaded = true;
                }
        }
 
@@ -3069,7 +3069,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds['page_namespace'] = $this->getNamespace();
                $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
                $options = [];
@@ -3096,7 +3096,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() < 0 ) {
                        $n = 0;
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $n = $dbr->selectField( 'archive', 'COUNT(*)',
                                [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
@@ -3121,7 +3121,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() < 0 ) {
                        return false;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $deleted = (bool)$dbr->selectField( 'archive', '1',
                        [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
                        __METHOD__
@@ -3258,7 +3258,7 @@ class Title implements LinkTarget {
         * This clears some fields in this object, and clears any associated
         * keys in the "bad links" section of the link cache.
         *
-        * - This is called from WikiPage::doEdit() and WikiPage::insertOn() to allow
+        * - This is called from WikiPage::doEditContent() and WikiPage::insertOn() to allow
         * loading of the new page_id. It's also called from
         * WikiPage::doDeleteArticleReal()
         *
@@ -3375,7 +3375,7 @@ class Title implements LinkTarget {
                if ( count( $options ) > 0 ) {
                        $db = wfGetDB( DB_MASTER );
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
 
                $res = $db->select(
@@ -3437,7 +3437,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
 
                $blNamespace = "{$prefix}_namespace";
                $blTitle = "{$prefix}_title";
@@ -3500,7 +3500,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        [ 'page', 'pagelinks' ],
                        [ 'pl_namespace', 'pl_title' ],
@@ -3860,7 +3860,7 @@ class Title implements LinkTarget {
                        return $data;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $res = $dbr->select(
                        'categorylinks',
@@ -3928,7 +3928,7 @@ class Title implements LinkTarget {
         * @return int|bool Old revision ID, or false if none exists
         */
        public function getPreviousRevisionID( $revId, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $revId = $db->selectField( 'revision', 'rev_id',
                        [
                                'rev_page' => $this->getArticleID( $flags ),
@@ -3953,7 +3953,7 @@ class Title implements LinkTarget {
         * @return int|bool Next revision ID, or false if none exists
         */
        public function getNextRevisionID( $revId, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $revId = $db->selectField( 'revision', 'rev_id',
                        [
                                'rev_page' => $this->getArticleID( $flags ),
@@ -3979,7 +3979,7 @@ class Title implements LinkTarget {
        public function getFirstRevision( $flags = 0 ) {
                $pageId = $this->getArticleID( $flags );
                if ( $pageId ) {
-                       $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+                       $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                        $row = $db->selectRow( 'revision', Revision::selectFields(),
                                [ 'rev_page' => $pageId ],
                                __METHOD__,
@@ -4009,7 +4009,7 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function isNewPage() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
        }
 
@@ -4026,7 +4026,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mIsBigDeletion === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $revCount = $dbr->selectRowCount(
                                'revision',
@@ -4053,7 +4053,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mEstimateRevisions === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
                                [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
                }
@@ -4080,7 +4080,7 @@ class Title implements LinkTarget {
                if ( !$old || !$new ) {
                        return 0; // nothing to compare
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds = [
                        'rev_page' => $this->getArticleID(),
                        'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
@@ -4158,7 +4158,7 @@ class Title implements LinkTarget {
                        }
                        return $authors;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
                        [
                                'rev_page' => $this->getArticleID(),
@@ -4385,6 +4385,7 @@ class Title implements LinkTarget {
                                                $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
                                                $fname
                                        );
+                                       MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
                                }
                        ),
                        DeferredUpdates::PRESEND
@@ -4413,7 +4414,7 @@ class Title implements LinkTarget {
         */
        public function getTouched( $db = null ) {
                if ( $db === null ) {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
                return $touched;
@@ -4497,7 +4498,7 @@ class Title implements LinkTarget {
        public function getRedirectsHere( $ns = null ) {
                $redirs = [];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $where = [
                        'rd_namespace' => $this->getNamespace(),
                        'rd_title' => $this->getDBkey(),
index 3dcd30f..4ac4dea 100644 (file)
@@ -59,7 +59,7 @@ class WatchedItemQueryService {
         * @throws MWException
         */
        private function getConnection() {
-               return $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] );
+               return $this->loadBalancer->getConnection( DB_REPLICA, [ 'watchlist' ] );
        }
 
        /**
index a13609b..9a74401 100644 (file)
@@ -190,13 +190,13 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * @param int $slaveOrMaster DB_MASTER or DB_SLAVE
+        * @param int $dbIndex DB_MASTER or DB_REPLICA
         *
         * @return DatabaseBase
         * @throws MWException
         */
-       private function getConnection( $slaveOrMaster ) {
-               return $this->loadBalancer->getConnection( $slaveOrMaster, [ 'watchlist' ] );
+       private function getConnection( $dbIndex ) {
+               return $this->loadBalancer->getConnection( $dbIndex, [ 'watchlist' ] );
        }
 
        /**
@@ -217,7 +217,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @return int
         */
        public function countWatchedItems( User $user ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $return = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -237,7 +237,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @return int
         */
        public function countWatchers( LinkTarget $target ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $return = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -263,7 +263,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @throws MWException
         */
        public function countVisitingWatchers( LinkTarget $target, $threshold ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $visitingWatchers = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -293,7 +293,7 @@ class WatchedItemStore implements StatsdAwareInterface {
        public function countWatchersMultiple( array $targets, array $options = [] ) {
                $dbOptions = [ 'GROUP BY' => [ 'wl_namespace', 'wl_title' ] ];
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                if ( array_key_exists( 'minimumWatchers', $options ) ) {
                        $dbOptions['HAVING'] = 'COUNT(*) >= ' . (int)$options['minimumWatchers'];
@@ -341,7 +341,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                array $targetsWithVisitThresholds,
                $minimumWatchers = null
        ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                $conds = $this->getVisitingWatchersCondition( $dbr, $targetsWithVisitThresholds );
 
@@ -452,7 +452,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        return false;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $row = $dbr->selectRow(
                        'watchlist',
                        'wl_notificationtimestamp',
@@ -499,7 +499,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                                "wl_title {$options['sort']}"
                        ];
                }
-               $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_SLAVE );
+               $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_REPLICA );
 
                $res = $db->select(
                        'watchlist',
@@ -569,7 +569,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        return $timestamps;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                $lb = new LinkBatch( $targetsToLoad );
                $res = $dbr->select(
@@ -885,7 +885,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        $queryOptions['LIMIT'] = $unreadLimit;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $rowCount = $dbr->selectRowCount(
                        'watchlist',
                        '1',
index b5c57ee..a5ae461 100644 (file)
@@ -83,6 +83,9 @@ class WebRequest {
        /** @var bool Whether this HTTP request is "safe" (even if it is an HTTP post) */
        protected $markedAsSafe = false;
 
+       /**
+        * @codeCoverageIgnore
+        */
        public function __construct() {
                $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
                        ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
@@ -351,7 +354,7 @@ class WebRequest {
         * @return array|string Cleaned-up version of the given
         * @private
         */
-       function normalizeUnicode( $data ) {
+       public function normalizeUnicode( $data ) {
                if ( is_array( $data ) ) {
                        foreach ( $data as $key => $val ) {
                                $data[$key] = $this->normalizeUnicode( $val );
@@ -394,6 +397,32 @@ class WebRequest {
                }
        }
 
+       /**
+        * Fetch a scalar from the input without normalization, or return $default
+        * if it's not set.
+        *
+        * Unlike self::getVal(), this does not perform any normalization on the
+        * input value.
+        *
+        * @since 1.28
+        * @param string $name
+        * @param string|null $default Optional default
+        * @return string
+        */
+       public function getRawVal( $name, $default = null ) {
+               $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
+               if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
+                       $val = $this->data[$name];
+               } else {
+                       $val = $default;
+               }
+               if ( is_null( $val ) ) {
+                       return $val;
+               } else {
+                       return (string)$val;
+               }
+       }
+
        /**
         * Fetch a scalar from the input or return $default if it's not set.
         * Returns a string. Arrays are discarded. Useful for
@@ -491,7 +520,7 @@ class WebRequest {
         * @return int
         */
        public function getInt( $name, $default = 0 ) {
-               return intval( $this->getVal( $name, $default ) );
+               return intval( $this->getRawVal( $name, $default ) );
        }
 
        /**
@@ -503,7 +532,7 @@ class WebRequest {
         * @return int|null
         */
        public function getIntOrNull( $name ) {
-               $val = $this->getVal( $name );
+               $val = $this->getRawVal( $name );
                return is_numeric( $val )
                        ? intval( $val )
                        : null;
@@ -520,7 +549,7 @@ class WebRequest {
         * @return float
         */
        public function getFloat( $name, $default = 0.0 ) {
-               return floatval( $this->getVal( $name, $default ) );
+               return floatval( $this->getRawVal( $name, $default ) );
        }
 
        /**
@@ -533,7 +562,7 @@ class WebRequest {
         * @return bool
         */
        public function getBool( $name, $default = false ) {
-               return (bool)$this->getVal( $name, $default );
+               return (bool)$this->getRawVal( $name, $default );
        }
 
        /**
@@ -546,7 +575,8 @@ class WebRequest {
         * @return bool
         */
        public function getFuzzyBool( $name, $default = false ) {
-               return $this->getBool( $name, $default ) && strcasecmp( $this->getVal( $name ), 'false' ) !== 0;
+               return $this->getBool( $name, $default )
+                       && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
        }
 
        /**
@@ -560,7 +590,7 @@ class WebRequest {
        public function getCheck( $name ) {
                # Checkboxes and buttons are only present when clicked
                # Presence connotes truth, absence false
-               return $this->getVal( $name, null ) !== null;
+               return $this->getRawVal( $name, null ) !== null;
        }
 
        /**
@@ -615,6 +645,7 @@ class WebRequest {
         * Get the values passed in the query string.
         * No transformation is performed on the values.
         *
+        * @codeCoverageIgnore
         * @return array
         */
        public function getQueryValues() {
@@ -625,6 +656,7 @@ class WebRequest {
         * Return the contents of the Query with no decoding. Use when you need to
         * know exactly what was sent, e.g. for an OAuth signature over the elements.
         *
+        * @codeCoverageIgnore
         * @return string
         */
        public function getRawQueryString() {
@@ -683,6 +715,9 @@ class WebRequest {
 
        /**
         * Return the session for this request
+        *
+        * This might unpersist an existing session if it was invalid.
+        *
         * @since 1.27
         * @note For performance, keep the session locally if you will be making
         *  much use of it instead of calling this method repeatedly.
index 0df372e..41378fb 100644 (file)
@@ -234,7 +234,7 @@ class HistoryAction extends FormlessAction {
                        return new FakeResultWrapper( [] );
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $direction === self::DIR_PREV ) {
                        list( $dirs, $oper ) = [ "ASC", ">=" ];
index ea66900..4d80a1c 100644 (file)
@@ -202,6 +202,7 @@ class InfoAction extends FormlessAction {
                $title = $this->getTitle();
                $id = $title->getArticleID();
                $config = $this->context->getConfig();
+               $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
 
                $pageCounts = $this->pageCounts( $this->page );
 
@@ -279,9 +280,18 @@ class InfoAction extends FormlessAction {
                        . ' ' . $this->msg( 'parentheses', $pageLang )->escaped() ];
 
                // Content model of the page
+               $modelHtml = htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) );
+               // If the user can change it, add a link to Special:ChangeContentModel
+               if ( $title->quickUserCan( 'editcontentmodel' ) ) {
+                       $modelHtml .= ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink(
+                               SpecialPage::getTitleValueFor( 'ChangeContentModel', $title->getPrefixedText() ),
+                               $this->msg( 'pageinfo-content-model-change' )->text()
+                       ) )->escaped();
+               }
+
                $pageInfo['header-basic'][] = [
                        $this->msg( 'pageinfo-content-model' ),
-                       htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) )
+                       $modelHtml
                ];
 
                // Search engine status
@@ -621,14 +631,15 @@ class InfoAction extends FormlessAction {
                                        $more = null;
                                }
 
+                               $templateListFormatter = new TemplatesOnThisPageFormatter(
+                                       $this->getContext(),
+                                       $linkRenderer
+                               );
+
                                $pageInfo['header-properties'][] = [
                                        $this->msg( 'pageinfo-templates' )
                                                ->numParams( $pageCounts['transclusion']['from'] ),
-                                       Linker::formatTemplates(
-                                               $transcludedTemplates,
-                                               false,
-                                               false,
-                                               $more )
+                                       $templateListFormatter->format( $transcludedTemplates, false, $more )
                                ];
                        }
 
@@ -644,14 +655,15 @@ class InfoAction extends FormlessAction {
                                        $more = null;
                                }
 
+                               $templateListFormatter = new TemplatesOnThisPageFormatter(
+                                       $this->getContext(),
+                                       $linkRenderer
+                               );
+
                                $pageInfo['header-properties'][] = [
                                        $this->msg( 'pageinfo-transclusions' )
                                                ->numParams( $pageCounts['transclusion']['to'] ),
-                                       Linker::formatTemplates(
-                                               $transcludedTargets,
-                                               false,
-                                               false,
-                                               $more )
+                                       $templateListFormatter->format( $transcludedTargets, false, $more )
                                ];
                        }
                }
@@ -676,8 +688,8 @@ class InfoAction extends FormlessAction {
                                $title = $page->getTitle();
                                $id = $title->getArticleID();
 
-                               $dbr = wfGetDB( DB_SLAVE );
-                               $dbrWatchlist = wfGetDB( DB_SLAVE, 'watchlist' );
+                               $dbr = wfGetDB( DB_REPLICA );
+                               $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
 
                                $setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
 
index b2002ff..942b731 100644 (file)
@@ -42,7 +42,7 @@ class PurgeAction extends FormAction {
        }
 
        public function onSubmit( $data ) {
-               return $this->page->doPurge();
+               return $this->page->doPurge( WikiPage::PURGE_ALL );
        }
 
        public function show() {
index c800ce7..e466e65 100644 (file)
@@ -112,13 +112,19 @@ class RevertAction extends FormAction {
        public function onSubmit( $data ) {
                $this->useTransactionalTimeLimit();
 
-               $source = $this->page->getFile()->getArchiveVirtualUrl(
-                       $this->getRequest()->getText( 'oldimage' )
-               );
+               $old = $this->getRequest()->getText( 'oldimage' );
+               $localFile = $this->page->getFile();
+               $oldFile = OldLocalFile::newFromArchiveName( $this->getTitle(), $localFile->getRepo(), $old );
+
+               $source = $localFile->getArchiveVirtualUrl( $old );
                $comment = $data['comment'];
 
+               if ( $localFile->getSha1() === $oldFile->getSha1() ) {
+                       return Status::newFatal( 'filerevert-identical' );
+               }
+
                // TODO: Preserve file properties from database instead of reloading from file
-               return $this->page->getFile()->upload(
+               return $localFile->upload(
                        $source,
                        $comment,
                        $comment,
index 3e760fd..aa2858d 100644 (file)
@@ -54,9 +54,12 @@ class RollbackAction extends FormlessAction {
                $user = $this->getUser();
                $from = $request->getVal( 'from' );
                $rev = $this->page->getRevision();
-               if ( $from === null || $from === '' ) {
+               if ( $from === null ) {
                        throw new ErrorPageError( 'rollbackfailed', 'rollback-missingparam' );
                }
+               if ( !$rev ) {
+                       throw new ErrorPageError( 'rollbackfailed', 'rollback-missingrevision' );
+               }
                if ( $from !== $rev->getUserText() ) {
                        throw new ErrorPageError( 'rollbackfailed', 'alreadyrolled', [
                                $this->getTitle()->getPrefixedText(),
index 3a24565..4a7f623 100644 (file)
@@ -41,6 +41,33 @@ class ViewAction extends FormlessAction {
        }
 
        public function show() {
+               $config = $this->context->getConfig();
+
+               if (
+                       $config->get( 'DebugToolbar' ) == false && // don't let this get stuck on pages
+                       $this->page->checkTouched() // page exists and is not a redirect
+               ) {
+                       // Include any redirect in the last-modified calculation
+                       $redirFromTitle = $this->page->getRedirectedFrom();
+                       if ( !$redirFromTitle ) {
+                               $touched = $this->page->getTouched();
+                       } elseif ( $config->get( 'MaxRedirects' ) <= 1 ) {
+                               $touched = max( $this->page->getTouched(), $redirFromTitle->getTouched() );
+                       } else {
+                               // Don't bother following the chain and getting the max mtime
+                               $touched = null;
+                       }
+
+                       // If a page was purged on HTTP GET, relect that timestamp to avoid sending 304s
+                       $touched = max( $touched, $this->page->getLastPurgeTimestamp() );
+
+                       // Send HTTP 304 if the IMS matches or otherwise set expiry/last-modified headers
+                       if ( $touched && $this->getOutput()->checkLastModified( $touched ) ) {
+                               wfDebug( __METHOD__ . ": done 304\n" );
+                               return;
+                       }
+               }
+
                $this->page->view();
        }
 }
index a4f54ee..8e57f93 100644 (file)
@@ -85,6 +85,7 @@ class ApiAuthManagerHelper {
                                        'key' => $message->getKey(),
                                        'params' => $message->getParams(),
                                ];
+                               ApiResult::setIndexedTagName( $res[$key]['params'], 'param' );
                                break;
                }
        }
index 66c1b53..f763e45 100644 (file)
@@ -599,12 +599,12 @@ abstract class ApiBase extends ContextSource {
        }
 
        /**
-        * Gets a default slave database connection object
+        * Gets a default replica DB connection object
         * @return DatabaseBase
         */
        protected function getDB() {
                if ( !isset( $this->mSlaveDB ) ) {
-                       $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
+                       $this->mSlaveDB = wfGetDB( DB_REPLICA, 'api' );
                }
 
                return $this->mSlaveDB;
@@ -826,7 +826,7 @@ abstract class ApiBase extends ContextSource {
         * @param array $params
         * @param bool|string $load Whether load the object's state from the database:
         *        - false: don't load (if the pageid is given, it will still be loaded)
-        *        - 'fromdb': load from a slave database
+        *        - 'fromdb': load from a replica DB
         *        - 'fromdbmaster': load from the master database
         * @return WikiPage
         */
@@ -1000,6 +1000,31 @@ abstract class ApiBase extends ContextSource {
                                        $type = $this->getModuleManager()->getNames( $paramName );
                                }
                        }
+
+                       $request = $this->getMain()->getRequest();
+                       $rawValue = $request->getRawVal( $encParamName );
+                       if ( $rawValue === null ) {
+                               $rawValue = $default;
+                       }
+
+                       // Preserve U+001F for self::parseMultiValue(), or error out if that won't be called
+                       if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) {
+                               if ( $multi ) {
+                                       // This loses the potential $wgContLang->checkTitleEncoding() transformation
+                                       // done by WebRequest for $_GET. Let's call that a feature.
+                                       $value = join( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
+                               } else {
+                                       $this->dieUsage(
+                                               "U+001F multi-value separation may only be used for multi-valued parameters.",
+                                               'badvalue_notmultivalue'
+                                       );
+                               }
+                       }
+
+                       // Check for NFC normalization, and warn
+                       if ( $rawValue !== $value ) {
+                               $this->handleParamNormalization( $paramName, $value, $rawValue );
+                       }
                }
 
                if ( isset( $value ) && ( $multi || is_array( $type ) ) ) {
@@ -1145,6 +1170,40 @@ abstract class ApiBase extends ContextSource {
                return $value;
        }
 
+       /**
+        * Handle when a parameter was Unicode-normalized
+        * @since 1.28
+        * @param string $paramName Unprefixed parameter name
+        * @param string $value Input that will be used.
+        * @param string $rawValue Input before normalization.
+        */
+       protected function handleParamNormalization( $paramName, $value, $rawValue ) {
+               $encParamName = $this->encodeParamName( $paramName );
+               $this->setWarning(
+                       "The value passed for '$encParamName' contains invalid or non-normalized data. "
+                       . 'Textual data should be valid, NFC-normalized Unicode without '
+                       . 'C0 control characters other than HT (\\t), LF (\\n), and CR (\\r).'
+               );
+       }
+
+       /**
+        * Split a multi-valued parameter string, like explode()
+        * @since 1.28
+        * @param string $value
+        * @param int $limit
+        * @return string[]
+        */
+       protected function explodeMultiValue( $value, $limit ) {
+               if ( substr( $value, 0, 1 ) === "\x1f" ) {
+                       $sep = "\x1f";
+                       $value = substr( $value, 1 );
+               } else {
+                       $sep = '|';
+               }
+
+               return explode( $sep, $value, $limit );
+       }
+
        /**
         * Return an array of values that were given in a 'a|b|c' notation,
         * after it optionally validates them against the list allowed values.
@@ -1159,13 +1218,13 @@ abstract class ApiBase extends ContextSource {
         * @return string|string[] (allowMultiple ? an_array_of_values : a_single_value)
         */
        protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues ) {
-               if ( trim( $value ) === '' && $allowMultiple ) {
+               if ( ( trim( $value ) === '' || trim( $value ) === "\x1f" ) && $allowMultiple ) {
                        return [];
                }
 
                // This is a bit awkward, but we want to avoid calling canApiHighLimits()
                // because it unstubs $wgUser
-               $valuesList = explode( '|', $value, self::LIMIT_SML2 + 1 );
+               $valuesList = $this->explodeMultiValue( $value, self::LIMIT_SML2 + 1 );
                $sizeLimit = count( $valuesList ) > self::LIMIT_SML1 && $this->mMainModule->canApiHighLimits()
                        ? self::LIMIT_SML2
                        : self::LIMIT_SML1;
index 00daba9..d6de834 100644 (file)
@@ -97,6 +97,7 @@ class ApiEditPage extends ApiBase {
                } else {
                        $contentHandler = ContentHandler::getForModelID( $params['contentmodel'] );
                }
+               $contentModel = $contentHandler->getModelID();
 
                $name = $titleObj->getPrefixedDBkey();
                $model = $contentHandler->getModelID();
@@ -111,11 +112,11 @@ class ApiEditPage extends ApiBase {
                }
 
                if ( !isset( $params['contentformat'] ) || $params['contentformat'] == '' ) {
-                       $params['contentformat'] = $contentHandler->getDefaultFormat();
+                       $contentFormat = $contentHandler->getDefaultFormat();
+               } else {
+                       $contentFormat = $params['contentformat'];
                }
 
-               $contentFormat = $params['contentformat'];
-
                if ( !$contentHandler->isSupportedFormat( $contentFormat ) ) {
 
                        $this->dieUsage( "The requested format $contentFormat is not supported for content model " .
@@ -265,9 +266,21 @@ class ApiEditPage extends ApiBase {
                        if ( !$newContent ) {
                                $this->dieUsageMsg( 'undo-failure' );
                        }
-
-                       $params['text'] = $newContent->serialize( $params['contentformat'] );
-
+                       if ( empty( $params['contentmodel'] )
+                               && empty( $params['contentformat'] )
+                       ) {
+                               // If we are reverting content model, the new content model
+                               // might not support the current serialization format, in
+                               // which case go back to the old serialization format,
+                               // but only if the user hasn't specified a format/model
+                               // parameter.
+                               if ( !$newContent->isSupportedFormat( $contentFormat ) ) {
+                                       $contentFormat = $undoafterRev->getContentFormat();
+                               }
+                               // Override content model with model of undid revision.
+                               $contentModel = $newContent->getModel();
+                       }
+                       $params['text'] = $newContent->serialize( $contentFormat );
                        // If no summary was given and we only undid one rev,
                        // use an autosummary
                        if ( is_null( $params['summary'] ) &&
@@ -288,7 +301,7 @@ class ApiEditPage extends ApiBase {
                $requestArray = [
                        'wpTextbox1' => $params['text'],
                        'format' => $contentFormat,
-                       'model' => $contentHandler->getModelID(),
+                       'model' => $contentModel,
                        'wpEditToken' => $params['token'],
                        'wpIgnoreBlankSummary' => true,
                        'wpIgnoreBlankArticle' => true,
@@ -519,7 +532,7 @@ class ApiEditPage extends ApiBase {
 
                        case EditPage::AS_END:
                        default:
-                               // $status came from WikiPage::doEdit()
+                               // $status came from WikiPage::doEditContent()
                                $errors = $status->getErrorsArray();
                                $this->dieUsageMsg( $errors[0] ); // TODO: Add new errors to message map
                                break;
index c19926a..6d9184f 100644 (file)
@@ -202,7 +202,7 @@ class ApiErrorFormatter {
 
                        case 'raw':
                                $value += [
-                                       'message' => $msg->getKey(),
+                                       'key' => $msg->getKey(),
                                        'params' => $msg->getParams(),
                                ];
                                ApiResult::setIndexedTagName( $value['params'], 'param' );
index 48e7698..10fb182 100644 (file)
@@ -34,7 +34,7 @@
 class ApiExpandTemplates extends ApiBase {
 
        public function execute() {
-               // Cache may vary on $wgUser because ParserOptions gets data from it
+               // Cache may vary on the user because ParserOptions gets data from it
                $this->getMain()->setCacheMode( 'anon-public-user-private' );
 
                // Get parameters
index 9bc0b3a..55edd99 100644 (file)
@@ -110,17 +110,18 @@ class ApiLogin extends ApiBase {
                }
 
                // Try bot passwords
-               if ( $authRes === false && $this->getConfig()->get( 'EnableBotPasswords' ) &&
-                       strpos( $params['name'], BotPassword::getSeparator() ) !== false
+               if (
+                       $authRes === false && $this->getConfig()->get( 'EnableBotPasswords' ) &&
+                       ( $botLoginData = BotPassword::canonicalizeLoginData( $params['name'], $params['password'] ) )
                ) {
                        $status = BotPassword::login(
-                               $params['name'], $params['password'], $this->getRequest()
+                               $botLoginData[0], $botLoginData[1], $this->getRequest()
                        );
                        if ( $status->isOK() ) {
                                $session = $status->getValue();
                                $authRes = 'Success';
                                $loginType = 'BotPassword';
-                       } else {
+                       } elseif ( !$botLoginData[2] ) {
                                $authRes = 'Failed';
                                $message = $status->getMessage();
                                LoggerFactory::getInstance( 'authentication' )->info(
index 22b079d..1f3c76a 100644 (file)
@@ -1322,9 +1322,9 @@ class ApiMain extends ApiBase {
                        }
                }
 
-               // If a majority of slaves are too lagged then disallow writes
-               $slaveCount = wfGetLB()->getServerCount() - 1;
-               if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
+               // If a majority of replica DBs are too lagged then disallow writes
+               $replicaCount = wfGetLB()->getServerCount() - 1;
+               if ( $numLagged >= ceil( $replicaCount / 2 ) ) {
                        $laggedServers = implode( ', ', $laggedServers );
                        wfDebugLog(
                                'api-readonly',
index 90438d4..ed229cb 100644 (file)
@@ -495,10 +495,14 @@ class ApiPageSet extends ApiBase {
         * @since 1.21
         */
        public function getNormalizedTitlesAsResult( $result = null ) {
+               global $wgContLang;
+
                $values = [];
                foreach ( $this->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
+                       $encode = ( $wgContLang->normalize( $rawTitleStr ) !== $rawTitleStr );
                        $values[] = [
-                               'from' => $rawTitleStr,
+                               'fromencoded' => $encode,
+                               'from' => $encode ? rawurlencode( $rawTitleStr ) : $rawTitleStr,
                                'to' => $titleStr
                        ];
                }
@@ -1403,6 +1407,23 @@ class ApiPageSet extends ApiBase {
                return $result;
        }
 
+       protected function handleParamNormalization( $paramName, $value, $rawValue ) {
+               parent::handleParamNormalization( $paramName, $value, $rawValue );
+
+               if ( $paramName === 'titles' ) {
+                       // For the 'titles' parameter, we want to split it like ApiBase would
+                       // and add any changed titles to $this->mNormalizedTitles
+                       $value = $this->explodeMultiValue( $value, self::LIMIT_SML2 + 1 );
+                       $l = count( $value );
+                       $rawValue = $this->explodeMultiValue( $rawValue, $l );
+                       for ( $i = 0; $i < $l; $i++ ) {
+                               if ( $value[$i] !== $rawValue[$i] ) {
+                                       $this->mNormalizedTitles[$rawValue[$i]] = $value[$i];
+                               }
+                       }
+               }
+       }
+
        private static $generators = null;
 
        /**
index c3c9e21..caf0cd7 100644 (file)
@@ -46,7 +46,39 @@ class ApiParamInfo extends ApiBase {
                $this->context->setLanguage( $this->getMain()->getLanguage() );
 
                if ( is_array( $params['modules'] ) ) {
-                       $modules = $params['modules'];
+                       $modules = [];
+                       foreach ( $params['modules'] as $path ) {
+                               if ( $path === '*' || $path === '**' ) {
+                                       $path = "main+$path";
+                               }
+                               if ( substr( $path, -2 ) === '+*' || substr( $path, -2 ) === ' *' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -2 );
+                                       $recursive = false;
+                               } elseif ( substr( $path, -3 ) === '+**' || substr( $path, -3 ) === ' **' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -3 );
+                                       $recursive = true;
+                               } else {
+                                       $submodules = false;
+                               }
+
+                               if ( $submodules ) {
+                                       try {
+                                               $module = $this->getModuleFromPath( $path );
+                                       } catch ( UsageException $ex ) {
+                                               $this->setWarning( $ex->getMessage() );
+                                       }
+                                       $submodules = $this->listAllSubmodules( $module, $recursive );
+                                       if ( $submodules ) {
+                                               $modules = array_merge( $modules, $submodules );
+                                       } else {
+                                               $this->setWarning( "Module $path has no submodules" );
+                                       }
+                               } else {
+                                       $modules[] = $path;
+                               }
+                       }
                } else {
                        $modules = [];
                }
@@ -69,6 +101,8 @@ class ApiParamInfo extends ApiBase {
                        $formatModules = [];
                }
 
+               $modules = array_unique( $modules );
+
                $res = [];
 
                foreach ( $modules as $m ) {
@@ -121,6 +155,29 @@ class ApiParamInfo extends ApiBase {
                $result->addValue( null, $this->getModuleName(), $res );
        }
 
+       /**
+        * List all submodules of a module
+        * @param ApiBase $module
+        * @param boolean $recursive
+        * @return string[]
+        */
+       private function listAllSubmodules( ApiBase $module, $recursive ) {
+               $manager = $module->getModuleManager();
+               if ( $manager ) {
+                       $paths = [];
+                       $names = $manager->getNames();
+                       sort( $names );
+                       foreach ( $names as $name ) {
+                               $submodule = $manager->getModule( $name );
+                               $paths[] = $submodule->getModulePath();
+                               if ( $recursive && $submodule->getModuleManager() ) {
+                                       $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) );
+                               }
+                       }
+               }
+               return $paths;
+       }
+
        /**
         * @param array $res Result array
         * @param string $key Result key
@@ -162,6 +219,7 @@ class ApiParamInfo extends ApiBase {
                                                'key' => $m->getKey(),
                                                'params' => $m->getParams(),
                                        ];
+                                       ApiResult::setIndexedTagName( $a['params'], 'param' );
                                        if ( $m instanceof ApiHelpParamValueMessage ) {
                                                $a['forvalue'] = $m->getParamValue();
                                        }
@@ -448,8 +506,10 @@ class ApiParamInfo extends ApiBase {
 
        protected function getExamplesMessages() {
                return [
-                       'action=paraminfo&modules=parse|phpfm|query+allpages|query+siteinfo'
+                       'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo'
                                => 'apihelp-paraminfo-example-1',
+                       'action=paraminfo&modules=query%2B*'
+                               => 'apihelp-paraminfo-example-2',
                ];
        }
 
index 822369a..8bbd88d 100644 (file)
@@ -37,6 +37,12 @@ class ApiPurge extends ApiBase {
         * Purges the cache of a page
         */
        public function execute() {
+               $main = $this->getMain();
+               if ( !$main->isInternalMode() && !$main->getRequest()->wasPosted() ) {
+                       $this->logFeatureUsage( 'purge-via-GET' );
+                       $this->setWarning( 'Use of action=purge via GET is deprecated. Use POST instead.' );
+               }
+
                $params = $this->extractRequestParams();
 
                $continuationManager = new ApiContinuationManager( $this, [], [] );
@@ -55,7 +61,12 @@ class ApiPurge extends ApiBase {
                        ApiQueryBase::addTitleInfo( $r, $title );
                        $page = WikiPage::factory( $title );
                        if ( !$user->pingLimiter( 'purge' ) ) {
-                               $page->doPurge(); // Directly purge and skip the UI part of purge().
+                               $flags = WikiPage::PURGE_ALL;
+                               if ( !$this->getRequest()->wasPosted() ) {
+                                       $flags ^= WikiPage::PURGE_GLOBAL_PCACHE; // skip DB_MASTER write
+                               }
+                               // Directly purge and skip the UI part of purge()
+                               $page->doPurge( $flags );
                                $r['purged'] = true;
                        } else {
                                $error = $this->parseMsg( [ 'actionthrottledtext' ] );
@@ -91,7 +102,9 @@ class ApiPurge extends ApiBase {
                                                # Update the links tables
                                                $updates = $content->getSecondaryDataUpdates(
                                                        $title, null, $forceRecursiveLinkUpdate, $p_result );
-                                               DataUpdate::runUpdates( $updates );
+                                               foreach ( $updates as $update ) {
+                                                       DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
+                                               }
 
                                                $r['linkupdate'] = true;
 
@@ -151,6 +164,18 @@ class ApiPurge extends ApiBase {
                return !$this->getUser()->isAllowed( 'purge' );
        }
 
+       protected function getHelpFlags() {
+               $flags = parent::getHelpFlags();
+
+               // Claim that we must be posted for the purposes of help and paraminfo.
+               // @todo Remove this when self::mustBePosted() is updated for T145649
+               if ( !in_array( 'mustbeposted', $flags, true ) ) {
+                       $flags[] = 'mustbeposted';
+               }
+
+               return $flags;
+       }
+
        public function getAllowedParams( $flags = 0 ) {
                $result = [
                        'forcelinkupdate' => false,
index a559683..3073a95 100644 (file)
@@ -55,6 +55,14 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                $result = $this->getResult();
 
+               // If the user wants no namespaces, they get no pages.
+               if ( $params['namespace'] === [] ) {
+                       if ( $resultPageSet === null ) {
+                               $result->addValue( 'query', $this->getModuleName(), [] );
+                       }
+                       return;
+               }
+
                // This module operates in two modes:
                // 'user': List deleted revs by a certain user
                // 'all': List all deleted revs in NS
@@ -153,10 +161,10 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                if ( $mode == 'all' ) {
                        if ( $params['namespace'] !== null ) {
                                $namespaces = $params['namespace'];
-                               $this->addWhereFld( 'ar_namespace', $namespaces );
                        } else {
                                $namespaces = MWNamespace::getValidNamespaces();
                        }
+                       $this->addWhereFld( 'ar_namespace', $namespaces );
 
                        // For from/to/prefix, we have to consider the potential
                        // transformations of the title in all specified namespaces.
@@ -447,7 +455,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                return [
                        'action=query&list=alldeletedrevisions&adruser=Example&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-user',
-                       'action=query&list=alldeletedrevisions&adrdir=newer&adrlimit=50'
+                       'action=query&list=alldeletedrevisions&adrdir=newer&adrnamespace=0&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-ns-main',
                ];
        }
index 1d250e9..661ec5a 100644 (file)
@@ -117,12 +117,12 @@ class ApiQueryAuthManagerInfo extends ApiQueryBase {
        protected function getExamplesMessages() {
                return [
                        'action=query&meta=authmanagerinfo&amirequestsfor=' . urlencode( AuthManager::ACTION_LOGIN )
-                               => 'apihelp-query+filerepoinfo-example-login',
+                               => 'apihelp-query+authmanagerinfo-example-login',
                        'action=query&meta=authmanagerinfo&amirequestsfor=' . urlencode( AuthManager::ACTION_LOGIN ) .
                                '&amimergerequestfields=1'
-                               => 'apihelp-query+filerepoinfo-example-login-merged',
+                               => 'apihelp-query+authmanagerinfo-example-login-merged',
                        'action=query&meta=authmanagerinfo&amisecuritysensitiveoperation=foo'
-                               => 'apihelp-query+filerepoinfo-example-securitysensitiveoperation',
+                               => 'apihelp-query+authmanagerinfo-example-securitysensitiveoperation',
                ];
        }
 
index 236fb9e..8e89c32 100644 (file)
@@ -264,6 +264,12 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                        }
                }
 
+               // MySQL (or at least 5.5.5-10.0.23-MariaDB) chooses a really bad query
+               // plan if it thinks there will be more matching rows in the linktable
+               // than are in page. Use STRAIGHT_JOIN here to force it to use the
+               // intended, fast plan. See T145079 for details.
+               $this->addOption( 'STRAIGHT_JOIN' );
+
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
 
                $res = $this->select( __METHOD__ );
index 8b8bd01..5d7c664 100644 (file)
@@ -173,10 +173,8 @@ class ApiQueryBlocks extends ApiQueryBase {
                        $this->addWhereFld( 'ipb_deleted', 0 );
                }
 
-               // Purge expired entries on one in every 10 queries
-               if ( !mt_rand( 0, 10 ) ) {
-                       Block::purgeExpired();
-               }
+               # Filter out expired rows
+               $this->addWhere( 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() ) );
 
                $res = $this->select( __METHOD__ );
 
index 5d32497..99f722d 100644 (file)
@@ -275,6 +275,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data['allcentralidlookupproviders'] = $providerIds;
 
                $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
+               $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
 
                Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
 
index bd2f080..7a14aac 100644 (file)
@@ -50,11 +50,11 @@ class ApiQueryTags extends ApiQueryBase {
                $limit = $params['limit'];
                $result = $this->getResult();
 
-               $extensionDefinedTags = array_fill_keys( ChangeTags::listExtensionDefinedTags(), 0 );
+               $softwareDefinedTags = array_fill_keys( ChangeTags::listSoftwareDefinedTags(), 0 );
                $explicitlyDefinedTags = array_fill_keys( ChangeTags::listExplicitlyDefinedTags(), 0 );
-               $extensionActivatedTags = array_fill_keys( ChangeTags::listExtensionActivatedTags(), 0 );
+               $softwareActivatedTags = array_fill_keys( ChangeTags::listSoftwareActivatedTags(), 0 );
 
-               $definedTags = array_merge( $extensionDefinedTags, $explicitlyDefinedTags );
+               $definedTags = array_merge( $softwareDefinedTags, $explicitlyDefinedTags );
 
                # Fetch defined tags that aren't past the continuation
                if ( $params['continue'] !== null ) {
@@ -105,16 +105,17 @@ class ApiQueryTags extends ApiQueryBase {
                                $tag['hitcount'] = $hitcount;
                        }
 
-                       $isExtension = isset( $extensionDefinedTags[$tagName] );
+                       $isSoftware = isset( $softwareDefinedTags[$tagName] );
                        $isExplicit = isset( $explicitlyDefinedTags[$tagName] );
 
                        if ( $fld_defined ) {
-                               $tag['defined'] = $isExtension || $isExplicit;
+                               $tag['defined'] = $isSoftware || $isExplicit;
                        }
 
                        if ( $fld_source ) {
                                $tag['source'] = [];
-                               if ( $isExtension ) {
+                               if ( $isSoftware ) {
+                                       // TODO: Can we change this to 'software'?
                                        $tag['source'][] = 'extension';
                                }
                                if ( $isExplicit ) {
@@ -123,7 +124,7 @@ class ApiQueryTags extends ApiQueryBase {
                        }
 
                        if ( $fld_active ) {
-                               $tag['active'] = $isExplicit || isset( $extensionActivatedTags[$tagName] );
+                               $tag['active'] = $isExplicit || isset( $softwareActivatedTags[$tagName] );
                        }
 
                        $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $tag );
index 51e1923..f92a916 100644 (file)
@@ -57,13 +57,13 @@ class ApiQueryContributions extends ApiQueryBase {
                $this->fld_patrolled = isset( $prop['patrolled'] );
                $this->fld_tags = isset( $prop['tags'] );
 
-               // Most of this code will use the 'contributions' group DB, which can map to slaves
+               // Most of this code will use the 'contributions' group DB, which can map to replica DBs
                // with extra user based indexes or partioning by user. The additional metadata
-               // queries should use a regular slave since the lookup pattern is not all by user.
-               $dbSecondary = $this->getDB(); // any random slave
+               // queries should use a regular replica DB since the lookup pattern is not all by user.
+               $dbSecondary = $this->getDB(); // any random replica DB
 
                // TODO: if the query is going only against the revision table, should this be done?
-               $this->selectNamedDB( 'contributions', DB_SLAVE, 'contributions' );
+               $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
 
                $this->idMode = false;
                if ( isset( $this->params['userprefix'] ) ) {
index e2599d1..c30f0cf 100644 (file)
@@ -57,7 +57,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
         * @return void
         */
        private function run( $resultPageSet = null ) {
-               $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
+               $this->selectNamedDB( 'watchlist', DB_REPLICA, 'watchlist' );
 
                $params = $this->extractRequestParams();
 
index 00846f5..e308ba4 100644 (file)
@@ -415,7 +415,7 @@ class ApiResult implements ApiSerializable {
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
                                /// @todo Add i18n message when replacing calls to ->setWarning()
                                $msg = new ApiRawMessage( 'This result was truncated because it would otherwise ' .
-                                       ' be larger than the limit of $1 bytes', 'truncatedresult' );
+                                       'be larger than the limit of $1 bytes', 'truncatedresult' );
                                $msg->numParams( $this->maxSize );
                                $this->errorFormatter->addWarning( 'result', $msg );
                                return false;
index 297c0fb..5a2492d 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Prepare an edit in shared cache so that it can be reused on edit
@@ -62,6 +63,8 @@ class ApiStashEdit extends ApiBase {
                        $this->dieUsage( 'Unsupported content model/format', 'badmodelformat' );
                }
 
+               $text = null;
+               $textHash = null;
                if ( strlen( $params['stashedtexthash'] ) ) {
                        // Load from cache since the client indicates the text is the same as last stash
                        $textHash = $params['stashedtexthash'];
@@ -138,7 +141,8 @@ class ApiStashEdit extends ApiBase {
                        $cache->set( $textKey, $text, self::MAX_CACHE_TTL );
                }
 
-               $this->getStats()->increment( "editstash.cache_stores.$status" );
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+               $stats->increment( "editstash.cache_stores.$status" );
 
                $this->getResult()->addValue(
                        null,
@@ -171,6 +175,7 @@ class ApiStashEdit extends ApiBase {
                        // De-duplicate requests on the same key
                        return self::ERROR_BUSY;
                }
+               /** @noinspection PhpUnusedLocalVariableInspection */
                $unlocker = new ScopedCallback( function () use ( $dbw, $key ) {
                        $dbw->unlock( $key, __METHOD__ );
                } );
@@ -246,7 +251,7 @@ class ApiStashEdit extends ApiBase {
 
                $cache = ObjectCache::getLocalClusterInstance();
                $logger = LoggerFactory::getInstance( 'StashEdit' );
-               $stats = RequestContext::getMain()->getStats();
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
 
                $key = self::getStashKey( $title, self::getContentHash( $content ), $user );
                $editInfo = $cache->get( $key );
@@ -256,7 +261,7 @@ class ApiStashEdit extends ApiBase {
                        // so as to use its results and make use of the time spent parsing.
                        // Skip this logic if there no master connection in case this method
                        // is called on an HTTP GET request for some reason.
-                       $lb = wfGetLB();
+                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
                        $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
                        if ( $dbw && $dbw->lock( $key, __METHOD__, 30 ) ) {
                                $editInfo = $cache->get( $key );
@@ -313,7 +318,7 @@ class ApiStashEdit extends ApiBase {
         * @return string|null TS_MW timestamp or null
         */
        private static function lastEditTime( User $user ) {
-               $time = wfGetDB( DB_SLAVE )->selectField(
+               $time = wfGetDB( DB_REPLICA )->selectField(
                        'recentchanges',
                        'MAX(rc_timestamp)',
                        [ 'rc_user_text' => $user->getName() ],
index c2d1e0d..f88c2db 100644 (file)
@@ -63,7 +63,7 @@ class ApiTag extends ApiBase {
        }
 
        protected static function validateLogId( $logid ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->selectField( 'logging', 'log_id', [ 'log_id' => $logid ],
                        __METHOD__ );
                return (bool)$result;
index f7ce552..7b44f40 100644 (file)
@@ -109,16 +109,19 @@ class ApiUpload extends ApiBase {
                // Get the result based on the current upload context:
                try {
                        $result = $this->getContextResult();
-                       if ( $result['result'] === 'Success' ) {
-                               $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
-                       }
                } catch ( UploadStashException $e ) { // XXX: don't spam exception log
                        list( $msg, $code ) = $this->handleStashException( get_class( $e ), $e->getMessage() );
                        $this->dieUsage( $msg, $code );
                }
-
                $this->getResult()->addValue( null, $this->getModuleName(), $result );
 
+               // Add 'imageinfo' in a separate addValue() call. File metadata can be unreasonably large,
+               // so otherwise when it exceeded $wgAPIMaxResultSize, no result would be returned (T143993).
+               if ( $result['result'] === 'Success' ) {
+                       $imageinfo = $this->mUpload->getImageInfo( $this->getResult() );
+                       $this->getResult()->addValue( $this->getModuleName(), 'imageinfo', $imageinfo );
+               }
+
                // Cleanup any temporary mess
                $this->mUpload->cleanupTempFile();
        }
@@ -445,7 +448,18 @@ class ApiUpload extends ApiBase {
                                }
                        }
                        unset( $progress['status'] ); // remove Status object
+                       $imageinfo = null;
+                       if ( isset( $progress['imageinfo'] ) ) {
+                               $imageinfo = $progress['imageinfo'];
+                               unset( $progress['imageinfo'] );
+                       }
+
                        $this->getResult()->addValue( null, $this->getModuleName(), $progress );
+                       // Add 'imageinfo' in a separate addValue() call. File metadata can be unreasonably large,
+                       // so otherwise when it exceeded $wgAPIMaxResultSize, no result would be returned (T143993).
+                       if ( $imageinfo ) {
+                               $this->getResult()->addValue( $this->getModuleName(), 'imageinfo', $imageinfo );
+                       }
 
                        return false;
                }
@@ -690,6 +704,30 @@ class ApiUpload extends ApiBase {
                                        : $warning['file'];
                                $warnings[$warning['warning']] = $localFile->getName();
                        }
+
+                       if ( isset( $warnings['no-change'] ) ) {
+                               /** @var File $file */
+                               $file = $warnings['no-change'];
+                               unset( $warnings['no-change'] );
+
+                               $warnings['nochange'] = [
+                                       'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() )
+                               ];
+                       }
+
+                       if ( isset( $warnings['duplicate-version'] ) ) {
+                               $dupes = [];
+                               /** @var File $dupe */
+                               foreach ( $warnings['duplicate-version'] as $dupe ) {
+                                       $dupes[] = [
+                                               'timestamp' => wfTimestamp( TS_ISO_8601, $dupe->getTimestamp() )
+                                       ];
+                               }
+                               unset( $warnings['duplicate-version'] );
+
+                               ApiResult::setIndexedTagName( $dupes, 'ver' );
+                               $warnings['duplicateversions'] = $dupes;
+                       }
                }
 
                return $warnings;
index 7cc2db3..2396f69 100644 (file)
        "api-help-param-deprecated": "Zastaralý.",
        "api-help-param-required": "Tento parametr je povinný.",
        "api-help-datatypes-header": "Datové typy",
-       "api-help-datatypes": "Některé typy parametrů v API potřebují bližší vysvětlení:\n;boolean\n:Booleovské parametry fungují jako zaškrtávací políčka v HTML: pokud je parametr uveden, bez ohledu na hodnotu, je považován za pravdivý. Pro nepravdivou hodnotu parametr zcela vynechte.\n;časová značka\n:Časové značky lze uvádět v několika formátech. Doporučuje se datum a čas podle ISO 8601. Všechny časy jsou v UTC a obsažené časové pásmo je ignorováno.\n:* Datum a čas podle ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (interpunkce a <kbd>Z</kbd> jsou nepovinné)\n:* Datum a čas podle ISO 8601 s (ignorovaným) zlomkem sekundy, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (pomlčky, dvojtečky a <kbd>Z</kbd> jsou nepovinné)\n:* Formát MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Obecný číselný formát, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (nepovinné časové pásmo <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> nebo <kbd>-<var>##</var></kbd> se ignoruje)\n:* Formát EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle RFC 2822 (časové pásmo lze vynechat), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle RFC 850 (časové pásmo lze vynechat), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle céčkové funkce ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Sekundy od 1970-01-01T00:00:00Z jako celé číslo o 1–13 číslicích (s výjimkou <kbd>0</kbd>)\n:* Řetězec <kbd>now</kbd>",
+       "api-help-datatypes": "Vstupem do MediaWiki by mělo být UTF-8 normalizované do NFC. Jiný vstup se MediaWiki může pokusit převést, ale tím se může stát, že některé operace (např. [[Special:ApiHelp/edit|editace]] s kontrolou MD5) selžou.\n\nNěkteré typy parametrů v API potřebují bližší vysvětlení:\n;boolean\n:Booleovské parametry fungují jako zaškrtávací políčka v HTML: pokud je parametr uveden, bez ohledu na hodnotu, je považován za pravdivý. Pro nepravdivou hodnotu parametr zcela vynechte.\n;časová značka\n:Časové značky lze uvádět v několika formátech. Doporučuje se datum a čas podle ISO 8601. Všechny časy jsou v UTC a obsažené časové pásmo je ignorováno.\n:* Datum a čas podle ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (interpunkce a <kbd>Z</kbd> jsou nepovinné)\n:* Datum a čas podle ISO 8601 s (ignorovaným) zlomkem sekundy, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (pomlčky, dvojtečky a <kbd>Z</kbd> jsou nepovinné)\n:* Formát MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Obecný číselný formát, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (nepovinné časové pásmo <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> nebo <kbd>-<var>##</var></kbd> se ignoruje)\n:* Formát EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle RFC 2822 (časové pásmo lze vynechat), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle RFC 850 (časové pásmo lze vynechat), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formát podle céčkové funkce ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Sekundy od 1970-01-01T00:00:00Z jako celé číslo o 1–13 číslicích (s výjimkou <kbd>0</kbd>)\n:* Řetězec <kbd>now</kbd>\n;alternativní oddělovač vícenásobných hodnot\n:Parametry, které přijímají několik hodnot, se zpravidla předávají s hodnotami oddělenými svislítkem, např. <kbd>param=hodnota1|hodnota2</kbd> nebo <kbd>param=hodnota1%7Chodnota2</kbd>. Pokud musí hodnota obsahovat svislítko, použijte jako oddělovač znak U+001F (Unit Separator) ''a'' před hodnotu přidejte U+001F, např. <kbd>param=%1Fhodnota1%1Fhodnota2</kbd>.",
        "api-help-param-type-integer": "Typ: {{PLURAL:$1|1=celé číslo|2=seznam celých čísel}}",
        "api-help-param-type-boolean": "Typ: boolean ([[Special:ApiHelp/main#main/datatypes|podrobnosti]])",
-       "api-help-param-list": "{{PLURAL:$1|1=Jedna z následujících hodnot|2=Hodnoty (oddělené <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Jedna z následujících hodnot|2=Hodnoty (oddělené <kbd>{{!}}</kbd> nebo [[Special:ApiHelp/main#main/datatypes|alternativou]].)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Musí být prázdné|Může být prázdné nebo $2}}",
        "api-help-param-limit": "Není dovoleno více než $1.",
        "api-help-param-limit2": "Není dovoleno více než $1 ($2 pro boty).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Hodnota nesmí|2=Hodnoty nesmějí}} být vyšší než $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Hodnota|2=Hodnoty}} musí ležet mezi $2 a $3.",
        "api-help-param-upload": "Musí se odeslat POST požadavkem jako načítaný soubor pomocí multipart/form-data.",
-       "api-help-param-multi-separate": "Hodnoty oddělujte pomocí <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Hodnoty oddělujte pomocí <kbd>|</kbd> nebo [[Special:ApiHelp/main#main/datatypes|alternativou]].",
        "api-help-param-multi-max": "Maximální počet hodnot je {{PLURAL:$1|$1}} (pro boty {{PLURAL:$2|$2}}).",
        "api-help-param-default": "Implicitní hodnota: $1",
        "api-help-param-default-empty": "Implicitní hodnota: <span class=\"apihelp-empty\">(prázdné)</span>",
        "api-help-permissions": "{{PLURAL:$1|Oprávnění}}:",
        "api-help-permissions-granted-to": "Uděleno {{PLURAL:$1|skupině|skupinám}}: $2",
        "api-help-right-apihighlimits": "Používání vyšších limitů v API dotazech (pomalé dotazy: $1, rychlé dotazy: $2). Limity pro pomalé dotazy se vztahují i na vícehodnotové parametry.",
+       "api-help-open-in-apisandbox": "<small>[otevřít v pískovišti]</small>",
        "api-credits-header": "Zásluhy",
        "api-credits": "Vývojáři API:\n* Roan Kattouw (hlavní vývojář září 2007–2009)\n* Viktor Vasiljev\n* Bryan Tong Minh\n* Sam Reed\n* Jurij Astrachan (tvůrce, hlavní vývojář září 2006–září 2007)\n* Brad Jorsch (hlavní vývojář od 2013)\n\nSvé komentáře, návrhy či dotazy posílejte na mediawiki-api@lists.wikimedia.org\nnebo založte chybové hlášení na https://phabricator.wikimedia.org/."
 }
index e293fed..41f1ff9 100644 (file)
        "apihelp-options-example-change": "Ändert die Einstellungen <kbd>skin</kbd> und <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Setzt alle Einstellungen zurück, dann <kbd>skin</kbd> und <kbd>nickname</kbd> festlegen.",
        "apihelp-paraminfo-description": "Ruft Informationen über API-Module ab.",
-       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der <var>action</var>- und <var>format</var>-Parameters, oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> bestimmen.",
+       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der Parameter <var>action</var> und <var>format</var> oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> oder alle Untermodule mit <kbd>+*</kbd> oder alle Untermodule rekursiv mit <kbd>+**</kbd> bestimmen.",
        "apihelp-paraminfo-param-helpformat": "Format der Hilfe-Zeichenfolgen.",
        "apihelp-paraminfo-param-querymodules": "Liste von Abfragemodulnamen (Werte von <var>prop</var>-, <var>meta</var>- oder <var>list</var>-Parameter). Benutze <kbd>$1modules=query+foo</kbd> anstatt <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Auch Informationen über die Hauptmodule (top-level) erhalten. Benutze <kbd>$1modules=main</kbd> stattdessen.",
        "apihelp-query+allusers-param-witheditsonly": "Listet nur Benutzer auf, die Bearbeitungen vorgenommen haben.",
        "apihelp-query+allusers-param-activeusers": "Listet nur Benutzer auf, die in den letzten $1 {{PLURAL:$1|Tag|Tagen}} aktiv waren.",
        "apihelp-query+allusers-example-Y": "Benutzer ab <kbd>Y</kbd> auflisten.",
+       "apihelp-query+authmanagerinfo-example-login": "Ruft die Anfragen ab, die beim Beginnen einer Anmeldung verwendet werden können.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Ruft die Anfragen ab, die beim Beginnen einer Anmeldung verwendet werden können, mit zusammengeführten Formularfeldern.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Testet, ob die Authentifizierung für die Aktion <kbd>foo</kbd> ausreichend ist.",
        "apihelp-query+backlinks-description": "Alle Seiten finden, die auf die angegebene Seite verlinken.",
        "apihelp-query+backlinks-param-title": "Zu suchender Titel. Darf nicht zusammen mit <var>$1pageid</var> benutzt werden.",
        "apihelp-query+backlinks-param-pageid": "Zu suchende Seiten-ID. Darf nicht zusammen mit <var>$1title</var> benutzt werden.",
        "apihelp-query+exturlusage-param-query": "Suchbegriff ohne Protokoll. Siehe [[Special:LinkSearch]]. Leer lassen, um alle externen Verknüpfungen aufzulisten.",
        "apihelp-query+exturlusage-param-namespace": "Die aufzulistenden Seiten-Namensräume.",
        "apihelp-query+exturlusage-param-limit": "Wie viele Seiten zurückgegeben werden sollen.",
+       "apihelp-query+exturlusage-example-simple": "Zeigt Seiten, die auf <kbd>http://www.mediawiki.org</kbd> verlinken.",
        "apihelp-query+filearchive-description": "Alle gelöschten Dateien der Reihe nach auflisten.",
        "apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
        "apihelp-query+watchlist-paramvalue-prop-sizes": "Ergänzt die alten und neuen Längen der Seite.",
        "apihelp-query+watchlist-paramvalue-type-new": "Seitenerstellungen.",
        "apihelp-query+watchlist-paramvalue-type-log": "Logbucheinträge.",
+       "apihelp-query+watchlistraw-description": "Ruft alle Seiten der Beobachtungsliste des aktuellen Benutzers ab.",
        "apihelp-query+watchlistraw-param-prop": "Zusätzlich zurückzugebende Eigenschaften:",
+       "apihelp-query+watchlistraw-param-fromtitle": "Titel (mit Namensraum-Präfix), bei dem die Aufzählung beginnen soll.",
+       "apihelp-query+watchlistraw-param-totitle": "Titel (mit Namensraum-Präfix), bei dem die Aufzählung enden soll.",
        "apihelp-resetpassword-param-user": "Benutzer, der zurückgesetzt werden soll.",
+       "apihelp-revisiondelete-description": "Löscht und stellt Versionen wieder her.",
+       "apihelp-revisiondelete-param-hide": "Was für jede Version versteckt werden soll.",
+       "apihelp-revisiondelete-param-show": "Was für jede Version wieder eingeblendet werden soll.",
        "apihelp-rsd-description": "Ein RSD-Schema (Really Simple Discovery) exportieren.",
        "apihelp-rsd-example-simple": "Das RSD-Schema exportieren",
        "apihelp-setnotificationtimestamp-param-entirewatchlist": "An allen beobachteten Seiten arbeiten.",
        "apihelp-stashedit-param-sectiontitle": "Der Titel für einen neuen Abschnitt.",
        "apihelp-stashedit-param-text": "Seiteninhalt.",
        "apihelp-stashedit-param-stashedtexthash": "Stattdessen zu verwendende Prüfsumme des Seiteninhalts von einem vorherigen Speicher.",
+       "apihelp-stashedit-param-contentmodel": "Inhaltsmodell des neuen Inhalts.",
        "apihelp-stashedit-param-summary": "Änderungszusammenfassung.",
        "apihelp-tag-param-reason": "Grund für die Änderung.",
        "apihelp-tokens-param-type": "Abzufragende Tokentypen.",
        "api-help-param-type-boolean": "Typ: boolisch ([[Special:ApiHelp/main#main/datatypes|Einzelheiten]])",
        "api-help-param-type-timestamp": "Typ: {{PLURAL:$1|1=Zeitstempel|2=Liste von Zeitstempeln}} ([[Special:ApiHelp/main#main/datatypes|erlaubte Formate]])",
        "api-help-param-type-user": "Typ: {{PLURAL:$1|1=Benutzername|2=Liste von Benutzernamen}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Einer der folgenden Werte|2=Werte (mit <kbd>{{!}}</kbd> trennen)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Einer der folgenden Werte|2=Werte (mit <kbd>{{!}}</kbd> trennen oder [[Special:ApiHelp/main#main/datatypes|Alternative]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Muss leer sein|Kann leer sein oder $2}}",
        "api-help-param-limit": "Nicht mehr als $1 erlaubt.",
        "api-help-param-limit2": "Nicht mehr als $1 ($2 für Bots) erlaubt.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Der Wert darf|2=Die Werte dürfen}} nicht größer sein als $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Der Wert muss|2=Die Werte müssen}} zwischen $2 und $3 sein.",
        "api-help-param-upload": "Muss als Dateiupload mithilfe eines multipart/form-data-Formular bereitgestellt werden.",
-       "api-help-param-multi-separate": "Werte mit <kbd>|</kbd> trennen.",
+       "api-help-param-multi-separate": "Werte mit <kbd>|</kbd> trennen oder [[Special:ApiHelp/main#main/datatypes|Alternative]].",
        "api-help-param-multi-max": "Maximale Anzahl der Werte ist {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} für Bots).",
        "api-help-param-default": "Standard: $1",
        "api-help-param-default-empty": "Standard: <span class=\"apihelp-empty\">(leer)</span>",
index 7892efa..0b86f10 100644 (file)
@@ -6,6 +6,7 @@
                        "Kumkumuk"
                ]
        },
+       "apihelp-main-param-action": "Performansa kamci aksiyon",
        "apihelp-block-description": "Enê karberi bloqe ke",
        "apihelp-block-param-reason": "Sebeba Bloqey",
        "apihelp-block-param-nocreate": "Hesab viraştişi bloqe ke.",
index 1815836..40388f9 100644 (file)
        "apihelp-options-example-complex": "Reset all preferences, then set <kbd>skin</kbd> and <kbd>nickname</kbd>.",
 
        "apihelp-paraminfo-description": "Obtain information about API modules.",
-       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>, or all submodules with <kbd>+*</kbd>, or all submodules recursively with <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format of help strings.",
        "apihelp-paraminfo-param-querymodules": "List of query module names (value of <var>prop</var>, <var>meta</var> or <var>list</var> parameter). Use <kbd>$1modules=query+foo</kbd> instead of <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Get information about the main (top-level) module as well. Use <kbd>$1modules=main</kbd> instead.",
        "apihelp-paraminfo-param-pagesetmodule": "Get information about the pageset module (providing titles= and friends) as well.",
        "apihelp-paraminfo-param-formatmodules": "List of format module names (value of <var>format</var> parameter). Use <var>$1modules</var> instead.",
        "apihelp-paraminfo-example-1": "Show info for <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, and <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Show info for all submodules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
 
        "apihelp-parse-description": "Parses content and returns parser output.\n\nSee the various prop-modules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd> to get information from the current version of a page.\n\nThere are several ways to specify the text to parse:\n# Specify a page or revision, using <var>$1page</var>, <var>$1pageid</var>, or <var>$1oldid</var>.\n# Specify content explicitly, using <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Specify only a summary to parse. <var>$1prop</var> should be given an empty value.",
        "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, <var>$1contentmodel</var> must be specified, and [[API]] will be used as the title.",
        "apihelp-query+authmanagerinfo-description": "Retrieve information about the current authentication status.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Test whether the user's current authentication status is sufficient for the specified security-sensitive operation.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Fetch information about the authentication requests needed for the specified authentication action.",
-       "apihelp-query+filerepoinfo-example-login": "Fetch the requests that may be used when beginning a login.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Fetch the requests that may be used when beginning a login, with form fields merged.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Test whether authentication is sufficient for action <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Fetch the requests that may be used when beginning a login.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Fetch the requests that may be used when beginning a login, with form fields merged.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Test whether authentication is sufficient for action <kbd>foo</kbd>.",
 
        "apihelp-query+backlinks-description": "Find all pages that link to the given page.",
        "apihelp-query+backlinks-param-title": "Title to search. Cannot be used together with <var>$1pageid</var>.",
        "api-help-param-deprecated": "Deprecated.",
        "api-help-param-required": "This parameter is required.",
        "api-help-datatypes-header": "Data types",
-       "api-help-datatypes": "Some parameter types in API requests need further explanation:\n;boolean\n:Boolean parameters work like HTML checkboxes: if the parameter is specified, regardless of value, it is considered true. For a false value, omit the parameter entirely.\n;timestamp\n:Timestamps may be specified in several formats. ISO 8601 date and time is recommended. All times are in UTC, any included timezone is ignored.\n:* ISO 8601 date and time, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (punctuation and <kbd>Z</kbd> are optional)\n:* ISO 8601 date and time with (ignored) fractional seconds, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (dashes, colons, and <kbd>Z</kbd> are optional)\n:* MediaWiki format, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Generic numeric format, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (optional timezone of <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> is ignored)\n:* EXIF format, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*RFC 2822 format (timezone may be omitted), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 format (timezone may be omitted), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime format, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Seconds since 1970-01-01T00:00:00Z as a 1 to 13 digit integer (excluding <kbd>0</kbd>)\n:* The string <kbd>now</kbd>",
+       "api-help-datatypes": "Input to MediaWiki should be NFC-normalized UTF-8. MediaWiki may attempt to convert other input, but this may cause some operations (such as [[Special:ApiHelp/edit|edits]] with MD5 checks) to fail.\n\nSome parameter types in API requests need further explanation:\n;boolean\n:Boolean parameters work like HTML checkboxes: if the parameter is specified, regardless of value, it is considered true. For a false value, omit the parameter entirely.\n;timestamp\n:Timestamps may be specified in several formats. ISO 8601 date and time is recommended. All times are in UTC, any included timezone is ignored.\n:* ISO 8601 date and time, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (punctuation and <kbd>Z</kbd> are optional)\n:* ISO 8601 date and time with (ignored) fractional seconds, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (dashes, colons, and <kbd>Z</kbd> are optional)\n:* MediaWiki format, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Generic numeric format, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (optional timezone of <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> is ignored)\n:* EXIF format, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*RFC 2822 format (timezone may be omitted), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 format (timezone may be omitted), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime format, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Seconds since 1970-01-01T00:00:00Z as a 1 to 13 digit integer (excluding <kbd>0</kbd>)\n:* The string <kbd>now</kbd>\n;alternative multiple-value separator\n:Parameters that take multiple values are normally submitted with the values separated using the pipe character, e.g. <kbd>param=value1|value2</kbd> or <kbd>param=value1%7Cvalue2</kbd>. If a value must contain the pipe character, use U+001F (Unit Separator) as the separator ''and'' prefix the value with U+001F, e.g. <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "Type: integer or <kbd>max</kbd>",
        "api-help-param-type-integer": "Type: {{PLURAL:$1|1=integer|2=list of integers}}",
        "api-help-param-type-boolean": "Type: boolean ([[Special:ApiHelp/main#main/datatypes|details]])",
        "api-help-param-type-password": "",
        "api-help-param-type-timestamp": "Type: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
        "api-help-param-type-user": "Type: {{PLURAL:$1|1=user name|2=list of user names}}",
-       "api-help-param-list": "{{PLURAL:$1|1=One of the following values|2=Values (separate with <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=One of the following values|2=Values (separate with <kbd>{{!}}</kbd> or [[Special:ApiHelp/main#main/datatypes|alternative]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Must be empty|Can be empty, or $2}}",
        "api-help-param-limit": "No more than $1 allowed.",
        "api-help-param-limit2": "No more than $1 ($2 for bots) allowed.",
        "api-help-param-integer-max": "The {{PLURAL:$1|1=value|2=values}} must be no greater than $3.",
        "api-help-param-integer-minmax": "The {{PLURAL:$1|1=value|2=values}} must be between $2 and $3.",
        "api-help-param-upload": "Must be posted as a file upload using multipart/form-data.",
-       "api-help-param-multi-separate": "Separate values with <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separate values with <kbd>|</kbd> or [[Special:ApiHelp/main#main/datatypes|alternative]].",
        "api-help-param-multi-max": "Maximum number of values is {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} for bots).",
        "api-help-param-default": "Default: $1",
        "api-help-param-default-empty": "Default: <span class=\"apihelp-empty\">(empty)</span>",
index b8756c5..8ed1cf5 100644 (file)
                        "Copper12"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|Preguntas frecuentes]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de correos]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API de anuncios]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Errores y peticiones]\n</div>\n<strong>Estado:</strong> Todas las características que se muestran en esta página debería funcionar, pero la API aún está en desarrollo activo y puede cambiar en cualquier momento. Suscríbete a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la lista de correo de mediawiki-api-announce] para estar al día de las actualizaciones.\n\n<strong>Solicitudes erróneas:</strong> Cuando se envían solicitudes erróneas a la API, se envía un encabezado HTTP con la clave \"MediaWiki-API-Error\" y ambos valores, del encabezado y el código de error, se establecerán en el mismo valor. Para más información, véase [[mw:API:Errors_and_warnings|API: Errores y advertencias]].\n\n<strong>Pruebas:</strong> para facilitar las pruebas de solicitudes a la API, consulta [[Special:ApiSandbox]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|Preguntas frecuentes]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de correo]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anuncios de la API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Errores y peticiones]\n</div>\n<strong>Estado:</strong> Todas las características que se muestran en esta página deberían funcionar, pero la API aún se encuentra en desarrollo activo y puede cambiar en cualquier momento. Suscríbete a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la lista de correo de mediawiki-api-announce] para estar al día de las actualizaciones.\n\n<strong>Solicitudes erróneas:</strong> Cuando se envían solicitudes erróneas a la API, se envía una cabecera HTTP con la clave \"MediaWiki-API-Error\". El valor de la cabecera y el código de error devuelto tomarán el mismo valor. Para más información, véase [[mw:API:Errors_and_warnings|API: Errores y advertencias]].\n\n<strong>Pruebas:</strong> para facilitar las pruebas de solicitudes a la API, consulta [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Qué acción se realizará.",
        "apihelp-main-param-format": "El formato de la salida.",
-       "apihelp-main-param-maxlag": "El máximo retraso puede ser utilizado cuando MediaWiki está instalado en una base de datos replicada clúster. Para guardar las acciones que causan más de replicación de sitios de retraso, este parámetro puede hacer que el cliente espere hasta que el retraso de la replicación es menor que el valor especificado. En caso de exceso de lag, código de error <samp>maxlag</samp> se devuelve con un mensaje parecido a <samp>la Espera de $host: $lag segundos quedado</samp>.<br />Véase [[mw:Manual:Maxlag_parameter|Manual: Maxlag parámetro]] para más información.",
+       "apihelp-main-param-maxlag": "El retraso (lag) máximo puede ser utilizado cuando MediaWiki está instalado en un conjunto de bases de datos replicadas. Para evitar cualquier acción que pudiera causar un retraso aún mayor en la replicación del sitio, este parámetro puede causar que el cliente espere hasta que el retraso de replicación sea menor que el valor especificado. En caso de exceso de retraso, se devuelve un código de error <samp>maxlag</samp> con un mensaje similar a <samp>Esperando a $host: $lag segundos de retraso</samp>.<br />Véase [[mw:Manual:Maxlag_parameter|Manual:Parámetro maxlag]] para más información.",
        "apihelp-main-param-smaxage": "Establece el encabezado HTTP <code>s-maxage</code> de control de caché a esta cantidad de segundos. Los errores nunca se almacenan en caché.",
        "apihelp-main-param-maxage": "Establece el encabezado HTTP <code>max-age</code> de control de caché a esta cantidad de segundos. Los errores nunca se almacenan en caché.",
        "apihelp-main-param-assert": "Comprobar que el usuario haya iniciado sesión si el valor es <kbd>user</kbd> o si tiene el permiso de bot si es <kbd>bot</kbd>.",
        "apihelp-main-param-requestid": "Cualquier valor dado aquí se incluirá en la respuesta. Se puede utilizar para distinguir solicitudes.",
        "apihelp-main-param-servedby": "Incluir el nombre del host que ha servido la solicitud en los resultados.",
        "apihelp-main-param-curtimestamp": "Incluir la marca de tiempo actual en el resultado.",
-       "apihelp-main-param-origin": "Cuando se accede a la API usando una petición AJAX de distinto dominio (CORS), se establece este valor al dominio de origen. Debe ser incluido en cualquier petición pre-vuelo, y por lo tanto debe ser parte de la URI de la petición (no del cuerpo POST). Debe coincidir exactamente con uno de los orígenes de la cabecera <code>Origin</code>, por lo que debería ser algo como <kbd>https://en.wikipedia.org</kbd> o <kbd>https://meta.wikimedia.org</kbd>. Si este parámetro no coincide con la cabecera <code>Origin</code>, se devolverá una respuesta 403.\nSi este parámetro coincide con la cabecera <code>Origin</code> y el origen está en lista blanca, se creará una cabecera <code>Access-Control-Allow-Origin</code>.",
-       "apihelp-main-param-uselang": "El idioma que se usará para las traducciones de mensajes. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> con <kbd>siprop=languages</kbd> devuelve una lista de códigos de idiomas, o especifica <kbd>user</kbd> para usar la preferencia de idioma del usuario actual, o especifica <kbd>content</kbd> para usar el idioma de contenido de este wiki.",
+       "apihelp-main-param-origin": "Cuando se accede a la API usando una petición AJAX de distinto dominio (CORS), se establece este valor al dominio de origen. Debe ser incluido en cualquier petición pre-vuelo, y por lo tanto debe ser parte de la URI de la petición (no del cuerpo POST).\n\nEn las peticiones con autenticación, debe coincidir exactamente con uno de los orígenes de la cabecera <code>Origin</code>, por lo que debería ser algo como <kbd>https://en.wikipedia.org</kbd> o <kbd>https://meta.wikimedia.org</kbd>. Si este parámetro no coincide con la cabecera <code>Origin</code>, se devolverá una respuesta 403. Si este parámetro coincide con la cabecera <code>Origin</code> y el origen está en la lista blanca, se creará una cabecera <code>Access-Control-Allow-Origin</code>.\n\nEn las peticiones sin autenticación, introduce el valor <kbd>*</kbd>. Esto creará una cabecera <code>Access-Control-Allow-Origin</code>, pero el valor de <code>Access-Control-Allow-Credentials</code> será <code>false</code> y todos los datos que dependan del usuario estarán restringidos.",
+       "apihelp-main-param-uselang": "El idioma que se utilizará para las traducciones de mensajes. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> con <kbd>siprop=languages</kbd> devuelve una lista de códigos de idiomas. También puedes introducir <kbd>user</kbd> para usar la preferencia de idioma del usuario actual, o <kbd>content</kbd> para usar el idioma de contenido de este wiki.",
        "apihelp-block-description": "Bloquear a un usuario.",
        "apihelp-block-param-user": "El nombre de usuario, dirección IP o intervalo de IP que quieres bloquear.",
        "apihelp-block-param-expiry": "Fecha de expiración. Puede ser relativa (por ejemplo, <kbd>5 months</kbd> o <kbd>2 weeks</kbd>) o absoluta (por ejemplo, <kbd>2014-09-18T12:34:56Z</kbd>). Si se establece en <kbd>infinite</kbd>, <kbd>indefinite</kbd>, o <kbd>never</kbd>, el bloqueo será permanente.",
        "apihelp-query+alllinks-example-unique-generator": "Obtiene todos los títulos enlazados, marcando los que falten.",
        "apihelp-query+alllinks-example-generator": "Obtiene páginas que contienen los enlaces.",
        "apihelp-query+allmessages-description": "Devolver los mensajes de este sitio.",
-       "apihelp-query+allmessages-param-messages": "Qué mensajes mostrar. <kbd>*</kbd> (por defecto) significa todos los mensajes.",
+       "apihelp-query+allmessages-param-messages": "Qué mensajes mostrar. <kbd>*</kbd> (predeterminado) significa todos los mensajes.",
        "apihelp-query+allmessages-param-prop": "Qué propiedades se obtendrán.",
        "apihelp-query+allmessages-param-enableparser": "Establecer para habilitar el analizador, se preprocesará el wikitexto del mensaje (sustitución de palabras mágicas, uso de plantillas, etc.).",
        "apihelp-query+allmessages-param-nocontent": "Si se establece, no incluya el contenido de los mensajes en la salida.",
        "apihelp-query+allusers-param-limit": "Cuántos nombres de usuario se devolverán.",
        "apihelp-query+allusers-param-activeusers": "Solo listar usuarios activos en {{PLURAL:$1|el último día|los $1 últimos días}}.",
        "apihelp-query+allusers-example-Y": "Listar usuarios que empiecen por <kbd>Y</kbd>.",
-       "apihelp-query+filerepoinfo-example-login": "Captura de las solicitudes que puede ser utilizadas al comienzo de inicio de sesión.",
+       "apihelp-query+authmanagerinfo-example-login": "Captura de las solicitudes que puede ser utilizadas al comienzo de inicio de sesión.",
        "apihelp-query+backlinks-description": "Encuentra todas las páginas que enlazan a la página dada.",
        "apihelp-query+backlinks-param-pageid": "Identificador de página que buscar. No puede usarse junto con <var>$1title</var>",
        "apihelp-query+backlinks-param-filterredir": "Cómo filtrar redirecciones. Si se establece a <kbd>nonredirects</kbd> cuando está activo <var>$1redirect</var>, esto sólo se aplica al segundo nivel.",
        "apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "Añade el comentario analizado para la edición.",
        "apihelp-query+recentchanges-paramvalue-prop-flags": "Añade marcas para la edición.",
        "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Añade la marca de tiempo de la edición.",
-       "apihelp-query+recentchanges-paramvalue-prop-title": "Añade el título de la página de la edición.",
+       "apihelp-query+recentchanges-paramvalue-prop-title": "Añade el título de página de la edición.",
        "apihelp-query+recentchanges-paramvalue-prop-ids": "Añade los códigos ID de la página, de los cambios recientes y de las revisiones antigua y nueva.",
        "apihelp-query+recentchanges-paramvalue-prop-sizes": "Añade la longitud antigua y la longitud nueva de la página en bytes.",
        "apihelp-query+recentchanges-paramvalue-prop-redirect": "Etiqueta la edición si la página es una redirección.",
        "apihelp-query+redirects-param-namespace": "Incluir solo páginas de estos espacios de nombres.",
        "apihelp-query+redirects-param-limit": "Cuántas redirecciones se devolverán.",
        "apihelp-query+redirects-example-simple": "Mostrar una lista de las redirecciones a la [[Main Page|Portada]]",
-       "apihelp-query+redirects-example-generator": "Obtener información sobre todas las redirecciones a la [[Main Page|Portada]]",
+       "apihelp-query+redirects-example-generator": "Obtener información sobre todas las redirecciones a la [[Main Page|Portada]].",
        "apihelp-query+revisions-param-end": "Enumerar hasta esta marca de tiempo.",
        "apihelp-query+revisions-param-user": "Incluir solo las revisiones realizadas por el usuario.",
        "apihelp-query+revisions-param-excludeuser": "Excluir las revisiones realizadas por el usuario.",
        "apihelp-watch-example-watch": "Vigilar la página <kbd>Main Page</kbd>.",
        "apihelp-watch-example-unwatch": "Dejar de vigilar la <kbd>Main Page</kbd>.",
        "apihelp-format-example-generic": "Devolver el resultado de la consulta en formato $1.",
+       "apihelp-json-description": "Extraer los datos de salida en formato JSON.",
+       "apihelp-json-param-callback": "Si se especifica, envuelve la salida dentro de una llamada a una función dada. Por motivos de seguridad, cualquier dato específico del usuario estará restringido.",
+       "apihelp-json-param-utf8": "Si se especifica, codifica la mayoría (pero no todos) de los caracteres no pertenecientes a ASCII como UTF-8 en lugar de reemplazarlos por secuencias de escape hexadecimal. Toma el comportamiento por defecto si <var>formatversion</var> no es <kbd>1</kbd>.",
+       "apihelp-json-param-ascii": "Si se especifica, codifica todos los caracteres no pertenecientes a ASCII mediante secuencias de escape hexadecimal. Toma el comportamiento por defecto si <var>formatversion</var> no es <kbd>1</kbd>.",
+       "apihelp-json-param-formatversion": "Formato de salida:\n;1: Formato retrocompatible (booleanos con estilo XML, claves <samp>*</samp> para nodos de contenido, etc.).\n;2: Formato moderno experimental. ¡Atención, las especificaciones pueden cambiar!\n;latest: Utiliza el último formato (actualmente <kbd>2</kbd>). Puede cambiar sin aviso.",
+       "apihelp-none-description": "No extraer nada.",
+       "apihelp-php-description": "Extraer los datos de salida en formato serializado PHP.",
+       "apihelp-rawfm-description": "Extraer los datos de salida, incluidos los elementos de depuración, en formato JSON (embellecido en HTML).",
+       "apihelp-xml-param-xslt": "Si se especifica, añade la página nombrada como una hoja de estilo XSL. El valor debe ser un título en el espacio de nombres {{ns:mediawiki}} que termine en <code>.xsl</code>.",
+       "apihelp-xml-param-includexmlnamespace": "Si se especifica, añade un espacio de nombres XML.",
        "api-help-main-header": "Módulo principal",
        "api-help-flag-deprecated": "Este módulo está en desuso.",
        "api-help-flag-readrights": "Este módulo requiere permisos de lectura.",
index 4b42964..ef9d7d1 100644 (file)
@@ -26,7 +26,8 @@
                        "Verdy p",
                        "Yasten",
                        "Trial",
-                       "Pols12"
+                       "Pols12",
+                       "The RedBurn"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].",
        "apihelp-edit-param-sectiontitle": "Le titre pour une nouvelle section.",
        "apihelp-edit-param-text": "Contenu de la page.",
        "apihelp-edit-param-summary": "Modifier le résumé. Également le titre de la section quand $1section=new et $1sectiontitle n’est pas défini.",
-       "apihelp-edit-param-tags": "Modifier les balises à appliquer à la révision.",
+       "apihelp-edit-param-tags": "Modifier les balises à appliquer à la version.",
        "apihelp-edit-param-minor": "Modification mineure.",
        "apihelp-edit-param-notminor": "Modification non mineure.",
        "apihelp-edit-param-bot": "Marquer cette modification comme robot.",
        "apihelp-options-example-change": "Modifier les préférences <kbd>skin</kbd> et <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Réinitialiser toutes les préférences, puis définir <kbd>skin</kbd> et <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obtenir des informations sur les modules de l’API.",
-       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>, ou tous les sous-modules avec <kbd>+*</kbd>, ou tous les sous-modules récursivement avec <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format des chaînes d’aide.",
        "apihelp-paraminfo-param-querymodules": "Liste des noms de module de requêtage (valeur des paramètres <var>prop</var>, <var>meta</var> ou <var>list</var>=). Utiliser <kbd>$1modules=query+foo</kbd> au lieu de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obtenir aussi des informations sur le module principal (niveau supérieur). Utiliser plutôt <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obtenir aussi des informations sur le module pageset (en fournissant titles= et ses amis).",
        "apihelp-paraminfo-param-formatmodules": "Liste des noms de module de mise en forme (valeur du paramètre <var>format</var>). Utiliser plutôt <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Afficher les informations pour <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> et <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Afficher les informations pour tous les sous-modules de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analyse le contenu et renvoie le résultat de l’analyseur.\n\nVoyez les différents modules prop de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> pour avoir de l’information sur la version actuelle d’une page.\n\nIl y a plusieurs moyens de spécifier le texte à analyser :\n# Spécifier une page ou une révision, en utilisant <var>$1page</var>, <var>$1pageid</var> ou <var>$1oldid</var>.\n# Spécifier explicitement un contenu, en utilisant <var>$1text</var>, <var>$1title</var> et <var>$1contentmodel</var>\n# Spécifier uniquement un résumé à analyser. <var>$1prop</var> doit recevoir une valeur vide.",
        "apihelp-parse-param-title": "Titre de la page à laquelle appartient le texte. Si omis, <var>$1contentmodel</var> doit être spécifié, et [[API]] sera utilisé comme titre.",
        "apihelp-parse-param-text": "Texte à analyser. utiliser <var>$1title</var> ou <var>$1contentmodel</var> pour contrôler le modèle de contenu.",
        "apihelp-protect-param-expiry": "Horodatages d’expiration. Si un seul horodatage est fourni, il sera utilisé pour toutes les protections. Utiliser <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd> ou <kbd>never</kbd> pour une protection sans expiration.",
        "apihelp-protect-param-reason": "Motif de (dé)protection.",
        "apihelp-protect-param-tags": "Modifier les balises à appliquer à l’entrée dans le journal de protection.",
-       "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne supporte la mise en cascade.",
+       "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne prend en charge la mise en cascade.",
        "apihelp-protect-param-watch": "Si activé, ajouter la page (dé)protégée à la liste de suivi de l'utilisateur actuel.",
        "apihelp-protect-param-watchlist": "Ajouter ou supprimer sans condition la page de la liste de suivi de l'utilisateur actuel, utiliser les préférences ou ne pas modifier le suivi.",
        "apihelp-protect-example-protect": "Protéger une page",
        "apihelp-query+authmanagerinfo-description": "Récupérer les informations concernant l’état d’authentification actuel.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Tester si l’état d’authentification actuel de l’utilisateur est suffisant pour l’opération spécifiée comme sensible du point de vue sécurité.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Récupérer les informations sur les requêtes d’authentification nécessaires pour l’action d’authentification spécifiée.",
-       "apihelp-query+filerepoinfo-example-login": "Récupérer les requêtes qui peuvent être utilisées en commençant une connexion.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Récupérer les requêtes qui peuvent être utilisées au début de la connexion, avec les champs de formulaire intégrés.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Tester si l’authentification est suffisante pour l’action <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Récupérer les requêtes qui peuvent être utilisées en commençant une connexion.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Récupérer les requêtes qui peuvent être utilisées au début de la connexion, avec les champs de formulaire intégrés.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Tester si l’authentification est suffisante pour l’action <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Trouver toutes les pages qui ont un lien vers la page donnée.",
        "apihelp-query+backlinks-param-title": "Titre à rechercher. Impossible à utiliser avec <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "ID de la page à chercher. Impossible à utiliser avec <var>$1title</var>.",
        "apihelp-query+search-paramvalue-prop-sectiontitle": "Ajoute le titre de la section correspondante.",
        "apihelp-query+search-paramvalue-prop-categorysnippet": "Ajoute un extrait analysé de la catégorie correspondante.",
        "apihelp-query+search-paramvalue-prop-isfilematch": "Ajoute un booléen indiquant si la recherche correspond au contenu du fichier.",
-       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
+       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Désuet et ignoré.</span>",
        "apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
        "apihelp-query+search-param-limit": "Combien de pages renvoyer au total.",
        "apihelp-query+search-param-interwiki": "Inclure les résultats interwiki dans la recherche, s’ils sont disponibles.",
        "apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Renvoie la liste des extensions de fichier (types de fichier) autorisées au téléchargement.",
        "apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.",
        "apihelp-query+siteinfo-paramvalue-prop-restrictions": "Renvoie l’information sur les types de restriction disponibles (protection).",
-       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant <var>$1inlanguagecode</var>).",
+       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que MédiaWiki prend en charge (éventuellement localisée en utilisant <var>$1inlanguagecode</var>).",
        "apihelp-query+siteinfo-paramvalue-prop-skins": "Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).",
        "apihelp-query+siteinfo-paramvalue-prop-extensiontags": "Renvoie une liste des balises d’extension de l’analyseur.",
        "apihelp-query+siteinfo-paramvalue-prop-functionhooks": "Renvoie une liste des accroches de fonction de l’analyseur.",
        "api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de recherche spécifié.\n\n<strong>NOTE :<strong> les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
        "api-pageset-param-redirects-generator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>, et dans les pages renvoyées par <var>$1generator</var>.",
        "api-pageset-param-redirects-nogenerator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>.",
-       "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki supporte la conversion en variantes. Les langues qui supportent la conversion en variante incluent $1.",
+       "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki prend en charge la conversion en variantes. Les langues qui prennent en charge la conversion en variante incluent $1.",
        "api-help-title": "Aide de l’API de MediaWiki",
        "api-help-lead": "Ceci est une page d’aide de l’API de MediaWiki générée automatiquement.\n\nDocumentation et exemples : https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "Module principal",
        "api-help-param-deprecated": "Obsolète.",
        "api-help-param-required": "Ce paramètre est obligatoire.",
        "api-help-datatypes-header": "Type de données",
-       "api-help-datatypes": "Certains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
+       "api-help-datatypes": "Les entrées dans MédiaWiki doivent être en UTF-8 à la norme NFC. MédiaWiki peut tenter de convertir d’autres types d’entrée, mais cela peut faire échouer certaines opérations (comme les [[Special:ApiHelp/edit|modifications]] avec contrôles MD5) to fail.\n\nCertains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
        "api-help-param-type-limit": "Type : entier ou <kbd>max</kbd>",
        "api-help-param-type-integer": "Type : {{PLURAL:$1|1=entier|2=liste d’entiers}}",
        "api-help-param-type-boolean": "Type : booléen ([[Special:ApiHelp/main#main/datatypes|détails]])",
        "api-help-param-type-timestamp": "Type : {{PLURAL:$1|1=horodatage|2=liste d’horodatages}} ([[Special:ApiHelp/main#main/datatypes|formats autorisés]])",
        "api-help-param-type-user": "Type : {{PLURAL:$1|1=nom d’utilisateur|2=liste de noms d’utilisateur}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd>)}} : $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]])}} : $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Doit être vide|Peut être vide, ou $2}}",
        "api-help-param-limit": "Pas plus de $1 autorisé.",
        "api-help-param-limit2": "Pas plus de $1 autorisé ($2 pour les robots).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=La valeur ne doit pas être supérieure|2=Les valeurs ne doivent pas être supérieures}} à $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=La valeur doit|2=Les valeurs doivent}} être entre $2 et $3.",
        "api-help-param-upload": "Doit être envoyé comme un fichier importé utilisant multipart/form-data.",
-       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]].",
        "api-help-param-multi-max": "Le nombre maximal de valeurs est {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} pour les robots).",
        "api-help-param-default": "Par défaut : $1",
        "api-help-param-default-empty": "Par défaut : <span class=\"apihelp-empty\">(vide)</span>",
index b752751..79e36cf 100644 (file)
        "apihelp-options-example-change": "Cambiar as preferencias <kbd>skin</kbd> and <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Restaurar todas as preferencias, logo fixar <kbd>skin</kbd> e <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obter información sobre módulos API.",
-       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>, ou tódolos submódulos con <kbd>+*</kbd>, ou tódolos submódulos recursivamente con <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Formato das cadeas de axuda.",
        "apihelp-paraminfo-param-querymodules": "Lista dos nomes de módulos de consulta (valores dos parámetros <var>prop</var>, <var>meta</var> ou <var>list</var>). Use <kbd>$1modules=query+foo</kbd> no canto de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obter información sobre o módulo principal (nivel superior). No canto use <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obter información sobre o módulo pageset (proporcionando títulos= e amigos).",
        "apihelp-paraminfo-param-formatmodules": "Lista dos nomes de módulo de formato (valores do parámetro <var>formato</var>). No canto use <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Amosar información para <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, e <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Mostrar a información para tódolos submódulos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analiza o contido e devolve o resultado do analizador.\n\nVexa varios módulos propostos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> para obter información sobre a versión actual dunha páxina.\n\nHai varias formas de especificar o texto a analizar:\n# Especificar unha páxina ou revisión, usando <var>$1page</var>, <var>$1pageid</var>, ou <var>$1oldid</var>.\n# Especificando contido explícitamente, usando <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Especificando só un resumo a analizar. <var>$1prop</var> debe ter un valor baleiro.",
        "apihelp-parse-param-title": "Título da páxina á que pertence o texto. Se non se indica, debe especificarse <var>$1contentmodel</var>, e [[API]] usarase como o título.",
        "apihelp-parse-param-text": "Texto a analizar. Use <var>$1title</var> ou <var>$1contentmodel</var> para controlar o modelo de contido.",
        "apihelp-query+authmanagerinfo-description": "Recuperar información sobre o estado de autenticación actual.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Comprobar se o estado de autenticación actual do usuario é abondo para a operación especificada como sensible dende o punto de vista da seguridade.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Recuperar a información sobre as peticións de autenticación necesarias para a acción de autenticación especificada.",
-       "apihelp-query+filerepoinfo-example-login": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión, xunto cos campos de formulario integrados.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Probar se a autenticación é abondo para a acción <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión, xunto cos campos de formulario integrados.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Probar se a autenticación é abondo para a acción <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Atopar todas as páxinas que ligan coa páxina dada.",
        "apihelp-query+backlinks-param-title": "Título a buscar. Non pode usarse xunto con <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "Identificador de páxina a buscar. Non pode usarse xunto con <var>$1title</var>.",
        "api-help-param-deprecated": "Obsoleto.",
        "api-help-param-required": "Este parámetro é obrigatorio.",
        "api-help-datatypes-header": "Tipos de datos",
-       "api-help-datatypes": "Algúns tipos de parámetros nas solicitudes de API necesitan máis explicación:\n;boolean\n:Os parámetros booleanos traballan como caixas de verificación HTML: se o parámetro se especifica, independentemente do seu valor, considérase verdadeiro. Para un valor falso, omíta o parámetro completo.\n;timestamp\n:Os selos de tempo poden especificarse en varios formatos. Recoméndase o ISO 8601 coa data e a hora. Todas as horas están en UTC, a inclusión da zona horaria é ignorada.\n:* ISO 8601 con data e hora, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (signos de puntuación e <kbd>Z</kbd> son opcionais)\n:* ISO 8601 data e hora (omítense) fraccións de segundo, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (guións, dous puntos e, <kbd>Z</kbd> son opcionais)\n:* Formato MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Formato numérico xenérico, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (opcional na zona horaria <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, o <kbd>-<var>##</var></kbd> omítese)\n:* Formato EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Formato RFC 2822 (a zona horaria pódese omitir), <kbd><var>Mon</var>, <var>15</var> <var>Xan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato RFC 850 (a zona horaria pódese omitir), <kbd><var>luns</var>, <var>15</var>-<var>xaneiro</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato C ctime, <kbd><var>luns</var> <var>xaneiro</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>de 2001</var></kbd>\n:* Segundos desde 1970-01-01T00:00:00Z como de 1 a 13, díxitos enteiros (excluíndo o <kbd>0</kbd>)\n:* O texto <kbd>now</kbd> (agora)",
+       "api-help-datatypes": "A entrada a MediaWiki debe ser normalizada NFC UTF-8. MediaWiki puede intentar converter outras entradas, pero isto pode provocar que algunhas operacións (como as [[Special:ApiHelp/edit|edición]] con comprobación MD5) fallen.\n\nAlgúns tipos de parámetros nas solicitudes de API necesitan máis explicación:\n;boolean\n:Os parámetros booleanos traballan como caixas de verificación HTML: se o parámetro se especifica, independentemente do seu valor, considérase verdadeiro. Para un valor falso, omíta o parámetro completo.\n;timestamp\n:Os selos de tempo poden especificarse en varios formatos. Recoméndase o ISO 8601 coa data e a hora. Todas as horas están en UTC, a inclusión da zona horaria é ignorada.\n:* ISO 8601 con data e hora, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (signos de puntuación e <kbd>Z</kbd> son opcionais)\n:* ISO 8601 data e hora (omítense) fraccións de segundo, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (guións, dous puntos e, <kbd>Z</kbd> son opcionais)\n:* Formato MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Formato numérico xenérico, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (opcional na zona horaria <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, o <kbd>-<var>##</var></kbd> omítese)\n:* Formato EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Formato RFC 2822 (a zona horaria pódese omitir), <kbd><var>Mon</var>, <var>15</var> <var>Xan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato RFC 850 (a zona horaria pódese omitir), <kbd><var>luns</var>, <var>15</var>-<var>xaneiro</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato C ctime, <kbd><var>luns</var> <var>xaneiro</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>de 2001</var></kbd>\n:* Segundos desde 1970-01-01T00:00:00Z como de 1 a 13, díxitos enteiros (excluíndo o <kbd>0</kbd>)\n:* O texto <kbd>now</kbd> (agora)",
        "api-help-param-type-limit": "Tipo: enteiro ou <kbd>max</kbd>",
        "api-help-param-type-integer": "Tipo: {{PLURAL:$1|1=enteiro|2=lista de enteiros}}",
        "api-help-param-type-boolean": "Tipo: booleano ([[Special:ApiHelp/main#main/datatypes|detalles]])",
        "api-help-param-type-timestamp": "Tipo: {{PLURAL:$1|1=selo de tempo|2=lista de selos de tempo}} ([[Special:ApiHelp/main#main/datatypes|formatos permitidos]])",
        "api-help-param-type-user": "Tipo: {{PLURAL:$1|1=nome de usuario|2=lista de nomes de usuarios}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Un valor dos seguintes valores|2=Valores (separados con <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Un valor dos seguintes valores|2=Valores (separados con <kbd>{{!}}</kbd> ou [[Special:ApiHelp/main#main/datatypes|outros]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Debe ser baleiro|Pode ser baleiro, ou $2}}",
        "api-help-param-limit": "Non se permiten máis de $1.",
        "api-help-param-limit2": "Non se permiten máis de $1 ($2 para bots).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=O valor debe ser menor |2=Os valores deben ser menores}} que $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=O valor debe estar entre $2 e $3 |2=Os valores deben estar entre $2 e $3}}.",
        "api-help-param-upload": "Debe ser enviado como un ficheiro importado usando multipart/form-data.",
-       "api-help-param-multi-separate": "Separe os valores con <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separe os valores con <kbd>|</kbd> ou [[Special:ApiHelp/main#main/datatypes|outros]].",
        "api-help-param-multi-max": "O número máximo de valores é {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} para os bots).",
        "api-help-param-default": "Por defecto: $1",
        "api-help-param-default-empty": "Por defecto: <span class=\"apihelp-empty\">(baleiro)</span>",
index a28cbda..dd0d07b 100644 (file)
        "apihelp-options-example-change": "לשנות את ההעדפות <kbd>skin</kbd> ו־<kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "לאתחל את כל ההעדפות ואז להגדיר את <kbd>skin</kbd> ואת <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "קבלת מידע על יחידות של API.",
-       "apihelp-paraminfo-param-modules": "רשימה של שמות יחידות (ערכים של הפרמטרים <var>action</var> ו־<var>format</var>, או <kbd>main</kbd>). אפשר להגדיר תת־יחידות עם <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "רשימה של שמות יחידות (ערכים של הפרמטרים <var>action</var> ו־<var>format</var>, או <kbd>main</kbd>). אפשר להגדיר תת־יחידות עם <kbd>+</kbd>, או כל התת־מודולים עם <kbd dir=\"ltr\">+*</kbd>, או כל התת־מודולים באופן רקורסיבי עם <kbd dir=\"ltr\">+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "תסדיר מחרוזות העזרה.",
        "apihelp-paraminfo-param-querymodules": "רשימת שמות יחידות query (ערך של הפרמטר <var>prop</var>‏, <var>meta</var> או <var>list</var>). יש להשתמש ב־<kbd>$1modules=query+foo</kbd> במקום <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "קבלת מידע עם היחידה הראשית (העליונה). יש להשתמש ב־<kbd>$1modules=main</kbd> במקום זה.",
        "apihelp-paraminfo-param-pagesetmodule": "קבלת מידע גם על יחידת pageset (שמספק את titles=‎ וידידיו).",
        "apihelp-paraminfo-param-formatmodules": "רשימת שמות תסדירים (ערכים של הפרמטר <var>format</var>). יש להשתמש ב־<var>$1modules</var> במקום זה.",
        "apihelp-paraminfo-example-1": "הצגת מידע עבור <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>‏, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>‏, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>‏, ו־<kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "הצגת מידע עבור כל התת־מודולים של <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "מפענח את התוכן ומחזיר פלט מפענח.\n\nר' את יחידת ה־prop השיונות של <kbd>[[Special:ApiHelp/query|action=query]]</kbd> כדי לקבל מידע על הגרסה הנוכחית של הדף.\n\nיש מספר דרכים לציין טקסט לפענוח:\n# ציון דף או גרסה באמצעות <var>$1page</var>‏, <var>$1pageid</var>, או <var>$1oldid</var>.\n# ציון התוכן במפורש, באמצעות <var>$1text</var>‏, <var>$1title</var>, ו־<var>$1contentmodel</var>.\n# ציון רק של  התקציר לפענוח. ל־<var>$1prop</var> צריך לתת ערך ריק.",
        "apihelp-parse-param-title": "שם הדף שהטקסט שייך אליו. אם זה מושמט, יש לציין את <var>$1contentmodel</var>, ו־[[API]] ישמש ככותרת.",
        "apihelp-parse-param-text": "הטקסט לפענוח. יש להשתמש ב־<var>$1title</var> או ב־<var>$1contentmodel</var>.",
        "apihelp-query+authmanagerinfo-description": "אחזור מידע אודות מצב האימות הנוכחי.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "בדיקה האם מצב האימות הנוכחי של המשתמש מספיק בשביל הפעולה הרגישה מבחינת אבטחה שצוינה.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "אחזור מידע על בקשות האימות הדרושות לפעולת האימות המבוקשת.",
-       "apihelp-query+filerepoinfo-example-login": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה.",
-       "apihelp-query+filerepoinfo-example-login-merged": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה, עם שדות טופס ממוזגים.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "בדיקה האם האימות מספיק בשביל הפעולה <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה, עם שדות טופס ממוזגים.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "בדיקה האם האימות מספיק בשביל הפעולה <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "מציאת כל הדפים שמקשרים לדף הנתון.",
        "apihelp-query+backlinks-param-title": "איזו כותרת לחפש. לא ניתן להשתמש בזה יחד עם <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "מזהה דף לחיפוש. לא ניתן להשתמש בזה יחד עם <var>$1title</var>.",
        "api-help-param-deprecated": "מיושן.",
        "api-help-param-required": "פרמטר זה נדרש.",
        "api-help-datatypes-header": "סוגי נתונים",
-       "api-help-datatypes": "×\97×\9cק ×\9eס×\95×\92×\99 ×\94פר×\9e×\98ר×\99×\9d ×\91×\91קש×\95ת API ×\93×\95רש×\99×\9d ×\94ס×\91ר × ×\95סף:\n;×\91×\95×\9c×\99×\90× ×\99 (boolean)\n:פר×\9e×\98ר×\99×\9d ×\91×\95×\9c×\99×\90× ×\99×\99×\9d ×¢×\95×\91×\93×\99×\9d ×\9b×\9e×\95 ×ª×\99×\91×\95ת ×¡×\99×\9e×\95×\9f ×©×\9c HTML: ×\90×\9d ×\94פר×\9e×\98ר ×¦×\95×\99×\9f, ×\91×\9c×\99 ×§×©×¨ ×\9cער×\9a ×©×\9c×\95, ×\94×\95×\90 ×\90×\9eת (true). ×\91ש×\91×\99×\9c ×¢×¨×\9a ×©×§×¨ (false), ×\99ש ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\94פר×\9e×\98ר ×\9c×\92×\9eר×\99.\n;×\97×\95ת×\9dÖ¾×\96×\9e×\9f (timestamp)\n:×\90פשר ×\9c×\9bת×\95×\91 ×\97×\95ת×\9e×\99Ö¾×\96×\9e×\9f ×\91×\9eספר ×ª×¡×\93×\99ר×\99×\9d. ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×\94×\95×\90 ×\94×\93×\91ר ×\94×\9e×\95×\9e×\9cת. ×\9b×\9c ×\94×\96×\9e× ×\99×\9d ×\9eצ×\95×\99× ×\99×\9d ×\91Ö¾ UTC, ×\9c×\90 ×ª×\94×\99×\94 ×\94שפע×\94 ×\9cש×\95×\9d ×\90×\96×\95ר ×\96×\9e×\9f ×©×\99צ×\95×\99×\9f.\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601â\80\8f, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×¤×\99ס×\95ק ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×¢×\9d ×\97×\9cק×\99 ×©× ×\99×\99×\94 (ש×\9c×\90 ×ª×\94×\99×\94 ×\9c×\94×\9d ×©×\95×\9d ×\94שפע×\94), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×§×\95×\95×\99×\9d ×\9eפר×\99×\93×\99×\9d, × ×§×\95×\93ת×\99×\99×\9d ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×¡×\93×\99ר MediaWikiâ\80\8f, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* ×ª×¡×\93×\99ר ×\9eספר×\99 ×\9b×\9c×\9c×\99, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (×\9c×\90×\96×\95ר ×\96×\9e×\9f ×\90×\95פצ×\99×\95× ×\9c×\99 ×©×\9c <kbd>GMT</kbd>â\80\8f, <kbd dir=\"ltr\">+<var>##</var></kbd>, ×\90×\95 <kbd dir=\"ltr\">-<var>##</var></kbd> ×\90×\99×\9f ×\94שפע×\94)\n:* ×ª×¡×\93×\99ר EXIFâ\80\8f, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 2822 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 850 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר C ctimeâ\80\8f, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* ×©× ×\99×\95ת ×\9e×\90×\96 1970-01-01T00:00:00Z ×\91ת×\95ר ×\9eספר ×©×\9c×\9a ×\91×\99×\9f 1 ×\9cÖ¾13 (×\9c×\90 ×\9b×\95×\9c×\9c <kbd>0</kbd>)\n:* ×\94×\9e×\97ר×\95×\96ת <kbd>now</kbd>",
+       "api-help-datatypes": "ק×\9c×\98 ×\9c×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×¦×¨×\99×\9a ×\9c×\94×\99×\95ת ×\91ק×\99×\93×\95×\93 UTF-8 ×\9e× ×\95ר×\9e×\9c ×\91Ö¾NFC. ×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×\99×\9b×\95×\9c×\94 ×\9cנס×\95ת ×\9c×\94×\9e×\99ר ×§×\9c×\98 ×\90×\97ר, ×\90×\91×\9c ×\96×\94 ×¢×\9c×\95×\9c ×\9c×\92ר×\95×\9d ×\9cפע×\95×\9c×\95ת ×\9eס×\95×\99×\9e×\95ת (×\9b×\92×\95×\9f [[Special:ApiHelp/edit|ער×\99×\9b×\95ת]] ×¢×\9d ×\91×\93×\99ק×\95ת MD5) ×\9c×\94×\99×\9bש×\9c.\n\n×\97×\9cק ×\9eס×\95×\92×\99 ×\94פר×\9e×\98ר×\99×\9d ×\91×\91קש×\95ת API ×\93×\95רש×\99×\9d ×\94ס×\91ר × ×\95סף:\n;×\91×\95×\9c×\99×\90× ×\99 (boolean)\n:פר×\9e×\98ר×\99×\9d ×\91×\95×\9c×\99×\90× ×\99×\99×\9d ×¢×\95×\91×\93×\99×\9d ×\9b×\9e×\95 ×ª×\99×\91×\95ת ×¡×\99×\9e×\95×\9f ×©×\9c HTML: ×\90×\9d ×\94פר×\9e×\98ר ×¦×\95×\99×\9f, ×\91×\9c×\99 ×§×©×¨ ×\9cער×\9a ×©×\9c×\95, ×\94×\95×\90 ×\90×\9eת (true). ×\91ש×\91×\99×\9c ×¢×¨×\9a ×©×§×¨ (false), ×\99ש ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\94פר×\9e×\98ר ×\9c×\92×\9eר×\99.\n;×\97×\95ת×\9dÖ¾×\96×\9e×\9f (timestamp)\n:×\90פשר ×\9c×\9bת×\95×\91 ×\97×\95ת×\9e×\99Ö¾×\96×\9e×\9f ×\91×\9eספר ×ª×¡×\93×\99ר×\99×\9d. ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×\94×\95×\90 ×\94×\93×\91ר ×\94×\9e×\95×\9e×\9cת. ×\9b×\9c ×\94×\96×\9e× ×\99×\9d ×\9eצ×\95×\99× ×\99×\9d ×\91Ö¾ UTC, ×\9c×\90 ×ª×\94×\99×\94 ×\94שפע×\94 ×\9cש×\95×\9d ×\90×\96×\95ר ×\96×\9e×\9f ×©×\99צ×\95×\99×\9f.\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601â\80\8f, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×¤×\99ס×\95ק ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×¢×\9d ×\97×\9cק×\99 ×©× ×\99×\99×\94 (ש×\9c×\90 ×ª×\94×\99×\94 ×\9c×\94×\9d ×©×\95×\9d ×\94שפע×\94), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×§×\95×\95×\99×\9d ×\9eפר×\99×\93×\99×\9d, × ×§×\95×\93ת×\99×\99×\9d ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×¡×\93×\99ר MediaWikiâ\80\8f, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* ×ª×¡×\93×\99ר ×\9eספר×\99 ×\9b×\9c×\9c×\99, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (×\9c×\90×\96×\95ר ×\96×\9e×\9f ×\90×\95פצ×\99×\95× ×\9c×\99 ×©×\9c <kbd>GMT</kbd>â\80\8f, <kbd dir=\"ltr\">+<var>##</var></kbd>, ×\90×\95 <kbd dir=\"ltr\">-<var>##</var></kbd> ×\90×\99×\9f ×\94שפע×\94)\n:* ×ª×¡×\93×\99ר EXIFâ\80\8f, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 2822 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 850 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר C ctimeâ\80\8f, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* ×©× ×\99×\95ת ×\9e×\90×\96 1970-01-01T00:00:00Z ×\91ת×\95ר ×\9eספר ×©×\9c×\9a ×\91×\99×\9f 1 ×\9cÖ¾13 (×\9c×\90 ×\9b×\95×\9c×\9c <kbd>0</kbd>)\n:* ×\94×\9e×\97ר×\95×\96ת <kbd>now</kbd>\n;×\9eפר×\99×\93 ×¢×¨×\9b×\99×\9d ×\9eר×\95×\91×\99×\9d ×\97×\9c×\95פ×\99\n:פר×\9e×\98ר×\99×\9d ×©×\9c×\95ק×\97×\99×\9d ×¢×¨×\9b×\99×\9d ×\9eר×\95×\91×\99×\9d ×\91×\93ר×\9aÖ¾×\9b×\9c×\9c × ×©×\9c×\97×\99×\9d ×¢×\9d ×\94ער×\9b×\99×\9d ×\9e×\95פר×\93×\99×\9d ×\91×\90×\9eצע×\95ת ×ª×\95 ×\9eק×\9c, ×\9c×\9eש×\9c <kbd>param=value1|value2</kbd> ×\90×\95 <kbd>param=value1%7Cvalue2</kbd>. ×\90×\9d ×\94ער×\9a ×¦×¨×\99×\9a ×\9c×\94×\9b×\99×\9c ×\90ת ×ª×\95 ×\94×\9eק×\9c, ×\99ש ×\9c×\94שת×\9eש ×\91Ö¾U+001F (×\9eפר×\99×\93 ×\99×\97×\99×\93×\95ת) ×\91ת×\95ר ×\94×\9eפר×\99×\93 ''×\95×\92×\9d'' ×\9c×\94×\95ס×\99×£ ×\9cת×\97×\99×\9cת ×\94ער×\9a U+001F, ×\9c×\9eש×\9c <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "סוג: מספר שלם או <kbd>max</kbd>",
        "api-help-param-type-integer": "סוג: {{PLURAL:$1|1=מספר שלם|2=רשימת מספרים שלמים}}",
        "api-help-param-type-boolean": "סוג: בוליאני ([[Special:ApiHelp/main#main/datatypes|פרטים]])",
        "api-help-param-type-timestamp": "סוג: {{PLURAL:$1|חותם־זמן|רשימת חותמי־זמן}} ([[Special:ApiHelp/main#main/datatypes|תסדירים מורשים]])",
        "api-help-param-type-user": "סוג: {{PLURAL:$1|1=שם משתמש|2=רשימת שמות משתמשים}}",
-       "api-help-param-list": "{{PLURAL:$1|1=אחד מהערכים הבאים|2=ערכים (מופרדים באמצעות \"<kbd>{{!}}</kbd>\")}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=אחד מהערכים הבאים|2=ערכים (מופרדים באמצעות \"<kbd>{{!}}</kbd>\" או or [[Special:ApiHelp/main#main/datatypes|תו חלופי]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=חייב להיות ריק|יכול להיות ריק או $2}}",
        "api-help-param-limit": "מספר הפרמטרים לא יכול להיות גדול מ־$1.",
        "api-help-param-limit2": "המספר המרבי המותר הוא $1 (עבור בוטים – $2).",
        "api-help-param-integer-max": "ה{{PLURAL:$1|1=ערך לא יכול להיות גדול|2=ערכים לא יכולים להיות גדולים}} מ־$3.",
        "api-help-param-integer-minmax": "ה{{PLURAL:$1|1=ערך חייב|2=ערכים חייבים}} להיות בין $2 ל־$3.",
        "api-help-param-upload": "חייב להישלח (posted) בתור העלאת קובץ באמצעות multipart/form-data.",
-       "api-help-param-multi-separate": "הפרדה בין ערכים נעשית באמצעות <kbd>|</kbd>",
+       "api-help-param-multi-separate": "הפרדה בין ערכים נעשית באמצעות <kbd>|</kbd> או [[Special:ApiHelp/main#main/datatypes|תו חלופי]].",
        "api-help-param-multi-max": "מספר הערכים המרבי הוא {{PLURAL:$1|$1}} (עבור בוטים – {{PLURAL:$2|$2}}).",
        "api-help-param-default": "ברירת מחדל: $1",
        "api-help-param-default-empty": "ברירת מחדל: <span class=\"apihelp-empty\">(ריק)</span>",
index e02abe0..9786543 100644 (file)
        "apihelp-linkaccount-example-link": "Avvia il processo di collegamento ad un'utenza da <kbd>Example</kbd>.",
        "apihelp-login-description": "Accedi e ottieni i cookie di autenticazione.\n\nQuesta azione deve essere usata esclusivamente in combinazione con [[Special:BotPasswords]]; utilizzarla per l'accesso all'account principale è deprecato e può fallire senza preavviso. Per accedere in modo sicuro all'utenza principale, usa <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Accedi e ottieni i cookies di autenticazione.\n\nQuesta azione è deprecata e può fallire senza preavviso. Per accedere in modo sicuro, usa [[Special:ApiHelp/clientlogin|action=clientlogin]].",
-       "apihelp-login-description-nonauthmanager": "Accedi e ottieni i cookie di autenticazione.\n\nIn caso di accesso riuscito, i cookies necessari saranno inclusi nella intestazioni di risposta HTTP. In caso di accesso fallito, ulteriori tentativi potrebbero essere limitati, in modo da contenere gli attacchi automatizzati per indovinare le password.",
        "apihelp-login-param-name": "Nome utente.",
        "apihelp-login-param-password": "Password.",
        "apihelp-login-param-domain": "Dominio (opzionale).",
        "apihelp-query+authmanagerinfo-description": "Recupera informazioni circa l'attuale stato di autenticazione.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Verifica se lo stato di autenticazione dell'utente attuale è sufficiente per la specifica operazione sensibile alla sicurezza.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Recupera informazioni circa le richieste di autenticazione necessarie per la specifica azione di autenticazione.",
-       "apihelp-query+filerepoinfo-example-login": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso, con i campi del modulo uniti.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Verificare se l'autenticazione è sufficiente per l'azione <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso, con i campi del modulo uniti.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Verificare se l'autenticazione è sufficiente per l'azione <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Trova tutte le pagine che puntano a quella specificata.",
        "apihelp-query+backlinks-param-namespace": "Il namespace da elencare.",
        "apihelp-query+backlinks-param-dir": "La direzione in cui elencare.",
        "api-help-param-type-boolean": "Tipo: booleano ([[Special:ApiHelp/main#main/datatypes|dettagli]])",
        "api-help-param-type-timestamp": "Tipo: {{PLURAL:$1|1=timestamp|2=elenco di timestamp}} ([[Special:ApiHelp/main#main/datatypes|formati consentiti]])",
        "api-help-param-type-user": "Tipo: {{PLURAL:$1|1=nome utente|2=elenco di nomi utente}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Uno dei seguenti valori|2=Valori (separati da <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Uno dei seguenti valori|2=Valori (separati da <kbd>{{!}}</kbd> o [[Special:ApiHelp/main#main/datatypes|alternativa]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Deve essere vuoto|Può essere vuoto, o $2}}",
        "api-help-param-limit": "Non più di $1 consentito.",
        "api-help-param-limit2": "Non più di $1 ($2 per bot) consentito.",
        "api-help-param-integer-min": "{{PLURAL:$1|1=Il valore non deve essere inferiore|2=I valori non devono essere inferiori}} a $2.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Il valore non deve essere superiore|2=I valori non devono essere superiori}} a $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Il valore deve essere compreso|2=I valori devono essere compresi}} tra $2 e $3.",
-       "api-help-param-multi-separate": "Separa i valori con <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separa i valori con <kbd>|</kbd> o [[Special:ApiHelp/main#main/datatypes|alternativa]].",
        "api-help-param-multi-max": "Il numero massimo di valori è {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} per i bot).",
        "api-help-param-default": "Predefinito: $1",
        "api-help-param-default-empty": "Predefinito: <span class=\"apihelp-empty\">(vuoto)</span>",
index af13948..f3beff6 100644 (file)
@@ -43,6 +43,7 @@
        "apihelp-checktoken-example-simple": "<kbd>csrf</kbd> トークンの妥当性を調べる。",
        "apihelp-clearhasmsg-description": "現在の利用者の <code>hasmsg</code> フラグを消去します。",
        "apihelp-clearhasmsg-example-1": "現在の利用者の <code>hasmsg</code> フラグを消去する。",
+       "apihelp-clientlogin-example-login": "利用者 <kbd>Example</kbd> としてのログイン処理をパスワード <kbd>ExamplePassword</kbd> で開始する",
        "apihelp-compare-description": "2つの版間の差分を取得します。\n\n\"from\" と \"to\" の両方の版番号、ページ名、もしくはページIDを渡す必要があります。",
        "apihelp-compare-param-fromtitle": "比較する1つ目のページ名。",
        "apihelp-compare-param-fromid": "比較する1つ目のページID。",
        "apihelp-query+backlinks-param-pageid": "検索するページID。<var>$1title</var>とは同時に使用できません。",
        "apihelp-query+backlinks-param-namespace": "列挙する名前空間。",
        "apihelp-query+backlinks-param-dir": "一覧表示する方向。",
+       "apihelp-query+backlinks-param-limit": "返すページの総数。<var>$1redirect</var> を有効化した場合は、各レベルに対し個別にlimitが適用されます (つまり、最大で 2 * <var>$1limit</var> 件の結果が返されます)。",
        "apihelp-query+backlinks-example-simple": "<kbd>Main page</kbd> へのリンクを表示する。",
        "apihelp-query+backlinks-example-generator": "<kbd>Main page</kbd> にリンクしているページの情報を取得する。",
        "apihelp-query+blocks-description": "ブロックされた利用者とIPアドレスを一覧表示します。",
        "apihelp-query+deletedrevs-param-end": "列挙の終点となるタイムスタンプ。",
        "apihelp-query+deletedrevs-param-from": "列挙の始点となるページ名。",
        "apihelp-query+deletedrevs-param-to": "列挙の終点となるページ名。",
+       "apihelp-query+deletedrevs-param-tag": "このタグが付与された版のみを一覧表示する。",
+       "apihelp-query+deletedrevs-param-user": "この利用者による版のみを一覧表示する。",
        "apihelp-query+deletedrevs-param-excludeuser": "この利用者による版を一覧表示しない。",
        "apihelp-query+deletedrevs-param-namespace": "この名前空間に含まれるページのみを一覧表示します。",
        "apihelp-query+deletedrevs-param-limit": "一覧表示する版の最大量。",
        "apihelp-query+info-description": "ページの基本的な情報を取得します。",
        "apihelp-query+info-param-prop": "追加で取得するプロパティ:",
        "apihelp-query+info-paramvalue-prop-protection": "それぞれのページの保護レベルを一覧表示する。",
+       "apihelp-query+info-param-token": "代わりに [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] を使用してください。",
        "apihelp-query+info-example-simple": "<kbd>Main Page</kbd> に関する情報を取得する。",
        "apihelp-query+iwbacklinks-param-prefix": "インターウィキ接頭辞。",
        "apihelp-query+iwbacklinks-param-title": "検索するウィキ間リンク。<var>$1blprefix</var>と同時に使用しなければなりません。",
        "api-help-param-deprecated": "廃止予定です。",
        "api-help-param-required": "このパラメーターは必須です。",
        "api-help-datatypes-header": "データ型",
-       "api-help-param-list": "{{PLURAL:$1|1=値 (次の値のいずれか1つ)|2=値 (<kbd>{{!}}</kbd>で区切る)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=å\80¤ (次ã\81®å\80¤ã\81®ã\81\84ã\81\9aã\82\8cã\81\8b\81¤)|2=å\80¤ (<kbd>{{!}}</kbd>ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|å\88¥ã\81®æ\96\87å­\97å\88\97]]ã\81§å\8cºå\88\87ã\82\8b)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=空欄にしてください|空欄にするか、または $2}}",
        "api-help-param-integer-min": "{{PLURAL:$1|値}}は $2 以上にしてください。",
        "api-help-param-integer-max": "{{PLURAL:$1|値}}は $3 以下にしてください。",
        "api-help-param-integer-minmax": "{{PLURAL:$1|値}}は $2 以上 $3 以下にしてください。",
        "api-help-param-upload": "multipart/form-data 形式でファイルをアップロードしてください。",
-       "api-help-param-multi-separate": "複数の値は <kbd>|</kbd> で区切ってください。",
+       "api-help-param-multi-separate": "è¤\87æ\95°ã\81®å\80¤ã\81¯ <kbd>|</kbd> ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|代ã\82\8fã\82\8aã\81®æ\96\87å­\97]]ã\81§å\8cºå\88\87ã\81£ã\81¦ã\81\8fã\81 ã\81\95ã\81\84ã\80\82",
        "api-help-param-multi-max": "値の最大値は {{PLURAL:$1|$1}} (ボットの場合は {{PLURAL:$2|$2}}) です。",
        "api-help-param-default": "既定値: $1",
        "api-help-param-default-empty": "既定値: <span class=\"apihelp-empty\">(空)</span>",
diff --git a/includes/api/i18n/lij.json b/includes/api/i18n/lij.json
new file mode 100644 (file)
index 0000000..5ea8613
--- /dev/null
@@ -0,0 +1,219 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Giromin Cangiaxo"
+               ]
+       },
+       "apihelp-main-param-action": "Açion da compî.",
+       "apihelp-main-param-format": "Formato de l'output.",
+       "apihelp-main-param-assert": "Veifica che l'utente o l'agge effetoòu l'accesso se s'è impostou <kbd>user</kbd>, ò ch'o l'agge i permissi di bot se s'è impostou <kbd>bot</kbd>.",
+       "apihelp-main-param-requestid": "Tutti i valoî fornii saian incruxi inta risposta. Porieivan ese doeuviæ pe distingue e receste.",
+       "apihelp-main-param-servedby": "Inciodi into risultou o nomme de l'host ch'o l'ha servio a recesta.",
+       "apihelp-main-param-curtimestamp": "Inciodi into risultou o timestamp attoâ.",
+       "apihelp-block-description": "Blocca un utente.",
+       "apihelp-block-param-user": "Nomme utente, adresso IP o range di IP da bloccâ.",
+       "apihelp-block-param-expiry": "Tempo de scadença. O poeu ese relativo (presempio, <kbd>5 months</kbd> o <kbd>2 weeks</kbd>) ò assoluo (presempio <kbd>2014-09-18T12:34:56Z</kbd>). Se impostou a <kbd>infinite</kbd>, <kbd>indefinite</kbd> ò <kbd>never</kbd>, o blòcco o no descaziâ mai.",
+       "apihelp-block-param-reason": "Raxon do blòcco.",
+       "apihelp-block-param-anononly": "Blocca solo che i utenti non registræ (saiv'a dî disattiva i contributi anonnimi da questo adresso IP).",
+       "apihelp-block-param-nocreate": "Impedisci a creaçion de utençe.",
+       "apihelp-block-param-autoblock": "Blocca aotomaticamente l'urtimo adreçço IP doeuviou da l'utente e i succescivi co-i quæ tentan l'accesso",
+       "apihelp-block-param-hidename": "Ascondi o nomme utente da-o registro di blocchi (Ghe voeu i permissi de <code>hideuser</code>).",
+       "apihelp-block-param-reblock": "Se l'utente o l'è za bloccou, sorvescrive o blocco existente.",
+       "apihelp-block-param-watchuser": "Oserva a paggina utente e e paggine de discuscion utente de l'utente ò de l'adresso IP.",
+       "apihelp-block-example-ip-simple": "Blocca l'adresso IP <kbd>192.0.2.5</kbd> pe trei giorni con motivaçion <kbd>First strike</kbd>.",
+       "apihelp-block-example-user-complex": "Blocca l'utente <kbd>Vandal</kbd> a tempo indeterminou con motivaçion <kbd>Vandalism</kbd>, e impediscighe a creaçion de noeuve utençe e l'invio de e-mail.",
+       "apihelp-changeauthenticationdata-description": "Modificâ i dæti d'aotenticaçion pe l'utente corente.",
+       "apihelp-changeauthenticationdata-example-password": "Tentativo de modificâ a password de l'utente corente a <kbd>ExamplePassword</kbd>.",
+       "apihelp-checktoken-description": "Veifica a validitæ de 'n token da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
+       "apihelp-checktoken-param-type": "Tipo de token in corso de test.",
+       "apihelp-checktoken-param-token": "Token da testâ.",
+       "apihelp-checktoken-param-maxtokenage": "Mascima etæ consentia pe-o token, in segondi.",
+       "apihelp-checktoken-example-simple": "Veifica a validitæ de 'n token <kbd>csrf</kbd>.",
+       "apihelp-clearhasmsg-description": "Scassa o flag <code>hasmsg</code> pe l'utente corente.",
+       "apihelp-clearhasmsg-example-1": "Scassa o flag <code>hasmsg</code> pe l'utente corente.",
+       "apihelp-clientlogin-description": "Accedi a-o wiki doeuviando o flusso interattivo.",
+       "apihelp-clientlogin-example-login": "Avvia o processo d'accesso a-a wiki comme utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
+       "apihelp-clientlogin-example-login2": "Continnoa l'accesso doppo una risposta de l'<samp>UI</samp> pe l'aotenticaçion a doî fattoî, fornindo un <var>OATHToken</var> de <kbd>987654</kbd>.",
+       "apihelp-compare-description": "Otegni e differençe tra 2 paggine.\n\nUn nummero de revixon, o tittolo de 'na paggina, ò un ID de paggina o dev'ese indicou segge pe-o \"da\" che pe-o \"a\".",
+       "apihelp-compare-param-fromtitle": "Primmo tittolo da confrontâ.",
+       "apihelp-compare-param-fromid": "Primo ID de paggina da confrontâ.",
+       "apihelp-compare-param-fromrev": "Primma revixon da confrontâ.",
+       "apihelp-compare-param-totitle": "Segondo tittolo da confrontâ.",
+       "apihelp-compare-param-toid": "Segondo ID de paggina da confrontâ.",
+       "apihelp-compare-param-torev": "Segonda revixon da confrontâ.",
+       "apihelp-compare-example-1": "Crea un diff tra revixon 1 e revixon 2.",
+       "apihelp-createaccount-description": "Crea una noeuva utença.",
+       "apihelp-createaccount-param-preservestate": "Se <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> o l'ha restituto true pe <samp>hasprimarypreservedstate</samp>, e receste contrssegnæ comme <samp>primary-required</samp> dovieivan ese omisse. Se invece o l'ha restituio un valô non voeuo pe <samp>preservedusername</samp>, quello nomme utente o dev'ese doeuviou pe-o parammetro <var>username</var>.",
+       "apihelp-createaccount-example-create": "Avvia o processo de creaçion d'utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
+       "apihelp-createaccount-param-name": "Nomme utente",
+       "apihelp-createaccount-param-password": "Password (a saiâ ignorâ se l'è impostou <var>$1mailpassword</var>).",
+       "apihelp-createaccount-param-domain": "Dominnio pe l'aotenticaçion esterna (opçionâ).",
+       "apihelp-createaccount-param-email": "Adresso Email de l'utente (opçionâ).",
+       "apihelp-createaccount-param-realname": "Nomme veo de l'utente (opçionâ).",
+       "apihelp-createaccount-param-mailpassword": "Se impostou insce 'n qualonque valô, una password random (caxoâ) a saiâ inviâ a l'utente.",
+       "apihelp-createaccount-param-reason": "Raxon facortativa da creaçion de l'utença da insei inti registri.",
+       "apihelp-createaccount-param-language": "Codiçe de lengua da impostâ comme predefinia pe l'utente (opçionâ, pe difetto a l'è a lengua do contegnuo).",
+       "apihelp-createaccount-example-pass": "Crea l'utente <kbd>testuser</kbd> con password <kbd>test123</kbd>.",
+       "apihelp-createaccount-example-mail": "Crea l'utente <kbd>testmailuser</kbd> e mandighe via e-mail una password generâ abrettio.",
+       "apihelp-delete-description": "Scassa 'na paggina",
+       "apihelp-delete-param-title": "Tittolo da paggina che se dexîa eliminâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-delete-param-pageid": "ID de paggina da paggina da scassâ. O no poeu vese doeuviou insemme con <var>$1title</var>.",
+       "apihelp-delete-param-reason": "Raxon da scassatua. S'a no saiâ indicâ, saiâ doeuviou 'na raxon generâ aotomaticamente.",
+       "apihelp-delete-param-watch": "O l'azonze a paggina a-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-delete-param-unwatch": "O rimoeuve a pagina da-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-delete-param-oldimage": "O nomme da vegia inmaggine da scassâ, comme fornia da [[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]].",
+       "apihelp-delete-example-simple": "Scassa <kbd>Main Page</kbd>.",
+       "apihelp-delete-example-reason": "Scassa a <kbd>Main Page</kbd> con motivaçion <kbd>Preparing for move</kbd>.",
+       "apihelp-disabled-description": "Questo modulo o l'è stæto disabilitou.",
+       "apihelp-edit-description": "Crea e modifica paggine.",
+       "apihelp-edit-param-title": "Tittolo da paggina da modificâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-edit-param-pageid": "ID de paggina da paggina da modificâ. O no poeu ese doeuviou insemme a <var>$1title</var>.",
+       "apihelp-edit-param-section": "Nummero de seçion. <kbd>0</kbd> pe-a seçion de d'ato, <kbd>new</kbd> pe 'na noeuva seçion.",
+       "apihelp-edit-param-sectiontitle": "O tittolo pe 'na noeuva seçion.",
+       "apihelp-edit-param-text": "Contegnuo da paggina.",
+       "apihelp-edit-param-summary": "Ogetto da modiffica. E ascì tittolo da seçion se $1sezione=new e $1sectiontitle o no l'è impostou.",
+       "apihelp-edit-param-tags": "Cangia i tag da apricâ a-a revixon.",
+       "apihelp-edit-param-minor": "Cangiamento menô.",
+       "apihelp-edit-param-notminor": "Cangiamento non-menô.",
+       "apihelp-edit-param-bot": "Marca sta modiffica comme bot.",
+       "apihelp-edit-param-createonly": "No modificâ a paggina s'a l'existe za.",
+       "apihelp-edit-param-nocreate": "O genera un errô se a paggina a no l'existe.",
+       "apihelp-edit-param-watch": "O l'azonze a paggina a-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-edit-param-unwatch": "O rimoeuve a pagina da-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-edit-param-redirect": "Resciorvi aotomaticamente i rimandi.",
+       "apihelp-edit-param-contentmodel": "Modello de contegnuo di noeuvi contegnui.",
+       "apihelp-edit-param-token": "O token o dev'ese delongo inviou comme urtimo parammetro, ò aomeno doppo o parametro $1text.",
+       "apihelp-edit-example-edit": "Modiffica 'na paggina.",
+       "apihelp-edit-example-prepend": "Antepon-i <kbd>_&#95;NOTOC_&#95;</kbd> a 'na paggina.",
+       "apihelp-emailuser-description": "Manda 'n'e-mail a 'n utente.",
+       "apihelp-emailuser-param-target": "Utente a chi inviâ l'e-mail.",
+       "apihelp-emailuser-param-subject": "Ogetto de l'e-mail.",
+       "apihelp-emailuser-param-text": "Testo de l'e-mail.",
+       "apihelp-emailuser-param-ccme": "Mandime una copia de questa mail.",
+       "apihelp-emailuser-example-email": "Manda un'e-mail a l'utente <kbd>WikiSysop</kbd> co-o testo <kbd>Content</kbd>.",
+       "apihelp-expandtemplates-description": "Espandi tutti i template into wikitesto.",
+       "apihelp-expandtemplates-param-title": "Tittolo da paggina.",
+       "apihelp-expandtemplates-param-text": "Wikitesto da convertî.",
+       "apihelp-expandtemplates-param-prop": "Quæ informaçion otegnî.\n\nNotta che se no l'è seleçionou arcun valô, o risultou o contegniâ o codiçe wiki, ma l'output o saiâ inte 'n formato obsoleto.",
+       "apihelp-expandtemplates-paramvalue-prop-wikitext": "O wikitext espanso.",
+       "apihelp-expandtemplates-paramvalue-prop-properties": "Propietæ da paggina definie da-e paole magiche esteise into wikitesto.",
+       "apihelp-expandtemplates-paramvalue-prop-volatile": "Se l'output o segge volatile e o no 'agge da ese riadoeuviou atr'onde a l'interno da paggina.",
+       "apihelp-expandtemplates-paramvalue-prop-ttl": "O tempo mascimo doppo o quæ e memoizaçioin tempoannie (cache) do risultou dovieivan ese invalidæ.",
+       "apihelp-feedcontributions-param-feedformat": "O formato do feed.",
+       "apihelp-feedrecentchanges-param-feedformat": "O formato do feed.",
+       "apihelp-feedrecentchanges-param-namespace": "Namespace a-o quæ limitâ i risultæ.",
+       "apihelp-feedrecentchanges-param-associated": "Inciodi namespace associou (discuscion ò prinçipâ)",
+       "apihelp-feedrecentchanges-param-days": "Intervallo de giorni pe-i quæ limitâ i risultæ.",
+       "apihelp-feedrecentchanges-param-limit": "Nummero mascimo di risultæ da restituî.",
+       "apihelp-feedrecentchanges-param-from": "Mostra i cangiamenti da alloa.",
+       "apihelp-feedrecentchanges-param-hideminor": "Ascondi e modiffiche menoî.",
+       "apihelp-feedrecentchanges-param-hidebots": "Ascondi e modiffiche fæte da di bot.",
+       "apihelp-feedrecentchanges-param-hideanons": "Ascondi e modiffiche fæte da di utenti anonnimi.",
+       "apihelp-feedrecentchanges-param-hideliu": "Ascondi e modiffiche fæte da-i utenti registræ.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Ascondi e modiffiche veificæ.",
+       "apihelp-feedrecentchanges-param-hidemyself": "O l'asconde e modiffiche fæte da l'utente attoale.",
+       "apihelp-feedrecentchanges-param-hidecategorization": "Ascondi e variaçioin d'apartegninça a-e categorie.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Filtra pe etichetta.",
+       "apihelp-feedrecentchanges-param-target": "Mostra solo e modifiche a-e paggine collegæ da questa paggina.",
+       "apihelp-feedrecentchanges-param-showlinkedto": "Fanni védde sôlo i cangiaménti a-e pàggine colegæ a-a quella speçificâ",
+       "apihelp-feedrecentchanges-param-categories": "Mostra solo e variaçioin in sce-e paggine de tutte queste categorie.",
+       "apihelp-feedrecentchanges-param-categories_any": "Mostra invece solo e variaçioin in sce-e paggine inte 'na qualonque categoria.",
+       "apihelp-feedrecentchanges-example-simple": "Mostra i urtime modiffiche.",
+       "apihelp-feedrecentchanges-example-30days": "Mostra e modifiche di urtimi 30 giorni.",
+       "apihelp-feedwatchlist-param-feedformat": "O formato do feed.",
+       "apihelp-feedwatchlist-param-hours": "Elenca e paggine modificæ inte quest'urtime oe.",
+       "apihelp-feedwatchlist-param-linktosections": "Collega direttamente a-e seçioin modificæ, se poscibbile.",
+       "apihelp-feedwatchlist-example-all6hrs": "Mostra tutte e modiffiche a-e pagine oservæ inti urtime 6 oe.",
+       "apihelp-filerevert-description": "Ripristina un file a 'na verscion precedente.",
+       "apihelp-filerevert-param-filename": "Nomme do file de destinaçion, sença o prefisso 'File:'.",
+       "apihelp-filerevert-param-comment": "Commento in sciô caregamento.",
+       "apihelp-filerevert-param-archivename": "Nomme de l'archivvio da verscion da ripristinâ.",
+       "apihelp-filerevert-example-revert": "Ripristina <kbd>Wiki.png</kbd> a-a verscion do <kbd>2011-03-05T15:27:40Z</kbd>.",
+       "apihelp-help-description": "Mostra a guidda pe-i modduli speçificæ.",
+       "apihelp-help-param-toc": "Inciodi un endexo inte l'output HTML.",
+       "apihelp-help-example-main": "Agiutto pe-o moddulo prinçipâ.",
+       "apihelp-help-example-submodules": "Agiutto pe <kbd>action=query</kbd> e tutti i so sotto-modduli.",
+       "apihelp-help-example-recursive": "Tutti i agiutti inte 'na paggina.",
+       "apihelp-help-example-help": "Agiutto pe-o moddulo d'agiutto mæximo.",
+       "apihelp-imagerotate-description": "Roeua un-a o ciù inmaggine.",
+       "apihelp-imagerotate-param-rotation": "Graddi de rotaçion de l'inmaggine in senso oaio.",
+       "apihelp-imagerotate-example-simple": "Roeua <kbd>File:Example.png</kbd> de <kbd>90</kbd> graddi.",
+       "apihelp-imagerotate-example-generator": "Roeua tutte e inmaggine in <kbd>Category:Flip</kbd> de <kbd>180</kbd> graddi.",
+       "apihelp-import-param-summary": "Ogetto into registro d'importaçion.",
+       "apihelp-import-param-xml": "File XML caregou.",
+       "apihelp-import-param-interwikisource": "Pe-e importaçioin interwiki: wiki da-e quæ importâ.",
+       "apihelp-import-param-interwikipage": "Pe-e importaçioin interwiki: paggina da importâ.",
+       "apihelp-import-param-fullhistory": "Pe-e importaçioin interwiki: importa l'intrega cronologia, non solo a verscion attoale.",
+       "apihelp-import-param-templates": "Pe-e importaçioin interwiki: importa tutti i template incioxi ascì.",
+       "apihelp-import-param-namespace": "Importa inte questo namespace. O no poeu ese doeuviou insemme a <var>$1rootpage</var>.",
+       "apihelp-import-param-rootpage": "Importa comme sottopaggina de questa paggina. O no poeu ese doeuviou insemme a <var>$1namespace</var>.",
+       "apihelp-import-example-import": "Importa [[meta:Help:ParserFunctions]] into namespace 100 con cronologia completa.",
+       "apihelp-linkaccount-description": "Colegamento de 'n'utença de 'n provider de terçe parte a l'utente corente.",
+       "apihelp-linkaccount-example-link": "Avvia o processo de collegamento a 'n'utença da <kbd>Example</kbd>.",
+       "apihelp-login-description": "Accedi e otegni i cookie d'aotenticaçion.\n\nQuest'açion dev'ese doeuviâ escluxivamente in combinaçion con [[Special:BotPasswords]]; doeuviâla pe l'accesso a l'account prinçipâ o l'è deprecou e o poeu fallî sença preaviso. Pe acedere in moddo seguo a l'utença prinçipâ, doeuvia <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
+       "apihelp-login-description-nobotpasswords": "Accedi e otegni i cookies d'aotenticaçion.\n\nQuest'açion a l'è deprecâ e a poeu fallî sença preaviso. Pe acede in moddo seguo, doeuvia [[Special:ApiHelp/clientlogin|action=clientlogin]].",
+       "apihelp-login-param-name": "Nomme utente.",
+       "apihelp-login-param-password": "Password.",
+       "apihelp-login-param-domain": "Dominnio (opçionâ).",
+       "apihelp-login-example-gettoken": "Recuppera un token de login.",
+       "apihelp-login-example-login": "Intra",
+       "apihelp-logout-description": "Sciorti e scassa i dæti da sescion.",
+       "apihelp-logout-example-logout": "Disconnetti l'utente attoale.",
+       "apihelp-mergehistory-description": "O l'unisce e cronologie de paggine.",
+       "apihelp-mergehistory-param-from": "O tittolo da paggina da-a quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "L'ID da paggina da-a quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "O tittolo da paggina inta quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "L'ID da paggina inta quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-timestamp": "O timestamp scin a-o quæle verscioin saian mesciæ da-a cronologia da paggina d'origine a quella da paggina de destinaçion. Se omisso, l'intrega cronologia da paggina d'origine a saiâ unia inta paggina de destinaçion.",
+       "apihelp-mergehistory-param-reason": "Raxon pe l'union da cronologia.",
+       "apihelp-mergehistory-example-merge": "Unisci l'intrega cronologia de <kbd>Oldpage</kbd> inte <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Unisci e verscioin da paggina <kbd>Oldpage</kbd> scin a <kbd>2015-12-31T04:37:41Z</kbd> inte <kbd>Newpage</kbd>.",
+       "apihelp-move-description": "Mescia 'na paggina",
+       "apihelp-move-param-from": "Tittolo da paggina da rinominâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-move-param-fromid": "ID de paggina da paggina da rinominâ. O no poeu ese doeuviou insemme a <var>$1title</var>.",
+       "apihelp-move-param-to": "Tittolo a-o quæ mesciâ a paggina.",
+       "apihelp-move-param-reason": "Raxon da rinommina.",
+       "apihelp-move-param-movetalk": "Rinommina a paggina de discuscion, s'a l'existe.",
+       "apihelp-move-param-movesubpages": "Rinommina e sottopaggine, se applicabile.",
+       "apihelp-move-param-noredirect": "No creâ un rinvio.",
+       "apihelp-move-param-watch": "Azonze a paggina e o redirect a-i oservæ speciali de l'utente attoale.",
+       "apihelp-move-param-unwatch": "Rimoeuvi a paggina e o redirect da-i oservæ speciali de l'utente attoale.",
+       "apihelp-move-param-ignorewarnings": "Ignora i messaggi d'avvertimento do scistema",
+       "apihelp-move-example-move": "Mescia <kbd>Badtitle</kbd> a <kbd>Goodtitle</kbd> sença lasciâ de redirect.",
+       "apihelp-opensearch-param-search": "Stringa de çerchia.",
+       "apihelp-opensearch-param-limit": "Nummero mascimo di risultæ da restituî.",
+       "apihelp-opensearch-param-suggest": "No stanni a fâ ninte se <var>[[mw:Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]]</var> o l'è faso.",
+       "apihelp-opensearch-param-format": "O formato de l'output.",
+       "apihelp-opensearch-example-te": "Troeuva e paggine che començan con <kbd>Te</kbd>.",
+       "apihelp-options-example-reset": "Reimposta tutte e preferençe.",
+       "apihelp-paraminfo-description": "Otegni de informaçioin in scî modduli API.",
+       "apihelp-paraminfo-param-helpformat": "Formato de stringhe d'agiutto.",
+       "apihelp-parse-param-summary": "Ogetto da analizâ.",
+       "apihelp-query+allcategories-param-prop": "Quæ propietæ otegnî:",
+       "apihelp-query+allcategories-paramvalue-prop-size": "Azonzi o nummero de paggine inta categoria.",
+       "apihelp-query+allcategories-paramvalue-prop-hidden": "Etichetta e categorie che son ascose con <code>_&#95;HIDDENCAT_&#95;</code>.",
+       "apihelp-query+allcategories-example-size": "Elenca e categorie con de informaçioin in sciô numero de paggine in ciascun-a.",
+       "apihelp-query+alldeletedrevisions-description": "Elenca tutte e verscioin scassæ da 'n utente ò inte 'n namespace.",
+       "apihelp-query+alldeletedrevisions-paraminfo-useronly": "O poeu ese doeuviou solo con <var>$3user</var>.",
+       "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "O no poeu ese doeuviou con <var>$3user</var>.",
+       "apihelp-query+alldeletedrevisions-param-start": "O timestamp da-o quæ començâ l'elenco.",
+       "apihelp-query+alldeletedrevisions-param-end": "O timestamp a-o quæ interrompî l'elenco.",
+       "apihelp-query+alldeletedrevisions-param-from": "Comença l'elenco a questo tittolo.",
+       "apihelp-query+alldeletedrevisions-param-to": "Interrompi l'elenco a questo titolo.",
+       "apihelp-query+alldeletedrevisions-param-prefix": "Riçerca pe tutti i titoli de pagine che començan con questo valô.",
+       "apihelp-query+alldeletedrevisions-param-user": "Elenca solo e verscioin de questo utente.",
+       "apihelp-query+alldeletedrevisions-param-excludeuser": "No elencâ e verscioin de questo utente.",
+       "apihelp-query+alldeletedrevisions-param-namespace": "Elenca solo e paggine inte questo namespace.",
+       "apihelp-query+alldeletedrevisions-example-user": "Elenca i urtimi 50 contributi scassæ de l'utente <kbd>Example</kbd>.",
+       "apihelp-query+alldeletedrevisions-example-ns-main": "Elenca e primme 50 verscioin scassæ into namespace prinçipâ.",
+       "apihelp-query+allfileusages-param-from": "O titolo do file da-o quæ començâ l'elenco.",
+       "apihelp-query+allfileusages-param-to": "O tittolo do file a-o quæ interrompî l'elenco.",
+       "apihelp-query+allfileusages-param-prefix": "Riçerca pe tutti i titoli di file che començan con questo valô.",
+       "apihelp-query+allfileusages-paramvalue-prop-title": "O l'azonze o tittolo do file.",
+       "apihelp-query+allfileusages-param-limit": "Quanti elementi totali restitoî.",
+       "apihelp-query+allfileusages-param-dir": "A direçion inta quæ elencâ.",
+       "apihelp-query+allfileusages-example-generator": "Otegni e paggine contegninte i file.",
+       "apihelp-query+allimages-param-sort": "Propietæ d'amerçamento.",
+       "apihelp-query+allimages-param-dir": "A direçion inta quæ elencâ.",
+       "apihelp-query+allimages-param-from": "O titolo de l'inmagine da-a quæ començâ l'elenco. O poeu ese doeuviou solo con $1sort=name."
+}
index d88e0ec..03cb2b5 100644 (file)
@@ -99,5 +99,6 @@
        "apihelp-query+watchlist-paramvalue-type-categorize": "वर्ग सदस्यता बदलते.",
        "apihelp-stashedit-param-title": "पानाच्या मथळ्याचे संपादन होत आहे.",
        "apihelp-stashedit-param-sectiontitle": "नविन विभागाचा मथळा",
-       "apihelp-stashedit-param-summary": "सारांश बदला."
+       "apihelp-stashedit-param-summary": "सारांश बदला.",
+       "api-help-examples": "{{PLURAL:$1|उदाहरण|उदाहरणे}}:"
 }
index e285130..6e85779 100644 (file)
@@ -51,7 +51,7 @@
        "apihelp-feedrecentchanges-param-tagfilter": "Filtrar per balisa.",
        "apihelp-filerevert-param-comment": "Telecargar lo comentari.",
        "apihelp-filerevert-param-archivename": "Nom d’archiu de la revision de restablir.",
-       "apihelp-import-param-summary": "Importar lo resumit.",
+       "apihelp-import-param-summary": "Resumit de l’importacion de l’entrada de jornal.",
        "apihelp-import-param-xml": "Fichièr XML telecargat.",
        "apihelp-login-param-name": "Nom d'utilizaire.",
        "apihelp-login-param-password": "Senhal.",
@@ -89,6 +89,7 @@
        "api-help-license-unknown": "Licéncia : <span class=\"apihelp-unknown\">desconeguda</span>",
        "api-help-parameters": "{{PLURAL:$1|Paramètre|Paramètres}} :",
        "api-help-param-deprecated": "Obsolèt.",
+       "api-help-param-required": "Aqueste paramètre es obligatòri.",
        "api-help-datatypes-header": "Tipe de donadas",
        "api-help-param-default": "Per defaut : $1",
        "api-credits-header": "Mercejaments"
index 6023be0..0dfd5f5 100644 (file)
        "apihelp-managetags-param-reason": "Opcjonalny powód utworzenia, usunięcia, włączenia lub wyłączenia znacznika.",
        "apihelp-managetags-param-ignorewarnings": "Czy zignorować ostrzeżenia, które pojawiają się w trakcie operacji.",
        "apihelp-mergehistory-description": "Łączenie historii edycji.",
+       "apihelp-mergehistory-param-reason": "Powód łączenia historii.",
        "apihelp-move-description": "Przenieś stronę.",
        "apihelp-move-param-reason": "Powód zmiany nazwy.",
        "apihelp-move-param-movetalk": "Zmień nazwę strony dyskusji, jeśli istnieje.",
index 650acb9..caa89b5 100644 (file)
        "apihelp-paraminfo-param-pagesetmodule": "{{doc-apihelp-param|paraminfo|pagesetmodule}}",
        "apihelp-paraminfo-param-formatmodules": "{{doc-apihelp-param|paraminfo|formatmodules}}",
        "apihelp-paraminfo-example-1": "{{doc-apihelp-example|paraminfo}}",
+       "apihelp-paraminfo-example-2": "{{doc-apihelp-example|paraminfo}}",
        "apihelp-parse-description": "{{doc-apihelp-description|parse}}",
        "apihelp-parse-param-title": "{{doc-apihelp-param|parse|title}}",
        "apihelp-parse-param-text": "{{doc-apihelp-param|parse|text}}",
        "apihelp-query+authmanagerinfo-description": "{{doc-apihelp-description|query+authmanagerinfo}}",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "{{doc-apihelp-param|query+authmanagerinfo|securitysensitiveoperation}}",
        "apihelp-query+authmanagerinfo-param-requestsfor": "{{doc-apihelp-param|query+authmanagerinfo|requestsfor}}",
-       "apihelp-query+filerepoinfo-example-login": "{{doc-apihelp-example|query+filerepoinfo}}",
-       "apihelp-query+filerepoinfo-example-login-merged": "{{doc-apihelp-example|query+filerepoinfo}}",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "{{doc-apihelp-example|query+filerepoinfo}}",
+       "apihelp-query+authmanagerinfo-example-login": "{{doc-apihelp-example|query+authmanagerinfo}}",
+       "apihelp-query+authmanagerinfo-example-login-merged": "{{doc-apihelp-example|query+authmanagerinfo}}",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "{{doc-apihelp-example|query+authmanagerinfo}}",
        "apihelp-query+backlinks-description": "{{doc-apihelp-description|query+backlinks}}",
        "apihelp-query+backlinks-param-title": "{{doc-apihelp-param|query+backlinks|title}}",
        "apihelp-query+backlinks-param-pageid": "{{doc-apihelp-param|query+backlinks|pageid}}",
index af95729..aa0d99a 100644 (file)
@@ -16,7 +16,8 @@
                        "Iniquity",
                        "Лилиә",
                        "Айсар",
-                       "Гизатуллина"
+                       "Гизатуллина",
+                       "MaxSem"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Документация]]\n* [[mw:API:FAQ|ЧаВО]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Почтовая рассылка]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Новости API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Ошибки и запросы]\n</div>\n<strong>Статус:</strong> Все отображаемые на этой странице функции должны работать, однако API находится в статусе активной разработки и может измениться в любой момент. Подпишитесь на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом «MediaWiki-API-Error», после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:API:Errors_and_warnings|API: Ошибки и предупреждения]].\n\n<strong>Тестирование:</strong> для удобства тестирования API-запросов, см. [[Special:ApiSandbox]].",
@@ -28,7 +29,7 @@
        "apihelp-main-param-requestid": "Любое заданное здесь значение будет включено в ответ. Может быть использовано для различения запросов.",
        "apihelp-main-param-servedby": "Включить в результаты имя хоста, обработавшего запрос.",
        "apihelp-main-param-curtimestamp": "Включить в результаты временную метку.",
-       "apihelp-main-param-origin": "При обращении к API, используя кросс-доменный AJAX-запрос (CORS), задайте параметру значение исходного домена. Он должен быть включён в любой предварительный запрос и таким образом должен быть частью URI-запроса (не тела POST).\n\nДля аутентифицированных запросов он должен точно соответствовать одному из источников в заголовке <code>Origin<code>, так что он должен быть задан наподобие <kbd>https://ru.wikipedia.org</kbd> или <kbd>https://meta.wikimedia.org</kbd>. Если параметр не соответствует заголовку <code>Origin<code>, будет возвращён ответ с кодом ошибки 403. Если параметр соответствует заголовку <code>Origin</code>, и источник находится в белом списке, будут установлены заголовки <code>Access-Control-Allow-Origin</code> и <code>Access-Control-Allow-Credentials</code>.\n\nДля неаутентифицированных запросов укажите значение <kbd>*</kbd>. Это приведёт к установке заголовка <code>Access-Control-Allow-Origin</code> заголовка должен быть установлен, но <code>Access-Control-Allow-Credentials</code> примет значение <code>false</code> и все пользовательские данные будут ограничены.",
+       "apihelp-main-param-origin": "При обращении к API, используя кросс-доменный AJAX-запрос (CORS), задайте параметру значение исходного домена. Он должен быть включён в любой предварительный запрос и таким образом должен быть частью URI-запроса (не тела POST).\n\nДля аутентифицированных запросов он должен точно соответствовать одному из источников в заголовке <code>Origin</code>, так что он должен быть задан наподобие <kbd>https://ru.wikipedia.org</kbd> или <kbd>https://meta.wikimedia.org</kbd>. Если параметр не соответствует заголовку <code>Origin</code>, будет возвращён ответ с кодом ошибки 403. Если параметр соответствует заголовку <code>Origin</code>, и источник находится в белом списке, будут установлены заголовки <code>Access-Control-Allow-Origin</code> и <code>Access-Control-Allow-Credentials</code>.\n\nДля неаутентифицированных запросов укажите значение <kbd>*</kbd>. Это приведёт к установке заголовка <code>Access-Control-Allow-Origin</code> заголовка должен быть установлен, но <code>Access-Control-Allow-Credentials</code> примет значение <code>false</code> и все пользовательские данные будут ограничены.",
        "apihelp-block-description": "Блокировка участника.",
        "apihelp-block-param-user": "Имя участника, IP-адрес или диапазон IP-адресов, которые вы хотите заблокировать.",
        "apihelp-block-param-reason": "Причина блокировки.",
        "api-help-param-type-boolean": "Тип: двоичный ([[Special:ApiHelp/main#main/datatypes|details]])",
        "api-help-param-type-timestamp": "Тип: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
        "api-help-param-type-user": "Тип: {{PLURAL:$1|1=user name|2=list of user names}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Одно из следующих значений|2=Значения (разделённые <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Одно из следующих значений|2=Значения (разделённые с помощью <kbd>{{!}}</kbd> или [[Special:ApiHelp/main#main/datatypes|альтернативного варианта]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Должен быть пустым|может быть пустым, или $2}}",
        "api-help-param-limit": "Не более чем $1 разрешено.",
        "api-help-param-limit2": "Разрешено не более чем $1 ($2 для ботов).",
        "api-help-param-integer-min": "{{PLURAL:$1|1=value|2=values}} должен быть не меньше чем $2.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=value|2=values}} должен быть не больше чем $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=value|2=values}} должен быть между $2 и $3.",
-       "api-help-param-multi-separate": "Разделяйте значения с помощью <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Разделяйте значения с помощью <kbd>|</kbd> или [[Special:ApiHelp/main#main/datatypes|альтернативного варианта]].",
        "api-help-param-multi-max": "Максимальное количество значений должно быть {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} для ботов).",
        "api-help-param-default": "По умолчанию: $1",
        "api-help-param-default-empty": "По умолчанию: <span class=\"apihelp-empty\">(пусто)</span>",
index 3205248..35389b0 100644 (file)
        "api-help-parameters": "{{PLURAL:$1|Parameter|Parametrar}}:",
        "api-help-param-deprecated": "Föråldrad.",
        "api-help-param-required": "Denna parameter är obligatorisk.",
-       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Måste vara tom|Kan vara tom, eller $2}}",
        "api-help-param-limit": "Inte mer än $1 tillåts.",
-       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts."
+       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts.",
+       "api-help-param-multi-separate": "Separera värden med <kbd>|</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]]."
 }
index 45b43df..1a079df 100644 (file)
        "apihelp-options-example-change": "Змінити налаштування <kbd>skin</kbd> та <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Скинути всі налаштування, потім встановити <kbd>skin</kbd> та <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Отримати інформацію про модулі API.",
-       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>, усі підмодулі через <kbd>+*</kbd> або усі підмодулі рекурсивно через <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Формат рядків довідки.",
        "apihelp-paraminfo-param-querymodules": "Список назв модулів запитів (значення параметра <var>prop</var>, <var>meta</var> або <var>list</var>). Використати <kbd>$1modules=query+foo</kbd> замість <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Отримати інформацію також про основний модуль (топ-рівень). Використати натомість <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Отримати також інформацію про модуль pageset (з вказанням titles= і рідних).",
        "apihelp-paraminfo-param-formatmodules": "Список назв модулів форматування (значення параметра <var>format</var>). Використати натомість <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Показати інформацію для <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> та <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Показати інформацію про всі підмодулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Аналізує вміст і видає парсер виходу.\n\nДив. різні prop-модулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>, щоб отримати інформацію з поточної версії сторінки.\n\nЄ декілька способів вказати текст для аналізу:\n# Вказати сторінку або версію, використавши <var>$1page</var>, <var>$1pageid</var> або <var>$1oldid</var>.\n# Вказати безпосередньо, використавши <var>$1text</var>, <var>$1title</var> і <var>$1contentmodel</var>.\n# Вказати лише підсумок аналізу. <var>$1prop</var> повинен мати порожнє значення.",
        "apihelp-parse-param-title": "Назва сторінки, якій належить текст. Якщо пропущена, має бути вказано <var>$1contentmodel</var>, а як назву буде вжито [[API]].",
        "apihelp-parse-param-text": "Текст для аналізу. Використати <var>$1title</var> або <var>$1contentmodel</var> для контролю моделі вмісту.",
        "apihelp-query+authmanagerinfo-description": "Отримати інформацію про поточний стан автентифікації.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Перевірити, чи поточний стан автентифікації користувача є достатнім для даної конфіденційної операції.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Отримати інформацію про запити автентифікації, потрібні для даної дії автентифікації.",
-       "apihelp-query+filerepoinfo-example-login": "Вибірка запитів, що можуть бути використані при початку входу.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Отримати запити, які можуть бути використані при початку входу, з об'єднаними полями форми.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Перевірити чи автентифікація є достатньою для дії <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Вибірка запитів, що можуть бути використані при початку входу.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Отримати запити, які можуть бути використані при початку входу, з об'єднаними полями форми.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Перевірити чи автентифікація є достатньою для дії <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Знайти усі сторінки, що посилаються на подану сторінку.",
        "apihelp-query+backlinks-param-title": "Назва для пошуку. Не можна використати разом з <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "ID сторінки для пошуку. Не можна використати разом з <var>$1title</var>.",
        "api-help-param-deprecated": "Застарілий.",
        "api-help-param-required": "Цей параметр є обов'язковим.",
        "api-help-datatypes-header": "Типи даних",
-       "api-help-datatypes": "Ð\94еÑ\8fкÑ\96 Ñ\82ипи Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в Ñ\83 Ð·Ð°Ð¿Ð¸Ñ\82аÑ\85 API Ð¿Ð¾Ñ\82Ñ\80ебÑ\83Ñ\8eÑ\82Ñ\8c Ñ\88иÑ\80Ñ\88ого Ð¿Ð¾Ñ\8fÑ\81неннÑ\8f:\n;boolean\n:Ð\9bогÑ\96Ñ\87нÑ\96 Ð¿Ð°Ñ\80амеÑ\82Ñ\80и Ð¿Ñ\80аÑ\86Ñ\8eÑ\8eÑ\82Ñ\8c Ñ\8fк Ð³Ð°Ð»Ð¾Ñ\87ки HTML: Ñ\8fкÑ\89о Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð²ÐºÐ°Ð·Ð°Ð½Ð¾, Ð½Ðµ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾ Ð²Ñ\96д Ð·Ð½Ð°Ñ\87еннÑ\8f, Ð²Ñ\96н Ð²Ð²Ð°Ð¶Ð°Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\96Ñ\81Ñ\82инним. Ð©Ð¾Ð± Ð·Ð½Ð°Ñ\87еннÑ\8f Ð±Ñ\83ло Ñ\85ибним, Ð¿Ñ\80опÑ\83Ñ\81Ñ\82Ñ\96Ñ\82Ñ\8c Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð·Ð¾Ð²Ñ\81Ñ\96м.\n;timestamp\n:ЧаÑ\81овÑ\96 Ð¼Ñ\96Ñ\82ки Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð²ÐºÐ°Ð·Ð°Ð½Ñ\96 Ñ\83 ÐºÑ\96лÑ\8cкоÑ\85 Ñ\84оÑ\80маÑ\82аÑ\85. Ð ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\87аÑ\81 Ñ\96 Ð´Ð°Ñ\82а Ð² ISO 8601. Ð£Ñ\81Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8f Ñ\87аÑ\81Ñ\83 Ð² UTC, Ð±Ñ\83дÑ\8c\8fкÑ\96 Ñ\87аÑ\81овÑ\96 Ð¿Ð¾Ñ\8fÑ\81и Ñ\96гноÑ\80Ñ\83Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f.\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (пÑ\83нкÑ\82Ñ\83аÑ\86Ñ\96Ñ\8f Ñ\96 <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзоквÑ\96)\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601 Ð· (Ñ\96гноÑ\80ованими) Ñ\87аÑ\81Ñ\82ками Ñ\81екÑ\83нди, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (деÑ\84Ñ\96Ñ\81и, Ð´Ð²Ð¾ÐºÑ\80апки Ñ\82а <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзковÑ\96)\n:* Ð¤Ð¾Ñ\80маÑ\82 MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Ð\97агалÑ\8cний Ñ\87иÑ\81ловий Ñ\84оÑ\80маÑ\82, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (необов'Ñ\8fзковий Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> Ð°Ð±Ð¾ <kbd>-<var>##</var></kbd> Ñ\96гноÑ\80Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f)\n:* Ð¤Ð¾Ñ\80маÑ\82 EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 2822 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 850 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 C ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Ð¡ÐµÐºÑ\83нди Ð²Ñ\96д 1970-01-01T00:00:00Z Ñ\83 Ð²Ð¸Ð³Ð»Ñ\8fдÑ\96 Ñ\86Ñ\96лого Ñ\87иÑ\81ла Ð²Ñ\96д 1 Ð´Ð¾ 13 Ñ\86иÑ\84Ñ\80 (без <kbd>0</kbd>)\n:* Ð Ñ\8fдок <kbd>now</kbd>",
+       "api-help-datatypes": "Ð\92Ñ\85Ñ\96днÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\83 MediaWiki Ð¼Ð°Ñ\8eÑ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð² NFC-ноÑ\80малÑ\96зованомÑ\83 UTF-8. MediaWiki Ð¼Ð¾Ð¶Ðµ Ñ\81пÑ\80обÑ\83ваÑ\82и ÐºÐ¾Ð½Ð²ÐµÑ\80Ñ\82Ñ\83ваÑ\82и Ð²Ñ\85Ñ\96днÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\96нÑ\88ого Ð²Ð¸Ð³Ð»Ñ\8fдÑ\83, Ð°Ð»Ðµ Ð²Ñ\96д Ñ\86Ñ\8cого Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð¿Ð¾Ñ\81Ñ\82Ñ\80аждаÑ\82и Ð´ÐµÑ\8fкÑ\96 Ð¾Ð¿ÐµÑ\80аÑ\86Ñ\96Ñ\97 (Ñ\8fк [[Special:ApiHelp/edit|Ñ\80едагÑ\83ваннÑ\8f]] Ð· Ð¿ÐµÑ\80евÑ\96Ñ\80коÑ\8e MD5).\n\nÐ\94еÑ\8fкÑ\96 Ñ\82ипи Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в Ñ\83 Ð·Ð°Ð¿Ð¸Ñ\82аÑ\85 API Ð¿Ð¾Ñ\82Ñ\80ебÑ\83Ñ\8eÑ\82Ñ\8c Ñ\88иÑ\80Ñ\88ого Ð¿Ð¾Ñ\8fÑ\81неннÑ\8f:\n;boolean\n:Ð\9bогÑ\96Ñ\87нÑ\96 Ð¿Ð°Ñ\80амеÑ\82Ñ\80и Ð¿Ñ\80аÑ\86Ñ\8eÑ\8eÑ\82Ñ\8c Ñ\8fк Ð³Ð°Ð»Ð¾Ñ\87ки HTML: Ñ\8fкÑ\89о Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð²ÐºÐ°Ð·Ð°Ð½Ð¾, Ð½Ðµ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾ Ð²Ñ\96д Ð·Ð½Ð°Ñ\87еннÑ\8f, Ð²Ñ\96н Ð²Ð²Ð°Ð¶Ð°Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\96Ñ\81Ñ\82инним. Ð©Ð¾Ð± Ð·Ð½Ð°Ñ\87еннÑ\8f Ð±Ñ\83ло Ñ\85ибним, Ð¿Ñ\80опÑ\83Ñ\81Ñ\82Ñ\96Ñ\82Ñ\8c Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð·Ð¾Ð²Ñ\81Ñ\96м.\n;timestamp\n:ЧаÑ\81овÑ\96 Ð¼Ñ\96Ñ\82ки Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð²ÐºÐ°Ð·Ð°Ð½Ñ\96 Ñ\83 ÐºÑ\96лÑ\8cкоÑ\85 Ñ\84оÑ\80маÑ\82аÑ\85. Ð ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\87аÑ\81 Ñ\96 Ð´Ð°Ñ\82а Ð² ISO 8601. Ð£Ñ\81Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8f Ñ\87аÑ\81Ñ\83 Ð² UTC, Ð±Ñ\83дÑ\8c\8fкÑ\96 Ñ\87аÑ\81овÑ\96 Ð¿Ð¾Ñ\8fÑ\81и Ñ\96гноÑ\80Ñ\83Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f.\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (пÑ\83нкÑ\82Ñ\83аÑ\86Ñ\96Ñ\8f Ñ\96 <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзоквÑ\96)\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601 Ð· (Ñ\96гноÑ\80ованими) Ñ\87аÑ\81Ñ\82ками Ñ\81екÑ\83нди, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (деÑ\84Ñ\96Ñ\81и, Ð´Ð²Ð¾ÐºÑ\80апки Ñ\82а <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзковÑ\96)\n:* Ð¤Ð¾Ñ\80маÑ\82 MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Ð\97агалÑ\8cний Ñ\87иÑ\81ловий Ñ\84оÑ\80маÑ\82, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (необов'Ñ\8fзковий Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> Ð°Ð±Ð¾ <kbd>-<var>##</var></kbd> Ñ\96гноÑ\80Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f)\n:* Ð¤Ð¾Ñ\80маÑ\82 EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 2822 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 850 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 C ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Ð¡ÐµÐºÑ\83нди Ð²Ñ\96д 1970-01-01T00:00:00Z Ñ\83 Ð²Ð¸Ð³Ð»Ñ\8fдÑ\96 Ñ\86Ñ\96лого Ñ\87иÑ\81ла Ð²Ñ\96д 1 Ð´Ð¾ 13 Ñ\86иÑ\84Ñ\80 (без <kbd>0</kbd>)\n:* Ð Ñ\8fдок <kbd>now</kbd>\n;алÑ\8cÑ\82еÑ\80наÑ\82ивний Ñ\80оздÑ\96лÑ\8cник Ð±Ð°Ð³Ð°Ñ\82Ñ\8cоÑ\85 Ð·Ð½Ð°Ñ\87енÑ\8c\n:Ð\9fаÑ\80амеÑ\82Ñ\80и, Ñ\89о Ð¿Ñ\80иймаÑ\8eÑ\82Ñ\8c Ð±Ð°Ð³Ð°Ñ\82о Ð·Ð½Ð°Ñ\87енÑ\8c, Ð·Ð°Ð·Ð²Ð¸Ñ\87ай Ð¿Ð¾Ð´Ð°Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f Ð·Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8fми, Ñ\80оздÑ\96леними Ð²ÐµÑ\80Ñ\82икалÑ\8cноÑ\8e Ñ\80иÑ\81коÑ\8e, Ð½Ð°Ð¿Ñ\80иклад, <kbd>param=value1|value2</kbd> Ð°Ð±Ð¾ <kbd>param=value1%7Cvalue2</kbd>. Ð¯ÐºÑ\89о Ð·Ð½Ð°Ñ\87еннÑ\8f Ð¿Ð¾Ð²Ð¸Ð½Ð½Ðµ Ð¼Ñ\96Ñ\81Ñ\82иÑ\82и Ð²ÐµÑ\80Ñ\82икалÑ\8cнÑ\83 Ñ\80иÑ\81кÑ\83, Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83йÑ\82е Ñ\8fк Ñ\80оздÑ\96лÑ\8cник U+001F (Ñ\80оздÑ\96лÑ\8cник Ð¾Ð´Ð¸Ð½Ð¸Ñ\86Ñ\8c) ''Ñ\82а'' Ð¿Ð¾Ñ\81Ñ\82авÑ\82е U+001F Ð¿ÐµÑ\80ед Ð·Ð½Ð°Ñ\87еннÑ\8fм, Ð½Ð°Ð¿Ñ\80иклад, <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "Тип: ціле число або <kbd>max</kbd>",
        "api-help-param-type-integer": "Тип: {{PLURAL:$1|1=ціле число|2=список цілих чисел}}",
        "api-help-param-type-boolean": "Тип: логічний ([[Special:ApiHelp/main#main/datatypes|деталі]])",
        "api-help-param-type-timestamp": "Тип: {{PLURAL:$1|1=часова мітка|2=список часових міток}} ([[Special:ApiHelp/main#main/datatypes|дозволені формати]])",
        "api-help-param-type-user": "Тип: {{PLURAL:$1|1=ім'я користувача|2=список імен користувачів}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Одне з наступних значень|2=Значення (розділені через <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Одне з наступних значень|2=Значення (розділені через <kbd>{{!}}</kbd> або [[Special:ApiHelp/main#main/datatypes|альтернативу]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Повинно бути пустим|Може бути пустим або $2}}",
        "api-help-param-limit": "Дозволено не більше $1.",
        "api-help-param-limit2": "Дозволено не більше $1 ($2 для ботів).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Значення має бути|2=Значення мають бути}} не більше $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Значення має бути|2=Значення мають бути}} між $2 і $3.",
        "api-help-param-upload": "Повинно бути надіслано у формі надсилання файлу використовуючи multipart/form-data.",
-       "api-help-param-multi-separate": "Розділіть значення з допомогою <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Розділіть значення з допомогою <kbd>|</kbd> або [[Special:ApiHelp/main#main/datatypes|альтернативу]].",
        "api-help-param-multi-max": "Максимальна кількість значень — {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} для ботів).",
        "api-help-param-default": "За замовчуванням: $1",
        "api-help-param-default-empty": "За замовчуванням: <span class=\"apihelp-empty\">(пусто)</span>",
index 860b7a7..573748d 100644 (file)
        "apihelp-options-example-change": "更改<kbd>skin</kbd>和<kbd>hideminor</kbd>设置。",
        "apihelp-options-example-complex": "重置所有设置,然后设置<kbd>skin</kbd>和<kbd>nickname</kbd>。",
        "apihelp-paraminfo-description": "获得关于API模块的信息。",
-       "apihelp-paraminfo-param-modules": "模块名称(<var>action</var>和<var>format</var>参数值,或<kbd>main</kbd>)的列表。可通过<kbd>+</kbd>指定子模块。",
+       "apihelp-paraminfo-param-modules": "模块名称(<var>action</var>和<var>format</var>参数值,或<kbd>main</kbd>)的列表。可通过<kbd>+</kbd>指定子模块,或通过<kbd>+*</kbd>指定所有子模块,或通过<kbd>+**</kbd>指定所有递归子模块。",
        "apihelp-paraminfo-param-helpformat": "帮助字符串的格式。",
        "apihelp-paraminfo-param-querymodules": "查询模块名称(<var>prop</var>、<var>meta</var>或<var>list</var>参数值)的列表。使用<kbd>$1modules=query+foo</kbd>而不是<kbd>$1querymodules=foo</kbd>。",
        "apihelp-paraminfo-param-mainmodule": "获取有关主要(最高级)模块的信息。也可使用<kbd>$1modules=main</kbd>。",
        "apihelp-paraminfo-param-pagesetmodule": "获取有关页面设置模块(提供titles=和朋友)的信息。",
        "apihelp-paraminfo-param-formatmodules": "格式模块名称(<var>format</var>参数的值)的列表。也可使用<var>$1modules</var>。",
        "apihelp-paraminfo-example-1": "显示<kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>、<kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>、<kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>和<kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>的信息。",
+       "apihelp-paraminfo-example-2": "显示<kbd>[[Special:ApiHelp/query|action=query]]</kbd>的所有子模块的信息。",
        "apihelp-parse-description": "解析内容并返回解析器输出。\n\n参见<kbd>[[Special:ApiHelp/query|action=query]]</kbd>的各种prop-module以从页面的当前版本获得信息。\n\n这里有几种方法可以指定解析的文本:\n# 指定一个页面或修订,使用<var>$1page</var>、<var>$1pageid</var>或<var>$1oldid</var>。\n# 明确指定内容,使用<var>$1text</var>、<var>$1title</var>和<var>$1contentmodel</var>。\n# 只指定一段摘要解析。<var>$1prop</var>应提供一个空值。",
        "apihelp-parse-param-title": "文本属于的页面标题。如果省略,<var>$1contentmodel</var>就必须被指定,且[[API]]将作为标题使用。",
        "apihelp-parse-param-text": "要解析的文本。使用<var>$1title</var>或<var>$1contentmodel</var>以控制内容模型。",
        "apihelp-query+authmanagerinfo-description": "检索有关当前身份验证状态的信息。",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "测试用户当前的身份验证状态是否足够用于指定的安全敏感操作。",
        "apihelp-query+authmanagerinfo-param-requestsfor": "取得指定身份验证操作所需的有关身份验证请求的信息。",
-       "apihelp-query+filerepoinfo-example-login": "检索当开始登录时可能使用的请求。",
-       "apihelp-query+filerepoinfo-example-login-merged": "检索当开始登录时可能使用的请求,并合并表单字段。",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "测试身份验证对操作<kbd>foo</kbd>是否足够。",
+       "apihelp-query+authmanagerinfo-example-login": "检索当开始登录时可能使用的请求。",
+       "apihelp-query+authmanagerinfo-example-login-merged": "检索当开始登录时可能使用的请求,并合并表单字段。",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "测试身份验证对操作<kbd>foo</kbd>是否足够。",
        "apihelp-query+backlinks-description": "查找所有链接至指定页面的页面。",
        "apihelp-query+backlinks-param-title": "要搜索的标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-query+backlinks-param-pageid": "要搜索的页面ID。不能与<var>$1title</var>一起使用。",
        "apihelp-query+search-paramvalue-prop-sectionsnippet": "Adds a parsed snippet of the matching section title.",
        "apihelp-query+search-paramvalue-prop-sectiontitle": "Adds the title of the matching section.",
        "apihelp-query+search-paramvalue-prop-categorysnippet": "Adds a parsed snippet of the matching category.",
-       "apihelp-query+search-paramvalue-prop-isfilematch": "Adds a boolean indicating if the search matched file content.",
+       "apihelp-query+search-paramvalue-prop-isfilematch": "添加布尔值,表明搜索是否匹配文件内容。",
        "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">已弃用并已忽略。</span>",
        "apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">已弃用并已忽略。</span>",
        "apihelp-query+search-param-limit": "返回的总计页面数。",
        "api-help-param-deprecated": "已弃用。",
        "api-help-param-required": "这个参数是必须的。",
        "api-help-datatypes-header": "数据类型",
-       "api-help-datatypes": "一些在API请求中的参数类型需要更进一步解释:\n;boolean\n:布尔参数就像HTML复选框一样工作:如果指定参数,无论何值都被认为是真。如果要假值,则可完全忽略参数。\n;timestamp\n:时间戳可被指定为很多格式。推荐使用ISO 8601日期和时间标准。所有时间为UTC时间,包含的任何时区会被忽略。\n:* ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>(标点和<kbd>Z</kbd>是可选项)\n:* 带小数秒(会被忽略)的ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd>(破折号、括号和<kbd>Z</kbd>是可选的)\n:* MediaWiki格式,<kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 一般数字格式,<kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>(<kbd>GMT</kbd>、<kbd>+<var>##</var></kbd>或<kbd>-<var>##</var></kbd>的可选时区会被忽略)\n:* EXIF格式,<kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 2822格式(时区可能会被省略),<kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850格式(时区可能会被省略),<kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime格式,<kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 秒数是从1970-01-01T00:00:00Z开始,作为1到13位数的整数(除了<kbd>0</kbd>)\n:* 字符串<kbd>now</kbd>",
+       "api-help-datatypes": "至MediaWiki的输入应为NFC标准化的UTF-8。MediaWiki可以尝试转换其他输入,但这可能导致一些操作失败(例如[[Special:ApiHelp/edit|edits]]与MD5校验)。\n\n一些在API请求中的参数类型需要更进一步解释:\n;boolean\n:布尔参数就像HTML复选框一样工作:如果指定参数,无论何值都被认为是真。如果要假值,则可完全忽略参数。\n;timestamp\n:时间戳可被指定为很多格式。推荐使用ISO 8601日期和时间标准。所有时间为UTC时间,包含的任何时区会被忽略。\n:* ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>(标点和<kbd>Z</kbd>是可选项)\n:* 带小数秒(会被忽略)的ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd>(破折号、括号和<kbd>Z</kbd>是可选的)\n:* MediaWiki格式,<kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 一般数字格式,<kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>(<kbd>GMT</kbd>、<kbd>+<var>##</var></kbd>或<kbd>-<var>##</var></kbd>的可选时区会被忽略)\n:* EXIF格式,<kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 2822格式(时区可能会被省略),<kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850格式(时区可能会被省略),<kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime格式,<kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 秒数是从1970-01-01T00:00:00Z开始,作为1到13位数的整数(除了<kbd>0</kbd>)\n:* 字符串<kbd>now</kbd>\n;替代多值分隔符\n:使用多个值的参数通常会与管道符号分隔的值一起提交,例如<kbd>param=value1|value2</kbd>或<kbd>param=value1%7Cvalue2</kbd>。如果值必须包含管道符号,使用U+001F(单位分隔符)作为分隔符,''并''在值前加前缀U+001F,例如<kbd>param=%1Fvalue1%1Fvalue2</kbd>。",
        "api-help-param-type-limit": "类型:整数或<kbd>max</kbd>",
        "api-help-param-type-integer": "类型:{{PLURAL:$1|1=整数|2=整数列表}}",
        "api-help-param-type-boolean": "类型:布尔值([[Special:ApiHelp/main#main/datatypes|详细信息]])",
        "api-help-param-type-timestamp": "类型:{{PLURAL:$1|1=时间戳|2=时间戳列表}}([[Special:ApiHelp/main#main/datatypes|允许格式]])",
        "api-help-param-type-user": "类型:{{PLURAL:$1|1=用户名|2=用户名列表}}",
-       "api-help-param-list": "{{PLURAL:$1|1=以下值中的一个|2=值(以<kbd>{{!}}</kbd>分隔)}}:$2",
+       "api-help-param-list": "{{PLURAL:$1|1=以下值中的一个|2=值(以<kbd>{{!}}</kbd>或[[Special:ApiHelp/main#main/datatypes|替代物]]分隔)}}:$2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=必须为空|可以为空,或$2}}",
        "api-help-param-limit": "不允许超过$1。",
        "api-help-param-limit2": "不允许超过$1个(对于机器人则是$2个)。",
        "api-help-param-integer-max": "{{PLURAL:$1|值}}必须不大于$3。",
        "api-help-param-integer-minmax": "{{PLURAL:$1|值}}必须介于$2和$3之间。",
        "api-help-param-upload": "必须被公布为使用multipart/form-data的一次文件上传。",
-       "api-help-param-multi-separate": "通过“<kbd>|</kbd>”隔开各值。",
+       "api-help-param-multi-separate": "通过<kbd>|</kbd>或[[Special:ApiHelp/main#main/datatypes|替代物]]隔开各值。",
        "api-help-param-multi-max": "值的最高数字是{{PLURAL:$1|$1}}(对于机器人则是{{PLURAL:$2|$2}})。",
        "api-help-param-default": "默认:$1",
        "api-help-param-default-empty": "默认:<span class=\"apihelp-empty\">(空)</span>",
        "api-help-permissions-granted-to": "{{PLURAL:$1|授予}}:$2",
        "api-help-right-apihighlimits": "在API查询中使用更高的上限(慢查询:$1;快查询:$2)。慢查询的限制也适用于多值参数。",
        "api-help-open-in-apisandbox": "<small>[在沙盒中打开]</small>",
-       "api-help-authmanager-general-usage": "使用此模块的一般程序是:\n# 通过<kbd>amirequestsfor=$4</kbd>取得来自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的可用字段,和来自<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>的<kbd>$5</kbd>令牌。\n# 向用户显示字段,并获得其提交内容。\n# 发送至此模块,提供<var>$1returnurl</var>及任何相关字段。\n# Check the <samp>status</samp> in the response.\n#* If you received <samp>PASS</samp> or <samp>FAIL</samp>, you're done. The operation either succeeded or it didn't.\n#* If you received <samp>UI</samp>, present the new fields to the user and obtain their submission. Then post to this module with <var>$1continue</var> and the relevant fields set, and repeat step 4.\n#* If you received <samp>REDIRECT</samp>, direct the user to the <samp>redirecttarget</samp> and wait for the return to <var>$1returnurl</var>. Then post to this module with <var>$1continue</var> and any fields passed to the return URL, and repeat step 4.\n#* If you received <samp>RESTART</samp>, that means the authentication worked but we don't have a linked user account. You might treat this as <samp>UI</samp> or as <samp>FAIL</samp>.",
+       "api-help-authmanager-general-usage": "使用此模块的一般程序是:\n# 通过<kbd>amirequestsfor=$4</kbd>取得来自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的可用字段,和来自<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>的<kbd>$5</kbd>令牌。\n# 向用户显示字段,并获得其提交内容。\n# 发送至此模块,提供<var>$1returnurl</var>及任何相关字段。\n# 在响应中检查<samp>status</samp>。\n#* 如果您收到了<samp>PASS</samp>或<samp>FAIL</samp>,您已经完成。The operation either succeeded or it didn't.\n#* 如果您收到了<samp>UI</samp>,present the new fields to the user and obtain their submission. Then post to this module with <var>$1continue</var> and the relevant fields set, and repeat step 4.\n#* 如果您收到了<samp>REDIRECT</samp>,direct the user to the <samp>redirecttarget</samp> and wait for the return to <var>$1returnurl</var>. Then post to this module with <var>$1continue</var> and any fields passed to the return URL, and repeat step 4.\n#* 如果您收到了<samp>RESTART</samp>,that means the authentication worked but we don't have a linked user account. You might treat this as <samp>UI</samp> or as <samp>FAIL</samp>.",
        "api-help-authmanagerhelper-request": "使用此身份验证请求,通过返回自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的<samp>id</samp>与<kbd>amirequestsfor=$1</kbd>。",
        "api-help-authmanagerhelper-messageformat": "返回消息使用的格式。",
        "api-help-authmanagerhelper-mergerequestfields": "合并用于所有身份验证请求的字段信息至一个数组中。",
+       "api-help-authmanagerhelper-preservestate": "从之前失败的登录尝试中保持状态,如果可能。",
+       "api-help-authmanagerhelper-continue": "此请求是在早先的<samp>UI</samp>或<samp>REDIRECT</samp>响应之后的附加请求。必需此值或<var>$1returnurl</var>。",
        "api-help-authmanagerhelper-additional-params": "此模块允许额外参数,取决于可用的身份验证请求。使用<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>与<kbd>amirequestsfor=$1</kbd>(或之前来自此模块的相应,如果可以)以决定可用请求及其使用的字段。",
        "api-credits-header": "制作人员",
        "api-credits": "API 开发人员:\n* Yuri Astrakhan(创建者,2006年9月~2007年9月的开发组领导)\n* Roan Kattouw(2007年9月~2009年的开发组领导)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Brad Jorsch(2013年至今的开发组领导)\n\n请将您的评论、建议和问题发送至mediawiki-api@lists.wikimedia.org,或提交错误请求至https://phabricator.wikimedia.org/。"
index acdc01b..51efe56 100644 (file)
@@ -37,8 +37,46 @@ use WebRequest;
  * In the future, it may also serve as the entry point to the authorization
  * system.
  *
+ * If you are looking at this because you are working on an extension that creates its own
+ * login or signup page, then 1) you really shouldn't do that, 2) if you feel you absolutely
+ * have to, subclass AuthManagerSpecialPage or build it on the client side using the clientlogin
+ * or the createaccount API. Trying to call this class directly will very likely end up in
+ * security vulnerabilities or broken UX in edge cases.
+ *
+ * If you are working on an extension that needs to integrate with the authentication system
+ * (e.g. by providing a new login method, or doing extra permission checks), you'll probably
+ * need to write an AuthenticationProvider.
+ *
+ * If you want to create a "reserved" user programmatically, User::newSystemUser() might be what
+ * you are looking for. If you want to change user data, use User::changeAuthenticationData().
+ * Code that is related to some SessionProvider or PrimaryAuthenticationProvider can
+ * create a (non-reserved) user by calling AuthManager::autoCreateUser(); it is then the provider's
+ * responsibility to ensure that the user can authenticate somehow (see especially
+ * PrimaryAuthenticationProvider::autoCreatedAccount()).
+ * If you are writing code that is not associated with such a provider and needs to create accounts
+ * programmatically for real users, you should rethink your architecture. There is no good way to
+ * do that as such code has no knowledge of what authentication methods are enabled on the wiki and
+ * cannot provide any means for users to access the accounts it would create.
+ *
+ * The two main control flows when using this class are as follows:
+ * * Login, user creation or account linking code will call getAuthenticationRequests(), populate
+ *   the requests with data (by using them to build a HTMLForm and have the user fill it, or by
+ *   exposing a form specification via the API, so that the client can build it), and pass them to
+ *   the appropriate begin* method. That will return either a success/failure response, or more
+ *   requests to fill (either by building a form or by redirecting the user to some external
+ *   provider which will send the data back), in which case they need to be submitted to the
+ *   appropriate continue* method and that step has to be repeated until the response is a success
+ *   or failure response. AuthManager will use the session to maintain internal state during the
+ *   process.
+ * * Code doing an authentication data change will call getAuthenticationRequests(), select
+ *   a single request, populate it, and pass it to allowsAuthenticationDataChange() and then
+ *   changeAuthenticationData(). If the data change is user-initiated, the whole process needs
+ *   to be preceded by a call to securitySensitiveOperationStatus() and aborted if that returns
+ *   a non-OK status.
+ *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 class AuthManager implements LoggerAwareInterface {
        /** Log in with an existing (not necessarily local) user */
@@ -568,6 +606,7 @@ class AuthManager implements LoggerAwareInterface {
 
                        $user = User::newFromName( $res->username, 'usable' );
                        if ( !$user ) {
+                               $provider = $this->getAuthenticationProvider( $state['primary'] );
                                throw new \DomainException(
                                        get_class( $provider ) . " returned an invalid username: {$res->username}"
                                );
@@ -643,6 +682,7 @@ class AuthManager implements LoggerAwareInterface {
                        $this->logger->info( 'Login for {user} succeeded', [
                                'user' => $user->getName(),
                        ] );
+                       /** @var RememberMeAuthenticationRequest $req */
                        $req = AuthenticationRequest::getRequestByClass(
                                $beginReqs, RememberMeAuthenticationRequest::class
                        );
@@ -737,7 +777,10 @@ class AuthManager implements LoggerAwareInterface {
        /**
         * Determine whether a username can authenticate
         *
-        * @param string $username
+        * This is mainly for internal purposes and only takes authentication data into account,
+        * not things like blocks that can change without the authentication system being aware.
+        *
+        * @param string $username MediaWiki username
         * @return bool
         */
        public function userCanAuthenticate( $username ) {
@@ -832,6 +875,9 @@ class AuthManager implements LoggerAwareInterface {
         * If $req was returned for AuthManager::ACTION_REMOVE, using $req should
         * no longer result in a successful login.
         *
+        * This method should only be called if allowsAuthenticationDataChange( $req, true )
+        * returned success.
+        *
         * @param AuthenticationRequest $req
         */
        public function changeAuthenticationData( AuthenticationRequest $req ) {
@@ -871,7 +917,7 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Determine whether a particular account can be created
-        * @param string $username
+        * @param string $username MediaWiki username
         * @param array $options
         *  - flags: (int) Bitfield of User:READ_* constants, default User::READ_NORMAL
         *  - creating: (bool) For internal use only. Never specify this.
@@ -1354,7 +1400,7 @@ class AuthManager implements LoggerAwareInterface {
                                        'creator' => $creator->getName(),
                                ] );
                                $status = $user->addToDatabase();
-                               if ( !$status->isOk() ) {
+                               if ( !$status->isOK() ) {
                                        // @codeCoverageIgnoreStart
                                        $ret = AuthenticationResponse::newFail( $status->getMessage() );
                                        $this->callMethodOnProviders( 7, 'postAccountCreation', [ $user, $creator, $ret ] );
@@ -1385,6 +1431,7 @@ class AuthManager implements LoggerAwareInterface {
                                        );
                                        $logEntry->setPerformer( $isAnon ? $user : $creator );
                                        $logEntry->setTarget( $user->getUserPage() );
+                                       /** @var CreationReasonAuthenticationRequest $req */
                                        $req = AuthenticationRequest::getRequestByClass(
                                                $state['reqs'], CreationReasonAuthenticationRequest::class
                                        );
@@ -1474,6 +1521,13 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Auto-create an account, and log into that account
+        *
+        * PrimaryAuthenticationProviders can invoke this method by returning a PASS from
+        * beginPrimaryAuthentication/continuePrimaryAuthentication with the username of a
+        * non-existing user. SessionProviders can invoke it by returning a SessionInfo with
+        * the username of a non-existing user from provideSessionInfo(). Calling this method
+        * explicitly (e.g. from a maintenance script) is also fine.
+        *
         * @param User $user User to auto-create
         * @param string $source What caused the auto-creation? This must be the ID
         *  of a PrimaryAuthenticationProvider or the constant self::AUTOCREATE_SOURCE_SESSION.
@@ -1489,7 +1543,7 @@ class AuthManager implements LoggerAwareInterface {
 
                $username = $user->getName();
 
-               // Try the local user from the slave DB
+               // Try the local user from the replica DB
                $localId = User::idFromName( $username );
                $flags = User::READ_NORMAL;
 
@@ -1550,7 +1604,7 @@ class AuthManager implements LoggerAwareInterface {
                        $this->logger->debug( __METHOD__ . ': name "{username}" is not creatable', [
                                'username' => $username,
                        ] );
-                       $session->set( 'AuthManager::AutoCreateBlacklist', 'noname', 600 );
+                       $session->set( 'AuthManager::AutoCreateBlacklist', 'noname' );
                        $user->setId( 0 );
                        $user->loadFromId();
                        return Status::newFatal( 'noname' );
@@ -1563,7 +1617,7 @@ class AuthManager implements LoggerAwareInterface {
                                'username' => $username,
                                'ip' => $anon->getName(),
                        ] );
-                       $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm', 600 );
+                       $session->set( 'AuthManager::AutoCreateBlacklist', 'authmanager-autocreate-noperm' );
                        $session->persist();
                        $user->setId( 0 );
                        $user->loadFromId();
@@ -1598,7 +1652,7 @@ class AuthManager implements LoggerAwareInterface {
                                        'username' => $username,
                                        'reason' => $ret->getWikiText( null, null, 'en' ),
                                ] );
-                               $session->set( 'AuthManager::AutoCreateBlacklist', $status, 600 );
+                               $session->set( 'AuthManager::AutoCreateBlacklist', $status );
                                $user->setId( 0 );
                                $user->loadFromId();
                                return $ret;
@@ -1627,15 +1681,13 @@ class AuthManager implements LoggerAwareInterface {
                $trxProfiler->setSilenced( true );
                try {
                        $status = $user->addToDatabase();
-                       if ( !$status->isOk() ) {
-                               // double-check for a race condition (T70012)
-                               $localId = User::idFromName( $username, User::READ_LATEST );
-                               if ( $localId ) {
+                       if ( !$status->isOK() ) {
+                               // Double-check for a race condition (T70012). We make use of the fact that when
+                               // addToDatabase fails due to the user already existing, the user object gets loaded.
+                               if ( $user->getId() ) {
                                        $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
                                                'username' => $username,
                                        ] );
-                                       $user->setId( $localId );
-                                       $user->loadFromId( User::READ_LATEST );
                                        if ( $login ) {
                                                $this->setSessionDataForUser( $user );
                                        }
@@ -2310,6 +2362,7 @@ class AuthManager implements LoggerAwareInterface {
        }
 
        /**
+        * Log the user in
         * @param User $user
         * @param bool|null $remember
         */
@@ -2374,6 +2427,7 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Reset the internal caching for unit testing
+        * @protected Unit tests only
         */
        public static function resetCache() {
                if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
index 4db0a84..11f3e22 100644 (file)
@@ -28,6 +28,11 @@ use Psr\Log\LoggerAwareInterface;
 
 /**
  * An AuthenticationProvider is used by AuthManager when authenticating users.
+ *
+ * This interface should not be implemented directly; use one of its children.
+ *
+ * Authentication providers can be registered via $wgAuthManagerAutoConfig.
+ *
  * @ingroup Auth
  * @since 1.27
  */
@@ -83,9 +88,9 @@ interface AuthenticationProvider extends LoggerAwareInterface {
         *    - ACTION_LINK: The local user being linked to.
         *    - ACTION_CHANGE: The user having data changed.
         *    - ACTION_REMOVE: The user having data removed.
-        *    This does not need to be copied into the returned requests, you only
-        *    need to pay attention to it if the set of requests differs based on
-        *    the user.
+        *    If you leave the username property of the returned requests empty, this
+        *    will automatically be copied there (except for ACTION_CREATE where it
+        *    wouldn't really make sense).
         * @return AuthenticationRequest[]
         */
        public function getAuthenticationRequests( $action, array $options );
index 2474b8b..ff4569b 100644 (file)
@@ -29,7 +29,7 @@ use Message;
  * This is a value object for authentication requests.
  *
  * An AuthenticationRequest represents a set of form fields that are needed on
- * and provided from the login, account creation, or password change forms.
+ * and provided from a login, account creation, password change or similar form.
  *
  * @ingroup Auth
  * @since 1.27
@@ -39,11 +39,14 @@ abstract class AuthenticationRequest {
        /** Indicates that the request is not required for authentication to proceed. */
        const OPTIONAL = 0;
 
-       /** Indicates that the request is required for authentication to proceed. */
+       /** Indicates that the request is required for authentication to proceed.
+        * This will only be used for UI purposes; it is the authentication providers'
+        * responsibility to verify that all required requests are present.
+        */
        const REQUIRED = 1;
 
        /** Indicates that the request is required by a primary authentication
-        * provdier. Since the user can choose which primary to authenticate with,
+        * provider. Since the user can choose which primary to authenticate with,
         * the request might or might not end up being actually required. */
        const PRIMARY_REQUIRED = 2;
 
@@ -60,7 +63,8 @@ abstract class AuthenticationRequest {
        /** @var string|null Return-to URL, in case of redirect */
        public $returnToUrl = null;
 
-       /** @var string|null Username. May not be used by all subclasses. */
+       /** @var string|null Username. See AuthenticationProvider::getAuthenticationRequests()
+        * for details of what this means and how it behaves. */
        public $username = null;
 
        /**
@@ -105,6 +109,11 @@ abstract class AuthenticationRequest {
         *  - sensitive: (bool) If set and truthy, the field is considered sensitive. Code using the
         *      request should avoid exposing the value of the field.
         *
+        * All AuthenticationRequests are populated from the same data, so most of the time you'll
+        * want to prefix fields names with something unique to the extension/provider (although
+        * in some cases sharing the field with other requests is the right thing to do, e.g. for
+        * a 'password' field).
+        *
         * @return array As above
         */
        abstract public function getFieldInfo();
@@ -126,10 +135,13 @@ abstract class AuthenticationRequest {
        /**
         * Initialize form submitted form data.
         *
-        * Should always return false if self::getFieldInfo() returns an empty
-        * array.
+        * The default behavior is to to check for each key of self::getFieldInfo()
+        * in the submitted data, and copy the value - after type-appropriate transformations -
+        * to $this->$key. Most subclasses won't need to override this; if you do override it,
+        * make sure to always return false if self::getFieldInfo() returns an empty array.
         *
-        * @param array $data Submitted data as an associative array
+        * @param array $data Submitted data as an associative array (keys will correspond
+        *   to getFieldInfo())
         * @return bool Whether the request data was successfully loaded
         */
        public function loadFromSubmission( array $data ) {
@@ -250,7 +262,7 @@ abstract class AuthenticationRequest {
         *
         * Only considers requests that have a "username" field.
         *
-        * @param AuthenticationRequest[] $requests
+        * @param AuthenticationRequest[] $reqs
         * @return string|null
         * @throws \UnexpectedValueException If multiple different usernames are present.
         */
index 5048cf8..6684fb9 100644 (file)
@@ -27,6 +27,10 @@ use Message;
 
 /**
  * This is a value object to hold authentication response data
+ *
+ * An AuthenticationResponse represents both the status of the authentication
+ * (success, failure, in progress) and it its state (what data is needed to continue).
+ *
  * @ingroup Auth
  * @since 1.27
  */
@@ -39,7 +43,8 @@ class AuthenticationResponse {
 
        /** Indicates that third-party authentication succeeded but no user exists.
         * Either treat this like a UI response or pass $this->createRequest to
-        * AuthManager::beginCreateAccount().
+        * AuthManager::beginCreateAccount(). For use by AuthManager only (providers
+        * should just return a PASS with no username).
         */
        const RESTART = 'RESTART';
 
@@ -67,13 +72,18 @@ class AuthenticationResponse {
 
        /**
         * @var AuthenticationRequest[] Needed AuthenticationRequests to continue
-        * after a UI or REDIRECT response
+        * after a UI or REDIRECT response. This plays the same role when continuing
+        * authentication as AuthManager::getAuthenticationRequests() does when
+        * beginning it.
         */
        public $neededRequests = [];
 
        /** @var Message|null I18n message to display in case of UI or FAIL */
        public $message = null;
 
+       /** @var string Whether the $message is an error or warning message, for styling reasons */
+       public $messageType = 'warning';
+
        /**
         * @var string|null Local user name from authentication.
         * May be null if the authentication passed but no local user is known.
@@ -84,35 +94,42 @@ class AuthenticationResponse {
         * @var AuthenticationRequest|null
         *
         * Returned with a PrimaryAuthenticationProvider login FAIL or a PASS with
-        * no username, this holds a request that should result in a PASS when
+        * no username, this can be set to a request that should result in a PASS when
         * passed to that provider's PrimaryAuthenticationProvider::beginPrimaryAccountCreation().
+        * The client will be able to send that back for expedited account creation where only
+        * the username needs to be filled.
         *
         * Returned with an AuthManager login FAIL or RESTART, this holds a
         * CreateFromLoginAuthenticationRequest that may be passed to
         * AuthManager::beginCreateAccount(), possibly in place of any
         * "primary-required" requests. It may also be passed to
-        * AuthManager::beginAuthentication() to preserve state.
+        * AuthManager::beginAuthentication() to preserve the list of
+        * accounts which can be linked after success (see $linkRequest).
         */
        public $createRequest = null;
 
        /**
-        * @var AuthenticationRequest|null Returned with a PrimaryAuthenticationProvider
-        *  login PASS with no username, this holds a request to pass to
-        *  AuthManager::changeAuthenticationData() to link the account once the
-        *  local user has been determined.
+        * @var AuthenticationRequest|null When returned with a PrimaryAuthenticationProvider
+        *  login PASS with no username, the request this holds will be passed to
+        *  AuthManager::changeAuthenticationData() once the local user has been determined and the
+        *  user has confirmed the account ownership (by reviewing the information given by
+        *  $linkRequest->describeCredentials()). The provider should handle that
+        *  changeAuthenticationData() call by doing the actual linking.
         */
        public $linkRequest = null;
 
        /**
         * @var AuthenticationRequest|null Returned with an AuthManager account
         *  creation PASS, this holds a request to pass to AuthManager::beginAuthentication()
-        *  to immediately log into the created account.
+        *  to immediately log into the created account. All provider methods except
+        *   postAuthentication will be skipped.
         */
        public $loginRequest = null;
 
        /**
         * @param string|null $username Local username
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::PASS
         */
        public static function newPass( $username = null ) {
                $ret = new AuthenticationResponse;
@@ -124,17 +141,20 @@ class AuthenticationResponse {
        /**
         * @param Message $msg
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::FAIL
         */
        public static function newFail( Message $msg ) {
                $ret = new AuthenticationResponse;
                $ret->status = AuthenticationResponse::FAIL;
                $ret->message = $msg;
+               $ret->messageType = 'error';
                return $ret;
        }
 
        /**
         * @param Message $msg
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::RESTART
         */
        public static function newRestart( Message $msg ) {
                $ret = new AuthenticationResponse;
@@ -145,6 +165,7 @@ class AuthenticationResponse {
 
        /**
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::ABSTAIN
         */
        public static function newAbstain() {
                $ret = new AuthenticationResponse;
@@ -155,17 +176,23 @@ class AuthenticationResponse {
        /**
         * @param AuthenticationRequest[] $reqs AuthenticationRequests needed to continue
         * @param Message $msg
+        * @param string $msgtype
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::UI
         */
-       public static function newUI( array $reqs, Message $msg ) {
+       public static function newUI( array $reqs, Message $msg, $msgtype = 'warning' ) {
                if ( !$reqs ) {
                        throw new \InvalidArgumentException( '$reqs may not be empty' );
                }
+               if ( $msgtype !== 'warning' && $msgtype !== 'error' ) {
+                       throw new \InvalidArgumentException( $msgtype . ' is not a valid message type.' );
+               }
 
                $ret = new AuthenticationResponse;
                $ret->status = AuthenticationResponse::UI;
                $ret->neededRequests = $reqs;
                $ret->message = $msg;
+               $ret->messageType = $msgtype;
                return $ret;
        }
 
@@ -174,6 +201,7 @@ class AuthenticationResponse {
         * @param string $redirectTarget URL
         * @param mixed $redirectApiData Data suitable for adding to an ApiResult
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::REDIRECT
         */
        public static function newRedirect( array $reqs, $redirectTarget, $redirectApiData = null ) {
                if ( !$reqs ) {
index beb11f4..7f121cd 100644 (file)
@@ -64,7 +64,8 @@ class ConfirmLinkSecondaryAuthenticationProvider extends AbstractSecondaryAuthen
                $req = new ConfirmLinkAuthenticationRequest( $maybeLink );
                return AuthenticationResponse::newUI(
                        [ $req ],
-                       wfMessage( 'authprovider-confirmlink-message' )
+                       wfMessage( 'authprovider-confirmlink-message' ),
+                       'warning'
                );
        }
 
@@ -150,7 +151,8 @@ class ConfirmLinkSecondaryAuthenticationProvider extends AbstractSecondaryAuthen
                                        'linkOk', wfMessage( 'ok' ), wfMessage( 'authprovider-confirmlink-ok-help' )
                                )
                        ],
-                       $combinedStatus->getMessage( 'authprovider-confirmlink-failed' )
+                       $combinedStatus->getMessage( 'authprovider-confirmlink-failed' ),
+                       'error'
                );
        }
 }
index a82f018..a485531 100644 (file)
@@ -51,15 +51,18 @@ class EmailNotificationSecondaryAuthenticationProvider
                        && !$this->manager->getAuthenticationSessionData( 'no-email' )
                ) {
                        // TODO show 'confirmemail_oncreate'/'confirmemail_sendfailed' message
-                       wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $user ) {
-                               $user = $user->getInstanceForUpdate();
-                               $status = $user->sendConfirmationMail();
-                               $user->saveSettings();
-                               if ( !$status->isGood() ) {
-                                       $this->logger->warning( 'Could not send confirmation email: ' .
-                                               $status->getWikiText( false, false, 'en' ) );
-                               }
-                       } );
+                       wfGetDB( DB_MASTER )->onTransactionIdle(
+                               function () use ( $user ) {
+                                       $user = $user->getInstanceForUpdate();
+                                       $status = $user->sendConfirmationMail();
+                                       $user->saveSettings();
+                                       if ( !$status->isGood() ) {
+                                               $this->logger->warning( 'Could not send confirmation email: ' .
+                                                       $status->getWikiText( false, false, 'en' ) );
+                                       }
+                               },
+                               __METHOD__
+                       );
                }
 
                return AuthenticationResponse::newPass();
index 13fae6e..8590cbd 100644 (file)
@@ -27,16 +27,19 @@ use StatusValue;
 use User;
 
 /**
- * A pre-authentication provider is a check that must pass for authentication
- * to proceed.
+ * A pre-authentication provider can prevent authentication early on.
  *
  * A PreAuthenticationProvider is used to supply arbitrary checks to be
  * performed before the PrimaryAuthenticationProviders are consulted during the
- * login process. Possible uses include checking that a per-IP throttle has not
- * been reached or that a captcha has been solved.
+ * login / account creation / account linking process. Possible uses include
+ * checking that a per-IP throttle has not been reached or that a captcha has been solved.
+ *
+ * This interface also provides callbacks that are invoked after login / account creation
+ * / account linking succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface PreAuthenticationProvider extends AuthenticationProvider {
 
@@ -52,10 +55,17 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of a login attempt. It will not be called for unfinished
+        * login attempts that fail by the session timing out.
+        *
+        * @note Under certain circumstances, this can be called even when testForAuthentication
+        *   was not; see AuthenticationRequest::$loginRequest.
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
@@ -97,12 +107,18 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-creation callback
+        *
+        * This will be called at the end of an account creation attempt. It will not be called if
+        * the account creation process results in a session timeout (possibly after a successful
+        * user creation, while a secondary provider is waiting for a response).
+        *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
@@ -118,10 +134,14 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-link callback
+        *
+        * This will be called at the end of an account linking attempt.
+        *
         * @param User $user User that was attempted to be linked.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountLink( $user, AuthenticationResponse $response );
 
index 35f3287..4033613 100644 (file)
@@ -27,27 +27,50 @@ use StatusValue;
 use User;
 
 /**
- * A primary authentication provider determines which user is trying to log in.
+ * A primary authentication provider is responsible for associating the submitted
+ * authentication data with a MediaWiki account.
  *
- * A PrimaryAuthenticationProvider is used as part of presenting a login form
- * to authenticate a user. In particular, the PrimaryAuthenticationProvider
- * takes form data and determines the authenticated user (if any) corresponds
- * to that form data. It might do this on the basis of a username and password
- * in that data, or by interacting with an external authentication service
- * (e.g. using OpenID), or by some other mechanism.
+ * When multiple primary authentication providers are configured for a site, they
+ * act as alternatives; the first one that recognizes the data will handle it,
+ * and further primary providers are not called (although they all get a chance
+ * to prevent actions).
  *
- * A PrimaryAuthenticationProvider would not be appropriate for something like
+ * For login, the PrimaryAuthenticationProvider takes form data and determines
+ * which authenticated user (if any) corresponds to that form data. It might
+ * do this on the basis of a username and password in that data, or by
+ * interacting with an external authentication service (e.g. using OpenID),
+ * or by some other mechanism.
+ *
+ * (A PrimaryAuthenticationProvider would not be appropriate for something like
  * HTTP authentication, OAuth, or SSL client certificates where each HTTP
  * request contains all the information needed to identify the user. In that
- * case you'll want to be looking at a \\MediaWiki\\Session\\SessionProvider
- * instead.
+ * case you'll want to be looking at a \MediaWiki\Session\SessionProvider
+ * instead.)
+ *
+ * For account creation, the PrimaryAuthenticationProvider takes form data and
+ * stores some authentication details which will allow it to verify a login by
+ * that user in the future. This might for example involve saving it in the
+ * database in a table that can be joined to the user table, or sending it to
+ * some external service for account creation, or authenticating the user with
+ * some remote service and then recording that the remote identity is linked to
+ * the local account.
+ * The creation of the local user (i.e. calling User::addToDatabase()) is handled
+ * by AuthManager once the primary authentication provider returns a PASS
+ * from begin/continueAccountCreation; do not try to do it yourself.
+ *
+ * For account linking, the PrimaryAuthenticationProvider verifies the user's
+ * identity at some external service (typically by redirecting the user and
+ * asking the external service to verify) and then records which local account
+ * is linked to which remote accounts. It should keep track of this and be able
+ * to enumerate linked accounts via getAuthenticationRequests(ACTION_REMOVE).
  *
  * This interface also provides methods for changing authentication data such
- * as passwords and for creating new users who can later be authenticated with
- * this provider.
+ * as passwords, and callbacks that are invoked after login / account creation
+ * / account linking succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface PrimaryAuthenticationProvider extends AuthenticationProvider {
        /** Provider can create accounts */
@@ -93,16 +116,25 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of any login attempt, regardless of whether this provider was
+        * the one that handled it. It will not be called for unfinished login attempts that fail by
+        * the session timing out.
+        *
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
        /**
         * Test whether the named user exists
-        * @param string $username
+        *
+        * Single-sign-on providers can use this to reserve a username for autocreation.
+        *
+        * @param string $username MediaWiki username
         * @param int $flags Bitfield of User:READ_* constants
         * @return bool
         */
@@ -110,7 +142,11 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Test whether the named user can authenticate with this provider
-        * @param string $username
+        *
+        * Should return true if the provider has any data for this user which can be used to
+        * authenticate it, even if the user is temporarily prevented from authentication somehow.
+        *
+        * @param string $username MediaWiki username
         * @return bool
         */
        public function testUserCanAuthenticate( $username );
@@ -184,6 +220,10 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * If $req was returned for AuthManager::ACTION_REMOVE, the corresponding
         * credentials should no longer result in a successful login.
         *
+        * It can be assumed that providerAllowsAuthenticationDataChange with $checkData === true
+        * was called before this, and passed. This method should never fail (other than throwing an
+        * exception).
+        *
         * @param AuthenticationRequest $req
         */
        public function providerChangeAuthenticationData( AuthenticationRequest $req );
@@ -249,7 +289,8 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * Post-creation callback
         *
         * Called after the user is added to the database, before secondary
-        * authentication providers are run.
+        * authentication providers are run. Only called if this provider was the one that issued
+        * a PASS.
         *
         * @param User $user User being created (has been added to the database now).
         *   This may become a "UserValue" in the future, or User may be refactored
@@ -266,7 +307,10 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
        /**
         * Post-creation callback
         *
-        * Called when the account creation process ends.
+        * This will be called at the end of any account creation attempt, regardless of whether this
+        * provider was the one that handled it. It will not be called if the account creation process
+        * results in a session timeout (possibly after a successful user creation, while a secondary
+        * provider is waiting for a response).
         *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
@@ -274,6 +318,7 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
@@ -340,10 +385,15 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-link callback
+        *
+        * This will be called at the end of any account linking attempt, regardless of whether this
+        * provider was the one that handled it.
+        *
         * @param User $user User that was attempted to be linked.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountLink( $user, AuthenticationResponse $response );
 
index dd97830..f11a12c 100644 (file)
@@ -112,17 +112,17 @@ class ResetPasswordSecondaryAuthenticationProvider extends AbstractSecondaryAuth
 
                $req = AuthenticationRequest::getRequestByClass( $reqs, get_class( $needReq ) );
                if ( !$req || !array_key_exists( 'retype', $req->getFieldInfo() ) ) {
-                       return AuthenticationResponse::newUI( $needReqs, $data->msg );
+                       return AuthenticationResponse::newUI( $needReqs, $data->msg, 'warning' );
                }
 
                if ( $req->password !== $req->retype ) {
-                       return AuthenticationResponse::newUI( $needReqs, new \Message( 'badretype' ) );
+                       return AuthenticationResponse::newUI( $needReqs, new \Message( 'badretype' ), 'error' );
                }
 
                $req->username = $user->getName();
                $status = $this->manager->allowsAuthenticationDataChange( $req );
                if ( !$status->isGood() ) {
-                       return AuthenticationResponse::newUI( $needReqs, $status->getMessage() );
+                       return AuthenticationResponse::newUI( $needReqs, $status->getMessage(), 'error' );
                }
                $this->manager->changeAuthenticationData( $req );
 
index 1ccc9c6..c55e65d 100644 (file)
@@ -27,16 +27,27 @@ use StatusValue;
 use User;
 
 /**
- * A secondary authentication provider performs additional authentication steps
- * after a PrimaryAuthenticationProvider has done its thing.
+ * A secondary provider mostly acts when the submitted authentication data has
+ * already been associated to a MediaWiki user account.
  *
- * A SecondaryAuthenticationProvider is used to perform arbitrary checks on an
- * authentication request after the user itself has been authenticated. For
- * example, it might implement a password reset, request the second factor for
- * two-factor auth, or prevent the login if the account is blocked.
+ * For login, a secondary provider performs additional authentication steps
+ * after a PrimaryAuthenticationProvider has identified which MediaWiki user is
+ * trying to log in. For example, it might implement a password reset, request
+ * the second factor for two-factor auth, or prevent the login if the account is blocked.
+ *
+ * For account creation, a secondary provider performs optional extra steps after
+ * a PrimaryAuthenticationProvider has created the user; for example, it can collect
+ * further user information such as a biography.
+ *
+ * (For account linking, secondary providers are not involved.)
+ *
+ * This interface also provides methods for changing authentication data such
+ * as a second-factor token, and callbacks that are invoked after login / account creation
+ * succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
@@ -75,10 +86,15 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of a login attempt. It will not be called for unfinished
+        * login attempts that fail by the session timing out.
+        *
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
@@ -129,6 +145,10 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
         * If $req was returned for AuthManager::ACTION_REMOVE, the corresponding
         * credentials should no longer result in a successful login.
         *
+        * It can be assumed that providerAllowsAuthenticationDataChange with $checkData === true
+        * was called before this, and passed. This method should never fail (other than throwing an
+        * exception).
+        *
         * @param AuthenticationRequest $req
         */
        public function providerChangeAuthenticationData( AuthenticationRequest $req );
@@ -151,6 +171,12 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Start an account creation flow
+        *
+        * @note There is no guarantee this will be called in a successful account
+        *   creation process as the user can just abandon the process at any time
+        *   after the primary provider has issued a PASS and still have a valid
+        *   account. Be prepared to handle any database inconsistencies that result
+        *   from this or continueSecondaryAccountCreation() not being called.
         * @param User $user User being created (has been added to the database).
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
@@ -167,6 +193,7 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Continue an authentication flow
+        *
         * @param User $user User being created (has been added to the database).
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
@@ -183,12 +210,18 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-creation callback
+        *
+        * This will be called at the end of an account creation attempt. It will not be called if
+        * the account creation process results in a session timeout (possibly after a successful
+        * user creation, while a secondary provider is waiting for a response).
+        *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
index ed94c1a..f16423d 100644 (file)
@@ -126,8 +126,8 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                        return AuthenticationResponse::newAbstain();
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $row = $dbw->selectRow(
+               $dbr = wfGetDB( DB_REPLICA );
+               $row = $dbr->selectRow(
                        'user',
                        [
                                'user_id', 'user_newpassword', 'user_newpass_time',
@@ -165,8 +165,8 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                        return false;
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $row = $dbw->selectRow(
+               $dbr = wfGetDB( DB_REPLICA );
+               $row = $dbr->selectRow(
                        'user',
                        [ 'user_newpassword', 'user_newpass_time' ],
                        [ 'user_name' => $username ],
@@ -304,10 +304,13 @@ class TemporaryPasswordPrimaryAuthenticationProvider
 
                if ( $sendMail ) {
                        // Send email after DB commit
-                       $dbw->onTransactionIdle( function () use ( $req ) {
-                               /** @var TemporaryPasswordAuthenticationRequest $req */
-                               $this->sendPasswordResetEmail( $req );
-                       } );
+                       $dbw->onTransactionIdle(
+                               function () use ( $req ) {
+                                       /** @var TemporaryPasswordAuthenticationRequest $req */
+                                       $this->sendPasswordResetEmail( $req );
+                               },
+                               __METHOD__
+                       );
                }
        }
 
@@ -375,9 +378,12 @@ class TemporaryPasswordPrimaryAuthenticationProvider
 
                if ( $mailpassword ) {
                        // Send email after DB commit
-                       wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $user, $creator, $req ) {
-                               $this->sendNewAccountEmail( $user, $creator, $req->password );
-                       } );
+                       wfGetDB( DB_MASTER )->onTransactionIdle(
+                               function () use ( $user, $creator, $req ) {
+                                       $this->sendNewAccountEmail( $user, $creator, $req->password );
+                               },
+                               __METHOD__
+                       );
                }
 
                return $mailpassword ? 'byemail' : null;
index 8c2a19e..9dfabfd 100644 (file)
@@ -137,13 +137,13 @@ class BacklinkCache {
        }
 
        /**
-        * Get the slave connection to the database
+        * Get the replica DB connection to the database
         * When non existing, will initialize the connection.
         * @return DatabaseBase
         */
        protected function getDB() {
                if ( !isset( $this->db ) ) {
-                       $this->db = wfGetDB( DB_SLAVE );
+                       $this->db = wfGetDB( DB_REPLICA );
                }
 
                return $this->db;
index 80f04ce..a34d235 100644 (file)
@@ -157,7 +157,7 @@ class GenderCache {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $table = [ 'user', 'user_properties' ];
                $fields = [ 'user_name', 'up_value' ];
                $conds = [ 'user_name' => $usersToCheck ];
index ea2e20b..71011e0 100644 (file)
  * @ingroup Cache
  */
 class HTMLFileCache extends FileCacheBase {
+       const MODE_NORMAL = 0; // normal cache mode
+       const MODE_OUTAGE = 1; // fallback cache for DB outages
+
        /**
-        * Construct an ObjectFileCache from a Title and an action
+        * Construct an HTMLFileCache object from a Title and an action
+        *
+        * @deprecated since 1.24, instantiate this class directly
         * @param Title|string $title Title object or prefixed DB key string
         * @param string $action
         * @throws MWException
         * @return HTMLFileCache
-        *
-        * @deprecated Since 1.24, instantiate this class directly
         */
        public static function newFromTitle( $title, $action ) {
                return new self( $title, $action );
@@ -93,10 +96,12 @@ class HTMLFileCache extends FileCacheBase {
        /**
         * Check if pages can be cached for this request/user
         * @param IContextSource $context
+        * @param integer $mode One of the HTMLFileCache::MODE_* constants
         * @return bool
         */
-       public static function useFileCache( IContextSource $context ) {
+       public static function useFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) {
                global $wgUseFileCache, $wgDebugToolbar, $wgContLang;
+
                if ( !$wgUseFileCache ) {
                        return false;
                }
@@ -121,15 +126,23 @@ class HTMLFileCache extends FileCacheBase {
 
                        return false;
                }
+
                $user = $context->getUser();
                // Check for non-standard user language; this covers uselang,
                // and extensions for auto-detecting user language.
                $ulang = $context->getLanguage();
 
                // Check that there are no other sources of variation
-               if ( $user->getId() || $user->getNewtalk() || !$ulang->equals( $wgContLang ) ) {
+               if ( $user->getId() || !$ulang->equals( $wgContLang ) ) {
                        return false;
                }
+
+               if ( $mode === self::MODE_NORMAL ) {
+                       if ( $user->getNewtalk() ) {
+                               return false;
+                       }
+               }
+
                // Allow extensions to disable caching
                return Hooks::run( 'HTMLFileCache::useFileCache', [ $context ] );
        }
@@ -137,17 +150,23 @@ class HTMLFileCache extends FileCacheBase {
        /**
         * Read from cache to context output
         * @param IContextSource $context
+        * @param integer $mode One of the HTMLFileCache::MODE_* constants
         * @return void
         */
-       public function loadFromFileCache( IContextSource $context ) {
-               global $wgMimeType, $wgLanguageCode;
+       public function loadFromFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) {
+               global $wgMimeType, $wgContLang;
 
                wfDebug( __METHOD__ . "()\n" );
                $filename = $this->cachePath();
 
+               if ( $mode === self::MODE_OUTAGE ) {
+                       // Avoid DB errors for queries in sendCacheControl()
+                       $context->getTitle()->resetArticleID( 0 );
+               }
+
                $context->getOutput()->sendCacheControl();
                header( "Content-Type: $wgMimeType; charset=UTF-8" );
-               header( "Content-Language: $wgLanguageCode" );
+               header( 'Content-Language: ' . $wgContLang->getHtmlCode() );
                if ( $this->useGzip() ) {
                        if ( wfClientAcceptsGzip() ) {
                                header( 'Content-Encoding: gzip' );
index ec37dd6..8a4d061 100644 (file)
@@ -40,7 +40,11 @@ class LinkBatch {
         */
        protected $caller;
 
-       function __construct( $arr = [] ) {
+       /**
+        * LinkBatch constructor.
+        * @param LinkTarget[] $arr Initial items to be added to the batch
+        */
+       public function __construct( $arr = [] ) {
                foreach ( $arr as $item ) {
                        $this->addObj( $item );
                }
@@ -188,7 +192,7 @@ class LinkBatch {
                }
 
                // This is similar to LinkHolderArray::replaceInternal
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $table = 'page';
                $fields = array_merge(
                        LinkCache::getSelectFields(),
index 3fd29f3..23cc26d 100644 (file)
@@ -29,19 +29,17 @@ use MediaWiki\MediaWikiServices;
  * @ingroup Cache
  */
 class LinkCache {
-       /**
-        * @var HashBagOStuff
-        */
+       /** @var HashBagOStuff */
        private $mGoodLinks;
-       /**
-        * @var HashBagOStuff
-        */
+       /** @var HashBagOStuff */
        private $mBadLinks;
+       /** @var WANObjectCache */
+       private $wanCache;
+
+       /** @var bool */
        private $mForUpdate = false;
 
-       /**
-        * @var TitleFormatter
-        */
+       /** @var TitleFormatter */
        private $titleFormatter;
 
        /**
@@ -50,9 +48,10 @@ class LinkCache {
         */
        const MAX_SIZE = 10000;
 
-       public function __construct( TitleFormatter $titleFormatter ) {
+       public function __construct( TitleFormatter $titleFormatter, WANObjectCache $cache ) {
                $this->mGoodLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
                $this->mBadLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
+               $this->wanCache = $cache;
                $this->titleFormatter = $titleFormatter;
        }
 
@@ -244,15 +243,31 @@ class LinkCache {
                        return 0;
                }
 
-               // Some fields heavily used for linking...
-               $db = $this->mForUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               // Cache template/file pages as they are less often viewed but heavily used
+               if ( $this->mForUpdate ) {
+                       $row = $this->fetchPageRow( wfGetDB( DB_MASTER ), $nt );
+               } elseif ( $this->isCacheable( $nt ) ) {
+                       // These pages are often transcluded heavily, so cache them
+                       $cache = $this->wanCache;
+                       $row = $cache->getWithSetCallback(
+                               $cache->makeKey( 'page', $nt->getNamespace(), sha1( $nt->getDBkey() ) ),
+                               $cache::TTL_DAY,
+                               function ( $curValue, &$ttl, array &$setOpts ) use ( $cache, $nt ) {
+                                       $dbr = wfGetDB( DB_REPLICA );
+                                       $setOpts += Database::getCacheSetOptions( $dbr );
 
-               $row = $db->selectRow( 'page', self::getSelectFields(),
-                       [ 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ],
-                       __METHOD__
-               );
+                                       $row = $this->fetchPageRow( $dbr, $nt );
+                                       $mtime = $row ? wfTimestamp( TS_UNIX, $row->page_touched ) : false;
+                                       $ttl = $cache->adaptiveTTL( $mtime, $ttl );
 
-               if ( $row !== false ) {
+                                       return $row;
+                               }
+                       );
+               } else {
+                       $row = $this->fetchPageRow( wfGetDB( DB_REPLICA ), $nt );
+               }
+
+               if ( $row ) {
                        $this->addGoodLinkObjFromRow( $nt, $row );
                        $id = intval( $row->page_id );
                } else {
@@ -263,6 +278,39 @@ class LinkCache {
                return $id;
        }
 
+       private function isCacheable( LinkTarget $title ) {
+               return ( $title->inNamespace( NS_TEMPLATE ) || $title->inNamespace( NS_FILE ) );
+       }
+
+       private function fetchPageRow( IDatabase $db, LinkTarget $nt ) {
+               $fields = self::getSelectFields();
+               if ( $this->isCacheable( $nt ) ) {
+                       $fields[] = 'page_touched';
+               }
+
+               return $db->selectRow(
+                       'page',
+                       $fields,
+                       [ 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ],
+                       __METHOD__
+               );
+       }
+
+       /**
+        * Purge the link cache for a title
+        *
+        * @param LinkTarget $title
+        * @since 1.28
+        */
+       public function invalidateTitle( LinkTarget $title ) {
+               if ( $this->isCacheable( $title ) ) {
+                       $cache = ObjectCache::getMainWANInstance();
+                       $cache->delete(
+                               $cache->makeKey( 'page', $title->getNamespace(), sha1( $title->getDBkey() ) )
+                       );
+               }
+       }
+
        /**
         * Clears cache
         */
index 279898c..90ad241 100644 (file)
@@ -165,7 +165,7 @@ class MessageBlobStore implements LoggerAwareInterface {
                $cache->set( $cacheKey, $blob,
                        // Add part of a day to TTL to avoid all modules expiring at once
                        $cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
-                       Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) )
+                       Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
                );
                return $blob;
        }
@@ -179,7 +179,7 @@ class MessageBlobStore implements LoggerAwareInterface {
        public function updateMessage( $key ) {
                $moduleNames = $this->getResourceLoader()->getModulesByMessage( $key );
                foreach ( $moduleNames as $moduleName ) {
-                       // Uses a holdoff to account for database slave lag (for MessageCache)
+                       // Uses a holdoff to account for database replica DB lag (for MessageCache)
                        $this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
                }
        }
index 62fab5f..e871855 100644 (file)
@@ -302,7 +302,7 @@ class MessageCache {
                                                $where[] = 'global cache is expired';
                                                $staleCache = $cache;
                                        } elseif ( $hashVolatile ) {
-                                               # DB results are slave lag prone until the holdoff TTL passes.
+                                               # DB results are replica DB lag prone until the holdoff TTL passes.
                                                # By then, updates should be reflected in loadFromDBWithLock().
                                                # One thread renerates the cache while others use old values.
                                                $where[] = 'global cache is expired/volatile';
@@ -442,7 +442,7 @@ class MessageCache {
        function loadFromDB( $code, $mode = null ) {
                global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
 
-               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_SLAVE );
+               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA );
 
                $cache = [];
 
@@ -564,7 +564,7 @@ class MessageCache {
                }
 
                // Mark this cache as definitely "latest" (non-volatile) so
-               // load() calls do try to refresh the cache with slave data
+               // load() calls do try to refresh the cache with replica DB data
                $this->mCache[$code]['LATEST'] = time();
 
                // Update caches if the lock was acquired
@@ -1089,7 +1089,7 @@ class MessageCache {
                if ( !$title || !$title instanceof Title ) {
                        global $wgTitle;
                        wfDebugLog( 'GlobalTitleFail', __METHOD__ . ' called by ' .
-                               wfGetAllCallers( 5 ) . ' with no title set.' );
+                               wfGetAllCallers( 6 ) . ' with no title set.' );
                        $title = $wgTitle;
                }
                // Sometimes $wgTitle isn't set either...
diff --git a/includes/cache/ObjectFileCache.php b/includes/cache/ObjectFileCache.php
deleted file mode 100644 (file)
index c7ef044..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-/**
- * Object cache in the file system.
- *
- * 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 Cache
- */
-
-/**
- * Object cache in the file system.
- *
- * @ingroup Cache
- */
-class ObjectFileCache extends FileCacheBase {
-       /**
-        * Construct an ObjectFileCache from a key and a type
-        * @param string $key
-        * @param string $type
-        * @return ObjectFileCache
-        */
-       public static function newFromKey( $key, $type ) {
-               $cache = new self();
-
-               $cache->mKey = (string)$key;
-               $cache->mType = (string)$type;
-
-               return $cache;
-       }
-
-       /**
-        * Get the base file cache directory
-        * @return string
-        */
-       protected function cacheDirectory() {
-               return $this->baseCacheDirectory() . '/object';
-       }
-}
index 48d0398..016306a 100644 (file)
@@ -100,7 +100,7 @@ class UserCache {
 
                // Lookup basic info for users not yet loaded...
                if ( count( $usersToQuery ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $table = [ 'user' ];
                        $conds = [ 'user_id' => $usersToQuery ];
                        $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
index c350178..e7e2d10 100644 (file)
@@ -39,7 +39,7 @@ class LCStoreDB implements LCStore {
                if ( $this->writesDone && $this->dbw ) {
                        $db = $this->dbw; // see the changes in finishWrite()
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
 
                $value = $db->selectField(
index 64d8139..515ab05 100644 (file)
@@ -164,7 +164,7 @@ class CategoryMembershipChange {
                /**
                 * T109700 - Default bot flag to true when there is no corresponding RC entry
                 * This means all changes caused by parser functions & Lua on reparse are marked as bot
-                * Also in the case no RC entry could be found due to slave lag
+                * Also in the case no RC entry could be found due to replica DB lag
                 */
                $bot = 1;
                $lastRevId = 0;
index daf76df..590fd37 100644 (file)
@@ -180,7 +180,7 @@ class RecentChange {
        public static function newFromConds(
                $conds,
                $fname = __METHOD__,
-               $dbType = DB_SLAVE
+               $dbType = DB_REPLICA
        ) {
                $db = wfGetDB( $dbType );
                $row = $db->selectRow( 'recentchanges', self::selectFields(), $conds, $fname );
@@ -285,6 +285,17 @@ class RecentChange {
                        $this->mAttribs['rc_ip'] = '';
                }
 
+               # Strict mode fixups (not-NULL fields)
+               foreach ( [ 'minor', 'bot', 'new', 'patrolled', 'deleted' ] as $field ) {
+                       $this->mAttribs["rc_$field"] = (int)$this->mAttribs["rc_$field"];
+               }
+               # ...more fixups (NULL fields)
+               foreach ( [ 'old_len', 'new_len' ] as $field ) {
+                       $this->mAttribs["rc_$field"] = isset( $this->mAttribs["rc_$field"] )
+                               ? (int)$this->mAttribs["rc_$field"]
+                               : null;
+               }
+
                # If our database is strict about IP addresses, use NULL instead of an empty string
                if ( $dbw->strictIPs() && $this->mAttribs['rc_ip'] == '' ) {
                        unset( $this->mAttribs['rc_ip'] );
@@ -325,20 +336,27 @@ class RecentChange {
                        $title = $this->getTitle();
 
                        // Never send an RC notification email about categorization changes
-                       if ( $this->mAttribs['rc_type'] != RC_CATEGORIZE ) {
-                               if ( Hooks::run( 'AbortEmailNotification', [ $editor, $title, $this ] ) ) {
-                                       # @todo FIXME: This would be better as an extension hook
-                                       $enotif = new EmailNotification();
-                                       $enotif->notifyOnPageChange(
-                                               $editor,
-                                               $title,
-                                               $this->mAttribs['rc_timestamp'],
-                                               $this->mAttribs['rc_comment'],
-                                               $this->mAttribs['rc_minor'],
-                                               $this->mAttribs['rc_last_oldid'],
-                                               $this->mExtra['pageStatus']
-                                       );
-                               }
+                       if (
+                               $this->mAttribs['rc_type'] != RC_CATEGORIZE &&
+                               Hooks::run( 'AbortEmailNotification', [ $editor, $title, $this ] )
+                       ) {
+                               // @FIXME: This would be better as an extension hook
+                               // Send emails or email jobs once this row is safely committed
+                               $dbw->onTransactionIdle(
+                                       function () use ( $editor, $title ) {
+                                               $enotif = new EmailNotification();
+                                               $enotif->notifyOnPageChange(
+                                                       $editor,
+                                                       $title,
+                                                       $this->mAttribs['rc_timestamp'],
+                                                       $this->mAttribs['rc_comment'],
+                                                       $this->mAttribs['rc_minor'],
+                                                       $this->mAttribs['rc_last_oldid'],
+                                                       $this->mExtra['pageStatus']
+                                               );
+                                       },
+                                       __METHOD__
+                               );
                        }
                }
 
@@ -776,7 +794,7 @@ class RecentChange {
                        'rc_comment' => $logComment,
                        'rc_this_oldid' => $revId,
                        'rc_last_oldid' => 0,
-                       'rc_bot' => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
+                       'rc_bot' => $user->isAllowed( 'bot' ) ? (int)$wgRequest->getBool( 'bot', true ) : 0,
                        'rc_ip' => self::checkIPAddress( $ip ),
                        'rc_patrolled' => $markPatrolled ? 1 : 0,
                        'rc_new' => 0, # obsolete
index a2945af..3ef9641 100644 (file)
@@ -29,6 +29,11 @@ class ChangeTags {
         */
        const MAX_DELETE_USES = 5000;
 
+       /**
+        * @var string[]
+        */
+       private static $coreTags = [ 'mw-contentmodelchange' ];
+
        /**
         * Creates HTML for the given tags
         *
@@ -174,7 +179,7 @@ class ChangeTags {
 
                // Might as well look for rcids and so on.
                if ( !$rc_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
+                       // Info might be out of date, somewhat fractionally, on replica DB.
                        // LogEntry/LogPage and WikiPage match rev/log/rc timestamps,
                        // so use that relation to avoid full table scans.
                        if ( $log_id ) {
@@ -201,7 +206,7 @@ class ChangeTags {
                                );
                        }
                } elseif ( !$log_id && !$rev_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
+                       // Info might be out of date, somewhat fractionally, on replica DB.
                        $log_id = $dbw->selectField(
                                'recentchanges',
                                'rc_logid',
@@ -313,7 +318,7 @@ class ChangeTags {
                $tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
 
                // Update the summary row.
-               // $prevTags can be out of date on slaves, especially when addTags is called consecutively,
+               // $prevTags can be out of date on replica DBs, especially when addTags is called consecutively,
                // causing loss of tags added recently in tag_summary table.
                $prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
                $prevTags = $prevTags ? $prevTags : '';
@@ -472,8 +477,8 @@ class ChangeTags {
                        // to be removed, a tag must not be defined by an extension, or equivalently it
                        // has to be either explicitly defined or not defined at all
                        // (assuming no edge case of a tag both explicitly-defined and extension-defined)
-                       $extensionDefinedTags = self::listExtensionDefinedTags();
-                       $intersect = array_intersect( $tagsToRemove, $extensionDefinedTags );
+                       $softwareDefinedTags = self::listSoftwareDefinedTags();
+                       $intersect = array_intersect( $tagsToRemove, $softwareDefinedTags );
                        if ( $intersect ) {
                                return self::restrictedTagError( 'tags-update-remove-not-allowed-one',
                                        'tags-update-remove-not-allowed-multi', $intersect );
@@ -633,7 +638,7 @@ class ChangeTags {
                        throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
                }
 
-               $fields['ts_tags'] = wfGetDB( DB_SLAVE )->buildGroupConcatField(
+               $fields['ts_tags'] = wfGetDB( DB_REPLICA )->buildGroupConcatField(
                        ',', 'change_tag', 'ct_tag', $join_cond
                );
 
@@ -1070,8 +1075,8 @@ class ChangeTags {
                        return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
                }
 
-               $extensionDefined = self::listExtensionDefinedTags();
-               if ( in_array( $tag, $extensionDefined ) ) {
+               $softwareDefined = self::listSoftwareDefinedTags();
+               if ( in_array( $tag, $softwareDefined ) ) {
                        // extension-defined tags can't be deleted unless the extension
                        // specifically allows it
                        $status = Status::newFatal( 'tags-delete-not-allowed' );
@@ -1126,22 +1131,26 @@ class ChangeTags {
        }
 
        /**
-        * Lists those tags which extensions report as being "active".
+        * Lists those tags which core or extensions report as being "active".
         *
         * @return array
         * @since 1.25
         */
-       public static function listExtensionActivatedTags() {
+       public static function listSoftwareActivatedTags() {
+               // core active tags
+               $tags = self::$coreTags;
+               if ( !Hooks::isRegistered( 'ChangeTagsListActive' ) ) {
+                       return $tags;
+               }
                return ObjectCache::getMainWANInstance()->getWithSetCallback(
                        wfMemcKey( 'active-tags' ),
                        WANObjectCache::TTL_MINUTE * 5,
-                       function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $tags ) {
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
 
                                // Ask extensions which tags they consider active
-                               $extensionActive = [];
-                               Hooks::run( 'ChangeTagsListActive', [ &$extensionActive ] );
-                               return $extensionActive;
+                               Hooks::run( 'ChangeTagsListActive', [ &$tags ] );
+                               return $tags;
                        },
                        [
                                'checkKeys' => [ wfMemcKey( 'active-tags' ) ],
@@ -1151,6 +1160,16 @@ class ChangeTags {
                );
        }
 
+       /**
+        * @see listSoftwareActivatedTags
+        * @deprecated since 1.28 call listSoftwareActivatedTags directly
+        * @return array
+        */
+       public static function listExtensionActivatedTags() {
+               wfDeprecated( __METHOD__, '1.28' );
+               return self::listSoftwareActivatedTags();
+       }
+
        /**
         * Basically lists defined tags which count even if they aren't applied to anything.
         * It returns a union of the results of listExplicitlyDefinedTags() and
@@ -1160,7 +1179,7 @@ class ChangeTags {
         */
        public static function listDefinedTags() {
                $tags1 = self::listExplicitlyDefinedTags();
-               $tags2 = self::listExtensionDefinedTags();
+               $tags2 = self::listSoftwareDefinedTags();
                return array_values( array_unique( array_merge( $tags1, $tags2 ) ) );
        }
 
@@ -1181,7 +1200,7 @@ class ChangeTags {
                        wfMemcKey( 'valid-tags-db' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -1198,7 +1217,7 @@ class ChangeTags {
        }
 
        /**
-        * Lists tags defined by extensions using the ListDefinedTags hook.
+        * Lists tags defined by core or extensions using the ListDefinedTags hook.
         * Extensions need only define those tags they deem to be in active use.
         *
         * Tries memcached first.
@@ -1206,14 +1225,18 @@ class ChangeTags {
         * @return string[] Array of strings: tags
         * @since 1.25
         */
-       public static function listExtensionDefinedTags() {
+       public static function listSoftwareDefinedTags() {
+               // core defined tags
+               $tags = self::$coreTags;
+               if ( !Hooks::isRegistered( 'ListDefinedTags' ) ) {
+                       return $tags;
+               }
                return ObjectCache::getMainWANInstance()->getWithSetCallback(
                        wfMemcKey( 'valid-tags-hook' ),
                        WANObjectCache::TTL_MINUTE * 5,
-                       function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $tags ) {
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
 
-                               $tags = [];
                                Hooks::run( 'ListDefinedTags', [ &$tags ] );
                                return array_filter( array_unique( $tags ) );
                        },
@@ -1225,6 +1248,17 @@ class ChangeTags {
                );
        }
 
+       /**
+        * Call listSoftwareDefinedTags directly
+        *
+        * @see listSoftwareDefinedTags
+        * @deprecated since 1.28
+        */
+       public static function listExtensionDefinedTags() {
+               wfDeprecated( __METHOD__, '1.28' );
+               return self::listSoftwareDefinedTags();
+       }
+
        /**
         * Invalidates the short-term cache of defined tags used by the
         * list*DefinedTags functions, as well as the tag statistics cache.
@@ -1266,7 +1300,7 @@ class ChangeTags {
                        wfMemcKey( 'change-tag-statistics' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
-                               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+                               $dbr = wfGetDB( DB_REPLICA, 'vslow' );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
index 41fdef5..4e50c8e 100644 (file)
@@ -453,10 +453,6 @@ abstract class ContentHandler {
        public function __construct( $modelId, $formats ) {
                $this->mModelID = $modelId;
                $this->mSupportedFormats = $formats;
-
-               $this->mModelName = preg_replace( '/(Content)?Handler$/', '', get_class( $this ) );
-               $this->mModelName = preg_replace( '/[_\\\\]/', '', $this->mModelName );
-               $this->mModelName = strtolower( $this->mModelName );
        }
 
        /**
@@ -900,7 +896,7 @@ abstract class ContentHandler {
         * have it / want it.
         */
        public function getAutoDeleteReason( Title $title, &$hasHistory ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Get the last revision
                $rev = Revision::newFromTitle( $title );
@@ -1018,9 +1014,21 @@ abstract class ContentHandler {
                        return false; // no content to undo
                }
 
-               $this->checkModelID( $cur_content->getModel() );
-               $this->checkModelID( $undo_content->getModel() );
-               $this->checkModelID( $undoafter_content->getModel() );
+               try {
+                       $this->checkModelID( $cur_content->getModel() );
+                       $this->checkModelID( $undo_content->getModel() );
+                       if ( $current->getId() !== $undo->getId() ) {
+                               // If we are undoing the most recent revision,
+                               // its ok to revert content model changes. However
+                               // if we are undoing a revision in the middle, then
+                               // doing that will be confusing.
+                               $this->checkModelID( $undoafter_content->getModel() );
+                       }
+               } catch ( MWException $e ) {
+                       // If the revisions have different content models
+                       // just return false
+                       return false;
+               }
 
                if ( $cur_content->equals( $undo_content ) ) {
                        // No use doing a merge if it's just a straight revert.
index eb1c67d..edb21f6 100644 (file)
@@ -39,4 +39,9 @@ class JsonContentHandler extends CodeContentHandler {
        protected function getContentClass() {
                return JsonContent::class;
        }
+
+       public function makeEmptyContent() {
+               $class = $this->getContentClass();
+               return new $class( '{}' );
+       }
 }
index 9768d36..55c4ad5 100644 (file)
@@ -233,4 +233,12 @@ class WikiTextStructure {
                $this->extractWikitextParts();
                return $this->auxText;
        }
+
+       /**
+        * Get the defaultsort property
+        * @return string|null
+        */
+       public function getDefaultSort() {
+               return $this->parserOutput->getProperty( 'defaultsort' );
+       }
 }
index 1c46d28..978ac44 100644 (file)
@@ -160,6 +160,7 @@ class WikitextContentHandler extends TextContentHandler {
                $fields['opening_text'] = $structure->getOpeningText();
                $fields['text'] = $structure->getMainText(); // overwrites one from ContentHandler
                $fields['auxiliary_text'] = $structure->getAuxiliaryText();
+               $fields['defaultsort'] = $structure->getDefaultSort();
 
                $title = $page->getTitle();
                if ( NS_FILE == $title->getNamespace() ) {
index b98174b..cc63446 100644 (file)
@@ -26,7 +26,7 @@
  *
  * @since 1.26
  */
-class DBAccessObjectUtils {
+class DBAccessObjectUtils implements IDBAccessObject {
        /**
         * @param integer $bitfield
         * @param integer $flags IDBAccessObject::READ_* constant
@@ -37,23 +37,45 @@ class DBAccessObjectUtils {
        }
 
        /**
-        * Get an appropriate DB index and options for a query
+        * Get an appropriate DB index, options, and fallback DB index for a query
         *
-        * @param integer $bitfield
-        * @return array (DB_MASTER/DB_SLAVE, SELECT options array)
+        * The fallback DB index and options are to be used if the entity is not found
+        * with the initial DB index, typically querying the master DB to avoid lag
+        *
+        * @param integer $bitfield Bitfield of IDBAccessObject::READ_* constants
+        * @return array List of DB indexes and options in this order:
+        *   - DB_MASTER or DB_REPLICA constant for the initial query
+        *   - SELECT options array for the initial query
+        *   - DB_MASTER constant for the fallback query; null if no fallback should happen
+        *   - SELECT options array for the fallback query; empty if no fallback should happen
         */
        public static function getDBOptions( $bitfield ) {
-               $index = self::hasFlags( $bitfield, IDBAccessObject::READ_LATEST )
-                       ? DB_MASTER
-                       : DB_SLAVE;
+               if ( self::hasFlags( $bitfield, self::READ_LATEST_IMMUTABLE ) ) {
+                       $index = DB_REPLICA; // override READ_LATEST if set
+                       $fallbackIndex = DB_MASTER;
+               } elseif ( self::hasFlags( $bitfield, self::READ_LATEST ) ) {
+                       $index = DB_MASTER;
+                       $fallbackIndex = null;
+               } else {
+                       $index = DB_REPLICA;
+                       $fallbackIndex = null;
+               }
+
+               $lockingOptions = [];
+               if ( self::hasFlags( $bitfield, self::READ_EXCLUSIVE ) ) {
+                       $lockingOptions[] = 'FOR UPDATE';
+               } elseif ( self::hasFlags( $bitfield, self::READ_LOCKING ) ) {
+                       $lockingOptions[] = 'LOCK IN SHARE MODE';
+               }
 
-               $options = [];
-               if ( self::hasFlags( $bitfield, IDBAccessObject::READ_EXCLUSIVE ) ) {
-                       $options[] = 'FOR UPDATE';
-               } elseif ( self::hasFlags( $bitfield, IDBAccessObject::READ_LOCKING ) ) {
-                       $options[] = 'LOCK IN SHARE MODE';
+               if ( $fallbackIndex !== null ) {
+                       $options = []; // locks on DB_REPLICA make no sense
+                       $fallbackOptions = $lockingOptions;
+               } else {
+                       $options = $lockingOptions;
+                       $fallbackOptions = []; // no fallback
                }
 
-               return [ $index, $options ];
+               return [ $index, $options, $fallbackIndex, $fallbackOptions ];
        }
 }
index c24962b..5acf3ae 100644 (file)
  * though certain objects may assume READ_LATEST for common use case or legacy reasons.
  *
  * There are four types of reads:
- *   - READ_NORMAL    : Potentially cached read of data (e.g. from a slave or stale replica)
+ *   - READ_NORMAL    : Potentially cached read of data (e.g. from a replica DB or stale replica)
  *   - READ_LATEST    : Up-to-date read as of transaction start (e.g. from master or a quorum read)
  *   - READ_LOCKING   : Up-to-date read as of now, that locks (shared) the records
  *   - READ_EXCLUSIVE : Up-to-date read as of now, that locks (exclusive) the records
  * All record locks persist for the duration of the transaction.
  *
  * A special constant READ_LATEST_IMMUTABLE can be used for fetching append-only data. Such
- * data is either (a) on a slave and up-to-date or (b) not yet there, but on the master/quorum.
- * Because the data is append-only, it can never be stale on a slave if present.
+ * data is either (a) on a replica DB and up-to-date or (b) not yet there, but on the master/quorum.
+ * Because the data is append-only, it can never be stale on a replica DB if present.
  *
  * Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
  * In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
@@ -54,7 +54,7 @@
  */
 interface IDBAccessObject {
        /** Constants for object loading bitfield flags (higher => higher QoS) */
-       /** @var integer Read from a slave/non-quorum */
+       /** @var integer Read from a replica DB/non-quorum */
        const READ_NORMAL = 0;
        /** @var integer Read from the master/quorum */
        const READ_LATEST = 1;
@@ -63,7 +63,7 @@ interface IDBAccessObject {
        /** @var integer Read from the master/quorum and lock out other writers and locking readers */
        const READ_EXCLUSIVE = 7; // READ_LOCKING (3) and "FOR UPDATE" (4)
 
-       /** @var integer Read from a slave/non-quorum immutable data, using the master/quorum on miss */
+       /** @var integer Read from a replica DB or without a quorum, using the master/quorum on miss */
        const READ_LATEST_IMMUTABLE = 8;
 
        // Convenience constant for tracking how data was loaded (higher => higher QoS)
diff --git a/includes/db/ChronologyProtector.php b/includes/db/ChronologyProtector.php
deleted file mode 100644 (file)
index 2539b87..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-/**
- * Generator of database load balancing objects.
- *
- * 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 Database
- */
-
-/**
- * Class for ensuring a consistent ordering of events as seen by the user, despite replication.
- * Kind of like Hawking's [[Chronology Protection Agency]].
- */
-class ChronologyProtector {
-       /** @var BagOStuff */
-       protected $store;
-
-       /** @var string Storage key name */
-       protected $key;
-       /** @var array Map of (ip: <IP>, agent: <user-agent>) */
-       protected $client;
-       /** @var bool Whether to no-op all method calls */
-       protected $enabled = true;
-       /** @var bool Whether to check and wait on positions */
-       protected $wait = true;
-
-       /** @var bool Whether the client data was loaded */
-       protected $initialized = false;
-       /** @var DBMasterPos[] Map of (DB master name => position) */
-       protected $startupPositions = [];
-       /** @var DBMasterPos[] Map of (DB master name => position) */
-       protected $shutdownPositions = [];
-
-       /**
-        * @param BagOStuff $store
-        * @param array $client Map of (ip: <IP>, agent: <user-agent>)
-        * @since 1.27
-        */
-       public function __construct( BagOStuff $store, array $client ) {
-               $this->store = $store;
-               $this->client = $client;
-               $this->key = $store->makeGlobalKey(
-                       'ChronologyProtector',
-                       md5( $client['ip'] . "\n" . $client['agent'] )
-               );
-       }
-
-       /**
-        * @param bool $enabled Whether to no-op all method calls
-        * @since 1.27
-        */
-       public function setEnabled( $enabled ) {
-               $this->enabled = $enabled;
-       }
-
-       /**
-        * @param bool $enabled Whether to check and wait on positions
-        * @since 1.27
-        */
-       public function setWaitEnabled( $enabled ) {
-               $this->wait = $enabled;
-       }
-
-       /**
-        * Initialise a LoadBalancer to give it appropriate chronology protection.
-        *
-        * If the stash has a previous master position recorded, this will try to
-        * make sure that the next query to a slave of that master will see changes up
-        * to that position by delaying execution. The delay may timeout and allow stale
-        * data if no non-lagged slaves are available.
-        *
-        * @param LoadBalancer $lb
-        * @return void
-        */
-       public function initLB( LoadBalancer $lb ) {
-               if ( !$this->enabled || $lb->getServerCount() <= 1 ) {
-                       return; // non-replicated setup or disabled
-               }
-
-               $this->initPositions();
-
-               $masterName = $lb->getServerName( $lb->getWriterIndex() );
-               if ( !empty( $this->startupPositions[$masterName] ) ) {
-                       $info = $lb->parentInfo();
-                       $pos = $this->startupPositions[$masterName];
-                       wfDebugLog( 'replication', __METHOD__ .
-                               ": LB '" . $info['id'] . "' waiting for master pos $pos\n" );
-                       $lb->waitFor( $pos );
-               }
-       }
-
-       /**
-        * Notify the ChronologyProtector that the LoadBalancer is about to shut
-        * down. Saves replication positions.
-        *
-        * @param LoadBalancer $lb
-        * @return void
-        */
-       public function shutdownLB( LoadBalancer $lb ) {
-               if ( !$this->enabled || $lb->getServerCount() <= 1 ) {
-                       return; // non-replicated setup or disabled
-               }
-
-               $info = $lb->parentInfo();
-               $masterName = $lb->getServerName( $lb->getWriterIndex() );
-
-               // Only save the position if writes have been done on the connection
-               $db = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
-               if ( !$db || !$db->doneWrites() ) {
-                       wfDebugLog( 'replication', __METHOD__ . ": LB {$info['id']}, no writes done\n" );
-
-                       return; // nothing to do
-               }
-
-               $pos = $db->getMasterPos();
-               wfDebugLog( 'replication', __METHOD__ . ": LB {$info['id']} has master pos $pos\n" );
-               $this->shutdownPositions[$masterName] = $pos;
-       }
-
-       /**
-        * Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now.
-        * May commit chronology data to persistent storage.
-        *
-        * @return array Empty on success; returns the (db name => position) map on failure
-        */
-       public function shutdown() {
-               if ( !$this->enabled || !count( $this->shutdownPositions ) ) {
-                       return true; // nothing to save
-               }
-
-               wfDebugLog( 'replication',
-                       __METHOD__ . ": saving master pos for " .
-                       implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
-               );
-
-               // CP-protected writes should overwhemingly go to the master datacenter, so get DC-local
-               // lock to merge the values. Use a DC-local get() and a synchronous all-DC set(). This
-               // makes it possible for the BagOStuff class to write in parallel to all DCs with one RTT.
-               if ( $this->store->lock( $this->key, 3 ) ) {
-                       $ok = $this->store->set(
-                               $this->key,
-                               self::mergePositions( $this->store->get( $this->key ), $this->shutdownPositions ),
-                               BagOStuff::TTL_MINUTE,
-                               BagOStuff::WRITE_SYNC
-                       );
-                       $this->store->unlock( $this->key );
-               } else {
-                       $ok = false;
-               }
-
-               if ( !$ok ) {
-                       // Raced out too many times or stash is down
-                       wfDebugLog( 'replication',
-                               __METHOD__ . ": failed to save master pos for " .
-                               implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
-                       );
-
-                       return $this->shutdownPositions;
-               }
-
-               return [];
-       }
-
-       /**
-        * Load in previous master positions for the client
-        */
-       protected function initPositions() {
-               if ( $this->initialized ) {
-                       return;
-               }
-
-               $this->initialized = true;
-               if ( $this->wait ) {
-                       $data = $this->store->get( $this->key );
-                       $this->startupPositions = $data ? $data['positions'] : [];
-
-                       wfDebugLog( 'replication', __METHOD__ . ": key is {$this->key} (read)\n" );
-               } else {
-                       $this->startupPositions = [];
-
-                       wfDebugLog( 'replication', __METHOD__ . ": key is {$this->key} (unread)\n" );
-               }
-       }
-
-       /**
-        * @param array|bool $curValue
-        * @param DBMasterPos[] $shutdownPositions
-        * @return array
-        */
-       private static function mergePositions( $curValue, array $shutdownPositions ) {
-               /** @var $curPositions DBMasterPos[] */
-               if ( $curValue === false ) {
-                       $curPositions = $shutdownPositions;
-               } else {
-                       $curPositions = $curValue['positions'];
-                       // Use the newest positions for each DB master
-                       foreach ( $shutdownPositions as $db => $pos ) {
-                               if ( !isset( $curPositions[$db] )
-                                       || $pos->asOfTime() > $curPositions[$db]->asOfTime()
-                               ) {
-                                       $curPositions[$db] = $pos;
-                               }
-                       }
-               }
-
-               return [ 'positions' => $curPositions ];
-       }
-}
index 577c98d..2af742e 100644 (file)
@@ -43,13 +43,13 @@ class CloneDatabase {
        /**
         * Constructor
         *
-        * @param DatabaseBase $db A database subclass
+        * @param IDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
         * @param string $oldTablePrefix Prefix on current tables, if not $wgDBprefix
         * @param bool $dropCurrentTables
         */
-       public function __construct( DatabaseBase $db, array $tablesToClone,
+       public function __construct( IDatabase $db, array $tablesToClone,
                $newTablePrefix, $oldTablePrefix = '', $dropCurrentTables = true
        ) {
                $this->db = $db;
@@ -76,7 +76,7 @@ class CloneDatabase {
                        if ( $wgSharedDB && in_array( $tbl, $wgSharedTables, true ) ) {
                                // Shared tables don't work properly when cloning due to
                                // how prefixes are handled (bug 65654)
-                               throw new MWException( "Cannot clone shared table $tbl." );
+                               throw new RuntimeException( "Cannot clone shared table $tbl." );
                        }
                        # Clean up from previous aborted run.  So that table escaping
                        # works correctly across DB engines, we need to change the pre-
@@ -93,7 +93,7 @@ class CloneDatabase {
                        ) {
                                if ( $oldTableName === $newTableName ) {
                                        // Last ditch check to avoid data loss
-                                       throw new MWException( "Not dropping new table, as '$newTableName'"
+                                       throw new LogicException( "Not dropping new table, as '$newTableName'"
                                                . " is name of both the old and the new table." );
                                }
                                $this->db->dropTable( $tbl, __METHOD__ );
@@ -129,11 +129,9 @@ class CloneDatabase {
         */
        public static function changePrefix( $prefix ) {
                global $wgDBprefix;
-               wfGetLBFactory()->forEachLB( function( $lb ) use ( $prefix ) {
-                       $lb->forEachOpenConnection( function ( $db ) use ( $prefix ) {
-                               $db->tablePrefix( $prefix );
-                       } );
-               } );
+
+               $lbFactory = wfGetLBFactory();
+               $lbFactory->setDomainPrefix( $prefix );
                $wgDBprefix = $prefix;
        }
 }
diff --git a/includes/db/DBConnRef.php b/includes/db/DBConnRef.php
deleted file mode 100644 (file)
index 790a073..0000000
+++ /dev/null
@@ -1,560 +0,0 @@
-<?php
-/**
- * Helper class to handle automatically marking connections as reusable (via RAII pattern)
- * as well handling deferring the actual network connection until the handle is used
- *
- * @note: proxy methods are defined explicity to avoid interface errors
- * @ingroup Database
- * @since 1.22
- */
-class DBConnRef implements IDatabase {
-       /** @var LoadBalancer */
-       private $lb;
-
-       /** @var DatabaseBase|null */
-       private $conn;
-
-       /** @var array|null */
-       private $params;
-
-       /**
-        * @param LoadBalancer $lb
-        * @param DatabaseBase|array $conn Connection or (server index, group, wiki ID) array
-        */
-       public function __construct( LoadBalancer $lb, $conn ) {
-               $this->lb = $lb;
-               if ( $conn instanceof DatabaseBase ) {
-                       $this->conn = $conn;
-               } else {
-                       $this->params = $conn;
-               }
-       }
-
-       function __call( $name, array $arguments ) {
-               if ( $this->conn === null ) {
-                       list( $db, $groups, $wiki ) = $this->params;
-                       $this->conn = $this->lb->getConnection( $db, $groups, $wiki );
-               }
-
-               return call_user_func_array( [ $this->conn, $name ], $arguments );
-       }
-
-       public function getServerInfo() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function bufferResults( $buffer = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function trxLevel() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function trxTimestamp() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function explicitTrxActive() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function tablePrefix( $prefix = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function dbSchema( $schema = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getLBInfo( $name = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function setLBInfo( $name, $value = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function implicitGroupby() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function implicitOrderby() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lastQuery() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function doneWrites() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lastDoneWrites() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function writesPending() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function writesOrCallbacksPending() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function pendingWriteQueryDuration() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function pendingWriteCallers() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function isOpen() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getFlag( $flag ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getProperty( $name ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getWikiID() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getType() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function open( $server, $user, $password, $dbName ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function fetchObject( $res ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function fetchRow( $res ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function numRows( $res ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function numFields( $res ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function fieldName( $res, $n ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function insertId() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function dataSeek( $res, $row ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lastErrno() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lastError() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function fieldInfo( $table, $field ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function affectedRows() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getSoftwareLink() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getServerVersion() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function close() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function reportConnectionError( $error = 'Unknown error' ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function freeResult( $res ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectField(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectFieldValues(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function select(
-               $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectSQLText(
-               $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectRow(
-               $table, $vars, $conds, $fname = __METHOD__,
-               $options = [], $join_conds = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function estimateRowCount(
-               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectRowCount(
-               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function fieldExists( $table, $field, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function indexExists( $table, $index, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function tableExists( $table, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function indexUnique( $table, $index ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function makeList( $a, $mode = LIST_COMMA ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function bitNot( $field ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function bitAnd( $fieldLeft, $fieldRight ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function bitOr( $fieldLeft, $fieldRight ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function buildConcat( $stringList ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function buildGroupConcatField(
-               $delim, $table, $field, $conds = '', $join_conds = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function selectDB( $db ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getDBname() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getServer() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function addQuotes( $s ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function buildLike() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function anyChar() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function anyString() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function nextSequenceValue( $seqName ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function upsert(
-               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function deleteJoin(
-               $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function delete( $table, $conds, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function insertSelect(
-               $destTable, $srcTable, $varMap, $conds,
-               $fname = __METHOD__, $insertOptions = [], $selectOptions = []
-       ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function unionSupportsOrderAndLimit() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function unionQueries( $sqls, $all ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function conditional( $cond, $trueVal, $falseVal ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function strreplace( $orig, $old, $new ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getServerUptime() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function wasDeadlock() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function wasLockTimeout() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function wasErrorReissuable() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function wasReadOnlyError() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function masterPosWait( DBMasterPos $pos, $timeout ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getSlavePos() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getMasterPos() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function serverIsReadOnly() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function onTransactionResolution( callable $callback ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function onTransactionIdle( callable $callback ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function onTransactionPreCommitOrIdle( callable $callback ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function startAtomic( $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function endAtomic( $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function doAtomicSection( $fname, callable $callback ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function commit( $fname = __METHOD__, $flush = '' ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function rollback( $fname = __METHOD__, $flush = '' ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function listTables( $prefix = null, $fname = __METHOD__ ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function timestamp( $ts = 0 ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function timestampOrNull( $ts = null ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function ping() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getLag() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getSessionLagStatus() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function maxListLen() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function encodeBlob( $b ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function decodeBlob( $b ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function setSessionOptions( array $options ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function setSchemaVars( $vars ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lockIsFree( $lockName, $method ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function lock( $lockName, $method, $timeout = 5 ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function unlock( $lockName, $method ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function namedLocksEnqueue() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function getInfinity() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function encodeExpiry( $expiry ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function decodeExpiry( $expiry, $format = TS_MW ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function setBigSelects( $value = true ) {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       public function isReadOnly() {
-               return $this->__call( __FUNCTION__, func_get_args() );
-       }
-
-       /**
-        * Clean up the connection when out of scope
-        */
-       function __destruct() {
-               if ( $this->conn !== null ) {
-                       $this->lb->reuseConnection( $this->conn );
-               }
-       }
-}
diff --git a/includes/db/Database.php b/includes/db/Database.php
deleted file mode 100644 (file)
index 59c1207..0000000
+++ /dev/null
@@ -1,3493 +0,0 @@
-<?php
-
-/**
- * @defgroup Database Database
- *
- * This file deals with database interface functions
- * and query specifics/optimisations.
- *
- * 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 Database
- */
-
-/**
- * Database abstraction object
- * @ingroup Database
- */
-abstract class DatabaseBase implements IDatabase {
-       /** Number of times to re-try an operation in case of deadlock */
-       const DEADLOCK_TRIES = 4;
-       /** Minimum time to wait before retry, in microseconds */
-       const DEADLOCK_DELAY_MIN = 500000;
-       /** Maximum time to wait before retry */
-       const DEADLOCK_DELAY_MAX = 1500000;
-
-       /** How long before it is worth doing a dummy query to test the connection */
-       const PING_TTL = 1.0;
-
-       /** @var string SQL query */
-       protected $mLastQuery = '';
-       /** @var bool */
-       protected $mDoneWrites = false;
-       /** @var string|bool */
-       protected $mPHPError = false;
-       /** @var string */
-       protected $mServer;
-       /** @var string */
-       protected $mUser;
-       /** @var string */
-       protected $mPassword;
-       /** @var string */
-       protected $mDBname;
-
-       /** @var BagOStuff APC cache */
-       protected $srvCache;
-
-       /** @var resource Database connection */
-       protected $mConn = null;
-       /** @var bool */
-       protected $mOpened = false;
-
-       /** @var array[] List of (callable, method name) */
-       protected $mTrxIdleCallbacks = [];
-       /** @var array[] List of (callable, method name) */
-       protected $mTrxPreCommitCallbacks = [];
-       /** @var array[] List of (callable, method name) */
-       protected $mTrxEndCallbacks = [];
-       /** @var bool Whether to suppress triggering of post-commit callbacks */
-       protected $suppressPostCommitCallbacks = false;
-
-       /** @var string */
-       protected $mTablePrefix;
-       /** @var string */
-       protected $mSchema;
-       /** @var integer */
-       protected $mFlags;
-       /** @var bool */
-       protected $mForeign;
-       /** @var array */
-       protected $mLBInfo = [];
-       /** @var bool|null */
-       protected $mDefaultBigSelects = null;
-       /** @var array|bool */
-       protected $mSchemaVars = false;
-       /** @var array */
-       protected $mSessionVars = [];
-       /** @var array|null */
-       protected $preparedArgs;
-       /** @var string|bool|null Stashed value of html_errors INI setting */
-       protected $htmlErrors;
-       /** @var string */
-       protected $delimiter = ';';
-
-       /**
-        * Either 1 if a transaction is active or 0 otherwise.
-        * The other Trx fields may not be meaningfull if this is 0.
-        *
-        * @var int
-        */
-       protected $mTrxLevel = 0;
-
-       /**
-        * Either a short hexidecimal string if a transaction is active or ""
-        *
-        * @var string
-        * @see DatabaseBase::mTrxLevel
-        */
-       protected $mTrxShortId = '';
-
-       /**
-        * The UNIX time that the transaction started. Callers can assume that if
-        * snapshot isolation is used, then the data is *at least* up to date to that
-        * point (possibly more up-to-date since the first SELECT defines the snapshot).
-        *
-        * @var float|null
-        * @see DatabaseBase::mTrxLevel
-        */
-       private $mTrxTimestamp = null;
-
-       /** @var float Lag estimate at the time of BEGIN */
-       private $mTrxSlaveLag = null;
-
-       /**
-        * Remembers the function name given for starting the most recent transaction via begin().
-        * Used to provide additional context for error reporting.
-        *
-        * @var string
-        * @see DatabaseBase::mTrxLevel
-        */
-       private $mTrxFname = null;
-
-       /**
-        * Record if possible write queries were done in the last transaction started
-        *
-        * @var bool
-        * @see DatabaseBase::mTrxLevel
-        */
-       private $mTrxDoneWrites = false;
-
-       /**
-        * Record if the current transaction was started implicitly due to DBO_TRX being set.
-        *
-        * @var bool
-        * @see DatabaseBase::mTrxLevel
-        */
-       private $mTrxAutomatic = false;
-
-       /**
-        * Array of levels of atomicity within transactions
-        *
-        * @var array
-        */
-       private $mTrxAtomicLevels = [];
-
-       /**
-        * Record if the current transaction was started implicitly by DatabaseBase::startAtomic
-        *
-        * @var bool
-        */
-       private $mTrxAutomaticAtomic = false;
-
-       /**
-        * Track the write query callers of the current transaction
-        *
-        * @var string[]
-        */
-       private $mTrxWriteCallers = [];
-
-       /**
-        * Track the seconds spent in write queries for the current transaction
-        *
-        * @var float
-        */
-       private $mTrxWriteDuration = 0.0;
-
-       /** @var array Map of (name => 1) for locks obtained via lock() */
-       private $mNamedLocksHeld = [];
-
-       /** @var IDatabase|null Lazy handle to the master DB this server replicates from */
-       private $lazyMasterHandle;
-
-       /**
-        * @since 1.21
-        * @var resource File handle for upgrade
-        */
-       protected $fileHandle = null;
-
-       /**
-        * @since 1.22
-        * @var string[] Process cache of VIEWs names in the database
-        */
-       protected $allViews = null;
-
-       /** @var float UNIX timestamp */
-       protected $lastPing = 0.0;
-
-       /** @var int[] Prior mFlags values */
-       private $priorFlags = [];
-
-       /** @var TransactionProfiler */
-       protected $trxProfiler;
-
-       public function getServerInfo() {
-               return $this->getServerVersion();
-       }
-
-       /**
-        * @return string Command delimiter used by this database engine
-        */
-       public function getDelimiter() {
-               return $this->delimiter;
-       }
-
-       /**
-        * Boolean, controls output of large amounts of debug information.
-        * @param bool|null $debug
-        *   - true to enable debugging
-        *   - false to disable debugging
-        *   - omitted or null to do nothing
-        *
-        * @return bool|null Previous value of the flag
-        */
-       public function debug( $debug = null ) {
-               return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
-       }
-
-       public function bufferResults( $buffer = null ) {
-               if ( is_null( $buffer ) ) {
-                       return !(bool)( $this->mFlags & DBO_NOBUFFER );
-               } else {
-                       return !wfSetBit( $this->mFlags, DBO_NOBUFFER, !$buffer );
-               }
-       }
-
-       /**
-        * Turns on (false) or off (true) the automatic generation and sending
-        * of a "we're sorry, but there has been a database error" page on
-        * database errors. Default is on (false). When turned off, the
-        * code should use lastErrno() and lastError() to handle the
-        * situation as appropriate.
-        *
-        * Do not use this function outside of the Database classes.
-        *
-        * @param null|bool $ignoreErrors
-        * @return bool The previous value of the flag.
-        */
-       protected function ignoreErrors( $ignoreErrors = null ) {
-               return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
-       }
-
-       public function trxLevel() {
-               return $this->mTrxLevel;
-       }
-
-       public function trxTimestamp() {
-               return $this->mTrxLevel ? $this->mTrxTimestamp : null;
-       }
-
-       public function tablePrefix( $prefix = null ) {
-               return wfSetVar( $this->mTablePrefix, $prefix );
-       }
-
-       public function dbSchema( $schema = null ) {
-               return wfSetVar( $this->mSchema, $schema );
-       }
-
-       /**
-        * Set the filehandle to copy write statements to.
-        *
-        * @param resource $fh File handle
-        */
-       public function setFileHandle( $fh ) {
-               $this->fileHandle = $fh;
-       }
-
-       public function getLBInfo( $name = null ) {
-               if ( is_null( $name ) ) {
-                       return $this->mLBInfo;
-               } else {
-                       if ( array_key_exists( $name, $this->mLBInfo ) ) {
-                               return $this->mLBInfo[$name];
-                       } else {
-                               return null;
-                       }
-               }
-       }
-
-       public function setLBInfo( $name, $value = null ) {
-               if ( is_null( $value ) ) {
-                       $this->mLBInfo = $name;
-               } else {
-                       $this->mLBInfo[$name] = $value;
-               }
-       }
-
-       /**
-        * Set a lazy-connecting DB handle to the master DB (for replication status purposes)
-        *
-        * @param IDatabase $conn
-        * @since 1.27
-        */
-       public function setLazyMasterHandle( IDatabase $conn ) {
-               $this->lazyMasterHandle = $conn;
-       }
-
-       /**
-        * @return IDatabase|null
-        * @see setLazyMasterHandle()
-        * @since 1.27
-        */
-       public function getLazyMasterHandle() {
-               return $this->lazyMasterHandle;
-       }
-
-       /**
-        * @return TransactionProfiler
-        */
-       protected function getTransactionProfiler() {
-               if ( !$this->trxProfiler ) {
-                       $this->trxProfiler = new TransactionProfiler();
-               }
-
-               return $this->trxProfiler;
-       }
-
-       /**
-        * @param TransactionProfiler $profiler
-        * @since 1.27
-        */
-       public function setTransactionProfiler( TransactionProfiler $profiler ) {
-               $this->trxProfiler = $profiler;
-       }
-
-       /**
-        * Returns true if this database supports (and uses) cascading deletes
-        *
-        * @return bool
-        */
-       public function cascadingDeletes() {
-               return false;
-       }
-
-       /**
-        * Returns true if this database supports (and uses) triggers (e.g. on the page table)
-        *
-        * @return bool
-        */
-       public function cleanupTriggers() {
-               return false;
-       }
-
-       /**
-        * Returns true if this database is strict about what can be put into an IP field.
-        * Specifically, it uses a NULL value instead of an empty string.
-        *
-        * @return bool
-        */
-       public function strictIPs() {
-               return false;
-       }
-
-       /**
-        * Returns true if this database uses timestamps rather than integers
-        *
-        * @return bool
-        */
-       public function realTimestamps() {
-               return false;
-       }
-
-       public function implicitGroupby() {
-               return true;
-       }
-
-       public function implicitOrderby() {
-               return true;
-       }
-
-       /**
-        * Returns true if this database can do a native search on IP columns
-        * e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
-        *
-        * @return bool
-        */
-       public function searchableIPs() {
-               return false;
-       }
-
-       /**
-        * Returns true if this database can use functional indexes
-        *
-        * @return bool
-        */
-       public function functionalIndexes() {
-               return false;
-       }
-
-       public function lastQuery() {
-               return $this->mLastQuery;
-       }
-
-       public function doneWrites() {
-               return (bool)$this->mDoneWrites;
-       }
-
-       public function lastDoneWrites() {
-               return $this->mDoneWrites ?: false;
-       }
-
-       public function writesPending() {
-               return $this->mTrxLevel && $this->mTrxDoneWrites;
-       }
-
-       public function writesOrCallbacksPending() {
-               return $this->mTrxLevel && (
-                       $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
-               );
-       }
-
-       public function pendingWriteQueryDuration() {
-               return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
-       }
-
-       public function pendingWriteCallers() {
-               return $this->mTrxLevel ? $this->mTrxWriteCallers : [];
-       }
-
-       public function isOpen() {
-               return $this->mOpened;
-       }
-
-       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
-               if ( $remember === self::REMEMBER_PRIOR ) {
-                       array_push( $this->priorFlags, $this->mFlags );
-               }
-               $this->mFlags |= $flag;
-       }
-
-       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
-               if ( $remember === self::REMEMBER_PRIOR ) {
-                       array_push( $this->priorFlags, $this->mFlags );
-               }
-               $this->mFlags &= ~$flag;
-       }
-
-       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
-               if ( !$this->priorFlags ) {
-                       return;
-               }
-
-               if ( $state === self::RESTORE_INITIAL ) {
-                       $this->mFlags = reset( $this->priorFlags );
-                       $this->priorFlags = [];
-               } else {
-                       $this->mFlags = array_pop( $this->priorFlags );
-               }
-       }
-
-       public function getFlag( $flag ) {
-               return !!( $this->mFlags & $flag );
-       }
-
-       public function getProperty( $name ) {
-               return $this->$name;
-       }
-
-       public function getWikiID() {
-               if ( $this->mTablePrefix ) {
-                       return "{$this->mDBname}-{$this->mTablePrefix}";
-               } else {
-                       return $this->mDBname;
-               }
-       }
-
-       /**
-        * Return a path to the DBMS-specific SQL file if it exists,
-        * otherwise default SQL file
-        *
-        * @param string $filename
-        * @return string
-        */
-       private function getSqlFilePath( $filename ) {
-               global $IP;
-               $dbmsSpecificFilePath = "$IP/maintenance/" . $this->getType() . "/$filename";
-               if ( file_exists( $dbmsSpecificFilePath ) ) {
-                       return $dbmsSpecificFilePath;
-               } else {
-                       return "$IP/maintenance/$filename";
-               }
-       }
-
-       /**
-        * Return a path to the DBMS-specific schema file,
-        * otherwise default to tables.sql
-        *
-        * @return string
-        */
-       public function getSchemaPath() {
-               return $this->getSqlFilePath( 'tables.sql' );
-       }
-
-       /**
-        * Return a path to the DBMS-specific update key file,
-        * otherwise default to update-keys.sql
-        *
-        * @return string
-        */
-       public function getUpdateKeysPath() {
-               return $this->getSqlFilePath( 'update-keys.sql' );
-       }
-
-       /**
-        * Get information about an index into an object
-        * @param string $table Table name
-        * @param string $index Index name
-        * @param string $fname Calling function name
-        * @return mixed Database-specific index description class or false if the index does not exist
-        */
-       abstract function indexInfo( $table, $index, $fname = __METHOD__ );
-
-       /**
-        * Wrapper for addslashes()
-        *
-        * @param string $s String to be slashed.
-        * @return string Slashed string.
-        */
-       abstract function strencode( $s );
-
-       /**
-        * Constructor.
-        *
-        * FIXME: It is possible to construct a Database object with no associated
-        * connection object, by specifying no parameters to __construct(). This
-        * feature is deprecated and should be removed.
-        *
-        * DatabaseBase subclasses should not be constructed directly in external
-        * code. DatabaseBase::factory() should be used instead.
-        *
-        * @param array $params Parameters passed from DatabaseBase::factory()
-        */
-       function __construct( array $params ) {
-               global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode;
-
-               $this->srvCache = ObjectCache::getLocalServerInstance( 'hash' );
-
-               $server = $params['host'];
-               $user = $params['user'];
-               $password = $params['password'];
-               $dbName = $params['dbname'];
-               $flags = $params['flags'];
-               $tablePrefix = $params['tablePrefix'];
-               $schema = $params['schema'];
-               $foreign = $params['foreign'];
-
-               $this->mFlags = $flags;
-               if ( $this->mFlags & DBO_DEFAULT ) {
-                       if ( $wgCommandLineMode ) {
-                               $this->mFlags &= ~DBO_TRX;
-                       } else {
-                               $this->mFlags |= DBO_TRX;
-                       }
-               }
-
-               $this->mSessionVars = $params['variables'];
-
-               /** Get the default table prefix*/
-               if ( $tablePrefix === 'get from global' ) {
-                       $this->mTablePrefix = $wgDBprefix;
-               } else {
-                       $this->mTablePrefix = $tablePrefix;
-               }
-
-               /** Get the database schema*/
-               if ( $schema === 'get from global' ) {
-                       $this->mSchema = $wgDBmwschema;
-               } else {
-                       $this->mSchema = $schema;
-               }
-
-               $this->mForeign = $foreign;
-
-               if ( isset( $params['trxProfiler'] ) ) {
-                       $this->trxProfiler = $params['trxProfiler']; // override
-               }
-
-               if ( $user ) {
-                       $this->open( $server, $user, $password, $dbName );
-               }
-       }
-
-       /**
-        * Called by serialize. Throw an exception when DB connection is serialized.
-        * This causes problems on some database engines because the connection is
-        * not restored on unserialize.
-        */
-       public function __sleep() {
-               throw new MWException( 'Database serialization may cause problems, since ' .
-                       'the connection is not restored on wakeup.' );
-       }
-
-       /**
-        * Given a DB type, construct the name of the appropriate child class of
-        * DatabaseBase. This is designed to replace all of the manual stuff like:
-        *    $class = 'Database' . ucfirst( strtolower( $dbType ) );
-        * as well as validate against the canonical list of DB types we have
-        *
-        * This factory function is mostly useful for when you need to connect to a
-        * database other than the MediaWiki default (such as for external auth,
-        * an extension, et cetera). Do not use this to connect to the MediaWiki
-        * database. Example uses in core:
-        * @see LoadBalancer::reallyOpenConnection()
-        * @see ForeignDBRepo::getMasterDB()
-        * @see WebInstallerDBConnect::execute()
-        *
-        * @since 1.18
-        *
-        * @param string $dbType A possible DB type
-        * @param array $p An array of options to pass to the constructor.
-        *    Valid options are: host, user, password, dbname, flags, tablePrefix, schema, driver
-        * @throws MWException If the database driver or extension cannot be found
-        * @return DatabaseBase|null DatabaseBase subclass or null
-        */
-       final public static function factory( $dbType, $p = [] ) {
-               $canonicalDBTypes = [
-                       'mysql' => [ 'mysqli', 'mysql' ],
-                       'postgres' => [],
-                       'sqlite' => [],
-                       'oracle' => [],
-                       'mssql' => [],
-               ];
-
-               $driver = false;
-               $dbType = strtolower( $dbType );
-               if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
-                       $possibleDrivers = $canonicalDBTypes[$dbType];
-                       if ( !empty( $p['driver'] ) ) {
-                               if ( in_array( $p['driver'], $possibleDrivers ) ) {
-                                       $driver = $p['driver'];
-                               } else {
-                                       throw new MWException( __METHOD__ .
-                                               " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" );
-                               }
-                       } else {
-                               foreach ( $possibleDrivers as $posDriver ) {
-                                       if ( extension_loaded( $posDriver ) ) {
-                                               $driver = $posDriver;
-                                               break;
-                                       }
-                               }
-                       }
-               } else {
-                       $driver = $dbType;
-               }
-               if ( $driver === false ) {
-                       throw new MWException( __METHOD__ .
-                               " no viable database extension found for type '$dbType'" );
-               }
-
-               // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema,
-               // and everything else doesn't use a schema (e.g. null)
-               // Although postgres and oracle support schemas, we don't use them (yet)
-               // to maintain backwards compatibility
-               $defaultSchemas = [
-                       'mssql' => 'get from global',
-               ];
-
-               $class = 'Database' . ucfirst( $driver );
-               if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
-                       // Resolve some defaults for b/c
-                       $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
-                       $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
-                       $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
-                       $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
-                       $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
-                       $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
-                       $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : 'get from global';
-                       if ( !isset( $p['schema'] ) ) {
-                               $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null;
-                       }
-                       $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
-
-                       return new $class( $p );
-               } else {
-                       return null;
-               }
-       }
-
-       protected function installErrorHandler() {
-               $this->mPHPError = false;
-               $this->htmlErrors = ini_set( 'html_errors', '0' );
-               set_error_handler( [ $this, 'connectionErrorHandler' ] );
-       }
-
-       /**
-        * @return bool|string
-        */
-       protected function restoreErrorHandler() {
-               restore_error_handler();
-               if ( $this->htmlErrors !== false ) {
-                       ini_set( 'html_errors', $this->htmlErrors );
-               }
-               if ( $this->mPHPError ) {
-                       $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
-                       $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
-
-                       return $error;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @param int $errno
-        * @param string $errstr
-        */
-       public function connectionErrorHandler( $errno, $errstr ) {
-               $this->mPHPError = $errstr;
-       }
-
-       /**
-        * Create a log context to pass to wfLogDBError or other logging functions.
-        *
-        * @param array $extras Additional data to add to context
-        * @return array
-        */
-       protected function getLogContext( array $extras = [] ) {
-               return array_merge(
-                       [
-                               'db_server' => $this->mServer,
-                               'db_name' => $this->mDBname,
-                               'db_user' => $this->mUser,
-                       ],
-                       $extras
-               );
-       }
-
-       public function close() {
-               if ( $this->mConn ) {
-                       if ( $this->trxLevel() ) {
-                               if ( !$this->mTrxAutomatic ) {
-                                       wfWarn( "Transaction still in progress (from {$this->mTrxFname}), " .
-                                               " performing implicit commit before closing connection!" );
-                               }
-
-                               $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
-                       }
-
-                       $closed = $this->closeConnection();
-                       $this->mConn = false;
-               } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
-                       throw new MWException( "Transaction callbacks still pending." );
-               } else {
-                       $closed = true;
-               }
-               $this->mOpened = false;
-
-               return $closed;
-       }
-
-       /**
-        * Make sure isOpen() returns true as a sanity check
-        *
-        * @throws DBUnexpectedError
-        */
-       protected function assertOpen() {
-               if ( !$this->isOpen() ) {
-                       throw new DBUnexpectedError( $this, "DB connection was already closed." );
-               }
-       }
-
-       /**
-        * Closes underlying database connection
-        * @since 1.20
-        * @return bool Whether connection was closed successfully
-        */
-       abstract protected function closeConnection();
-
-       function reportConnectionError( $error = 'Unknown error' ) {
-               $myError = $this->lastError();
-               if ( $myError ) {
-                       $error = $myError;
-               }
-
-               # New method
-               throw new DBConnectionError( $this, $error );
-       }
-
-       /**
-        * The DBMS-dependent part of query()
-        *
-        * @param string $sql SQL query.
-        * @return ResultWrapper|bool Result object to feed to fetchObject,
-        *   fetchRow, ...; or false on failure
-        */
-       abstract protected function doQuery( $sql );
-
-       /**
-        * Determine whether a query writes to the DB.
-        * Should return true if unsure.
-        *
-        * @param string $sql
-        * @return bool
-        */
-       protected function isWriteQuery( $sql ) {
-               return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
-       }
-
-       /**
-        * Determine whether a SQL statement is sensitive to isolation level.
-        * A SQL statement is considered transactable if its result could vary
-        * depending on the transaction isolation level. Operational commands
-        * such as 'SET' and 'SHOW' are not considered to be transactable.
-        *
-        * @param string $sql
-        * @return bool
-        */
-       protected function isTransactableQuery( $sql ) {
-               $verb = substr( $sql, 0, strcspn( $sql, " \t\r\n" ) );
-               return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ] );
-       }
-
-       public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
-               global $wgUser;
-
-               $priorWritesPending = $this->writesOrCallbacksPending();
-               $this->mLastQuery = $sql;
-
-               $isWrite = $this->isWriteQuery( $sql );
-               if ( $isWrite ) {
-                       $reason = $this->getReadOnlyReason();
-                       if ( $reason !== false ) {
-                               throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
-                       }
-                       # Set a flag indicating that writes have been done
-                       $this->mDoneWrites = microtime( true );
-               }
-
-               # Add a comment for easy SHOW PROCESSLIST interpretation
-               if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) {
-                       $userName = $wgUser->getName();
-                       if ( mb_strlen( $userName ) > 15 ) {
-                               $userName = mb_substr( $userName, 0, 15 ) . '...';
-                       }
-                       $userName = str_replace( '/', '', $userName );
-               } else {
-                       $userName = '';
-               }
-
-               // Add trace comment to the begin of the sql string, right after the operator.
-               // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598)
-               $commentedSql = preg_replace( '/\s|$/', " /* $fname $userName */ ", $sql, 1 );
-
-               # Start implicit transactions that wrap the request if DBO_TRX is enabled
-               if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX )
-                       && $this->isTransactableQuery( $sql )
-               ) {
-                       $this->begin( __METHOD__ . " ($fname)", self::TRANSACTION_INTERNAL );
-                       $this->mTrxAutomatic = true;
-               }
-
-               # Keep track of whether the transaction has write queries pending
-               if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) {
-                       $this->mTrxDoneWrites = true;
-                       $this->getTransactionProfiler()->transactionWritingIn(
-                               $this->mServer, $this->mDBname, $this->mTrxShortId );
-               }
-
-               if ( $this->debug() ) {
-                       wfDebugLog( 'queries', sprintf( "%s: %s", $this->mDBname, $commentedSql ) );
-               }
-
-               # Avoid fatals if close() was called
-               $this->assertOpen();
-
-               # Send the query to the server
-               $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
-
-               # Try reconnecting if the connection was lost
-               if ( false === $ret && $this->wasErrorReissuable() ) {
-                       $recoverable = $this->canRecoverFromDisconnect( $sql, $priorWritesPending );
-                       # Stash the last error values before anything might clear them
-                       $lastError = $this->lastError();
-                       $lastErrno = $this->lastErrno();
-                       # Update state tracking to reflect transaction loss due to disconnection
-                       $this->handleTransactionLoss();
-                       wfDebug( "Connection lost, reconnecting...\n" );
-                       if ( $this->reconnect() ) {
-                               wfDebug( "Reconnected\n" );
-                               $msg = __METHOD__ . ": lost connection to {$this->getServer()}; reconnected";
-                               wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
-
-                               if ( !$recoverable ) {
-                                       # Callers may catch the exception and continue to use the DB
-                                       $this->reportQueryError( $lastError, $lastErrno, $sql, $fname );
-                               } else {
-                                       # Should be safe to silently retry the query
-                                       $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
-                               }
-                       } else {
-                               wfDebug( "Failed\n" );
-                       }
-               }
-
-               if ( false === $ret ) {
-                       # Deadlocks cause the entire transaction to abort, not just the statement.
-                       # http://dev.mysql.com/doc/refman/5.7/en/innodb-error-handling.html
-                       # https://www.postgresql.org/docs/9.1/static/explicit-locking.html
-                       if ( $this->wasDeadlock() ) {
-                               if ( $this->explicitTrxActive() || $priorWritesPending ) {
-                                       $tempIgnore = false; // not recoverable
-                               }
-                               # Update state tracking to reflect transaction loss
-                               $this->handleTransactionLoss();
-                       }
-
-                       $this->reportQueryError(
-                               $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
-               }
-
-               $res = $this->resultObject( $ret );
-
-               return $res;
-       }
-
-       private function doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ) {
-               $isMaster = !is_null( $this->getLBInfo( 'master' ) );
-               # generalizeSQL() will probably cut down the query to reasonable
-               # logging size most of the time. The substr is really just a sanity check.
-               if ( $isMaster ) {
-                       $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
-               } else {
-                       $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
-               }
-
-               # Include query transaction state
-               $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
-
-               $profiler = Profiler::instance();
-               if ( !( $profiler instanceof ProfilerStub ) ) {
-                       $queryProfSection = $profiler->scopedProfileIn( $queryProf );
-               }
-
-               $startTime = microtime( true );
-               $ret = $this->doQuery( $commentedSql );
-               $queryRuntime = microtime( true ) - $startTime;
-
-               unset( $queryProfSection ); // profile out (if set)
-
-               if ( $ret !== false ) {
-                       $this->lastPing = $startTime;
-                       if ( $isWrite && $this->mTrxLevel ) {
-                               $this->mTrxWriteDuration += $queryRuntime;
-                               $this->mTrxWriteCallers[] = $fname;
-                       }
-               }
-
-               $this->getTransactionProfiler()->recordQueryCompletion(
-                       $queryProf, $startTime, $isWrite, $this->affectedRows()
-               );
-               MWDebug::query( $sql, $fname, $isMaster, $queryRuntime );
-
-               return $ret;
-       }
-
-       private function canRecoverFromDisconnect( $sql, $priorWritesPending ) {
-               # Transaction dropped; this can mean lost writes, or REPEATABLE-READ snapshots.
-               # Dropped connections also mean that named locks are automatically released.
-               # Only allow error suppression in autocommit mode or when the lost transaction
-               # didn't matter anyway (aside from DBO_TRX snapshot loss).
-               if ( $this->mNamedLocksHeld ) {
-                       return false; // possible critical section violation
-               } elseif ( $sql === 'COMMIT' ) {
-                       return !$priorWritesPending; // nothing written anyway? (T127428)
-               } elseif ( $sql === 'ROLLBACK' ) {
-                       return true; // transaction lost...which is also what was requested :)
-               } elseif ( $this->explicitTrxActive() ) {
-                       return false; // don't drop atomocity
-               } elseif ( $priorWritesPending ) {
-                       return false; // prior writes lost from implicit transaction
-               }
-
-               return true;
-       }
-
-       private function handleTransactionLoss() {
-               $this->mTrxLevel = 0;
-               $this->mTrxIdleCallbacks = []; // bug 65263
-               $this->mTrxPreCommitCallbacks = []; // bug 65263
-               try {
-                       // Handle callbacks in mTrxEndCallbacks
-                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
-                       return null;
-               } catch ( Exception $e ) {
-                       // Already logged; move on...
-                       return $e;
-               }
-       }
-
-       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
-               if ( $this->ignoreErrors() || $tempIgnore ) {
-                       wfDebug( "SQL ERROR (ignored): $error\n" );
-               } else {
-                       $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
-                       wfLogDBError(
-                               "{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
-                               $this->getLogContext( [
-                                       'method' => __METHOD__,
-                                       'errno' => $errno,
-                                       'error' => $error,
-                                       'sql1line' => $sql1line,
-                                       'fname' => $fname,
-                               ] )
-                       );
-                       wfDebug( "SQL ERROR: " . $error . "\n" );
-                       throw new DBQueryError( $this, $error, $errno, $sql, $fname );
-               }
-       }
-
-       /**
-        * Intended to be compatible with the PEAR::DB wrapper functions.
-        * http://pear.php.net/manual/en/package.database.db.intro-execute.php
-        *
-        * ? = scalar value, quoted as necessary
-        * ! = raw SQL bit (a function for instance)
-        * & = filename; reads the file and inserts as a blob
-        *     (we don't use this though...)
-        *
-        * @param string $sql
-        * @param string $func
-        *
-        * @return array
-        */
-       protected function prepare( $sql, $func = 'DatabaseBase::prepare' ) {
-               /* MySQL doesn't support prepared statements (yet), so just
-                * pack up the query for reference. We'll manually replace
-                * the bits later.
-                */
-               return [ 'query' => $sql, 'func' => $func ];
-       }
-
-       /**
-        * Free a prepared query, generated by prepare().
-        * @param string $prepared
-        */
-       protected function freePrepared( $prepared ) {
-               /* No-op by default */
-       }
-
-       /**
-        * Execute a prepared query with the various arguments
-        * @param string $prepared The prepared sql
-        * @param mixed $args Either an array here, or put scalars as varargs
-        *
-        * @return ResultWrapper
-        */
-       public function execute( $prepared, $args = null ) {
-               if ( !is_array( $args ) ) {
-                       # Pull the var args
-                       $args = func_get_args();
-                       array_shift( $args );
-               }
-
-               $sql = $this->fillPrepared( $prepared['query'], $args );
-
-               return $this->query( $sql, $prepared['func'] );
-       }
-
-       /**
-        * For faking prepared SQL statements on DBs that don't support it directly.
-        *
-        * @param string $preparedQuery A 'preparable' SQL statement
-        * @param array $args Array of Arguments to fill it with
-        * @return string Executable SQL
-        */
-       public function fillPrepared( $preparedQuery, $args ) {
-               reset( $args );
-               $this->preparedArgs =& $args;
-
-               return preg_replace_callback( '/(\\\\[?!&]|[?!&])/',
-                       [ &$this, 'fillPreparedArg' ], $preparedQuery );
-       }
-
-       /**
-        * preg_callback func for fillPrepared()
-        * The arguments should be in $this->preparedArgs and must not be touched
-        * while we're doing this.
-        *
-        * @param array $matches
-        * @throws DBUnexpectedError
-        * @return string
-        */
-       protected function fillPreparedArg( $matches ) {
-               switch ( $matches[1] ) {
-                       case '\\?':
-                               return '?';
-                       case '\\!':
-                               return '!';
-                       case '\\&':
-                               return '&';
-               }
-
-               list( /* $n */, $arg ) = each( $this->preparedArgs );
-
-               switch ( $matches[1] ) {
-                       case '?':
-                               return $this->addQuotes( $arg );
-                       case '!':
-                               return $arg;
-                       case '&':
-                               # return $this->addQuotes( file_get_contents( $arg ) );
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       '& mode is not implemented. If it\'s really needed, uncomment the line above.'
-                               );
-                       default:
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       'Received invalid match. This should never happen!'
-                               );
-               }
-       }
-
-       public function freeResult( $res ) {
-       }
-
-       public function selectField(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = []
-       ) {
-               if ( $var === '*' ) { // sanity
-                       throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
-               }
-
-               if ( !is_array( $options ) ) {
-                       $options = [ $options ];
-               }
-
-               $options['LIMIT'] = 1;
-
-               $res = $this->select( $table, $var, $cond, $fname, $options );
-               if ( $res === false || !$this->numRows( $res ) ) {
-                       return false;
-               }
-
-               $row = $this->fetchRow( $res );
-
-               if ( $row !== false ) {
-                       return reset( $row );
-               } else {
-                       return false;
-               }
-       }
-
-       public function selectFieldValues(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = []
-       ) {
-               if ( $var === '*' ) { // sanity
-                       throw new DBUnexpectedError( $this, "Cannot use a * field" );
-               } elseif ( !is_string( $var ) ) { // sanity
-                       throw new DBUnexpectedError( $this, "Cannot use an array of fields" );
-               }
-
-               if ( !is_array( $options ) ) {
-                       $options = [ $options ];
-               }
-
-               $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
-               if ( $res === false ) {
-                       return false;
-               }
-
-               $values = [];
-               foreach ( $res as $row ) {
-                       $values[] = $row->$var;
-               }
-
-               return $values;
-       }
-
-       /**
-        * Returns an optional USE INDEX clause to go after the table, and a
-        * string to go at the end of the query.
-        *
-        * @param array $options Associative array of options to be turned into
-        *   an SQL query, valid keys are listed in the function.
-        * @return array
-        * @see DatabaseBase::select()
-        */
-       public function makeSelectOptions( $options ) {
-               $preLimitTail = $postLimitTail = '';
-               $startOpts = '';
-
-               $noKeyOptions = [];
-
-               foreach ( $options as $key => $option ) {
-                       if ( is_numeric( $key ) ) {
-                               $noKeyOptions[$option] = true;
-                       }
-               }
-
-               $preLimitTail .= $this->makeGroupByWithHaving( $options );
-
-               $preLimitTail .= $this->makeOrderBy( $options );
-
-               // if (isset($options['LIMIT'])) {
-               //      $tailOpts .= $this->limitResult('', $options['LIMIT'],
-               //              isset($options['OFFSET']) ? $options['OFFSET']
-               //              : false);
-               // }
-
-               if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
-                       $postLimitTail .= ' FOR UPDATE';
-               }
-
-               if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) {
-                       $postLimitTail .= ' LOCK IN SHARE MODE';
-               }
-
-               if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
-                       $startOpts .= 'DISTINCT';
-               }
-
-               # Various MySQL extensions
-               if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) {
-                       $startOpts .= ' /*! STRAIGHT_JOIN */';
-               }
-
-               if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) {
-                       $startOpts .= ' HIGH_PRIORITY';
-               }
-
-               if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) {
-                       $startOpts .= ' SQL_BIG_RESULT';
-               }
-
-               if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) {
-                       $startOpts .= ' SQL_BUFFER_RESULT';
-               }
-
-               if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) {
-                       $startOpts .= ' SQL_SMALL_RESULT';
-               }
-
-               if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) {
-                       $startOpts .= ' SQL_CALC_FOUND_ROWS';
-               }
-
-               if ( isset( $noKeyOptions['SQL_CACHE'] ) ) {
-                       $startOpts .= ' SQL_CACHE';
-               }
-
-               if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) {
-                       $startOpts .= ' SQL_NO_CACHE';
-               }
-
-               if ( isset( $options['USE INDEX'] ) && is_string( $options['USE INDEX'] ) ) {
-                       $useIndex = $this->useIndexClause( $options['USE INDEX'] );
-               } else {
-                       $useIndex = '';
-               }
-
-               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ];
-       }
-
-       /**
-        * Returns an optional GROUP BY with an optional HAVING
-        *
-        * @param array $options Associative array of options
-        * @return string
-        * @see DatabaseBase::select()
-        * @since 1.21
-        */
-       public function makeGroupByWithHaving( $options ) {
-               $sql = '';
-               if ( isset( $options['GROUP BY'] ) ) {
-                       $gb = is_array( $options['GROUP BY'] )
-                               ? implode( ',', $options['GROUP BY'] )
-                               : $options['GROUP BY'];
-                       $sql .= ' GROUP BY ' . $gb;
-               }
-               if ( isset( $options['HAVING'] ) ) {
-                       $having = is_array( $options['HAVING'] )
-                               ? $this->makeList( $options['HAVING'], LIST_AND )
-                               : $options['HAVING'];
-                       $sql .= ' HAVING ' . $having;
-               }
-
-               return $sql;
-       }
-
-       /**
-        * Returns an optional ORDER BY
-        *
-        * @param array $options Associative array of options
-        * @return string
-        * @see DatabaseBase::select()
-        * @since 1.21
-        */
-       public function makeOrderBy( $options ) {
-               if ( isset( $options['ORDER BY'] ) ) {
-                       $ob = is_array( $options['ORDER BY'] )
-                               ? implode( ',', $options['ORDER BY'] )
-                               : $options['ORDER BY'];
-
-                       return ' ORDER BY ' . $ob;
-               }
-
-               return '';
-       }
-
-       // See IDatabase::select for the docs for this function
-       public function select( $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = [] ) {
-               $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
-
-               return $this->query( $sql, $fname );
-       }
-
-       public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = []
-       ) {
-               if ( is_array( $vars ) ) {
-                       $vars = implode( ',', $this->fieldNamesWithAlias( $vars ) );
-               }
-
-               $options = (array)$options;
-               $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) )
-                       ? $options['USE INDEX']
-                       : [];
-
-               if ( is_array( $table ) ) {
-                       $from = ' FROM ' .
-                               $this->tableNamesWithUseIndexOrJOIN( $table, $useIndexes, $join_conds );
-               } elseif ( $table != '' ) {
-                       if ( $table[0] == ' ' ) {
-                               $from = ' FROM ' . $table;
-                       } else {
-                               $from = ' FROM ' .
-                                       $this->tableNamesWithUseIndexOrJOIN( [ $table ], $useIndexes, [] );
-                       }
-               } else {
-                       $from = '';
-               }
-
-               list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) =
-                       $this->makeSelectOptions( $options );
-
-               if ( !empty( $conds ) ) {
-                       if ( is_array( $conds ) ) {
-                               $conds = $this->makeList( $conds, LIST_AND );
-                       }
-                       $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail";
-               } else {
-                       $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail";
-               }
-
-               if ( isset( $options['LIMIT'] ) ) {
-                       $sql = $this->limitResult( $sql, $options['LIMIT'],
-                               isset( $options['OFFSET'] ) ? $options['OFFSET'] : false );
-               }
-               $sql = "$sql $postLimitTail";
-
-               if ( isset( $options['EXPLAIN'] ) ) {
-                       $sql = 'EXPLAIN ' . $sql;
-               }
-
-               return $sql;
-       }
-
-       public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
-               $options = [], $join_conds = []
-       ) {
-               $options = (array)$options;
-               $options['LIMIT'] = 1;
-               $res = $this->select( $table, $vars, $conds, $fname, $options, $join_conds );
-
-               if ( $res === false ) {
-                       return false;
-               }
-
-               if ( !$this->numRows( $res ) ) {
-                       return false;
-               }
-
-               $obj = $this->fetchObject( $res );
-
-               return $obj;
-       }
-
-       public function estimateRowCount(
-               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
-       ) {
-               $rows = 0;
-               $res = $this->select( $table, [ 'rowcount' => 'COUNT(*)' ], $conds, $fname, $options );
-
-               if ( $res ) {
-                       $row = $this->fetchRow( $res );
-                       $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
-               }
-
-               return $rows;
-       }
-
-       public function selectRowCount(
-               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
-       ) {
-               $rows = 0;
-               $sql = $this->selectSQLText( $tables, '1', $conds, $fname, $options, $join_conds );
-               $res = $this->query( "SELECT COUNT(*) AS rowcount FROM ($sql) tmp_count", $fname );
-
-               if ( $res ) {
-                       $row = $this->fetchRow( $res );
-                       $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
-               }
-
-               return $rows;
-       }
-
-       /**
-        * Removes most variables from an SQL query and replaces them with X or N for numbers.
-        * It's only slightly flawed. Don't use for anything important.
-        *
-        * @param string $sql A SQL Query
-        *
-        * @return string
-        */
-       protected static function generalizeSQL( $sql ) {
-               # This does the same as the regexp below would do, but in such a way
-               # as to avoid crashing php on some large strings.
-               # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
-
-               $sql = str_replace( "\\\\", '', $sql );
-               $sql = str_replace( "\\'", '', $sql );
-               $sql = str_replace( "\\\"", '', $sql );
-               $sql = preg_replace( "/'.*'/s", "'X'", $sql );
-               $sql = preg_replace( '/".*"/s', "'X'", $sql );
-
-               # All newlines, tabs, etc replaced by single space
-               $sql = preg_replace( '/\s+/', ' ', $sql );
-
-               # All numbers => N,
-               # except the ones surrounded by characters, e.g. l10n
-               $sql = preg_replace( '/-?\d+(,-?\d+)+/s', 'N,...,N', $sql );
-               $sql = preg_replace( '/(?<![a-zA-Z])-?\d+(?![a-zA-Z])/s', 'N', $sql );
-
-               return $sql;
-       }
-
-       public function fieldExists( $table, $field, $fname = __METHOD__ ) {
-               $info = $this->fieldInfo( $table, $field );
-
-               return (bool)$info;
-       }
-
-       public function indexExists( $table, $index, $fname = __METHOD__ ) {
-               if ( !$this->tableExists( $table ) ) {
-                       return null;
-               }
-
-               $info = $this->indexInfo( $table, $index, $fname );
-               if ( is_null( $info ) ) {
-                       return null;
-               } else {
-                       return $info !== false;
-               }
-       }
-
-       public function tableExists( $table, $fname = __METHOD__ ) {
-               $table = $this->tableName( $table );
-               $old = $this->ignoreErrors( true );
-               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
-               $this->ignoreErrors( $old );
-
-               return (bool)$res;
-       }
-
-       public function indexUnique( $table, $index ) {
-               $indexInfo = $this->indexInfo( $table, $index );
-
-               if ( !$indexInfo ) {
-                       return null;
-               }
-
-               return !$indexInfo[0]->Non_unique;
-       }
-
-       /**
-        * Helper for DatabaseBase::insert().
-        *
-        * @param array $options
-        * @return string
-        */
-       protected function makeInsertOptions( $options ) {
-               return implode( ' ', $options );
-       }
-
-       public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
-               # No rows to insert, easy just return now
-               if ( !count( $a ) ) {
-                       return true;
-               }
-
-               $table = $this->tableName( $table );
-
-               if ( !is_array( $options ) ) {
-                       $options = [ $options ];
-               }
-
-               $fh = null;
-               if ( isset( $options['fileHandle'] ) ) {
-                       $fh = $options['fileHandle'];
-               }
-               $options = $this->makeInsertOptions( $options );
-
-               if ( isset( $a[0] ) && is_array( $a[0] ) ) {
-                       $multi = true;
-                       $keys = array_keys( $a[0] );
-               } else {
-                       $multi = false;
-                       $keys = array_keys( $a );
-               }
-
-               $sql = 'INSERT ' . $options .
-                       " INTO $table (" . implode( ',', $keys ) . ') VALUES ';
-
-               if ( $multi ) {
-                       $first = true;
-                       foreach ( $a as $row ) {
-                               if ( $first ) {
-                                       $first = false;
-                               } else {
-                                       $sql .= ',';
-                               }
-                               $sql .= '(' . $this->makeList( $row ) . ')';
-                       }
-               } else {
-                       $sql .= '(' . $this->makeList( $a ) . ')';
-               }
-
-               if ( $fh !== null && false === fwrite( $fh, $sql ) ) {
-                       return false;
-               } elseif ( $fh !== null ) {
-                       return true;
-               }
-
-               return (bool)$this->query( $sql, $fname );
-       }
-
-       /**
-        * Make UPDATE options array for DatabaseBase::makeUpdateOptions
-        *
-        * @param array $options
-        * @return array
-        */
-       protected function makeUpdateOptionsArray( $options ) {
-               if ( !is_array( $options ) ) {
-                       $options = [ $options ];
-               }
-
-               $opts = [];
-
-               if ( in_array( 'LOW_PRIORITY', $options ) ) {
-                       $opts[] = $this->lowPriorityOption();
-               }
-
-               if ( in_array( 'IGNORE', $options ) ) {
-                       $opts[] = 'IGNORE';
-               }
-
-               return $opts;
-       }
-
-       /**
-        * Make UPDATE options for the DatabaseBase::update function
-        *
-        * @param array $options The options passed to DatabaseBase::update
-        * @return string
-        */
-       protected function makeUpdateOptions( $options ) {
-               $opts = $this->makeUpdateOptionsArray( $options );
-
-               return implode( ' ', $opts );
-       }
-
-       function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
-               $table = $this->tableName( $table );
-               $opts = $this->makeUpdateOptions( $options );
-               $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
-
-               if ( $conds !== [] && $conds !== '*' ) {
-                       $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
-               }
-
-               return $this->query( $sql, $fname );
-       }
-
-       public function makeList( $a, $mode = LIST_COMMA ) {
-               if ( !is_array( $a ) ) {
-                       throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
-               }
-
-               $first = true;
-               $list = '';
-
-               foreach ( $a as $field => $value ) {
-                       if ( !$first ) {
-                               if ( $mode == LIST_AND ) {
-                                       $list .= ' AND ';
-                               } elseif ( $mode == LIST_OR ) {
-                                       $list .= ' OR ';
-                               } else {
-                                       $list .= ',';
-                               }
-                       } else {
-                               $first = false;
-                       }
-
-                       if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) {
-                               $list .= "($value)";
-                       } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) {
-                               $list .= "$value";
-                       } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) {
-                               // Remove null from array to be handled separately if found
-                               $includeNull = false;
-                               foreach ( array_keys( $value, null, true ) as $nullKey ) {
-                                       $includeNull = true;
-                                       unset( $value[$nullKey] );
-                               }
-                               if ( count( $value ) == 0 && !$includeNull ) {
-                                       throw new MWException( __METHOD__ . ": empty input for field $field" );
-                               } elseif ( count( $value ) == 0 ) {
-                                       // only check if $field is null
-                                       $list .= "$field IS NULL";
-                               } else {
-                                       // IN clause contains at least one valid element
-                                       if ( $includeNull ) {
-                                               // Group subconditions to ensure correct precedence
-                                               $list .= '(';
-                                       }
-                                       if ( count( $value ) == 1 ) {
-                                               // Special-case single values, as IN isn't terribly efficient
-                                               // Don't necessarily assume the single key is 0; we don't
-                                               // enforce linear numeric ordering on other arrays here.
-                                               $value = array_values( $value )[0];
-                                               $list .= $field . " = " . $this->addQuotes( $value );
-                                       } else {
-                                               $list .= $field . " IN (" . $this->makeList( $value ) . ") ";
-                                       }
-                                       // if null present in array, append IS NULL
-                                       if ( $includeNull ) {
-                                               $list .= " OR $field IS NULL)";
-                                       }
-                               }
-                       } elseif ( $value === null ) {
-                               if ( $mode == LIST_AND || $mode == LIST_OR ) {
-                                       $list .= "$field IS ";
-                               } elseif ( $mode == LIST_SET ) {
-                                       $list .= "$field = ";
-                               }
-                               $list .= 'NULL';
-                       } else {
-                               if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
-                                       $list .= "$field = ";
-                               }
-                               $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
-                       }
-               }
-
-               return $list;
-       }
-
-       public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
-               $conds = [];
-
-               foreach ( $data as $base => $sub ) {
-                       if ( count( $sub ) ) {
-                               $conds[] = $this->makeList(
-                                       [ $baseKey => $base, $subKey => array_keys( $sub ) ],
-                                       LIST_AND );
-                       }
-               }
-
-               if ( $conds ) {
-                       return $this->makeList( $conds, LIST_OR );
-               } else {
-                       // Nothing to search for...
-                       return false;
-               }
-       }
-
-       /**
-        * Return aggregated value alias
-        *
-        * @param array $valuedata
-        * @param string $valuename
-        *
-        * @return string
-        */
-       public function aggregateValue( $valuedata, $valuename = 'value' ) {
-               return $valuename;
-       }
-
-       public function bitNot( $field ) {
-               return "(~$field)";
-       }
-
-       public function bitAnd( $fieldLeft, $fieldRight ) {
-               return "($fieldLeft & $fieldRight)";
-       }
-
-       public function bitOr( $fieldLeft, $fieldRight ) {
-               return "($fieldLeft | $fieldRight)";
-       }
-
-       public function buildConcat( $stringList ) {
-               return 'CONCAT(' . implode( ',', $stringList ) . ')';
-       }
-
-       public function buildGroupConcatField(
-               $delim, $table, $field, $conds = '', $join_conds = []
-       ) {
-               $fld = "GROUP_CONCAT($field SEPARATOR " . $this->addQuotes( $delim ) . ')';
-
-               return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
-       }
-
-       public function selectDB( $db ) {
-               # Stub. Shouldn't cause serious problems if it's not overridden, but
-               # if your database engine supports a concept similar to MySQL's
-               # databases you may as well.
-               $this->mDBname = $db;
-
-               return true;
-       }
-
-       public function getDBname() {
-               return $this->mDBname;
-       }
-
-       public function getServer() {
-               return $this->mServer;
-       }
-
-       /**
-        * Format a table name ready for use in constructing an SQL query
-        *
-        * This does two important things: it quotes the table names to clean them up,
-        * and it adds a table prefix if only given a table name with no quotes.
-        *
-        * All functions of this object which require a table name call this function
-        * themselves. Pass the canonical name to such functions. This is only needed
-        * when calling query() directly.
-        *
-        * @note This function does not sanitize user input. It is not safe to use
-        *   this function to escape user input.
-        * @param string $name Database table name
-        * @param string $format One of:
-        *   quoted - Automatically pass the table name through addIdentifierQuotes()
-        *            so that it can be used in a query.
-        *   raw - Do not add identifier quotes to the table name
-        * @return string Full database name
-        */
-       public function tableName( $name, $format = 'quoted' ) {
-               global $wgSharedDB, $wgSharedPrefix, $wgSharedTables, $wgSharedSchema;
-               # Skip the entire process when we have a string quoted on both ends.
-               # Note that we check the end so that we will still quote any use of
-               # use of `database`.table. But won't break things if someone wants
-               # to query a database table with a dot in the name.
-               if ( $this->isQuotedIdentifier( $name ) ) {
-                       return $name;
-               }
-
-               # Lets test for any bits of text that should never show up in a table
-               # name. Basically anything like JOIN or ON which are actually part of
-               # SQL queries, but may end up inside of the table value to combine
-               # sql. Such as how the API is doing.
-               # Note that we use a whitespace test rather than a \b test to avoid
-               # any remote case where a word like on may be inside of a table name
-               # surrounded by symbols which may be considered word breaks.
-               if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
-                       return $name;
-               }
-
-               # Split database and table into proper variables.
-               # We reverse the explode so that database.table and table both output
-               # the correct table.
-               $dbDetails = explode( '.', $name, 3 );
-               if ( count( $dbDetails ) == 3 ) {
-                       list( $database, $schema, $table ) = $dbDetails;
-                       # We don't want any prefix added in this case
-                       $prefix = '';
-               } elseif ( count( $dbDetails ) == 2 ) {
-                       list( $database, $table ) = $dbDetails;
-                       # We don't want any prefix added in this case
-                       # In dbs that support it, $database may actually be the schema
-                       # but that doesn't affect any of the functionality here
-                       $prefix = '';
-                       $schema = null;
-               } else {
-                       list( $table ) = $dbDetails;
-                       if ( $wgSharedDB !== null # We have a shared database
-                               && $this->mForeign == false # We're not working on a foreign database
-                               && !$this->isQuotedIdentifier( $table ) # Prevent shared tables listing '`table`'
-                               && in_array( $table, $wgSharedTables ) # A shared table is selected
-                       ) {
-                               $database = $wgSharedDB;
-                               $schema = $wgSharedSchema === null ? $this->mSchema : $wgSharedSchema;
-                               $prefix = $wgSharedPrefix === null ? $this->mTablePrefix : $wgSharedPrefix;
-                       } else {
-                               $database = null;
-                               $schema = $this->mSchema; # Default schema
-                               $prefix = $this->mTablePrefix; # Default prefix
-                       }
-               }
-
-               # Quote $table and apply the prefix if not quoted.
-               # $tableName might be empty if this is called from Database::replaceVars()
-               $tableName = "{$prefix}{$table}";
-               if ( $format == 'quoted' && !$this->isQuotedIdentifier( $tableName ) && $tableName !== '' ) {
-                       $tableName = $this->addIdentifierQuotes( $tableName );
-               }
-
-               # Quote $schema and merge it with the table name if needed
-               if ( strlen( $schema ) ) {
-                       if ( $format == 'quoted' && !$this->isQuotedIdentifier( $schema ) ) {
-                               $schema = $this->addIdentifierQuotes( $schema );
-                       }
-                       $tableName = $schema . '.' . $tableName;
-               }
-
-               # Quote $database and merge it with the table name if needed
-               if ( $database !== null ) {
-                       if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
-                               $database = $this->addIdentifierQuotes( $database );
-                       }
-                       $tableName = $database . '.' . $tableName;
-               }
-
-               return $tableName;
-       }
-
-       /**
-        * Fetch a number of table names into an array
-        * This is handy when you need to construct SQL for joins
-        *
-        * Example:
-        * extract( $dbr->tableNames( 'user', 'watchlist' ) );
-        * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
-        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
-        *
-        * @return array
-        */
-       public function tableNames() {
-               $inArray = func_get_args();
-               $retVal = [];
-
-               foreach ( $inArray as $name ) {
-                       $retVal[$name] = $this->tableName( $name );
-               }
-
-               return $retVal;
-       }
-
-       /**
-        * Fetch a number of table names into an zero-indexed numerical array
-        * This is handy when you need to construct SQL for joins
-        *
-        * Example:
-        * list( $user, $watchlist ) = $dbr->tableNamesN( 'user', 'watchlist' );
-        * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
-        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
-        *
-        * @return array
-        */
-       public function tableNamesN() {
-               $inArray = func_get_args();
-               $retVal = [];
-
-               foreach ( $inArray as $name ) {
-                       $retVal[] = $this->tableName( $name );
-               }
-
-               return $retVal;
-       }
-
-       /**
-        * Get an aliased table name
-        * e.g. tableName AS newTableName
-        *
-        * @param string $name Table name, see tableName()
-        * @param string|bool $alias Alias (optional)
-        * @return string SQL name for aliased table. Will not alias a table to its own name
-        */
-       public function tableNameWithAlias( $name, $alias = false ) {
-               if ( !$alias || $alias == $name ) {
-                       return $this->tableName( $name );
-               } else {
-                       return $this->tableName( $name ) . ' ' . $this->addIdentifierQuotes( $alias );
-               }
-       }
-
-       /**
-        * Gets an array of aliased table names
-        *
-        * @param array $tables [ [alias] => table ]
-        * @return string[] See tableNameWithAlias()
-        */
-       public function tableNamesWithAlias( $tables ) {
-               $retval = [];
-               foreach ( $tables as $alias => $table ) {
-                       if ( is_numeric( $alias ) ) {
-                               $alias = $table;
-                       }
-                       $retval[] = $this->tableNameWithAlias( $table, $alias );
-               }
-
-               return $retval;
-       }
-
-       /**
-        * Get an aliased field name
-        * e.g. fieldName AS newFieldName
-        *
-        * @param string $name Field name
-        * @param string|bool $alias Alias (optional)
-        * @return string SQL name for aliased field. Will not alias a field to its own name
-        */
-       public function fieldNameWithAlias( $name, $alias = false ) {
-               if ( !$alias || (string)$alias === (string)$name ) {
-                       return $name;
-               } else {
-                       return $name . ' AS ' . $this->addIdentifierQuotes( $alias ); // PostgreSQL needs AS
-               }
-       }
-
-       /**
-        * Gets an array of aliased field names
-        *
-        * @param array $fields [ [alias] => field ]
-        * @return string[] See fieldNameWithAlias()
-        */
-       public function fieldNamesWithAlias( $fields ) {
-               $retval = [];
-               foreach ( $fields as $alias => $field ) {
-                       if ( is_numeric( $alias ) ) {
-                               $alias = $field;
-                       }
-                       $retval[] = $this->fieldNameWithAlias( $field, $alias );
-               }
-
-               return $retval;
-       }
-
-       /**
-        * Get the aliased table name clause for a FROM clause
-        * which might have a JOIN and/or USE INDEX clause
-        *
-        * @param array $tables ( [alias] => table )
-        * @param array $use_index Same as for select()
-        * @param array $join_conds Same as for select()
-        * @return string
-        */
-       protected function tableNamesWithUseIndexOrJOIN(
-               $tables, $use_index = [], $join_conds = []
-       ) {
-               $ret = [];
-               $retJOIN = [];
-               $use_index = (array)$use_index;
-               $join_conds = (array)$join_conds;
-
-               foreach ( $tables as $alias => $table ) {
-                       if ( !is_string( $alias ) ) {
-                               // No alias? Set it equal to the table name
-                               $alias = $table;
-                       }
-                       // Is there a JOIN clause for this table?
-                       if ( isset( $join_conds[$alias] ) ) {
-                               list( $joinType, $conds ) = $join_conds[$alias];
-                               $tableClause = $joinType;
-                               $tableClause .= ' ' . $this->tableNameWithAlias( $table, $alias );
-                               if ( isset( $use_index[$alias] ) ) { // has USE INDEX?
-                                       $use = $this->useIndexClause( implode( ',', (array)$use_index[$alias] ) );
-                                       if ( $use != '' ) {
-                                               $tableClause .= ' ' . $use;
-                                       }
-                               }
-                               $on = $this->makeList( (array)$conds, LIST_AND );
-                               if ( $on != '' ) {
-                                       $tableClause .= ' ON (' . $on . ')';
-                               }
-
-                               $retJOIN[] = $tableClause;
-                       } elseif ( isset( $use_index[$alias] ) ) {
-                               // Is there an INDEX clause for this table?
-                               $tableClause = $this->tableNameWithAlias( $table, $alias );
-                               $tableClause .= ' ' . $this->useIndexClause(
-                                       implode( ',', (array)$use_index[$alias] )
-                               );
-
-                               $ret[] = $tableClause;
-                       } else {
-                               $tableClause = $this->tableNameWithAlias( $table, $alias );
-
-                               $ret[] = $tableClause;
-                       }
-               }
-
-               // We can't separate explicit JOIN clauses with ',', use ' ' for those
-               $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : "";
-               $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : "";
-
-               // Compile our final table clause
-               return implode( ' ', [ $implicitJoins, $explicitJoins ] );
-       }
-
-       /**
-        * Get the name of an index in a given table.
-        *
-        * @param string $index
-        * @return string
-        */
-       protected function indexName( $index ) {
-               // Backwards-compatibility hack
-               $renamed = [
-                       'ar_usertext_timestamp' => 'usertext_timestamp',
-                       'un_user_id' => 'user_id',
-                       'un_user_ip' => 'user_ip',
-               ];
-
-               if ( isset( $renamed[$index] ) ) {
-                       return $renamed[$index];
-               } else {
-                       return $index;
-               }
-       }
-
-       public function addQuotes( $s ) {
-               if ( $s instanceof Blob ) {
-                       $s = $s->fetch();
-               }
-               if ( $s === null ) {
-                       return 'NULL';
-               } else {
-                       # This will also quote numeric values. This should be harmless,
-                       # and protects against weird problems that occur when they really
-                       # _are_ strings such as article titles and string->number->string
-                       # conversion is not 1:1.
-                       return "'" . $this->strencode( $s ) . "'";
-               }
-       }
-
-       /**
-        * Quotes an identifier using `backticks` or "double quotes" depending on the database type.
-        * MySQL uses `backticks` while basically everything else uses double quotes.
-        * Since MySQL is the odd one out here the double quotes are our generic
-        * and we implement backticks in DatabaseMysql.
-        *
-        * @param string $s
-        * @return string
-        */
-       public function addIdentifierQuotes( $s ) {
-               return '"' . str_replace( '"', '""', $s ) . '"';
-       }
-
-       /**
-        * Returns if the given identifier looks quoted or not according to
-        * the database convention for quoting identifiers .
-        *
-        * @note Do not use this to determine if untrusted input is safe.
-        *   A malicious user can trick this function.
-        * @param string $name
-        * @return bool
-        */
-       public function isQuotedIdentifier( $name ) {
-               return $name[0] == '"' && substr( $name, -1, 1 ) == '"';
-       }
-
-       /**
-        * @param string $s
-        * @return string
-        */
-       protected function escapeLikeInternal( $s ) {
-               return addcslashes( $s, '\%_' );
-       }
-
-       public function buildLike() {
-               $params = func_get_args();
-
-               if ( count( $params ) > 0 && is_array( $params[0] ) ) {
-                       $params = $params[0];
-               }
-
-               $s = '';
-
-               foreach ( $params as $value ) {
-                       if ( $value instanceof LikeMatch ) {
-                               $s .= $value->toString();
-                       } else {
-                               $s .= $this->escapeLikeInternal( $value );
-                       }
-               }
-
-               return " LIKE {$this->addQuotes( $s )} ";
-       }
-
-       public function anyChar() {
-               return new LikeMatch( '_' );
-       }
-
-       public function anyString() {
-               return new LikeMatch( '%' );
-       }
-
-       public function nextSequenceValue( $seqName ) {
-               return null;
-       }
-
-       /**
-        * USE INDEX clause. Unlikely to be useful for anything but MySQL. This
-        * is only needed because a) MySQL must be as efficient as possible due to
-        * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
-        * which index to pick. Anyway, other databases might have different
-        * indexes on a given table. So don't bother overriding this unless you're
-        * MySQL.
-        * @param string $index
-        * @return string
-        */
-       public function useIndexClause( $index ) {
-               return '';
-       }
-
-       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
-               $quotedTable = $this->tableName( $table );
-
-               if ( count( $rows ) == 0 ) {
-                       return;
-               }
-
-               # Single row case
-               if ( !is_array( reset( $rows ) ) ) {
-                       $rows = [ $rows ];
-               }
-
-               // @FXIME: this is not atomic, but a trx would break affectedRows()
-               foreach ( $rows as $row ) {
-                       # Delete rows which collide
-                       if ( $uniqueIndexes ) {
-                               $sql = "DELETE FROM $quotedTable WHERE ";
-                               $first = true;
-                               foreach ( $uniqueIndexes as $index ) {
-                                       if ( $first ) {
-                                               $first = false;
-                                               $sql .= '( ';
-                                       } else {
-                                               $sql .= ' ) OR ( ';
-                                       }
-                                       if ( is_array( $index ) ) {
-                                               $first2 = true;
-                                               foreach ( $index as $col ) {
-                                                       if ( $first2 ) {
-                                                               $first2 = false;
-                                                       } else {
-                                                               $sql .= ' AND ';
-                                                       }
-                                                       $sql .= $col . '=' . $this->addQuotes( $row[$col] );
-                                               }
-                                       } else {
-                                               $sql .= $index . '=' . $this->addQuotes( $row[$index] );
-                                       }
-                               }
-                               $sql .= ' )';
-                               $this->query( $sql, $fname );
-                       }
-
-                       # Now insert the row
-                       $this->insert( $table, $row, $fname );
-               }
-       }
-
-       /**
-        * REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE
-        * statement.
-        *
-        * @param string $table Table name
-        * @param array|string $rows Row(s) to insert
-        * @param string $fname Caller function name
-        *
-        * @return ResultWrapper
-        */
-       protected function nativeReplace( $table, $rows, $fname ) {
-               $table = $this->tableName( $table );
-
-               # Single row case
-               if ( !is_array( reset( $rows ) ) ) {
-                       $rows = [ $rows ];
-               }
-
-               $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) . ') VALUES ';
-               $first = true;
-
-               foreach ( $rows as $row ) {
-                       if ( $first ) {
-                               $first = false;
-                       } else {
-                               $sql .= ',';
-                       }
-
-                       $sql .= '(' . $this->makeList( $row ) . ')';
-               }
-
-               return $this->query( $sql, $fname );
-       }
-
-       public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
-               $fname = __METHOD__
-       ) {
-               if ( !count( $rows ) ) {
-                       return true; // nothing to do
-               }
-
-               if ( !is_array( reset( $rows ) ) ) {
-                       $rows = [ $rows ];
-               }
-
-               if ( count( $uniqueIndexes ) ) {
-                       $clauses = []; // list WHERE clauses that each identify a single row
-                       foreach ( $rows as $row ) {
-                               foreach ( $uniqueIndexes as $index ) {
-                                       $index = is_array( $index ) ? $index : [ $index ]; // columns
-                                       $rowKey = []; // unique key to this row
-                                       foreach ( $index as $column ) {
-                                               $rowKey[$column] = $row[$column];
-                                       }
-                                       $clauses[] = $this->makeList( $rowKey, LIST_AND );
-                               }
-                       }
-                       $where = [ $this->makeList( $clauses, LIST_OR ) ];
-               } else {
-                       $where = false;
-               }
-
-               $useTrx = !$this->mTrxLevel;
-               if ( $useTrx ) {
-                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
-               }
-               try {
-                       # Update any existing conflicting row(s)
-                       if ( $where !== false ) {
-                               $ok = $this->update( $table, $set, $where, $fname );
-                       } else {
-                               $ok = true;
-                       }
-                       # Now insert any non-conflicting row(s)
-                       $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
-               } catch ( Exception $e ) {
-                       if ( $useTrx ) {
-                               $this->rollback( $fname );
-                       }
-                       throw $e;
-               }
-               if ( $useTrx ) {
-                       $this->commit( $fname, self::TRANSACTION_INTERNAL );
-               }
-
-               return $ok;
-       }
-
-       public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
-               $fname = __METHOD__
-       ) {
-               if ( !$conds ) {
-                       throw new DBUnexpectedError( $this,
-                               'DatabaseBase::deleteJoin() called with empty $conds' );
-               }
-
-               $delTable = $this->tableName( $delTable );
-               $joinTable = $this->tableName( $joinTable );
-               $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
-               if ( $conds != '*' ) {
-                       $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
-               }
-               $sql .= ')';
-
-               $this->query( $sql, $fname );
-       }
-
-       /**
-        * Returns the size of a text field, or -1 for "unlimited"
-        *
-        * @param string $table
-        * @param string $field
-        * @return int
-        */
-       public function textFieldSize( $table, $field ) {
-               $table = $this->tableName( $table );
-               $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
-               $res = $this->query( $sql, 'DatabaseBase::textFieldSize' );
-               $row = $this->fetchObject( $res );
-
-               $m = [];
-
-               if ( preg_match( '/\((.*)\)/', $row->Type, $m ) ) {
-                       $size = $m[1];
-               } else {
-                       $size = -1;
-               }
-
-               return $size;
-       }
-
-       /**
-        * A string to insert into queries to show that they're low-priority, like
-        * MySQL's LOW_PRIORITY. If no such feature exists, return an empty
-        * string and nothing bad should happen.
-        *
-        * @return string Returns the text of the low priority option if it is
-        *   supported, or a blank string otherwise
-        */
-       public function lowPriorityOption() {
-               return '';
-       }
-
-       public function delete( $table, $conds, $fname = __METHOD__ ) {
-               if ( !$conds ) {
-                       throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' );
-               }
-
-               $table = $this->tableName( $table );
-               $sql = "DELETE FROM $table";
-
-               if ( $conds != '*' ) {
-                       if ( is_array( $conds ) ) {
-                               $conds = $this->makeList( $conds, LIST_AND );
-                       }
-                       $sql .= ' WHERE ' . $conds;
-               }
-
-               return $this->query( $sql, $fname );
-       }
-
-       public function insertSelect( $destTable, $srcTable, $varMap, $conds,
-               $fname = __METHOD__,
-               $insertOptions = [], $selectOptions = []
-       ) {
-               $destTable = $this->tableName( $destTable );
-
-               if ( !is_array( $insertOptions ) ) {
-                       $insertOptions = [ $insertOptions ];
-               }
-
-               $insertOptions = $this->makeInsertOptions( $insertOptions );
-
-               if ( !is_array( $selectOptions ) ) {
-                       $selectOptions = [ $selectOptions ];
-               }
-
-               list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
-
-               if ( is_array( $srcTable ) ) {
-                       $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
-               } else {
-                       $srcTable = $this->tableName( $srcTable );
-               }
-
-               $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
-                       " SELECT $startOpts " . implode( ',', $varMap ) .
-                       " FROM $srcTable $useIndex ";
-
-               if ( $conds != '*' ) {
-                       if ( is_array( $conds ) ) {
-                               $conds = $this->makeList( $conds, LIST_AND );
-                       }
-                       $sql .= " WHERE $conds";
-               }
-
-               $sql .= " $tailOpts";
-
-               return $this->query( $sql, $fname );
-       }
-
-       /**
-        * Construct a LIMIT query with optional offset. This is used for query
-        * pages. The SQL should be adjusted so that only the first $limit rows
-        * are returned. If $offset is provided as well, then the first $offset
-        * rows should be discarded, and the next $limit rows should be returned.
-        * If the result of the query is not ordered, then the rows to be returned
-        * are theoretically arbitrary.
-        *
-        * $sql is expected to be a SELECT, if that makes a difference.
-        *
-        * The version provided by default works in MySQL and SQLite. It will very
-        * likely need to be overridden for most other DBMSes.
-        *
-        * @param string $sql SQL query we will append the limit too
-        * @param int $limit The SQL limit
-        * @param int|bool $offset The SQL offset (default false)
-        * @throws DBUnexpectedError
-        * @return string
-        */
-       public function limitResult( $sql, $limit, $offset = false ) {
-               if ( !is_numeric( $limit ) ) {
-                       throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
-               }
-
-               return "$sql LIMIT "
-                       . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
-                       . "{$limit} ";
-       }
-
-       public function unionSupportsOrderAndLimit() {
-               return true; // True for almost every DB supported
-       }
-
-       public function unionQueries( $sqls, $all ) {
-               $glue = $all ? ') UNION ALL (' : ') UNION (';
-
-               return '(' . implode( $glue, $sqls ) . ')';
-       }
-
-       public function conditional( $cond, $trueVal, $falseVal ) {
-               if ( is_array( $cond ) ) {
-                       $cond = $this->makeList( $cond, LIST_AND );
-               }
-
-               return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
-       }
-
-       public function strreplace( $orig, $old, $new ) {
-               return "REPLACE({$orig}, {$old}, {$new})";
-       }
-
-       public function getServerUptime() {
-               return 0;
-       }
-
-       public function wasDeadlock() {
-               return false;
-       }
-
-       public function wasLockTimeout() {
-               return false;
-       }
-
-       public function wasErrorReissuable() {
-               return false;
-       }
-
-       public function wasReadOnlyError() {
-               return false;
-       }
-
-       /**
-        * Determines if the given query error was a connection drop
-        * STUB
-        *
-        * @param integer|string $errno
-        * @return bool
-        */
-       public function wasConnectionError( $errno ) {
-               return false;
-       }
-
-       /**
-        * Perform a deadlock-prone transaction.
-        *
-        * This function invokes a callback function to perform a set of write
-        * queries. If a deadlock occurs during the processing, the transaction
-        * will be rolled back and the callback function will be called again.
-        *
-        * Avoid using this method outside of Job or Maintenance classes.
-        *
-        * Usage:
-        *   $dbw->deadlockLoop( callback, ... );
-        *
-        * Extra arguments are passed through to the specified callback function.
-        * This method requires that no transactions are already active to avoid
-        * causing premature commits or exceptions.
-        *
-        * Returns whatever the callback function returned on its successful,
-        * iteration, or false on error, for example if the retry limit was
-        * reached.
-        *
-        * @return mixed
-        * @throws DBUnexpectedError
-        * @throws Exception
-        */
-       public function deadlockLoop() {
-               $args = func_get_args();
-               $function = array_shift( $args );
-               $tries = self::DEADLOCK_TRIES;
-
-               $this->begin( __METHOD__ );
-
-               $retVal = null;
-               /** @var Exception $e */
-               $e = null;
-               do {
-                       try {
-                               $retVal = call_user_func_array( $function, $args );
-                               break;
-                       } catch ( DBQueryError $e ) {
-                               if ( $this->wasDeadlock() ) {
-                                       // Retry after a randomized delay
-                                       usleep( mt_rand( self::DEADLOCK_DELAY_MIN, self::DEADLOCK_DELAY_MAX ) );
-                               } else {
-                                       // Throw the error back up
-                                       throw $e;
-                               }
-                       }
-               } while ( --$tries > 0 );
-
-               if ( $tries <= 0 ) {
-                       // Too many deadlocks; give up
-                       $this->rollback( __METHOD__ );
-                       throw $e;
-               } else {
-                       $this->commit( __METHOD__ );
-
-                       return $retVal;
-               }
-       }
-
-       public function masterPosWait( DBMasterPos $pos, $timeout ) {
-               # Real waits are implemented in the subclass.
-               return 0;
-       }
-
-       public function getSlavePos() {
-               # Stub
-               return false;
-       }
-
-       public function getMasterPos() {
-               # Stub
-               return false;
-       }
-
-       public function serverIsReadOnly() {
-               return false;
-       }
-
-       final public function onTransactionResolution( callable $callback ) {
-               if ( !$this->mTrxLevel ) {
-                       throw new DBUnexpectedError( $this, "No transaction is active." );
-               }
-               $this->mTrxEndCallbacks[] = [ $callback, wfGetCaller() ];
-       }
-
-       final public function onTransactionIdle( callable $callback ) {
-               $this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
-               if ( !$this->mTrxLevel ) {
-                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
-               }
-       }
-
-       final public function onTransactionPreCommitOrIdle( callable $callback ) {
-               if ( $this->mTrxLevel ) {
-                       $this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
-               } else {
-                       // If no transaction is active, then make one for this callback
-                       $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
-                       try {
-                               call_user_func( $callback );
-                               $this->commit( __METHOD__ );
-                       } catch ( Exception $e ) {
-                               $this->rollback( __METHOD__ );
-                               throw $e;
-                       }
-               }
-       }
-
-       /**
-        * Whether to disable running of post-commit callbacks
-        *
-        * This method should not be used outside of Database/LoadBalancer
-        *
-        * @param bool $suppress
-        * @since 1.28
-        */
-       final public function setPostCommitCallbackSupression( $suppress ) {
-               $this->suppressPostCommitCallbacks = $suppress;
-       }
-
-       /**
-        * Actually run and consume any "on transaction idle/resolution" callbacks.
-        *
-        * This method should not be used outside of Database/LoadBalancer
-        *
-        * @param integer $trigger IDatabase::TRIGGER_* constant
-        * @since 1.20
-        * @throws Exception
-        */
-       public function runOnTransactionIdleCallbacks( $trigger ) {
-               if ( $this->suppressPostCommitCallbacks ) {
-                       return;
-               }
-
-               $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
-               /** @var Exception $e */
-               $e = null; // first exception
-               do { // callbacks may add callbacks :)
-                       $callbacks = array_merge(
-                               $this->mTrxIdleCallbacks,
-                               $this->mTrxEndCallbacks // include "transaction resolution" callbacks
-                       );
-                       $this->mTrxIdleCallbacks = []; // consumed (and recursion guard)
-                       $this->mTrxEndCallbacks = []; // consumed (recursion guard)
-                       foreach ( $callbacks as $callback ) {
-                               try {
-                                       list( $phpCallback ) = $callback;
-                                       $this->clearFlag( DBO_TRX ); // make each query its own transaction
-                                       call_user_func_array( $phpCallback, [ $trigger ] );
-                                       if ( $autoTrx ) {
-                                               $this->setFlag( DBO_TRX ); // restore automatic begin()
-                                       } else {
-                                               $this->clearFlag( DBO_TRX ); // restore auto-commit
-                                       }
-                               } catch ( Exception $ex ) {
-                                       MWExceptionHandler::logException( $ex );
-                                       $e = $e ?: $ex;
-                                       // Some callbacks may use startAtomic/endAtomic, so make sure
-                                       // their transactions are ended so other callbacks don't fail
-                                       if ( $this->trxLevel() ) {
-                                               $this->rollback( __METHOD__ );
-                                       }
-                               }
-                       }
-               } while ( count( $this->mTrxIdleCallbacks ) );
-
-               if ( $e instanceof Exception ) {
-                       throw $e; // re-throw any first exception
-               }
-       }
-
-       /**
-        * Actually run and consume any "on transaction pre-commit" callbacks.
-        *
-        * This method should not be used outside of Database/LoadBalancer
-        *
-        * @since 1.22
-        * @throws Exception
-        */
-       public function runOnTransactionPreCommitCallbacks() {
-               $e = null; // first exception
-               do { // callbacks may add callbacks :)
-                       $callbacks = $this->mTrxPreCommitCallbacks;
-                       $this->mTrxPreCommitCallbacks = []; // consumed (and recursion guard)
-                       foreach ( $callbacks as $callback ) {
-                               try {
-                                       list( $phpCallback ) = $callback;
-                                       call_user_func( $phpCallback );
-                               } catch ( Exception $ex ) {
-                                       MWExceptionHandler::logException( $ex );
-                                       $e = $e ?: $ex;
-                               }
-                       }
-               } while ( count( $this->mTrxPreCommitCallbacks ) );
-
-               if ( $e instanceof Exception ) {
-                       throw $e; // re-throw any first exception
-               }
-       }
-
-       final public function startAtomic( $fname = __METHOD__ ) {
-               if ( !$this->mTrxLevel ) {
-                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
-                       $this->mTrxAutomatic = true;
-                       // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
-                       // in all changes being in one transaction to keep requests transactional.
-                       if ( !$this->getFlag( DBO_TRX ) ) {
-                               $this->mTrxAutomaticAtomic = true;
-                       }
-               }
-
-               $this->mTrxAtomicLevels[] = $fname;
-       }
-
-       final public function endAtomic( $fname = __METHOD__ ) {
-               if ( !$this->mTrxLevel ) {
-                       throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
-               }
-               if ( !$this->mTrxAtomicLevels ||
-                       array_pop( $this->mTrxAtomicLevels ) !== $fname
-               ) {
-                       throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
-               }
-
-               if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
-                       $this->commit( $fname, self::FLUSHING_INTERNAL );
-               }
-       }
-
-       final public function doAtomicSection( $fname, callable $callback ) {
-               $this->startAtomic( $fname );
-               try {
-                       $res = call_user_func_array( $callback, [ $this, $fname ] );
-               } catch ( Exception $e ) {
-                       $this->rollback( $fname );
-                       throw $e;
-               }
-               $this->endAtomic( $fname );
-
-               return $res;
-       }
-
-       final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
-               // Protect against mismatched atomic section, transaction nesting, and snapshot loss
-               if ( $this->mTrxLevel ) {
-                       if ( $this->mTrxAtomicLevels ) {
-                               $levels = implode( ', ', $this->mTrxAtomicLevels );
-                               $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
-                               throw new DBUnexpectedError( $this, $msg );
-                       } elseif ( !$this->mTrxAutomatic ) {
-                               $msg = "$fname: Explicit transaction already active (from {$this->mTrxFname}).";
-                               throw new DBUnexpectedError( $this, $msg );
-                       } else {
-                               // @TODO: make this an exception at some point
-                               $msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
-                               wfLogDBError( $msg );
-                               return; // join the main transaction set
-                       }
-               } elseif ( $this->getFlag( DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
-                       // @TODO: make this an exception at some point
-                       wfLogDBError( "$fname: Implicit transaction expected (DBO_TRX set)." );
-                       return; // let any writes be in the main transaction
-               }
-
-               // Avoid fatals if close() was called
-               $this->assertOpen();
-
-               $this->doBegin( $fname );
-               $this->mTrxTimestamp = microtime( true );
-               $this->mTrxFname = $fname;
-               $this->mTrxDoneWrites = false;
-               $this->mTrxAutomatic = false;
-               $this->mTrxAutomaticAtomic = false;
-               $this->mTrxAtomicLevels = [];
-               $this->mTrxShortId = wfRandomString( 12 );
-               $this->mTrxWriteDuration = 0.0;
-               $this->mTrxWriteCallers = [];
-               // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
-               // Get an estimate of the slave lag before then, treating estimate staleness
-               // as lag itself just to be safe
-               $status = $this->getApproximateLagStatus();
-               $this->mTrxSlaveLag = $status['lag'] + ( microtime( true ) - $status['since'] );
-       }
-
-       /**
-        * Issues the BEGIN command to the database server.
-        *
-        * @see DatabaseBase::begin()
-        * @param string $fname
-        */
-       protected function doBegin( $fname ) {
-               $this->query( 'BEGIN', $fname );
-               $this->mTrxLevel = 1;
-       }
-
-       final public function commit( $fname = __METHOD__, $flush = '' ) {
-               if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
-                       // There are still atomic sections open. This cannot be ignored
-                       $levels = implode( ', ', $this->mTrxAtomicLevels );
-                       throw new DBUnexpectedError(
-                               $this,
-                               "$fname: Got COMMIT while atomic sections $levels are still open."
-                       );
-               }
-
-               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
-                       if ( !$this->mTrxLevel ) {
-                               return; // nothing to do
-                       } elseif ( !$this->mTrxAutomatic ) {
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       "$fname: Flushing an explicit transaction, getting out of sync."
-                               );
-                       }
-               } else {
-                       if ( !$this->mTrxLevel ) {
-                               wfWarn( "$fname: No transaction to commit, something got out of sync." );
-                               return; // nothing to do
-                       } elseif ( $this->mTrxAutomatic ) {
-                               // @TODO: make this an exception at some point
-                               wfLogDBError( "$fname: Explicit commit of implicit transaction." );
-                               return; // wait for the main transaction set commit round
-                       }
-               }
-
-               // Avoid fatals if close() was called
-               $this->assertOpen();
-
-               $this->runOnTransactionPreCommitCallbacks();
-               $writeTime = $this->pendingWriteQueryDuration();
-               $this->doCommit( $fname );
-               if ( $this->mTrxDoneWrites ) {
-                       $this->mDoneWrites = microtime( true );
-                       $this->getTransactionProfiler()->transactionWritingOut(
-                               $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
-               }
-
-               $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
-       }
-
-       /**
-        * Issues the COMMIT command to the database server.
-        *
-        * @see DatabaseBase::commit()
-        * @param string $fname
-        */
-       protected function doCommit( $fname ) {
-               if ( $this->mTrxLevel ) {
-                       $this->query( 'COMMIT', $fname );
-                       $this->mTrxLevel = 0;
-               }
-       }
-
-       final public function rollback( $fname = __METHOD__, $flush = '' ) {
-               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
-                       if ( !$this->mTrxLevel ) {
-                               return; // nothing to do
-                       }
-               } else {
-                       if ( !$this->mTrxLevel ) {
-                               wfWarn( "$fname: No transaction to rollback, something got out of sync." );
-                               return; // nothing to do
-                       } elseif ( $this->getFlag( DBO_TRX ) ) {
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       "$fname: Expected mass rollback of all peer databases (DBO_TRX set)."
-                               );
-                       }
-               }
-
-               // Avoid fatals if close() was called
-               $this->assertOpen();
-
-               $this->doRollback( $fname );
-               $this->mTrxAtomicLevels = [];
-               if ( $this->mTrxDoneWrites ) {
-                       $this->getTransactionProfiler()->transactionWritingOut(
-                               $this->mServer, $this->mDBname, $this->mTrxShortId );
-               }
-
-               $this->mTrxIdleCallbacks = []; // clear
-               $this->mTrxPreCommitCallbacks = []; // clear
-               $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
-       }
-
-       /**
-        * Issues the ROLLBACK command to the database server.
-        *
-        * @see DatabaseBase::rollback()
-        * @param string $fname
-        */
-       protected function doRollback( $fname ) {
-               if ( $this->mTrxLevel ) {
-                       # Disconnects cause rollback anyway, so ignore those errors
-                       $ignoreErrors = true;
-                       $this->query( 'ROLLBACK', $fname, $ignoreErrors );
-                       $this->mTrxLevel = 0;
-               }
-       }
-
-       public function explicitTrxActive() {
-               return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
-       }
-
-       /**
-        * Creates a new table with structure copied from existing table
-        * Note that unlike most database abstraction functions, this function does not
-        * automatically append database prefix, because it works at a lower
-        * abstraction level.
-        * The table names passed to this function shall not be quoted (this
-        * function calls addIdentifierQuotes when needed).
-        *
-        * @param string $oldName Name of table whose structure should be copied
-        * @param string $newName Name of table to be created
-        * @param bool $temporary Whether the new table should be temporary
-        * @param string $fname Calling function name
-        * @throws MWException
-        * @return bool True if operation was successful
-        */
-       public function duplicateTableStructure( $oldName, $newName, $temporary = false,
-               $fname = __METHOD__
-       ) {
-               throw new MWException(
-                       'DatabaseBase::duplicateTableStructure is not implemented in descendant class' );
-       }
-
-       function listTables( $prefix = null, $fname = __METHOD__ ) {
-               throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' );
-       }
-
-       /**
-        * Reset the views process cache set by listViews()
-        * @since 1.22
-        */
-       final public function clearViewsCache() {
-               $this->allViews = null;
-       }
-
-       /**
-        * Lists all the VIEWs in the database
-        *
-        * For caching purposes the list of all views should be stored in
-        * $this->allViews. The process cache can be cleared with clearViewsCache()
-        *
-        * @param string $prefix Only show VIEWs with this prefix, eg. unit_test_
-        * @param string $fname Name of calling function
-        * @throws MWException
-        * @return array
-        * @since 1.22
-        */
-       public function listViews( $prefix = null, $fname = __METHOD__ ) {
-               throw new MWException( 'DatabaseBase::listViews is not implemented in descendant class' );
-       }
-
-       /**
-        * Differentiates between a TABLE and a VIEW
-        *
-        * @param string $name Name of the database-structure to test.
-        * @throws MWException
-        * @return bool
-        * @since 1.22
-        */
-       public function isView( $name ) {
-               throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' );
-       }
-
-       public function timestamp( $ts = 0 ) {
-               return wfTimestamp( TS_MW, $ts );
-       }
-
-       public function timestampOrNull( $ts = null ) {
-               if ( is_null( $ts ) ) {
-                       return null;
-               } else {
-                       return $this->timestamp( $ts );
-               }
-       }
-
-       /**
-        * Take the result from a query, and wrap it in a ResultWrapper if
-        * necessary. Boolean values are passed through as is, to indicate success
-        * of write queries or failure.
-        *
-        * Once upon a time, DatabaseBase::query() returned a bare MySQL result
-        * resource, and it was necessary to call this function to convert it to
-        * a wrapper. Nowadays, raw database objects are never exposed to external
-        * callers, so this is unnecessary in external code.
-        *
-        * @param bool|ResultWrapper|resource|object $result
-        * @return bool|ResultWrapper
-        */
-       protected function resultObject( $result ) {
-               if ( !$result ) {
-                       return false;
-               } elseif ( $result instanceof ResultWrapper ) {
-                       return $result;
-               } elseif ( $result === true ) {
-                       // Successful write query
-                       return $result;
-               } else {
-                       return new ResultWrapper( $this, $result );
-               }
-       }
-
-       public function ping() {
-               if ( $this->isOpen() && ( microtime( true ) - $this->lastPing ) < self::PING_TTL ) {
-                       return true;
-               }
-               try {
-                       // This will reconnect if possible, or error out if not
-                       $this->query( "SELECT 1 AS ping", __METHOD__ );
-                       return true;
-               } catch ( DBError $e ) {
-                       return false;
-               }
-       }
-
-       /**
-        * @return bool
-        */
-       protected function reconnect() {
-               $this->closeConnection();
-               $this->mOpened = false;
-               $this->mConn = false;
-               try {
-                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
-                       $this->lastPing = microtime( true );
-                       $ok = true;
-               } catch ( DBConnectionError $e ) {
-                       $ok = false;
-               }
-
-               return $ok;
-       }
-
-       public function getSessionLagStatus() {
-               return $this->getTransactionLagStatus() ?: $this->getApproximateLagStatus();
-       }
-
-       /**
-        * Get the slave lag when the current transaction started
-        *
-        * This is useful when transactions might use snapshot isolation
-        * (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
-        * is this lag plus transaction duration. If they don't, it is still
-        * safe to be pessimistic. This returns null if there is no transaction.
-        *
-        * @return array|null ('lag': seconds or false on error, 'since': UNIX timestamp of BEGIN)
-        * @since 1.27
-        */
-       public function getTransactionLagStatus() {
-               return $this->mTrxLevel
-                       ? [ 'lag' => $this->mTrxSlaveLag, 'since' => $this->trxTimestamp() ]
-                       : null;
-       }
-
-       /**
-        * Get a slave lag estimate for this server
-        *
-        * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of estimate)
-        * @since 1.27
-        */
-       public function getApproximateLagStatus() {
-               return [
-                       'lag'   => $this->getLBInfo( 'slave' ) ? $this->getLag() : 0,
-                       'since' => microtime( true )
-               ];
-       }
-
-       /**
-        * Merge the result of getSessionLagStatus() for several DBs
-        * using the most pessimistic values to estimate the lag of
-        * any data derived from them in combination
-        *
-        * This is information is useful for caching modules
-        *
-        * @see WANObjectCache::set()
-        * @see WANObjectCache::getWithSetCallback()
-        *
-        * @param IDatabase $db1
-        * @param IDatabase ...
-        * @return array Map of values:
-        *   - lag: highest lag of any of the DBs or false on error (e.g. replication stopped)
-        *   - since: oldest UNIX timestamp of any of the DB lag estimates
-        *   - pending: whether any of the DBs have uncommitted changes
-        * @since 1.27
-        */
-       public static function getCacheSetOptions( IDatabase $db1 ) {
-               $res = [ 'lag' => 0, 'since' => INF, 'pending' => false ];
-               foreach ( func_get_args() as $db ) {
-                       /** @var IDatabase $db */
-                       $status = $db->getSessionLagStatus();
-                       if ( $status['lag'] === false ) {
-                               $res['lag'] = false;
-                       } elseif ( $res['lag'] !== false ) {
-                               $res['lag'] = max( $res['lag'], $status['lag'] );
-                       }
-                       $res['since'] = min( $res['since'], $status['since'] );
-                       $res['pending'] = $res['pending'] ?: $db->writesPending();
-               }
-
-               return $res;
-       }
-
-       public function getLag() {
-               return 0;
-       }
-
-       function maxListLen() {
-               return 0;
-       }
-
-       public function encodeBlob( $b ) {
-               return $b;
-       }
-
-       public function decodeBlob( $b ) {
-               if ( $b instanceof Blob ) {
-                       $b = $b->fetch();
-               }
-               return $b;
-       }
-
-       public function setSessionOptions( array $options ) {
-       }
-
-       /**
-        * Read and execute SQL commands from a file.
-        *
-        * Returns true on success, error string or exception on failure (depending
-        * on object's error ignore settings).
-        *
-        * @param string $filename File name to open
-        * @param bool|callable $lineCallback Optional function called before reading each line
-        * @param bool|callable $resultCallback Optional function called for each MySQL result
-        * @param bool|string $fname Calling function name or false if name should be
-        *   generated dynamically using $filename
-        * @param bool|callable $inputCallback Optional function called for each
-        *   complete line sent
-        * @throws Exception|MWException
-        * @return bool|string
-        */
-       public function sourceFile(
-               $filename, $lineCallback = false, $resultCallback = false, $fname = false, $inputCallback = false
-       ) {
-               MediaWiki\suppressWarnings();
-               $fp = fopen( $filename, 'r' );
-               MediaWiki\restoreWarnings();
-
-               if ( false === $fp ) {
-                       throw new MWException( "Could not open \"{$filename}\".\n" );
-               }
-
-               if ( !$fname ) {
-                       $fname = __METHOD__ . "( $filename )";
-               }
-
-               try {
-                       $error = $this->sourceStream( $fp, $lineCallback, $resultCallback, $fname, $inputCallback );
-               } catch ( Exception $e ) {
-                       fclose( $fp );
-                       throw $e;
-               }
-
-               fclose( $fp );
-
-               return $error;
-       }
-
-       /**
-        * Get the full path of a patch file. Originally based on archive()
-        * from updaters.inc. Keep in mind this always returns a patch, as
-        * it fails back to MySQL if no DB-specific patch can be found
-        *
-        * @param string $patch The name of the patch, like patch-something.sql
-        * @return string Full path to patch file
-        */
-       public function patchPath( $patch ) {
-               global $IP;
-
-               $dbType = $this->getType();
-               if ( file_exists( "$IP/maintenance/$dbType/archives/$patch" ) ) {
-                       return "$IP/maintenance/$dbType/archives/$patch";
-               } else {
-                       return "$IP/maintenance/archives/$patch";
-               }
-       }
-
-       public function setSchemaVars( $vars ) {
-               $this->mSchemaVars = $vars;
-       }
-
-       /**
-        * Read and execute commands from an open file handle.
-        *
-        * Returns true on success, error string or exception on failure (depending
-        * on object's error ignore settings).
-        *
-        * @param resource $fp File handle
-        * @param bool|callable $lineCallback Optional function called before reading each query
-        * @param bool|callable $resultCallback Optional function called for each MySQL result
-        * @param string $fname Calling function name
-        * @param bool|callable $inputCallback Optional function called for each complete query sent
-        * @return bool|string
-        */
-       public function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
-               $fname = __METHOD__, $inputCallback = false
-       ) {
-               $cmd = '';
-
-               while ( !feof( $fp ) ) {
-                       if ( $lineCallback ) {
-                               call_user_func( $lineCallback );
-                       }
-
-                       $line = trim( fgets( $fp ) );
-
-                       if ( $line == '' ) {
-                               continue;
-                       }
-
-                       if ( '-' == $line[0] && '-' == $line[1] ) {
-                               continue;
-                       }
-
-                       if ( $cmd != '' ) {
-                               $cmd .= ' ';
-                       }
-
-                       $done = $this->streamStatementEnd( $cmd, $line );
-
-                       $cmd .= "$line\n";
-
-                       if ( $done || feof( $fp ) ) {
-                               $cmd = $this->replaceVars( $cmd );
-
-                               if ( ( $inputCallback && call_user_func( $inputCallback, $cmd ) ) || !$inputCallback ) {
-                                       $res = $this->query( $cmd, $fname );
-
-                                       if ( $resultCallback ) {
-                                               call_user_func( $resultCallback, $res, $this );
-                                       }
-
-                                       if ( false === $res ) {
-                                               $err = $this->lastError();
-
-                                               return "Query \"{$cmd}\" failed with error code \"$err\".\n";
-                                       }
-                               }
-                               $cmd = '';
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * Called by sourceStream() to check if we've reached a statement end
-        *
-        * @param string $sql SQL assembled so far
-        * @param string $newLine New line about to be added to $sql
-        * @return bool Whether $newLine contains end of the statement
-        */
-       public function streamStatementEnd( &$sql, &$newLine ) {
-               if ( $this->delimiter ) {
-                       $prev = $newLine;
-                       $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
-                       if ( $newLine != $prev ) {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Database independent variable replacement. Replaces a set of variables
-        * in an SQL statement with their contents as given by $this->getSchemaVars().
-        *
-        * Supports '{$var}' `{$var}` and / *$var* / (without the spaces) style variables.
-        *
-        * - '{$var}' should be used for text and is passed through the database's
-        *   addQuotes method.
-        * - `{$var}` should be used for identifiers (e.g. table and database names).
-        *   It is passed through the database's addIdentifierQuotes method which
-        *   can be overridden if the database uses something other than backticks.
-        * - / *_* / or / *$wgDBprefix* / passes the name that follows through the
-        *   database's tableName method.
-        * - / *i* / passes the name that follows through the database's indexName method.
-        * - In all other cases, / *$var* / is left unencoded. Except for table options,
-        *   its use should be avoided. In 1.24 and older, string encoding was applied.
-        *
-        * @param string $ins SQL statement to replace variables in
-        * @return string The new SQL statement with variables replaced
-        */
-       protected function replaceVars( $ins ) {
-               $vars = $this->getSchemaVars();
-               return preg_replace_callback(
-                       '!
-                               /\* (\$wgDBprefix|[_i]) \*/ (\w*) | # 1-2. tableName, indexName
-                               \'\{\$ (\w+) }\'                  | # 3. addQuotes
-                               `\{\$ (\w+) }`                    | # 4. addIdentifierQuotes
-                               /\*\$ (\w+) \*/                     # 5. leave unencoded
-                       !x',
-                       function ( $m ) use ( $vars ) {
-                               // Note: Because of <https://bugs.php.net/bug.php?id=51881>,
-                               // check for both nonexistent keys *and* the empty string.
-                               if ( isset( $m[1] ) && $m[1] !== '' ) {
-                                       if ( $m[1] === 'i' ) {
-                                               return $this->indexName( $m[2] );
-                                       } else {
-                                               return $this->tableName( $m[2] );
-                                       }
-                               } elseif ( isset( $m[3] ) && $m[3] !== '' && array_key_exists( $m[3], $vars ) ) {
-                                       return $this->addQuotes( $vars[$m[3]] );
-                               } elseif ( isset( $m[4] ) && $m[4] !== '' && array_key_exists( $m[4], $vars ) ) {
-                                       return $this->addIdentifierQuotes( $vars[$m[4]] );
-                               } elseif ( isset( $m[5] ) && $m[5] !== '' && array_key_exists( $m[5], $vars ) ) {
-                                       return $vars[$m[5]];
-                               } else {
-                                       return $m[0];
-                               }
-                       },
-                       $ins
-               );
-       }
-
-       /**
-        * Get schema variables. If none have been set via setSchemaVars(), then
-        * use some defaults from the current object.
-        *
-        * @return array
-        */
-       protected function getSchemaVars() {
-               if ( $this->mSchemaVars ) {
-                       return $this->mSchemaVars;
-               } else {
-                       return $this->getDefaultSchemaVars();
-               }
-       }
-
-       /**
-        * Get schema variables to use if none have been set via setSchemaVars().
-        *
-        * Override this in derived classes to provide variables for tables.sql
-        * and SQL patch files.
-        *
-        * @return array
-        */
-       protected function getDefaultSchemaVars() {
-               return [];
-       }
-
-       public function lockIsFree( $lockName, $method ) {
-               return true;
-       }
-
-       public function lock( $lockName, $method, $timeout = 5 ) {
-               $this->mNamedLocksHeld[$lockName] = 1;
-
-               return true;
-       }
-
-       public function unlock( $lockName, $method ) {
-               unset( $this->mNamedLocksHeld[$lockName] );
-
-               return true;
-       }
-
-       public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
-               if ( $this->writesOrCallbacksPending() ) {
-                       // This only flushes transactions to clear snapshots, not to write data
-                       throw new DBUnexpectedError(
-                               $this,
-                               "$fname: Cannot COMMIT to clear snapshot because writes are pending."
-                       );
-               }
-
-               if ( !$this->lock( $lockKey, $fname, $timeout ) ) {
-                       return null;
-               }
-
-               $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
-                       if ( $this->trxLevel() ) {
-                               // There is a good chance an exception was thrown, causing any early return
-                               // from the caller. Let any error handler get a chance to issue rollback().
-                               // If there isn't one, let the error bubble up and trigger server-side rollback.
-                               $this->onTransactionResolution( function () use ( $lockKey, $fname ) {
-                                       $this->unlock( $lockKey, $fname );
-                               } );
-                       } else {
-                               $this->unlock( $lockKey, $fname );
-                       }
-               } );
-
-               $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
-
-               return $unlocker;
-       }
-
-       public function namedLocksEnqueue() {
-               return false;
-       }
-
-       /**
-        * Lock specific tables
-        *
-        * @param array $read Array of tables to lock for read access
-        * @param array $write Array of tables to lock for write access
-        * @param string $method Name of caller
-        * @param bool $lowPriority Whether to indicate writes to be LOW PRIORITY
-        * @return bool
-        */
-       public function lockTables( $read, $write, $method, $lowPriority = true ) {
-               return true;
-       }
-
-       /**
-        * Unlock specific tables
-        *
-        * @param string $method The caller
-        * @return bool
-        */
-       public function unlockTables( $method ) {
-               return true;
-       }
-
-       /**
-        * Delete a table
-        * @param string $tableName
-        * @param string $fName
-        * @return bool|ResultWrapper
-        * @since 1.18
-        */
-       public function dropTable( $tableName, $fName = __METHOD__ ) {
-               if ( !$this->tableExists( $tableName, $fName ) ) {
-                       return false;
-               }
-               $sql = "DROP TABLE " . $this->tableName( $tableName );
-               if ( $this->cascadingDeletes() ) {
-                       $sql .= " CASCADE";
-               }
-
-               return $this->query( $sql, $fName );
-       }
-
-       /**
-        * Get search engine class. All subclasses of this need to implement this
-        * if they wish to use searching.
-        *
-        * @return string
-        */
-       public function getSearchEngine() {
-               return 'SearchEngineDummy';
-       }
-
-       public function getInfinity() {
-               return 'infinity';
-       }
-
-       public function encodeExpiry( $expiry ) {
-               return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
-                       ? $this->getInfinity()
-                       : $this->timestamp( $expiry );
-       }
-
-       public function decodeExpiry( $expiry, $format = TS_MW ) {
-               return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
-                       ? 'infinity'
-                       : wfTimestamp( $format, $expiry );
-       }
-
-       public function setBigSelects( $value = true ) {
-               // no-op
-       }
-
-       public function isReadOnly() {
-               return ( $this->getReadOnlyReason() !== false );
-       }
-
-       /**
-        * @return string|bool Reason this DB is read-only or false if it is not
-        */
-       protected function getReadOnlyReason() {
-               $reason = $this->getLBInfo( 'readOnlyReason' );
-
-               return is_string( $reason ) ? $reason : false;
-       }
-
-       /**
-        * @since 1.19
-        * @return string
-        */
-       public function __toString() {
-               return (string)$this->mConn;
-       }
-
-       /**
-        * Run a few simple sanity checks
-        */
-       public function __destruct() {
-               if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
-                       trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
-               }
-               $danglingCallbacks = array_merge(
-                       $this->mTrxIdleCallbacks,
-                       $this->mTrxPreCommitCallbacks,
-                       $this->mTrxEndCallbacks
-               );
-               if ( $danglingCallbacks ) {
-                       $callers = [];
-                       foreach ( $danglingCallbacks as $callbackInfo ) {
-                               $callers[] = $callbackInfo[1];
-                       }
-                       $callers = implode( ', ', $callers );
-                       trigger_error( "DB transaction callbacks still pending (from $callers)." );
-               }
-       }
-}
-
-/**
- * @since 1.27
- */
-abstract class Database extends DatabaseBase {
-       // B/C until nothing type hints for DatabaseBase
-       // @TODO: finish renaming DatabaseBase => Database
-}
diff --git a/includes/db/DatabaseError.php b/includes/db/DatabaseError.php
deleted file mode 100644 (file)
index 4cd02b1..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-<?php
-/**
- * This file contains database error 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-
-/**
- * Database error base class
- * @ingroup Database
- */
-class DBError extends MWException {
-       /** @var DatabaseBase */
-       public $db;
-
-       /**
-        * Construct a database error
-        * @param DatabaseBase $db Object which threw the error
-        * @param string $error A simple error message to be used for debugging
-        */
-       function __construct( DatabaseBase $db = null, $error ) {
-               $this->db = $db;
-               parent::__construct( $error );
-       }
-}
-
-/**
- * Base class for the more common types of database errors. These are known to occur
- * frequently, so we try to give friendly error messages for them.
- *
- * @ingroup Database
- * @since 1.23
- */
-class DBExpectedError extends DBError {
-       /**
-        * @return string
-        */
-       function getText() {
-               global $wgShowDBErrorBacktrace;
-
-               $s = $this->getTextContent() . "\n";
-
-               if ( $wgShowDBErrorBacktrace ) {
-                       $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
-               }
-
-               return $s;
-       }
-
-       /**
-        * @return string
-        */
-       function getHTML() {
-               global $wgShowDBErrorBacktrace;
-
-               $s = $this->getHTMLContent();
-
-               if ( $wgShowDBErrorBacktrace ) {
-                       $s .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
-               }
-
-               return $s;
-       }
-
-       function getPageTitle() {
-               return $this->msg( 'databaseerror', 'Database error' );
-       }
-
-       /**
-        * @return string
-        */
-       protected function getTextContent() {
-               return $this->getMessage();
-       }
-
-       /**
-        * @return string
-        */
-       protected function getHTMLContent() {
-               return '<p>' . nl2br( htmlspecialchars( $this->getTextContent() ) ) . '</p>';
-       }
-}
-
-/**
- * @ingroup Database
- */
-class DBConnectionError extends DBExpectedError {
-       /** @var string Error text */
-       public $error;
-
-       /**
-        * @param DatabaseBase $db Object throwing the error
-        * @param string $error Error text
-        */
-       function __construct( DatabaseBase $db = null, $error = 'unknown error' ) {
-               $msg = 'DB connection error';
-
-               if ( trim( $error ) != '' ) {
-                       $msg .= ": $error";
-               } elseif ( $db ) {
-                       $error = $this->db->getServer();
-               }
-
-               parent::__construct( $db, $msg );
-               $this->error = $error;
-       }
-
-       /**
-        * @return bool
-        */
-       function useOutputPage() {
-               // Not likely to work
-               return false;
-       }
-
-       /**
-        * @param string $key
-        * @param string $fallback Unescaped alternative error text in case the
-        *   message cache cannot be used. Can contain parameters as in regular
-        *   messages, that should be passed as additional parameters.
-        * @return string Unprocessed plain error text with parameters replaced
-        */
-       function msg( $key, $fallback /*[, params...] */ ) {
-               $args = array_slice( func_get_args(), 2 );
-
-               if ( $this->useMessageCache() ) {
-                       return wfMessage( $key, $args )->useDatabase( false )->text();
-               } else {
-                       return wfMsgReplaceArgs( $fallback, $args );
-               }
-       }
-
-       /**
-        * @return bool
-        */
-       function isLoggable() {
-               // Don't send to the exception log, already in dberror log
-               return false;
-       }
-
-       /**
-        * @return string Safe HTML
-        */
-       function getHTML() {
-               global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
-
-               $sorry = htmlspecialchars( $this->msg(
-                       'dberr-problems',
-                       'Sorry! This site is experiencing technical difficulties.'
-               ) );
-               $again = htmlspecialchars( $this->msg(
-                       'dberr-again',
-                       'Try waiting a few minutes and reloading.'
-               ) );
-
-               if ( $wgShowHostnames || $wgShowSQLErrors ) {
-                       $info = str_replace(
-                               '$1', Html::element( 'span', [ 'dir' => 'ltr' ], $this->error ),
-                               htmlspecialchars( $this->msg( 'dberr-info', '(Cannot access the database: $1)' ) )
-                       );
-               } else {
-                       $info = htmlspecialchars( $this->msg(
-                               'dberr-info-hidden',
-                               '(Cannot access the database)'
-                       ) );
-               }
-
-               # No database access
-               MessageCache::singleton()->disable();
-
-               $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
-
-               if ( $wgShowDBErrorBacktrace ) {
-                       $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
-               }
-
-               $html .= '<hr />';
-               $html .= $this->searchForm();
-
-               return $html;
-       }
-
-       protected function getTextContent() {
-               global $wgShowHostnames, $wgShowSQLErrors;
-
-               if ( $wgShowHostnames || $wgShowSQLErrors ) {
-                       return $this->getMessage();
-               } else {
-                       return 'DB connection error';
-               }
-       }
-
-       /**
-        * Output the exception report using HTML.
-        *
-        * @return void
-        */
-       public function reportHTML() {
-               global $wgUseFileCache;
-
-               // Check whether we can serve a file-cached copy of the page with the error underneath
-               if ( $wgUseFileCache ) {
-                       try {
-                               $cache = $this->fileCachedPage();
-                               // Cached version on file system?
-                               if ( $cache !== null ) {
-                                       // Hack: extend the body for error messages
-                                       $cache = str_replace( [ '</html>', '</body>' ], '', $cache );
-                                       // Add cache notice...
-                                       $cache .= '<div style="border:1px solid #ffd0d0;padding:1em;">' .
-                                               htmlspecialchars( $this->msg( 'dberr-cachederror',
-                                                       'This is a cached copy of the requested page, and may not be up to date.' ) ) .
-                                               '</div>';
-
-                                       // Output cached page with notices on bottom and re-close body
-                                       echo "{$cache}<hr />{$this->getHTML()}</body></html>";
-
-                                       return;
-                               }
-                       } catch ( Exception $e ) {
-                               // Do nothing, just use the default page
-                       }
-               }
-
-               // We can't, cough and die in the usual fashion
-               parent::reportHTML();
-       }
-
-       /**
-        * @return string
-        */
-       function searchForm() {
-               global $wgSitename, $wgCanonicalServer, $wgRequest;
-
-               $usegoogle = htmlspecialchars( $this->msg(
-                       'dberr-usegoogle',
-                       'You can try searching via Google in the meantime.'
-               ) );
-               $outofdate = htmlspecialchars( $this->msg(
-                       'dberr-outofdate',
-                       'Note that their indexes of our content may be out of date.'
-               ) );
-               $googlesearch = htmlspecialchars( $this->msg( 'searchbutton', 'Search' ) );
-
-               $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
-
-               $server = htmlspecialchars( $wgCanonicalServer );
-               $sitename = htmlspecialchars( $wgSitename );
-
-               $trygoogle = <<<EOT
-<div style="margin: 1.5em">$usegoogle<br />
-<small>$outofdate</small>
-</div>
-<form method="get" action="//www.google.com/search" id="googlesearch">
-       <input type="hidden" name="domains" value="$server" />
-       <input type="hidden" name="num" value="50" />
-       <input type="hidden" name="ie" value="UTF-8" />
-       <input type="hidden" name="oe" value="UTF-8" />
-
-       <input type="text" name="q" size="31" maxlength="255" value="$search" />
-       <input type="submit" name="btnG" value="$googlesearch" />
-       <p>
-               <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
-               <label><input type="radio" name="sitesearch" value="" />WWW</label>
-       </p>
-</form>
-EOT;
-
-               return $trygoogle;
-       }
-
-       /**
-        * @return string
-        */
-       private function fileCachedPage() {
-               $context = RequestContext::getMain();
-
-               if ( $context->getOutput()->isDisabled() ) {
-                       // Done already?
-                       return '';
-               }
-
-               if ( $context->getTitle() ) {
-                       // Use the main context's title if we managed to set it
-                       $t = $context->getTitle()->getPrefixedDBkey();
-               } else {
-                       // Fallback to the raw title URL param. We can't use the Title
-                       // class is it may hit the interwiki table and give a DB error.
-                       // We may get a cache miss due to not sanitizing the title though.
-                       $t = str_replace( ' ', '_', $context->getRequest()->getVal( 'title' ) );
-                       if ( $t == '' ) { // fallback to main page
-                               $t = Title::newFromText(
-                                       $this->msg( 'mainpage', 'Main Page' ) )->getPrefixedDBkey();
-                       }
-               }
-
-               $cache = new HTMLFileCache( $t, 'view' );
-               if ( $cache->isCached() ) {
-                       return $cache->fetchText();
-               } else {
-                       return '';
-               }
-       }
-}
-
-/**
- * @ingroup Database
- */
-class DBQueryError extends DBExpectedError {
-       public $error, $errno, $sql, $fname;
-
-       /**
-        * @param DatabaseBase $db
-        * @param string $error
-        * @param int|string $errno
-        * @param string $sql
-        * @param string $fname
-        */
-       function __construct( DatabaseBase $db, $error, $errno, $sql, $fname ) {
-               if ( $db->wasConnectionError( $errno ) ) {
-                       $message = "A connection error occured. \n" .
-                               "Query: $sql\n" .
-                               "Function: $fname\n" .
-                               "Error: $errno $error\n";
-               } else {
-                       $message = "A database error has occurred. Did you forget to run " .
-                               "maintenance/update.php after upgrading?  See: " .
-                               "https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
-                               "Query: $sql\n" .
-                               "Function: $fname\n" .
-                               "Error: $errno $error\n";
-               }
-               parent::__construct( $db, $message );
-
-               $this->error = $error;
-               $this->errno = $errno;
-               $this->sql = $sql;
-               $this->fname = $fname;
-       }
-
-       /**
-        * @return string
-        */
-       function getPageTitle() {
-               return $this->msg( 'databaseerror', 'Database error' );
-       }
-
-       /**
-        * @return string
-        */
-       protected function getHTMLContent() {
-               $key = 'databaseerror-text';
-               $s = Html::element( 'p', [], $this->msg( $key, $this->getFallbackMessage( $key ) ) );
-
-               $details = $this->getTechnicalDetails();
-               if ( $details ) {
-                       $s .= '<ul>';
-                       foreach ( $details as $key => $detail ) {
-                               $s .= str_replace(
-                                       '$1', call_user_func_array( 'Html::element', $detail ),
-                                       Html::element( 'li', [],
-                                               $this->msg( $key, $this->getFallbackMessage( $key ) )
-                                       )
-                               );
-                       }
-                       $s .= '</ul>';
-               }
-
-               return $s;
-       }
-
-       /**
-        * @return string
-        */
-       protected function getTextContent() {
-               $key = 'databaseerror-textcl';
-               $s = $this->msg( $key, $this->getFallbackMessage( $key ) ) . "\n";
-
-               foreach ( $this->getTechnicalDetails() as $key => $detail ) {
-                       $s .= $this->msg( $key, $this->getFallbackMessage( $key ), $detail[2] ) . "\n";
-               }
-
-               return $s;
-       }
-
-       /**
-        * Make a list of technical details that can be shown to the user. This information can
-        * aid in debugging yet may be useful to an attacker trying to exploit a security weakness
-        * in the software or server configuration.
-        *
-        * Thus no such details are shown by default, though if $wgShowHostnames is true, only the
-        * full SQL query is hidden; in fact, the error message often does contain a hostname, and
-        * sites using this option probably don't care much about "security by obscurity". Of course,
-        * if $wgShowSQLErrors is true, the SQL query *is* shown.
-        *
-        * @return array Keys are message keys; values are arrays of arguments for Html::element().
-        *   Array will be empty if users are not allowed to see any of these details at all.
-        */
-       protected function getTechnicalDetails() {
-               global $wgShowHostnames, $wgShowSQLErrors;
-
-               $attribs = [ 'dir' => 'ltr' ];
-               $details = [];
-
-               if ( $wgShowSQLErrors ) {
-                       $details['databaseerror-query'] = [
-                               'div', [ 'class' => 'mw-code' ] + $attribs, $this->sql ];
-               }
-
-               if ( $wgShowHostnames || $wgShowSQLErrors ) {
-                       $errorMessage = $this->errno . ' ' . $this->error;
-                       $details['databaseerror-function'] = [ 'code', $attribs, $this->fname ];
-                       $details['databaseerror-error'] = [ 'samp', $attribs, $errorMessage ];
-               }
-
-               return $details;
-       }
-
-       /**
-        * @param string $key Message key
-        * @return string English message text
-        */
-       private function getFallbackMessage( $key ) {
-               $messages = [
-                       'databaseerror-text' => 'A database query error has occurred.
-This may indicate a bug in the software.',
-                       'databaseerror-textcl' => 'A database query error has occurred.',
-                       'databaseerror-query' => 'Query: $1',
-                       'databaseerror-function' => 'Function: $1',
-                       'databaseerror-error' => 'Error: $1',
-               ];
-
-               return $messages[$key];
-       }
-}
-
-/**
- * @ingroup Database
- */
-class DBUnexpectedError extends DBError {
-}
-
-/**
- * @ingroup Database
- */
-class DBReadOnlyError extends DBExpectedError {
-       function getPageTitle() {
-               return $this->msg( 'readonly', 'Database is locked' );
-       }
-}
-
-/**
- * @ingroup Database
- */
-class DBTransactionError extends DBExpectedError {
-}
index 5e0365a..1c58a9a 100644 (file)
@@ -28,7 +28,7 @@
 /**
  * @ingroup Database
  */
-class DatabaseMssql extends Database {
+class DatabaseMssql extends DatabaseBase {
        protected $mInsertId = null;
        protected $mLastResult = null;
        protected $mAffectedRows = null;
@@ -81,7 +81,7 @@ class DatabaseMssql extends Database {
         * @param string $password
         * @param string $dbName
         * @throws DBConnectionError
-        * @return bool|DatabaseBase|null
+        * @return bool|resource|null
         */
        public function open( $server, $user, $password, $dbName ) {
                # Test for driver support, to avoid suppressed fatal error
@@ -165,7 +165,7 @@ class DatabaseMssql extends Database {
         * @throws DBUnexpectedError
         */
        protected function doQuery( $sql ) {
-               if ( $this->debug() ) {
+               if ( $this->getFlag( DBO_DEBUG ) ) {
                        wfDebug( "SQL: [$sql]\n" );
                }
                $this->offset = 0;
@@ -730,12 +730,12 @@ class DatabaseMssql extends Database {
         * @return null|ResultWrapper
         * @throws Exception
         */
-       public function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = []
        ) {
                $this->mScrollableCursor = false;
                try {
-                       $ret = parent::insertSelect(
+                       $ret = parent::nativeInsertSelect(
                                $destTable,
                                $srcTable,
                                $varMap,
@@ -777,7 +777,6 @@ class DatabaseMssql extends Database {
         * @return bool
         * @throws DBUnexpectedError
         * @throws Exception
-        * @throws MWException
         */
        function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
                $table = $this->tableName( $table );
@@ -814,13 +813,12 @@ class DatabaseMssql extends Database {
         * @param array $binaryColumns Contains a list of column names that are binary types
         *      This is a custom parameter only present for MS SQL.
         *
-        * @throws MWException|DBUnexpectedError
+        * @throws DBUnexpectedError
         * @return string
         */
        public function makeList( $a, $mode = LIST_COMMA, $binaryColumns = [] ) {
                if ( !is_array( $a ) ) {
-                       throw new DBUnexpectedError( $this,
-                               'DatabaseBase::makeList called with incorrect parameters' );
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with incorrect parameters' );
                }
 
                if ( $mode != LIST_NAMES ) {
@@ -1075,22 +1073,22 @@ class DatabaseMssql extends Database {
         * Throws an exception if it is invalid.
         * Reference: http://msdn.microsoft.com/en-us/library/aa224033%28v=SQL.80%29.aspx
         * @param string $identifier
-        * @throws MWException
+        * @throws InvalidArgumentException
         * @return string
         */
        private function escapeIdentifier( $identifier ) {
                if ( strlen( $identifier ) == 0 ) {
-                       throw new MWException( "An identifier must not be empty" );
+                       throw new InvalidArgumentException( "An identifier must not be empty" );
                }
                if ( strlen( $identifier ) > 128 ) {
-                       throw new MWException( "The identifier '$identifier' is too long (max. 128)" );
+                       throw new InvalidArgumentException( "The identifier '$identifier' is too long (max. 128)" );
                }
                if ( ( strpos( $identifier, '[' ) !== false )
                        || ( strpos( $identifier, ']' ) !== false )
                ) {
                        // It may be allowed if you quoted with double quotation marks, but
                        // that would break if QUOTED_IDENTIFIER is OFF
-                       throw new MWException( "Square brackets are not allowed in '$identifier'" );
+                       throw new InvalidArgumentException( "Square brackets are not allowed in '$identifier'" );
                }
 
                return "[$identifier]";
@@ -1216,7 +1214,7 @@ class DatabaseMssql extends Database {
                }
 
                // we want this to be compatible with the output of parent::makeSelectOptions()
-               return [ $startOpts, '', $tailOpts, '' ];
+               return [ $startOpts, '', $tailOpts, '', '' ];
        }
 
        /**
@@ -1411,148 +1409,3 @@ class DatabaseMssql extends Database {
                return wfSetVar( $this->mIgnoreErrors, $value );
        }
 } // end DatabaseMssql class
-
-/**
- * Utility class.
- *
- * @ingroup Database
- */
-class MssqlField implements Field {
-       private $name, $tableName, $default, $max_length, $nullable, $type;
-
-       function __construct( $info ) {
-               $this->name = $info['COLUMN_NAME'];
-               $this->tableName = $info['TABLE_NAME'];
-               $this->default = $info['COLUMN_DEFAULT'];
-               $this->max_length = $info['CHARACTER_MAXIMUM_LENGTH'];
-               $this->nullable = !( strtolower( $info['IS_NULLABLE'] ) == 'no' );
-               $this->type = $info['DATA_TYPE'];
-       }
-
-       function name() {
-               return $this->name;
-       }
-
-       function tableName() {
-               return $this->tableName;
-       }
-
-       function defaultValue() {
-               return $this->default;
-       }
-
-       function maxLength() {
-               return $this->max_length;
-       }
-
-       function isNullable() {
-               return $this->nullable;
-       }
-
-       function type() {
-               return $this->type;
-       }
-}
-
-class MssqlBlob extends Blob {
-       public function __construct( $data ) {
-               if ( $data instanceof MssqlBlob ) {
-                       return $data;
-               } elseif ( $data instanceof Blob ) {
-                       $this->mData = $data->fetch();
-               } elseif ( is_array( $data ) && is_object( $data ) ) {
-                       $this->mData = serialize( $data );
-               } else {
-                       $this->mData = $data;
-               }
-       }
-
-       /**
-        * Returns an unquoted hex representation of a binary string
-        * for insertion into varbinary-type fields
-        * @return string
-        */
-       public function fetch() {
-               if ( $this->mData === null ) {
-                       return 'null';
-               }
-
-               $ret = '0x';
-               $dataLength = strlen( $this->mData );
-               for ( $i = 0; $i < $dataLength; $i++ ) {
-                       $ret .= bin2hex( pack( 'C', ord( $this->mData[$i] ) ) );
-               }
-
-               return $ret;
-       }
-}
-
-class MssqlResultWrapper extends ResultWrapper {
-       private $mSeekTo = null;
-
-       /**
-        * @return stdClass|bool
-        */
-       public function fetchObject() {
-               $res = $this->result;
-
-               if ( $this->mSeekTo !== null ) {
-                       $result = sqlsrv_fetch_object( $res, 'stdClass', [],
-                               SQLSRV_SCROLL_ABSOLUTE, $this->mSeekTo );
-                       $this->mSeekTo = null;
-               } else {
-                       $result = sqlsrv_fetch_object( $res );
-               }
-
-               // MediaWiki expects us to return boolean false when there are no more rows instead of null
-               if ( $result === null ) {
-                       return false;
-               }
-
-               return $result;
-       }
-
-       /**
-        * @return array|bool
-        */
-       public function fetchRow() {
-               $res = $this->result;
-
-               if ( $this->mSeekTo !== null ) {
-                       $result = sqlsrv_fetch_array( $res, SQLSRV_FETCH_BOTH,
-                               SQLSRV_SCROLL_ABSOLUTE, $this->mSeekTo );
-                       $this->mSeekTo = null;
-               } else {
-                       $result = sqlsrv_fetch_array( $res );
-               }
-
-               // MediaWiki expects us to return boolean false when there are no more rows instead of null
-               if ( $result === null ) {
-                       return false;
-               }
-
-               return $result;
-       }
-
-       /**
-        * @param int $row
-        * @return bool
-        */
-       public function seek( $row ) {
-               $res = $this->result;
-
-               // check bounds
-               $numRows = $this->db->numRows( $res );
-               $row = intval( $row );
-
-               if ( $numRows === 0 ) {
-                       return false;
-               } elseif ( $row < 0 || $row > $numRows - 1 ) {
-                       return false;
-               }
-
-               // Unlike MySQL, the seek actually happens on the next access
-               $this->mSeekTo = $row;
-               return true;
-       }
-}
index 93814d2..46c6678 100644 (file)
  * @since 1.22
  * @see Database
  */
-abstract class DatabaseMysqlBase extends Database {
+abstract class DatabaseMysqlBase extends DatabaseBase {
        /** @var MysqlMasterPos */
-       protected $lastKnownSlavePos;
-       /** @var string Method to detect slave lag */
+       protected $lastKnownReplicaPos;
+       /** @var string Method to detect replica DB lag */
        protected $lagDetectionMethod;
-       /** @var array Method to detect slave lag */
+       /** @var array Method to detect replica DB lag */
        protected $lagDetectionOptions = [];
        /** @var bool bool Whether to use GTID methods */
        protected $useGTIDs = false;
@@ -46,6 +46,11 @@ abstract class DatabaseMysqlBase extends Database {
        protected $sslCAPath;
        /** @var string[]|null */
        protected $sslCiphers;
+       /** @var string sql_mode value to send on connection */
+       protected $sqlMode;
+       /** @var bool Use experimental UTF-8 transmission encoding */
+       protected $utf8Mode;
+
        /** @var string|null */
        private $serverVersion = null;
 
@@ -82,6 +87,8 @@ abstract class DatabaseMysqlBase extends Database {
                                $this->$var = $params[$var];
                        }
                }
+               $this->sqlMode = isset( $params['sqlMode'] ) ? $params['sqlMode'] : '';
+               $this->utf8Mode = !empty( $params['utf8Mode'] );
        }
 
        /**
@@ -100,13 +107,9 @@ abstract class DatabaseMysqlBase extends Database {
         * @return bool
         */
        function open( $server, $user, $password, $dbName ) {
-               global $wgAllDBsAreLocalhost, $wgSQLMode;
-
                # Close/unset connection handle
                $this->close();
 
-               # Debugging hack -- fake cluster
-               $realServer = $wgAllDBsAreLocalhost ? 'localhost' : $server;
                $this->mServer = $server;
                $this->mUser = $user;
                $this->mPassword = $password;
@@ -114,7 +117,7 @@ abstract class DatabaseMysqlBase extends Database {
 
                $this->installErrorHandler();
                try {
-                       $this->mConn = $this->mysqlConnect( $realServer );
+                       $this->mConn = $this->mysqlConnect( $this->mServer );
                } catch ( Exception $ex ) {
                        $this->restoreErrorHandler();
                        throw $ex;
@@ -126,14 +129,14 @@ abstract class DatabaseMysqlBase extends Database {
                        if ( !$error ) {
                                $error = $this->lastError();
                        }
-                       wfLogDBError(
+                       $this->queryLogger->error(
                                "Error connecting to {db_server}: {error}",
                                $this->getLogContext( [
                                        'method' => __METHOD__,
                                        'error' => $error,
                                ] )
                        );
-                       wfDebug( "DB connection error\n" .
+                       $this->queryLogger->debug( "DB connection error\n" .
                                "Server: $server, User: $user, Password: " .
                                substr( $password, 0, 3 ) . "..., error: " . $error . "\n" );
 
@@ -145,14 +148,14 @@ abstract class DatabaseMysqlBase extends Database {
                        $success = $this->selectDB( $dbName );
                        MediaWiki\restoreWarnings();
                        if ( !$success ) {
-                               wfLogDBError(
+                               $this->queryLogger->error(
                                        "Error selecting database {db_name} on server {db_server}",
                                        $this->getLogContext( [
                                                'method' => __METHOD__,
                                        ] )
                                );
-                               wfDebug( "Error selecting database $dbName on server {$this->mServer} " .
-                                       "from client host " . wfHostname() . "\n" );
+                               $this->queryLogger->debug(
+                                       "Error selecting database $dbName on server {$this->mServer}" );
 
                                $this->reportConnectionError( "Error selecting database $dbName" );
                        }
@@ -166,8 +169,8 @@ abstract class DatabaseMysqlBase extends Database {
                // Abstract over any insane MySQL defaults
                $set = [ 'group_concat_max_len = 262144' ];
                // Set SQL mode, default is turning them all off, can be overridden or skipped with null
-               if ( is_string( $wgSQLMode ) ) {
-                       $set[] = 'sql_mode = ' . $this->addQuotes( $wgSQLMode );
+               if ( is_string( $this->sqlMode ) ) {
+                       $set[] = 'sql_mode = ' . $this->addQuotes( $this->sqlMode );
                }
                // Set any custom settings defined by site config
                // (e.g. https://dev.mysql.com/doc/refman/4.1/en/innodb-parameters.html)
@@ -183,7 +186,7 @@ abstract class DatabaseMysqlBase extends Database {
                        // Use doQuery() to avoid opening implicit transactions (DBO_TRX)
                        $success = $this->doQuery( 'SET ' . implode( ', ', $set ) );
                        if ( !$success ) {
-                               wfLogDBError(
+                               $this->queryLogger->error(
                                        'Error setting MySQL variables on server {db_server} (check $wgSQLMode)',
                                        $this->getLogContext( [
                                                'method' => __METHOD__,
@@ -204,9 +207,7 @@ abstract class DatabaseMysqlBase extends Database {
         * @return bool
         */
        protected function connectInitCharset() {
-               global $wgDBmysql5;
-
-               if ( $wgDBmysql5 ) {
+               if ( $this->utf8Mode ) {
                        // Tell the server we're communicating with it in UTF-8.
                        // This may engage various charset conversions.
                        return $this->mysqlSetCharset( 'utf8' );
@@ -657,7 +658,7 @@ abstract class DatabaseMysqlBase extends Database {
                        // Standard method: use master server ID (works with stock pt-heartbeat)
                        $masterInfo = $this->getMasterServerInfo();
                        if ( !$masterInfo ) {
-                               wfLogDBError(
+                               $this->queryLogger->error(
                                        "Unable to query master of {db_server} for server ID",
                                        $this->getLogContext( [
                                                'method' => __METHOD__
@@ -680,7 +681,7 @@ abstract class DatabaseMysqlBase extends Database {
                        return max( $nowUnix - $timeUnix, 0.0 );
                }
 
-               wfLogDBError(
+               $this->queryLogger->error(
                        "Unable to find pt-heartbeat row for {db_server}",
                        $this->getLogContext( [
                                'method' => __METHOD__
@@ -695,7 +696,7 @@ abstract class DatabaseMysqlBase extends Database {
                $key = $cache->makeGlobalKey(
                        'mysql',
                        'master-info',
-                       // Using one key for all cluster slaves is preferable
+                       // Using one key for all cluster replica DBs is preferable
                        $this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
                );
 
@@ -771,7 +772,7 @@ abstract class DatabaseMysqlBase extends Database {
 
                if ( $this->getLBInfo( 'is static' ) === true ) {
                        return 0; // this is a copy of a read-only dataset with no master DB
-               } elseif ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) {
+               } elseif ( $this->lastKnownReplicaPos && $this->lastKnownReplicaPos->hasReached( $pos ) ) {
                        return 0; // already reached this point for sure
                }
 
@@ -797,16 +798,16 @@ abstract class DatabaseMysqlBase extends Database {
                if ( $status === null ) {
                        // T126436: jobs programmed to wait on master positions might be referencing binlogs
                        // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try
-                       // to detect this and treat the slave as having reached the position; a proper master
+                       // to detect this and treat the replica DB as having reached the position; a proper master
                        // switchover already requires that the new master be caught up before the switch.
-                       $slavePos = $this->getSlavePos();
-                       if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) {
-                               $this->lastKnownSlavePos = $slavePos;
+                       $replicationPos = $this->getSlavePos();
+                       if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
+                               $this->lastKnownReplicaPos = $replicationPos;
                                $status = 0;
                        }
                } elseif ( $status >= 0 ) {
                        // Remember that this position was reached to save queries next time
-                       $this->lastKnownSlavePos = $pos;
+                       $this->lastKnownReplicaPos = $pos;
                }
 
                return $status;
@@ -880,6 +881,14 @@ abstract class DatabaseMysqlBase extends Database {
                return "FORCE INDEX (" . $this->indexName( $index ) . ")";
        }
 
+       /**
+        * @param string $index
+        * @return string
+        */
+       function ignoreIndexClause( $index ) {
+               return "IGNORE INDEX (" . $this->indexName( $index ) . ")";
+       }
+
        /**
         * @return string
         */
@@ -976,7 +985,7 @@ abstract class DatabaseMysqlBase extends Database {
                        return true;
                }
 
-               wfDebug( __METHOD__ . " failed to acquire lock\n" );
+               $this->queryLogger->debug( __METHOD__ . " failed to acquire lock\n" );
 
                return false;
        }
@@ -998,7 +1007,7 @@ abstract class DatabaseMysqlBase extends Database {
                        return true;
                }
 
-               wfDebug( __METHOD__ . " failed to release lock\n" );
+               $this->queryLogger->debug( __METHOD__ . " failed to release lock\n" );
 
                return false;
        }
@@ -1090,7 +1099,7 @@ abstract class DatabaseMysqlBase extends Database {
         */
        function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__ ) {
                if ( !$conds ) {
-                       throw new DBUnexpectedError( $this, 'DatabaseBase::deleteJoin() called with empty $conds' );
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with empty $conds' );
                }
 
                $delTable = $this->tableName( $delTable );
@@ -1343,243 +1352,3 @@ abstract class DatabaseMysqlBase extends Database {
        }
 }
 
-/**
- * Utility class.
- * @ingroup Database
- */
-class MySQLField implements Field {
-       private $name, $tablename, $default, $max_length, $nullable,
-               $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary,
-               $is_numeric, $is_blob, $is_unsigned, $is_zerofill;
-
-       function __construct( $info ) {
-               $this->name = $info->name;
-               $this->tablename = $info->table;
-               $this->default = $info->def;
-               $this->max_length = $info->max_length;
-               $this->nullable = !$info->not_null;
-               $this->is_pk = $info->primary_key;
-               $this->is_unique = $info->unique_key;
-               $this->is_multiple = $info->multiple_key;
-               $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
-               $this->type = $info->type;
-               $this->binary = isset( $info->binary ) ? $info->binary : false;
-               $this->is_numeric = isset( $info->numeric ) ? $info->numeric : false;
-               $this->is_blob = isset( $info->blob ) ? $info->blob : false;
-               $this->is_unsigned = isset( $info->unsigned ) ? $info->unsigned : false;
-               $this->is_zerofill = isset( $info->zerofill ) ? $info->zerofill : false;
-       }
-
-       /**
-        * @return string
-        */
-       function name() {
-               return $this->name;
-       }
-
-       /**
-        * @return string
-        */
-       function tableName() {
-               return $this->tablename;
-       }
-
-       /**
-        * @return string
-        */
-       function type() {
-               return $this->type;
-       }
-
-       /**
-        * @return bool
-        */
-       function isNullable() {
-               return $this->nullable;
-       }
-
-       function defaultValue() {
-               return $this->default;
-       }
-
-       /**
-        * @return bool
-        */
-       function isKey() {
-               return $this->is_key;
-       }
-
-       /**
-        * @return bool
-        */
-       function isMultipleKey() {
-               return $this->is_multiple;
-       }
-
-       /**
-        * @return bool
-        */
-       function isBinary() {
-               return $this->binary;
-       }
-
-       /**
-        * @return bool
-        */
-       function isNumeric() {
-               return $this->is_numeric;
-       }
-
-       /**
-        * @return bool
-        */
-       function isBlob() {
-               return $this->is_blob;
-       }
-
-       /**
-        * @return bool
-        */
-       function isUnsigned() {
-               return $this->is_unsigned;
-       }
-
-       /**
-        * @return bool
-        */
-       function isZerofill() {
-               return $this->is_zerofill;
-       }
-}
-
-/**
- * DBMasterPos class for MySQL/MariaDB
- *
- * Note that master positions and sync logic here make some assumptions:
- *  - Binlog-based usage assumes single-source replication and non-hierarchical replication.
- *  - GTID-based usage allows getting/syncing with multi-source replication. It is assumed
- *    that GTID sets are complete (e.g. include all domains on the server).
- */
-class MySQLMasterPos implements DBMasterPos {
-       /** @var string Binlog file */
-       public $file;
-       /** @var int Binglog file position */
-       public $pos;
-       /** @var string[] GTID list */
-       public $gtids = [];
-       /** @var float UNIX timestamp */
-       public $asOfTime = 0.0;
-
-       /**
-        * @param string $file Binlog file name
-        * @param integer $pos Binlog position
-        * @param string $gtid Comma separated GTID set [optional]
-        */
-       function __construct( $file, $pos, $gtid = '' ) {
-               $this->file = $file;
-               $this->pos = $pos;
-               $this->gtids = array_map( 'trim', explode( ',', $gtid ) );
-               $this->asOfTime = microtime( true );
-       }
-
-       /**
-        * @return string <binlog file>/<position>, e.g db1034-bin.000976/843431247
-        */
-       function __toString() {
-               return "{$this->file}/{$this->pos}";
-       }
-
-       function asOfTime() {
-               return $this->asOfTime;
-       }
-
-       function hasReached( DBMasterPos $pos ) {
-               if ( !( $pos instanceof self ) ) {
-                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
-               }
-
-               // Prefer GTID comparisons, which work with multi-tier replication
-               $thisPosByDomain = $this->getGtidCoordinates();
-               $thatPosByDomain = $pos->getGtidCoordinates();
-               if ( $thisPosByDomain && $thatPosByDomain ) {
-                       $reached = true;
-                       // Check that this has positions GTE all of those in $pos for all domains in $pos
-                       foreach ( $thatPosByDomain as $domain => $thatPos ) {
-                               $thisPos = isset( $thisPosByDomain[$domain] ) ? $thisPosByDomain[$domain] : -1;
-                               $reached = $reached && ( $thatPos <= $thisPos );
-                       }
-
-                       return $reached;
-               }
-
-               // Fallback to the binlog file comparisons
-               $thisBinPos = $this->getBinlogCoordinates();
-               $thatBinPos = $pos->getBinlogCoordinates();
-               if ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] ) {
-                       return ( $thisBinPos['pos'] >= $thatBinPos['pos'] );
-               }
-
-               // Comparing totally different binlogs does not make sense
-               return false;
-       }
-
-       function channelsMatch( DBMasterPos $pos ) {
-               if ( !( $pos instanceof self ) ) {
-                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
-               }
-
-               // Prefer GTID comparisons, which work with multi-tier replication
-               $thisPosDomains = array_keys( $this->getGtidCoordinates() );
-               $thatPosDomains = array_keys( $pos->getGtidCoordinates() );
-               if ( $thisPosDomains && $thatPosDomains ) {
-                       // Check that this has GTIDs for all domains in $pos
-                       return !array_diff( $thatPosDomains, $thisPosDomains );
-               }
-
-               // Fallback to the binlog file comparisons
-               $thisBinPos = $this->getBinlogCoordinates();
-               $thatBinPos = $pos->getBinlogCoordinates();
-
-               return ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] );
-       }
-
-       /**
-        * @note: this returns false for multi-source replication GTID sets
-        * @see https://mariadb.com/kb/en/mariadb/gtid
-        * @see https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html
-        * @return array Map of (domain => integer position) or false
-        */
-       protected function getGtidCoordinates() {
-               $gtidInfos = [];
-               foreach ( $this->gtids as $gtid ) {
-                       $m = [];
-                       // MariaDB style: <domain>-<server id>-<sequence number>
-                       if ( preg_match( '!^(\d+)-\d+-(\d+)$!', $gtid, $m ) ) {
-                               $gtidInfos[(int)$m[1]] = (int)$m[2];
-                       // MySQL style: <UUID domain>:<sequence number>
-                       } elseif ( preg_match( '!^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}):(\d+)$!', $gtid, $m ) ) {
-                               $gtidInfos[$m[1]] = (int)$m[2];
-                       } else {
-                               $gtidInfos = [];
-                               break; // unrecognized GTID
-                       }
-
-               }
-
-               return $gtidInfos;
-       }
-
-       /**
-        * @see http://dev.mysql.com/doc/refman/5.7/en/show-master-status.html
-        * @see http://dev.mysql.com/doc/refman/5.7/en/show-slave-status.html
-        * @return array|bool (binlog, (integer file number, integer position)) or false
-        */
-       protected function getBinlogCoordinates() {
-               $m = [];
-               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
-                       return [ 'binlog' => $m[1], 'pos' => [ (int)$m[2], (int)$m[3] ] ];
-               }
-
-               return false;
-       }
-}
index f9ba050..ec3336a 100644 (file)
@@ -50,7 +50,7 @@ class ORAResult {
        }
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @param resource $stmt A valid OCI statement identifier
         * @param bool $unique
         */
@@ -129,63 +129,9 @@ class ORAResult {
 }
 
 /**
- * Utility class.
  * @ingroup Database
  */
-class ORAField implements Field {
-       private $name, $tablename, $default, $max_length, $nullable,
-               $is_pk, $is_unique, $is_multiple, $is_key, $type;
-
-       function __construct( $info ) {
-               $this->name = $info['column_name'];
-               $this->tablename = $info['table_name'];
-               $this->default = $info['data_default'];
-               $this->max_length = $info['data_length'];
-               $this->nullable = $info['not_null'];
-               $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
-               $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
-               $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
-               $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
-               $this->type = $info['data_type'];
-       }
-
-       function name() {
-               return $this->name;
-       }
-
-       function tableName() {
-               return $this->tablename;
-       }
-
-       function defaultValue() {
-               return $this->default;
-       }
-
-       function maxLength() {
-               return $this->max_length;
-       }
-
-       function isNullable() {
-               return $this->nullable;
-       }
-
-       function isKey() {
-               return $this->is_key;
-       }
-
-       function isMultipleKey() {
-               return $this->is_multiple;
-       }
-
-       function type() {
-               return $this->type;
-       }
-}
-
-/**
- * @ingroup Database
- */
-class DatabaseOracle extends Database {
+class DatabaseOracle extends DatabaseBase {
        /** @var resource */
        protected $mLastResult = null;
 
@@ -265,7 +211,7 @@ class DatabaseOracle extends Database {
         * @param string $password
         * @param string $dbName
         * @throws DBConnectionError
-        * @return DatabaseBase|null
+        * @return resource|null
         */
        function open( $server, $user, $password, $dbName ) {
                global $wgDBOracleDRCP;
@@ -369,7 +315,7 @@ class DatabaseOracle extends Database {
        protected function doQuery( $sql ) {
                wfDebug( "SQL: [$sql]\n" );
                if ( !StringUtils::isUtf8( $sql ) ) {
-                       throw new MWException( "SQL encoding is invalid\n$sql" );
+                       throw new InvalidArgumentException( "SQL encoding is invalid\n$sql" );
                }
 
                // handle some oracle specifics
@@ -732,14 +678,15 @@ class DatabaseOracle extends Database {
                return oci_free_statement( $stmt );
        }
 
-       function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = []
        ) {
                $destTable = $this->tableName( $destTable );
                if ( !is_array( $selectOptions ) ) {
                        $selectOptions = [ $selectOptions ];
                }
-               list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
+               list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) =
+                       $this->makeSelectOptions( $selectOptions );
                if ( is_array( $srcTable ) ) {
                        $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
                } else {
@@ -761,7 +708,7 @@ class DatabaseOracle extends Database {
 
                $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
                        " SELECT $startOpts " . implode( ',', $varMap ) .
-                       " FROM $srcTable $useIndex ";
+                       " FROM $srcTable $useIndex $ignoreIndex ";
                if ( $conds != '*' ) {
                        $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
                }
@@ -1375,7 +1322,13 @@ class DatabaseOracle extends Database {
                        $useIndex = '';
                }
 
-               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ];
+               if ( isset( $options['IGNORE INDEX'] ) && !is_array( $options['IGNORE INDEX'] ) ) {
+                       $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] );
+               } else {
+                       $ignoreIndex = '';
+               }
+
+               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
        }
 
        public function delete( $table, $conds, $fname = __METHOD__ ) {
@@ -1555,6 +1508,15 @@ class DatabaseOracle extends Database {
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return 'CAST ( ' . $field . ' AS VARCHAR2 )';
+       }
+
        public function getSearchEngine() {
                return 'SearchOracle';
        }
index 1ecdd26..cb95304 100644 (file)
@@ -26,7 +26,7 @@ class PostgresField implements Field {
                $has_default, $default;
 
        /**
-        * @param DatabaseBase $db
+        * @param IDatabase $db
         * @param string $table
         * @param string $field
         * @return null|PostgresField
@@ -140,7 +140,7 @@ class SavepointPostgres {
        protected $didbegin;
 
        /**
-        * @param DatabaseBase $dbw
+        * @param IDatabase $dbw
         * @param int $id
         */
        public function __construct( $dbw, $id ) {
@@ -204,7 +204,7 @@ class SavepointPostgres {
 /**
  * @ingroup Database
  */
-class DatabasePostgres extends Database {
+class DatabasePostgres extends DatabaseBase {
        /** @var resource */
        protected $mLastResult = null;
 
@@ -276,7 +276,7 @@ class DatabasePostgres extends Database {
         * @param string $password
         * @param string $dbName
         * @throws DBConnectionError|Exception
-        * @return DatabaseBase|null
+        * @return resource|bool|null
         */
        function open( $server, $user, $password, $dbName ) {
                # Test for Postgres support, to avoid suppressed fatal error
@@ -904,7 +904,7 @@ __INDEXATTR__;
         * @param array $selectOptions
         * @return bool
         */
-       function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = [] ) {
                $destTable = $this->tableName( $destTable );
 
@@ -927,7 +927,8 @@ __INDEXATTR__;
                if ( !is_array( $selectOptions ) ) {
                        $selectOptions = [ $selectOptions ];
                }
-               list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
+               list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) =
+                       $this->makeSelectOptions( $selectOptions );
                if ( is_array( $srcTable ) ) {
                        $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
                } else {
@@ -936,7 +937,7 @@ __INDEXATTR__;
 
                $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
                        " SELECT $startOpts " . implode( ',', $varMap ) .
-                       " FROM $srcTable $useIndex";
+                       " FROM $srcTable $useIndex $ignoreIndex ";
 
                if ( $conds != '*' ) {
                        $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
@@ -1482,7 +1483,7 @@ SQL;
         */
        function makeSelectOptions( $options ) {
                $preLimitTail = $postLimitTail = '';
-               $startOpts = $useIndex = '';
+               $startOpts = $useIndex = $ignoreIndex = '';
 
                $noKeyOptions = [];
                foreach ( $options as $key => $option ) {
@@ -1512,7 +1513,7 @@ SQL;
                        $startOpts .= 'DISTINCT';
                }
 
-               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail ];
+               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
        }
 
        function getDBname() {
@@ -1535,6 +1536,15 @@ SQL;
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return $field . '::text';
+       }
+
        public function getSearchEngine() {
                return 'SearchPostgres';
        }
@@ -1579,21 +1589,21 @@ SQL;
         */
        public function lock( $lockName, $method, $timeout = 5 ) {
                $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
-               for ( $attempts = 1; $attempts <= $timeout; ++$attempts ) {
-                       $result = $this->query(
-                               "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
-                       $row = $this->fetchObject( $result );
-                       if ( $row->lockstatus === 't' ) {
-                               parent::lock( $lockName, $method, $timeout ); // record
-                               return true;
-                       } else {
-                               sleep( 1 );
-                       }
-               }
+               $loop = new WaitConditionLoop(
+                       function () use ( $lockName, $key, $timeout, $method ) {
+                               $res = $this->query( "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
+                               $row = $this->fetchObject( $res );
+                               if ( $row->lockstatus === 't' ) {
+                                       parent::lock( $lockName, $method, $timeout ); // record
+                                       return true;
+                               }
 
-               wfDebug( __METHOD__ . " failed to acquire lock\n" );
+                               return WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
 
-               return false;
+               return ( $loop->invoke() === $loop::CONDITION_REACHED );
        }
 
        /**
@@ -1626,6 +1636,3 @@ SQL;
                return Wikimedia\base_convert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
        }
 } // end DatabasePostgres class
-
-class PostgresBlob extends Blob {
-}
index 5bbba88..28fb5b5 100644 (file)
@@ -25,7 +25,7 @@
 /**
  * @ingroup Database
  */
-class DatabaseSqlite extends Database {
+class DatabaseSqlite extends DatabaseBase {
        /** @var bool Whether full text is enabled */
        private static $fulltextEnabled = null;
 
@@ -59,7 +59,7 @@ class DatabaseSqlite extends Database {
         * @param array $p
         */
        function __construct( array $p ) {
-               global $wgSharedDB, $wgSQLiteDataDir;
+               global $wgSQLiteDataDir;
 
                $this->dbDir = isset( $p['dbDirectory'] ) ? $p['dbDirectory'] : $wgSQLiteDataDir;
 
@@ -76,8 +76,13 @@ class DatabaseSqlite extends Database {
                        // Super doesn't open when $user is false, but we can work with $dbName
                        if ( $p['dbname'] && !$this->isOpen() ) {
                                if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
-                                       if ( $wgSharedDB ) {
-                                               $this->attachDatabase( $wgSharedDB );
+                                       $done = [];
+                                       foreach ( $this->tableAliases as $params ) {
+                                               if ( isset( $done[$params['dbname']] ) ) {
+                                                       continue;
+                                               }
+                                               $this->attachDatabase( $params['dbname'] );
+                                               $done[$params['dbname']] = 1;
                                        }
                                }
                        }
@@ -277,12 +282,6 @@ class DatabaseSqlite extends Database {
                return $this->query( "ATTACH DATABASE $file AS $name", $fname );
        }
 
-       /**
-        * @see DatabaseBase::isWriteQuery()
-        *
-        * @param string $sql
-        * @return bool
-        */
        function isWriteQuery( $sql ) {
                return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
        }
@@ -832,6 +831,15 @@ class DatabaseSqlite extends Database {
                return parent::buildLike( $params ) . "ESCAPE '\' ";
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return 'CAST ( ' . $field . ' AS TEXT )';
+       }
+
        /**
         * @return string
         */
@@ -941,19 +949,19 @@ class DatabaseSqlite extends Database {
        }
 
        /**
-        * @throws MWException
         * @param string $oldName
         * @param string $newName
         * @param bool $temporary
         * @param string $fname
         * @return bool|ResultWrapper
+        * @throws RuntimeException
         */
        function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) {
                $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" .
                        $this->addQuotes( $oldName ) . " AND type='table'", $fname );
                $obj = $this->fetchObject( $res );
                if ( !$obj ) {
-                       throw new MWException( "Couldn't retrieve structure for table $oldName" );
+                       throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
                }
                $sql = $obj->sql;
                $sql = preg_replace(
@@ -1034,52 +1042,27 @@ class DatabaseSqlite extends Database {
        }
 
        /**
-        * @return string
+        * Override due to no CASCADE support
+        *
+        * @param string $tableName
+        * @param string $fName
+        * @return bool|ResultWrapper
+        * @throws DBReadOnlyError
         */
-       public function __toString() {
-               return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
-       }
-
-} // end DatabaseSqlite class
-
-/**
- * @ingroup Database
- */
-class SQLiteField implements Field {
-       private $info, $tableName;
-
-       function __construct( $info, $tableName ) {
-               $this->info = $info;
-               $this->tableName = $tableName;
-       }
-
-       function name() {
-               return $this->info->name;
-       }
-
-       function tableName() {
-               return $this->tableName;
-       }
-
-       function defaultValue() {
-               if ( is_string( $this->info->dflt_value ) ) {
-                       // Typically quoted
-                       if ( preg_match( '/^\'(.*)\'$', $this->info->dflt_value ) ) {
-                               return str_replace( "''", "'", $this->info->dflt_value );
-                       }
+       public function dropTable( $tableName, $fName = __METHOD__ ) {
+               if ( !$this->tableExists( $tableName, $fName ) ) {
+                       return false;
                }
+               $sql = "DROP TABLE " . $this->tableName( $tableName );
 
-               return $this->info->dflt_value;
+               return $this->query( $sql, $fName );
        }
 
        /**
-        * @return bool
+        * @return string
         */
-       function isNullable() {
-               return !$this->info->notnull;
+       public function __toString() {
+               return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
        }
 
-       function type() {
-               return $this->info->type;
-       }
-} // end SQLiteField
+} // end DatabaseSqlite class
diff --git a/includes/db/DatabaseUtility.php b/includes/db/DatabaseUtility.php
deleted file mode 100644 (file)
index b6c37ee..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-<?php
-/**
- * This file contains database-related utility 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.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-
-/**
- * Utility class
- * @ingroup Database
- *
- * This allows us to distinguish a blob from a normal string and an array of strings
- */
-class Blob {
-       /** @var string */
-       protected $mData;
-
-       function __construct( $data ) {
-               $this->mData = $data;
-       }
-
-       function fetch() {
-               return $this->mData;
-       }
-}
-
-/**
- * Base for all database-specific classes representing information about database fields
- * @ingroup Database
- */
-interface Field {
-       /**
-        * Field name
-        * @return string
-        */
-       function name();
-
-       /**
-        * Name of table this field belongs to
-        * @return string
-        */
-       function tableName();
-
-       /**
-        * Database type
-        * @return string
-        */
-       function type();
-
-       /**
-        * Whether this field can store NULL values
-        * @return bool
-        */
-       function isNullable();
-}
-
-/**
- * Result wrapper for grabbing data queried by someone else
- * @ingroup Database
- */
-class ResultWrapper implements Iterator {
-       /** @var resource */
-       public $result;
-
-       /** @var DatabaseBase */
-       protected $db;
-
-       /** @var int */
-       protected $pos = 0;
-
-       /** @var object|null */
-       protected $currentRow = null;
-
-       /**
-        * Create a new result object from a result resource and a Database object
-        *
-        * @param DatabaseBase $database
-        * @param resource|ResultWrapper $result
-        */
-       function __construct( $database, $result ) {
-               $this->db = $database;
-
-               if ( $result instanceof ResultWrapper ) {
-                       $this->result = $result->result;
-               } else {
-                       $this->result = $result;
-               }
-       }
-
-       /**
-        * Get the number of rows in a result object
-        *
-        * @return int
-        */
-       function numRows() {
-               return $this->db->numRows( $this );
-       }
-
-       /**
-        * Fetch the next row from the given result object, in object form. Fields can be retrieved with
-        * $row->fieldname, with fields acting like member variables. If no more rows are available,
-        * false is returned.
-        *
-        * @return stdClass|bool
-        * @throws DBUnexpectedError Thrown if the database returns an error
-        */
-       function fetchObject() {
-               return $this->db->fetchObject( $this );
-       }
-
-       /**
-        * Fetch the next row from the given result object, in associative array form. Fields are
-        * retrieved with $row['fieldname']. If no more rows are available, false is returned.
-        *
-        * @return array|bool
-        * @throws DBUnexpectedError Thrown if the database returns an error
-        */
-       function fetchRow() {
-               return $this->db->fetchRow( $this );
-       }
-
-       /**
-        * Free a result object
-        */
-       function free() {
-               $this->db->freeResult( $this );
-               unset( $this->result );
-               unset( $this->db );
-       }
-
-       /**
-        * Change the position of the cursor in a result object.
-        * See mysql_data_seek()
-        *
-        * @param int $row
-        */
-       function seek( $row ) {
-               $this->db->dataSeek( $this, $row );
-       }
-
-       /*
-        * ======= Iterator functions =======
-        * Note that using these in combination with the non-iterator functions
-        * above may cause rows to be skipped or repeated.
-        */
-
-       function rewind() {
-               if ( $this->numRows() ) {
-                       $this->db->dataSeek( $this, 0 );
-               }
-               $this->pos = 0;
-               $this->currentRow = null;
-       }
-
-       /**
-        * @return stdClass|array|bool
-        */
-       function current() {
-               if ( is_null( $this->currentRow ) ) {
-                       $this->next();
-               }
-
-               return $this->currentRow;
-       }
-
-       /**
-        * @return int
-        */
-       function key() {
-               return $this->pos;
-       }
-
-       /**
-        * @return stdClass
-        */
-       function next() {
-               $this->pos++;
-               $this->currentRow = $this->fetchObject();
-
-               return $this->currentRow;
-       }
-
-       /**
-        * @return bool
-        */
-       function valid() {
-               return $this->current() !== false;
-       }
-}
-
-/**
- * Overloads the relevant methods of the real ResultsWrapper so it
- * doesn't go anywhere near an actual database.
- */
-class FakeResultWrapper extends ResultWrapper {
-       /** @var array */
-       public $result = [];
-
-       /** @var null And it's going to stay that way :D */
-       protected $db = null;
-
-       /** @var int */
-       protected $pos = 0;
-
-       /** @var array|stdClass|bool */
-       protected $currentRow = null;
-
-       /**
-        * @param array $array
-        */
-       function __construct( $array ) {
-               $this->result = $array;
-       }
-
-       /**
-        * @return int
-        */
-       function numRows() {
-               return count( $this->result );
-       }
-
-       /**
-        * @return array|bool
-        */
-       function fetchRow() {
-               if ( $this->pos < count( $this->result ) ) {
-                       $this->currentRow = $this->result[$this->pos];
-               } else {
-                       $this->currentRow = false;
-               }
-               $this->pos++;
-               if ( is_object( $this->currentRow ) ) {
-                       return get_object_vars( $this->currentRow );
-               } else {
-                       return $this->currentRow;
-               }
-       }
-
-       function seek( $row ) {
-               $this->pos = $row;
-       }
-
-       function free() {
-       }
-
-       /**
-        * Callers want to be able to access fields with $this->fieldName
-        * @return bool|stdClass
-        */
-       function fetchObject() {
-               $this->fetchRow();
-               if ( $this->currentRow ) {
-                       return (object)$this->currentRow;
-               } else {
-                       return false;
-               }
-       }
-
-       function rewind() {
-               $this->pos = 0;
-               $this->currentRow = null;
-       }
-
-       /**
-        * @return bool|stdClass
-        */
-       function next() {
-               return $this->fetchObject();
-       }
-}
-
-/**
- * Used by DatabaseBase::buildLike() to represent characters that have special
- * meaning in SQL LIKE clauses and thus need no escaping. Don't instantiate it
- * manually, use DatabaseBase::anyChar() and anyString() instead.
- */
-class LikeMatch {
-       /** @var string */
-       private $str;
-
-       /**
-        * Store a string into a LikeMatch marker object.
-        *
-        * @param string $s
-        */
-       public function __construct( $s ) {
-               $this->str = $s;
-       }
-
-       /**
-        * Return the original stored string.
-        *
-        * @return string
-        */
-       public function toString() {
-               return $this->str;
-       }
-}
-
-/**
- * An object representing a master or slave position in a replicated setup.
- *
- * The implementation details of this opaque type are up to the database subclass.
- */
-interface DBMasterPos {
-       /**
-        * @return float UNIX timestamp
-        * @since 1.25
-        */
-       public function asOfTime();
-
-       /**
-        * @param DBMasterPos $pos
-        * @return bool Whether this position is at or higher than $pos
-        * @since 1.27
-        */
-       public function hasReached( DBMasterPos $pos );
-
-       /**
-        * @param DBMasterPos $pos
-        * @return bool Whether this position appears to be for the same channel as another
-        * @since 1.27
-        */
-       public function channelsMatch( DBMasterPos $pos );
-
-       /**
-        * @return string
-        * @since 1.27
-        */
-       public function __toString();
-}
diff --git a/includes/db/IDatabase.php b/includes/db/IDatabase.php
deleted file mode 100644 (file)
index 1aa931e..0000000
+++ /dev/null
@@ -1,1676 +0,0 @@
-<?php
-
-/**
- * @defgroup Database Database
- *
- * This file deals with database interface functions
- * and query specifics/optimisations.
- *
- * 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 Database
- */
-
-/**
- * Basic database interface for live and lazy-loaded DB handles
- *
- * @todo: loosen up DB classes from MWException
- * @note: IDatabase and DBConnRef should be updated to reflect any changes
- * @ingroup Database
- */
-interface IDatabase {
-       /** @var int Callback triggered immediately due to no active transaction */
-       const TRIGGER_IDLE = 1;
-       /** @var int Callback triggered by commit */
-       const TRIGGER_COMMIT = 2;
-       /** @var int Callback triggered by rollback */
-       const TRIGGER_ROLLBACK = 3;
-
-       /** @var string Transaction is requested by regular caller outside of the DB layer */
-       const TRANSACTION_EXPLICIT = '';
-       /** @var string Transaction is requested interally via DBO_TRX/startAtomic() */
-       const TRANSACTION_INTERNAL = 'implicit';
-
-       /** @var string Transaction operation comes from service managing all DBs */
-       const FLUSHING_ALL_PEERS = 'flush';
-       /** @var string Transaction operation comes from the database class internally */
-       const FLUSHING_INTERNAL = 'flush';
-
-       /** @var string No not remember the prior flags */
-       const REMEMBER_NOTHING = '';
-       /** @var string Remember the prior flags */
-       const REMEMBER_PRIOR = 'remember';
-       /** @var string Restore to the prior flag state */
-       const RESTORE_PRIOR = 'prior';
-       /** @var string Restore to the initial flag state */
-       const RESTORE_INITIAL = 'initial';
-
-       /**
-        * A string describing the current software version, and possibly
-        * other details in a user-friendly way. Will be listed on Special:Version, etc.
-        * Use getServerVersion() to get machine-friendly information.
-        *
-        * @return string Version information from the database server
-        */
-       public function getServerInfo();
-
-       /**
-        * Turns buffering of SQL result sets on (true) or off (false). Default is
-        * "on".
-        *
-        * Unbuffered queries are very troublesome in MySQL:
-        *
-        *   - If another query is executed while the first query is being read
-        *     out, the first query is killed. This means you can't call normal
-        *     MediaWiki functions while you are reading an unbuffered query result
-        *     from a normal wfGetDB() connection.
-        *
-        *   - Unbuffered queries cause the MySQL server to use large amounts of
-        *     memory and to hold broad locks which block other queries.
-        *
-        * If you want to limit client-side memory, it's almost always better to
-        * split up queries into batches using a LIMIT clause than to switch off
-        * buffering.
-        *
-        * @param null|bool $buffer
-        * @return null|bool The previous value of the flag
-        */
-       public function bufferResults( $buffer = null );
-
-       /**
-        * Gets the current transaction level.
-        *
-        * Historically, transactions were allowed to be "nested". This is no
-        * longer supported, so this function really only returns a boolean.
-        *
-        * @return int The previous value
-        */
-       public function trxLevel();
-
-       /**
-        * Get the UNIX timestamp of the time that the transaction was established
-        *
-        * This can be used to reason about the staleness of SELECT data
-        * in REPEATABLE-READ transaction isolation level.
-        *
-        * @return float|null Returns null if there is not active transaction
-        * @since 1.25
-        */
-       public function trxTimestamp();
-
-       /**
-        * @return bool Whether an explicit transaction or atomic sections are still open
-        * @since 1.28
-        */
-       public function explicitTrxActive();
-
-       /**
-        * Get/set the table prefix.
-        * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
-        * @return string The previous table prefix.
-        */
-       public function tablePrefix( $prefix = null );
-
-       /**
-        * Get/set the db schema.
-        * @param string $schema The database schema to set, or omitted to leave it unchanged.
-        * @return string The previous db schema.
-        */
-       public function dbSchema( $schema = null );
-
-       /**
-        * Get properties passed down from the server info array of the load
-        * balancer.
-        *
-        * @param string $name The entry of the info array to get, or null to get the
-        *   whole array
-        *
-        * @return array|mixed|null
-        */
-       public function getLBInfo( $name = null );
-
-       /**
-        * Set the LB info array, or a member of it. If called with one parameter,
-        * the LB info array is set to that parameter. If it is called with two
-        * parameters, the member with the given name is set to the given value.
-        *
-        * @param string $name
-        * @param array $value
-        */
-       public function setLBInfo( $name, $value = null );
-
-       /**
-        * Returns true if this database does an implicit sort when doing GROUP BY
-        *
-        * @return bool
-        */
-       public function implicitGroupby();
-
-       /**
-        * Returns true if this database does an implicit order by when the column has an index
-        * For example: SELECT page_title FROM page LIMIT 1
-        *
-        * @return bool
-        */
-       public function implicitOrderby();
-
-       /**
-        * Return the last query that went through IDatabase::query()
-        * @return string
-        */
-       public function lastQuery();
-
-       /**
-        * Returns true if the connection may have been used for write queries.
-        * Should return true if unsure.
-        *
-        * @return bool
-        */
-       public function doneWrites();
-
-       /**
-        * Returns the last time the connection may have been used for write queries.
-        * Should return a timestamp if unsure.
-        *
-        * @return int|float UNIX timestamp or false
-        * @since 1.24
-        */
-       public function lastDoneWrites();
-
-       /**
-        * @return bool Whether there is a transaction open with possible write queries
-        * @since 1.27
-        */
-       public function writesPending();
-
-       /**
-        * Returns true if there is a transaction open with possible write
-        * queries or transaction pre-commit/idle callbacks waiting on it to finish.
-        *
-        * @return bool
-        */
-       public function writesOrCallbacksPending();
-
-       /**
-        * Get the time spend running write queries for this transaction
-        *
-        * High times could be due to scanning, updates, locking, and such
-        *
-        * @return float|bool Returns false if not transaction is active
-        * @since 1.26
-        */
-       public function pendingWriteQueryDuration();
-
-       /**
-        * Get the list of method names that did write queries for this transaction
-        *
-        * @return array
-        * @since 1.27
-        */
-       public function pendingWriteCallers();
-
-       /**
-        * Is a connection to the database open?
-        * @return bool
-        */
-       public function isOpen();
-
-       /**
-        * Set a flag for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
-        *       and removes it in command line mode
-        *   - DBO_PERSISTENT: use persistant database connection
-        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
-        */
-       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING );
-
-       /**
-        * Clear a flag for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
-        *       and removes it in command line mode
-        *   - DBO_PERSISTENT: use persistant database connection
-        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
-        */
-       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING );
-
-       /**
-        * Restore the flags to their prior state before the last setFlag/clearFlag call
-        *
-        * @param string $state IDatabase::RESTORE_* constant. [default: RESTORE_PRIOR]
-        * @since 1.28
-        */
-       public function restoreFlags( $state = self::RESTORE_PRIOR );
-
-       /**
-        * Returns a boolean whether the flag $flag is set for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - DBO_PERSISTENT: use persistant database connection
-        * @return bool
-        */
-       public function getFlag( $flag );
-
-       /**
-        * General read-only accessor
-        *
-        * @param string $name
-        * @return string
-        */
-       public function getProperty( $name );
-
-       /**
-        * @return string
-        */
-       public function getWikiID();
-
-       /**
-        * Get the type of the DBMS, as it appears in $wgDBtype.
-        *
-        * @return string
-        */
-       public function getType();
-
-       /**
-        * Open a connection to the database. Usually aborts on failure
-        *
-        * @param string $server Database server host
-        * @param string $user Database user name
-        * @param string $password Database user password
-        * @param string $dbName Database name
-        * @return bool
-        * @throws DBConnectionError
-        */
-       public function open( $server, $user, $password, $dbName );
-
-       /**
-        * Fetch the next row from the given result object, in object form.
-        * Fields can be retrieved with $row->fieldname, with fields acting like
-        * member variables.
-        * If no more rows are available, false is returned.
-        *
-        * @param ResultWrapper|stdClass $res Object as returned from IDatabase::query(), etc.
-        * @return stdClass|bool
-        * @throws DBUnexpectedError Thrown if the database returns an error
-        */
-       public function fetchObject( $res );
-
-       /**
-        * Fetch the next row from the given result object, in associative array
-        * form. Fields are retrieved with $row['fieldname'].
-        * If no more rows are available, false is returned.
-        *
-        * @param ResultWrapper $res Result object as returned from IDatabase::query(), etc.
-        * @return array|bool
-        * @throws DBUnexpectedError Thrown if the database returns an error
-        */
-       public function fetchRow( $res );
-
-       /**
-        * Get the number of rows in a result object
-        *
-        * @param mixed $res A SQL result
-        * @return int
-        */
-       public function numRows( $res );
-
-       /**
-        * Get the number of fields in a result object
-        * @see http://www.php.net/mysql_num_fields
-        *
-        * @param mixed $res A SQL result
-        * @return int
-        */
-       public function numFields( $res );
-
-       /**
-        * Get a field name in a result object
-        * @see http://www.php.net/mysql_field_name
-        *
-        * @param mixed $res A SQL result
-        * @param int $n
-        * @return string
-        */
-       public function fieldName( $res, $n );
-
-       /**
-        * Get the inserted value of an auto-increment row
-        *
-        * The value inserted should be fetched from nextSequenceValue()
-        *
-        * Example:
-        * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
-        * $dbw->insert( 'page', [ 'page_id' => $id ] );
-        * $id = $dbw->insertId();
-        *
-        * @return int
-        */
-       public function insertId();
-
-       /**
-        * Change the position of the cursor in a result object
-        * @see http://www.php.net/mysql_data_seek
-        *
-        * @param mixed $res A SQL result
-        * @param int $row
-        */
-       public function dataSeek( $res, $row );
-
-       /**
-        * Get the last error number
-        * @see http://www.php.net/mysql_errno
-        *
-        * @return int
-        */
-       public function lastErrno();
-
-       /**
-        * Get a description of the last error
-        * @see http://www.php.net/mysql_error
-        *
-        * @return string
-        */
-       public function lastError();
-
-       /**
-        * mysql_fetch_field() wrapper
-        * Returns false if the field doesn't exist
-        *
-        * @param string $table Table name
-        * @param string $field Field name
-        *
-        * @return Field
-        */
-       public function fieldInfo( $table, $field );
-
-       /**
-        * Get the number of rows affected by the last write query
-        * @see http://www.php.net/mysql_affected_rows
-        *
-        * @return int
-        */
-       public function affectedRows();
-
-       /**
-        * Returns a wikitext link to the DB's website, e.g.,
-        *   return "[http://www.mysql.com/ MySQL]";
-        * Should at least contain plain text, if for some reason
-        * your database has no website.
-        *
-        * @return string Wikitext of a link to the server software's web site
-        */
-       public function getSoftwareLink();
-
-       /**
-        * A string describing the current software version, like from
-        * mysql_get_server_info().
-        *
-        * @return string Version information from the database server.
-        */
-       public function getServerVersion();
-
-       /**
-        * Closes a database connection.
-        * if it is open : commits any open transactions
-        *
-        * @throws MWException
-        * @return bool Operation success. true if already closed.
-        */
-       public function close();
-
-       /**
-        * @param string $error Fallback error message, used if none is given by DB
-        * @throws DBConnectionError
-        */
-       public function reportConnectionError( $error = 'Unknown error' );
-
-       /**
-        * Run an SQL query and return the result. Normally throws a DBQueryError
-        * on failure. If errors are ignored, returns false instead.
-        *
-        * In new code, the query wrappers select(), insert(), update(), delete(),
-        * etc. should be used where possible, since they give much better DBMS
-        * independence and automatically quote or validate user input in a variety
-        * of contexts. This function is generally only useful for queries which are
-        * explicitly DBMS-dependent and are unsupported by the query wrappers, such
-        * as CREATE TABLE.
-        *
-        * However, the query wrappers themselves should call this function.
-        *
-        * @param string $sql SQL query
-        * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST
-        *     comment (you can use __METHOD__ or add some extra info)
-        * @param bool $tempIgnore Whether to avoid throwing an exception on errors...
-        *     maybe best to catch the exception instead?
-        * @throws MWException
-        * @return bool|ResultWrapper True for a successful write query, ResultWrapper object
-        *     for a successful read query, or false on failure if $tempIgnore set
-        */
-       public function query( $sql, $fname = __METHOD__, $tempIgnore = false );
-
-       /**
-        * Report a query error. Log the error, and if neither the object ignore
-        * flag nor the $tempIgnore flag is set, throw a DBQueryError.
-        *
-        * @param string $error
-        * @param int $errno
-        * @param string $sql
-        * @param string $fname
-        * @param bool $tempIgnore
-        * @throws DBQueryError
-        */
-       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false );
-
-       /**
-        * Free a result object returned by query() or select(). It's usually not
-        * necessary to call this, just use unset() or let the variable holding
-        * the result object go out of scope.
-        *
-        * @param mixed $res A SQL result
-        */
-       public function freeResult( $res );
-
-       /**
-        * A SELECT wrapper which returns a single field from a single result row.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly
-        * ignored, returns false on failure.
-        *
-        * If no result rows are returned from the query, false is returned.
-        *
-        * @param string|array $table Table name. See IDatabase::select() for details.
-        * @param string $var The field name to select. This must be a valid SQL
-        *   fragment: do not use unvalidated user input.
-        * @param string|array $cond The condition array. See IDatabase::select() for details.
-        * @param string $fname The function name of the caller.
-        * @param string|array $options The query options. See IDatabase::select() for details.
-        *
-        * @return bool|mixed The value from the field, or false on failure.
-        */
-       public function selectField(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = []
-       );
-
-       /**
-        * A SELECT wrapper which returns a list of single field values from result rows.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly
-        * ignored, returns false on failure.
-        *
-        * If no result rows are returned from the query, false is returned.
-        *
-        * @param string|array $table Table name. See IDatabase::select() for details.
-        * @param string $var The field name to select. This must be a valid SQL
-        *   fragment: do not use unvalidated user input.
-        * @param string|array $cond The condition array. See IDatabase::select() for details.
-        * @param string $fname The function name of the caller.
-        * @param string|array $options The query options. See IDatabase::select() for details.
-        *
-        * @return bool|array The values from the field, or false on failure
-        * @since 1.25
-        */
-       public function selectFieldValues(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = []
-       );
-
-       /**
-        * Execute a SELECT query constructed using the various parameters provided.
-        * See below for full details of the parameters.
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param string|array $conds Conditions
-        * @param string $fname Caller function name
-        * @param array $options Query options
-        * @param array $join_conds Join conditions
-        *
-        *
-        * @param string|array $table
-        *
-        * May be either an array of table names, or a single string holding a table
-        * name. If an array is given, table aliases can be specified, for example:
-        *
-        *    [ 'a' => 'user' ]
-        *
-        * This includes the user table in the query, with the alias "a" available
-        * for use in field names (e.g. a.user_name).
-        *
-        * All of the table names given here are automatically run through
-        * DatabaseBase::tableName(), which causes the table prefix (if any) to be
-        * added, and various other table name mappings to be performed.
-        *
-        * Do not use untrusted user input as a table name. Alias names should
-        * not have characters outside of the Basic multilingual plane.
-        *
-        * @param string|array $vars
-        *
-        * May be either a field name or an array of field names. The field names
-        * can be complete fragments of SQL, for direct inclusion into the SELECT
-        * query. If an array is given, field aliases can be specified, for example:
-        *
-        *   [ 'maxrev' => 'MAX(rev_id)' ]
-        *
-        * This includes an expression with the alias "maxrev" in the query.
-        *
-        * If an expression is given, care must be taken to ensure that it is
-        * DBMS-independent.
-        *
-        * Untrusted user input must not be passed to this parameter.
-        *
-        * @param string|array $conds
-        *
-        * May be either a string containing a single condition, or an array of
-        * conditions. If an array is given, the conditions constructed from each
-        * element are combined with AND.
-        *
-        * Array elements may take one of two forms:
-        *
-        *   - Elements with a numeric key are interpreted as raw SQL fragments.
-        *   - Elements with a string key are interpreted as equality conditions,
-        *     where the key is the field name.
-        *     - If the value of such an array element is a scalar (such as a
-        *       string), it will be treated as data and thus quoted appropriately.
-        *       If it is null, an IS NULL clause will be added.
-        *     - If the value is an array, an IN (...) clause will be constructed
-        *       from its non-null elements, and an IS NULL clause will be added
-        *       if null is present, such that the field may match any of the
-        *       elements in the array. The non-null elements will be quoted.
-        *
-        * Note that expressions are often DBMS-dependent in their syntax.
-        * DBMS-independent wrappers are provided for constructing several types of
-        * expression commonly used in condition queries. See:
-        *    - IDatabase::buildLike()
-        *    - IDatabase::conditional()
-        *
-        * Untrusted user input is safe in the values of string keys, however untrusted
-        * input must not be used in the array key names or in the values of numeric keys.
-        * Escaping of untrusted input used in values of numeric keys should be done via
-        * IDatabase::addQuotes()
-        *
-        * @param string|array $options
-        *
-        * Optional: Array of query options. Boolean options are specified by
-        * including them in the array as a string value with a numeric key, for
-        * example:
-        *
-        *    [ 'FOR UPDATE' ]
-        *
-        * The supported options are:
-        *
-        *   - OFFSET: Skip this many rows at the start of the result set. OFFSET
-        *     with LIMIT can theoretically be used for paging through a result set,
-        *     but this is discouraged in MediaWiki for performance reasons.
-        *
-        *   - LIMIT: Integer: return at most this many rows. The rows are sorted
-        *     and then the first rows are taken until the limit is reached. LIMIT
-        *     is applied to a result set after OFFSET.
-        *
-        *   - FOR UPDATE: Boolean: lock the returned rows so that they can't be
-        *     changed until the next COMMIT.
-        *
-        *   - DISTINCT: Boolean: return only unique result rows.
-        *
-        *   - GROUP BY: May be either an SQL fragment string naming a field or
-        *     expression to group by, or an array of such SQL fragments.
-        *
-        *   - HAVING: May be either an string containing a HAVING clause or an array of
-        *     conditions building the HAVING clause. If an array is given, the conditions
-        *     constructed from each element are combined with AND.
-        *
-        *   - ORDER BY: May be either an SQL fragment giving a field name or
-        *     expression to order by, or an array of such SQL fragments.
-        *
-        *   - USE INDEX: This may be either a string giving the index name to use
-        *     for the query, or an array. If it is an associative array, each key
-        *     gives the table name (or alias), each value gives the index name to
-        *     use for that table. All strings are SQL fragments and so should be
-        *     validated by the caller.
-        *
-        *   - EXPLAIN: In MySQL, this causes an EXPLAIN SELECT query to be run,
-        *     instead of SELECT.
-        *
-        * And also the following boolean MySQL extensions, see the MySQL manual
-        * for documentation:
-        *
-        *    - LOCK IN SHARE MODE
-        *    - STRAIGHT_JOIN
-        *    - HIGH_PRIORITY
-        *    - SQL_BIG_RESULT
-        *    - SQL_BUFFER_RESULT
-        *    - SQL_SMALL_RESULT
-        *    - SQL_CALC_FOUND_ROWS
-        *    - SQL_CACHE
-        *    - SQL_NO_CACHE
-        *
-        *
-        * @param string|array $join_conds
-        *
-        * Optional associative array of table-specific join conditions. In the
-        * most common case, this is unnecessary, since the join condition can be
-        * in $conds. However, it is useful for doing a LEFT JOIN.
-        *
-        * The key of the array contains the table name or alias. The value is an
-        * array with two elements, numbered 0 and 1. The first gives the type of
-        * join, the second is the same as the $conds parameter. Thus it can be
-        * an SQL fragment, or an array where the string keys are equality and the
-        * numeric keys are SQL fragments all AND'd together. For example:
-        *
-        *    [ 'page' => [ 'LEFT JOIN', 'page_latest=rev_id' ] ]
-        *
-        * @return ResultWrapper|bool If the query returned no rows, a ResultWrapper
-        *   with no rows in it will be returned. If there was a query error, a
-        *   DBQueryError exception will be thrown, except if the "ignore errors"
-        *   option was set, in which case false will be returned.
-        */
-       public function select(
-               $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = []
-       );
-
-       /**
-        * The equivalent of IDatabase::select() except that the constructed SQL
-        * is returned, instead of being immediately executed. This can be useful for
-        * doing UNION queries, where the SQL text of each query is needed. In general,
-        * however, callers outside of Database classes should just use select().
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param string|array $conds Conditions
-        * @param string $fname Caller function name
-        * @param string|array $options Query options
-        * @param string|array $join_conds Join conditions
-        *
-        * @return string SQL query string.
-        * @see IDatabase::select()
-        */
-       public function selectSQLText(
-               $table, $vars, $conds = '', $fname = __METHOD__,
-               $options = [], $join_conds = []
-       );
-
-       /**
-        * Single row SELECT wrapper. Equivalent to IDatabase::select(), except
-        * that a single row object is returned. If the query returns no rows,
-        * false is returned.
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param array $conds Conditions
-        * @param string $fname Caller function name
-        * @param string|array $options Query options
-        * @param array|string $join_conds Join conditions
-        *
-        * @return stdClass|bool
-        */
-       public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
-               $options = [], $join_conds = []
-       );
-
-       /**
-        * Estimate the number of rows in dataset
-        *
-        * MySQL allows you to estimate the number of rows that would be returned
-        * by a SELECT query, using EXPLAIN SELECT. The estimate is provided using
-        * index cardinality statistics, and is notoriously inaccurate, especially
-        * when large numbers of rows have recently been added or deleted.
-        *
-        * For DBMSs that don't support fast result size estimation, this function
-        * will actually perform the SELECT COUNT(*).
-        *
-        * Takes the same arguments as IDatabase::select().
-        *
-        * @param string $table Table name
-        * @param string $vars Unused
-        * @param array|string $conds Filters on the table
-        * @param string $fname Function name for profiling
-        * @param array $options Options for select
-        * @return int Row count
-        */
-       public function estimateRowCount(
-               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
-       );
-
-       /**
-        * Get the number of rows in dataset
-        *
-        * This is useful when trying to do COUNT(*) but with a LIMIT for performance.
-        *
-        * Takes the same arguments as IDatabase::select().
-        *
-        * @since 1.27 Added $join_conds parameter
-        *
-        * @param array|string $tables Table names
-        * @param string $vars Unused
-        * @param array|string $conds Filters on the table
-        * @param string $fname Function name for profiling
-        * @param array $options Options for select
-        * @param array $join_conds Join conditions (since 1.27)
-        * @return int Row count
-        */
-       public function selectRowCount(
-               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
-       );
-
-       /**
-        * Determines whether a field exists in a table
-        *
-        * @param string $table Table name
-        * @param string $field Filed to check on that table
-        * @param string $fname Calling function name (optional)
-        * @return bool Whether $table has filed $field
-        */
-       public function fieldExists( $table, $field, $fname = __METHOD__ );
-
-       /**
-        * Determines whether an index exists
-        * Usually throws a DBQueryError on failure
-        * If errors are explicitly ignored, returns NULL on failure
-        *
-        * @param string $table
-        * @param string $index
-        * @param string $fname
-        * @return bool|null
-        */
-       public function indexExists( $table, $index, $fname = __METHOD__ );
-
-       /**
-        * Query whether a given table exists
-        *
-        * @param string $table
-        * @param string $fname
-        * @return bool
-        */
-       public function tableExists( $table, $fname = __METHOD__ );
-
-       /**
-        * Determines if a given index is unique
-        *
-        * @param string $table
-        * @param string $index
-        *
-        * @return bool
-        */
-       public function indexUnique( $table, $index );
-
-       /**
-        * INSERT wrapper, inserts an array into a table.
-        *
-        * $a may be either:
-        *
-        *   - A single associative array. The array keys are the field names, and
-        *     the values are the values to insert. The values are treated as data
-        *     and will be quoted appropriately. If NULL is inserted, this will be
-        *     converted to a database NULL.
-        *   - An array with numeric keys, holding a list of associative arrays.
-        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
-        *     each subarray must be identical to each other, and in the same order.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
-        * returns success.
-        *
-        * $options is an array of options, with boolean options encoded as values
-        * with numeric keys, in the same style as $options in
-        * IDatabase::select(). Supported options are:
-        *
-        *   - IGNORE: Boolean: if present, duplicate key errors are ignored, and
-        *     any rows which cause duplicate key errors are not inserted. It's
-        *     possible to determine how many rows were successfully inserted using
-        *     IDatabase::affectedRows().
-        *
-        * @param string $table Table name. This will be passed through
-        *   DatabaseBase::tableName().
-        * @param array $a Array of rows to insert
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @param array $options Array of options
-        *
-        * @return bool
-        */
-       public function insert( $table, $a, $fname = __METHOD__, $options = [] );
-
-       /**
-        * UPDATE wrapper. Takes a condition array and a SET array.
-        *
-        * @param string $table Name of the table to UPDATE. This will be passed through
-        *   DatabaseBase::tableName().
-        * @param array $values An array of values to SET. For each array element,
-        *   the key gives the field name, and the value gives the data to set
-        *   that field to. The data will be quoted by IDatabase::addQuotes().
-        * @param array $conds An array of conditions (WHERE). See
-        *   IDatabase::select() for the details of the format of condition
-        *   arrays. Use '*' to update all rows.
-        * @param string $fname The function name of the caller (from __METHOD__),
-        *   for logging and profiling.
-        * @param array $options An array of UPDATE options, can be:
-        *   - IGNORE: Ignore unique key conflicts
-        *   - LOW_PRIORITY: MySQL-specific, see MySQL manual.
-        * @return bool
-        */
-       public function update( $table, $values, $conds, $fname = __METHOD__, $options = [] );
-
-       /**
-        * Makes an encoded list of strings from an array
-        *
-        * @param array $a Containing the data
-        * @param int $mode Constant
-        *    - LIST_COMMA: Comma separated, no field names
-        *    - LIST_AND:   ANDed WHERE clause (without the WHERE). See the
-        *      documentation for $conds in IDatabase::select().
-        *    - LIST_OR:    ORed WHERE clause (without the WHERE)
-        *    - LIST_SET:   Comma separated with field names, like a SET clause
-        *    - LIST_NAMES: Comma separated field names
-        * @throws MWException|DBUnexpectedError
-        * @return string
-        */
-       public function makeList( $a, $mode = LIST_COMMA );
-
-       /**
-        * Build a partial where clause from a 2-d array such as used for LinkBatch.
-        * The keys on each level may be either integers or strings.
-        *
-        * @param array $data Organized as 2-d
-        *    [ baseKeyVal => [ subKeyVal => [ignored], ... ], ... ]
-        * @param string $baseKey Field name to match the base-level keys to (eg 'pl_namespace')
-        * @param string $subKey Field name to match the sub-level keys to (eg 'pl_title')
-        * @return string|bool SQL fragment, or false if no items in array
-        */
-       public function makeWhereFrom2d( $data, $baseKey, $subKey );
-
-       /**
-        * @param string $field
-        * @return string
-        */
-       public function bitNot( $field );
-
-       /**
-        * @param string $fieldLeft
-        * @param string $fieldRight
-        * @return string
-        */
-       public function bitAnd( $fieldLeft, $fieldRight );
-
-       /**
-        * @param string $fieldLeft
-        * @param string $fieldRight
-        * @return string
-        */
-       public function bitOr( $fieldLeft, $fieldRight );
-
-       /**
-        * Build a concatenation list to feed into a SQL query
-        * @param array $stringList List of raw SQL expressions; caller is
-        *   responsible for any quoting
-        * @return string
-        */
-       public function buildConcat( $stringList );
-
-       /**
-        * Build a GROUP_CONCAT or equivalent statement for a query.
-        *
-        * This is useful for combining a field for several rows into a single string.
-        * NULL values will not appear in the output, duplicated values will appear,
-        * and the resulting delimiter-separated values have no defined sort order.
-        * Code using the results may need to use the PHP unique() or sort() methods.
-        *
-        * @param string $delim Glue to bind the results together
-        * @param string|array $table Table name
-        * @param string $field Field name
-        * @param string|array $conds Conditions
-        * @param string|array $join_conds Join conditions
-        * @return string SQL text
-        * @since 1.23
-        */
-       public function buildGroupConcatField(
-               $delim, $table, $field, $conds = '', $join_conds = []
-       );
-
-       /**
-        * Change the current database
-        *
-        * @param string $db
-        * @return bool Success or failure
-        */
-       public function selectDB( $db );
-
-       /**
-        * Get the current DB name
-        * @return string
-        */
-       public function getDBname();
-
-       /**
-        * Get the server hostname or IP address
-        * @return string
-        */
-       public function getServer();
-
-       /**
-        * Adds quotes and backslashes.
-        *
-        * @param string|Blob $s
-        * @return string
-        */
-       public function addQuotes( $s );
-
-       /**
-        * LIKE statement wrapper, receives a variable-length argument list with
-        * parts of pattern to match containing either string literals that will be
-        * escaped or tokens returned by anyChar() or anyString(). Alternatively,
-        * the function could be provided with an array of aforementioned
-        * parameters.
-        *
-        * Example: $dbr->buildLike( 'My_page_title/', $dbr->anyString() ) returns
-        * a LIKE clause that searches for subpages of 'My page title'.
-        * Alternatively:
-        *   $pattern = [ 'My_page_title/', $dbr->anyString() ];
-        *   $query .= $dbr->buildLike( $pattern );
-        *
-        * @since 1.16
-        * @return string Fully built LIKE statement
-        */
-       public function buildLike();
-
-       /**
-        * Returns a token for buildLike() that denotes a '_' to be used in a LIKE query
-        *
-        * @return LikeMatch
-        */
-       public function anyChar();
-
-       /**
-        * Returns a token for buildLike() that denotes a '%' to be used in a LIKE query
-        *
-        * @return LikeMatch
-        */
-       public function anyString();
-
-       /**
-        * Returns an appropriately quoted sequence value for inserting a new row.
-        * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
-        * subclass will return an integer, and save the value for insertId()
-        *
-        * Any implementation of this function should *not* involve reusing
-        * sequence numbers created for rolled-back transactions.
-        * See http://bugs.mysql.com/bug.php?id=30767 for details.
-        * @param string $seqName
-        * @return null|int
-        */
-       public function nextSequenceValue( $seqName );
-
-       /**
-        * REPLACE query wrapper.
-        *
-        * REPLACE is a very handy MySQL extension, which functions like an INSERT
-        * except that when there is a duplicate key error, the old row is deleted
-        * and the new row is inserted in its place.
-        *
-        * We simulate this with standard SQL with a DELETE followed by INSERT. To
-        * perform the delete, we need to know what the unique indexes are so that
-        * we know how to find the conflicting rows.
-        *
-        * It may be more efficient to leave off unique indexes which are unlikely
-        * to collide. However if you do this, you run the risk of encountering
-        * errors which wouldn't have occurred in MySQL.
-        *
-        * @param string $table The table to replace the row(s) in.
-        * @param array $uniqueIndexes Is an array of indexes. Each element may be either
-        *    a field name or an array of field names
-        * @param array $rows Can be either a single row to insert, or multiple rows,
-        *    in the same format as for IDatabase::insert()
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        */
-       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ );
-
-       /**
-        * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
-        *
-        * This updates any conflicting rows (according to the unique indexes) using
-        * the provided SET clause and inserts any remaining (non-conflicted) rows.
-        *
-        * $rows may be either:
-        *   - A single associative array. The array keys are the field names, and
-        *     the values are the values to insert. The values are treated as data
-        *     and will be quoted appropriately. If NULL is inserted, this will be
-        *     converted to a database NULL.
-        *   - An array with numeric keys, holding a list of associative arrays.
-        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
-        *     each subarray must be identical to each other, and in the same order.
-        *
-        * It may be more efficient to leave off unique indexes which are unlikely
-        * to collide. However if you do this, you run the risk of encountering
-        * errors which wouldn't have occurred in MySQL.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
-        * returns success.
-        *
-        * @since 1.22
-        *
-        * @param string $table Table name. This will be passed through DatabaseBase::tableName().
-        * @param array $rows A single row or list of rows to insert
-        * @param array $uniqueIndexes List of single field names or field name tuples
-        * @param array $set An array of values to SET. For each array element, the
-        *   key gives the field name, and the value gives the data to set that
-        *   field to. The data will be quoted by IDatabase::addQuotes().
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @throws Exception
-        * @return bool
-        */
-       public function upsert(
-               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
-       );
-
-       /**
-        * DELETE where the condition is a join.
-        *
-        * MySQL overrides this to use a multi-table DELETE syntax, in other databases
-        * we use sub-selects
-        *
-        * For safety, an empty $conds will not delete everything. If you want to
-        * delete all rows where the join condition matches, set $conds='*'.
-        *
-        * DO NOT put the join condition in $conds.
-        *
-        * @param string $delTable The table to delete from.
-        * @param string $joinTable The other table.
-        * @param string $delVar The variable to join on, in the first table.
-        * @param string $joinVar The variable to join on, in the second table.
-        * @param array $conds Condition array of field names mapped to variables,
-        *   ANDed together in the WHERE clause
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @throws DBUnexpectedError
-        */
-       public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
-               $fname = __METHOD__
-       );
-
-       /**
-        * DELETE query wrapper.
-        *
-        * @param array $table Table name
-        * @param string|array $conds Array of conditions. See $conds in IDatabase::select()
-        *   for the format. Use $conds == "*" to delete all rows
-        * @param string $fname Name of the calling function
-        * @throws DBUnexpectedError
-        * @return bool|ResultWrapper
-        */
-       public function delete( $table, $conds, $fname = __METHOD__ );
-
-       /**
-        * INSERT SELECT wrapper. Takes data from a SELECT query and inserts it
-        * into another table.
-        *
-        * @param string $destTable The table name to insert into
-        * @param string|array $srcTable May be either a table name, or an array of table names
-        *    to include in a join.
-        *
-        * @param array $varMap Must be an associative array of the form
-        *    [ 'dest1' => 'source1', ... ]. Source items may be literals
-        *    rather than field names, but strings should be quoted with
-        *    IDatabase::addQuotes()
-        *
-        * @param array $conds Condition array. See $conds in IDatabase::select() for
-        *    the details of the format of condition arrays. May be "*" to copy the
-        *    whole table.
-        *
-        * @param string $fname The function name of the caller, from __METHOD__
-        *
-        * @param array $insertOptions Options for the INSERT part of the query, see
-        *    IDatabase::insert() for details.
-        * @param array $selectOptions Options for the SELECT part of the query, see
-        *    IDatabase::select() for details.
-        *
-        * @return ResultWrapper
-        */
-       public function insertSelect( $destTable, $srcTable, $varMap, $conds,
-               $fname = __METHOD__,
-               $insertOptions = [], $selectOptions = []
-       );
-
-       /**
-        * Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries
-        * within the UNION construct.
-        * @return bool
-        */
-       public function unionSupportsOrderAndLimit();
-
-       /**
-        * Construct a UNION query
-        * This is used for providing overload point for other DB abstractions
-        * not compatible with the MySQL syntax.
-        * @param array $sqls SQL statements to combine
-        * @param bool $all Use UNION ALL
-        * @return string SQL fragment
-        */
-       public function unionQueries( $sqls, $all );
-
-       /**
-        * Returns an SQL expression for a simple conditional. This doesn't need
-        * to be overridden unless CASE isn't supported in your DBMS.
-        *
-        * @param string|array $cond SQL expression which will result in a boolean value
-        * @param string $trueVal SQL expression to return if true
-        * @param string $falseVal SQL expression to return if false
-        * @return string SQL fragment
-        */
-       public function conditional( $cond, $trueVal, $falseVal );
-
-       /**
-        * Returns a comand for str_replace function in SQL query.
-        * Uses REPLACE() in MySQL
-        *
-        * @param string $orig Column to modify
-        * @param string $old Column to seek
-        * @param string $new Column to replace with
-        *
-        * @return string
-        */
-       public function strreplace( $orig, $old, $new );
-
-       /**
-        * Determines how long the server has been up
-        *
-        * @return int
-        */
-       public function getServerUptime();
-
-       /**
-        * Determines if the last failure was due to a deadlock
-        *
-        * @return bool
-        */
-       public function wasDeadlock();
-
-       /**
-        * Determines if the last failure was due to a lock timeout
-        *
-        * @return bool
-        */
-       public function wasLockTimeout();
-
-       /**
-        * Determines if the last query error was due to a dropped connection and should
-        * be dealt with by pinging the connection and reissuing the query.
-        *
-        * @return bool
-        */
-       public function wasErrorReissuable();
-
-       /**
-        * Determines if the last failure was due to the database being read-only.
-        *
-        * @return bool
-        */
-       public function wasReadOnlyError();
-
-       /**
-        * Wait for the slave to catch up to a given master position
-        *
-        * @param DBMasterPos $pos
-        * @param int $timeout The maximum number of seconds to wait for synchronisation
-        * @return int|null Zero if the slave was past that position already,
-        *   greater than zero if we waited for some period of time, less than
-        *   zero if it timed out, and null on error
-        */
-       public function masterPosWait( DBMasterPos $pos, $timeout );
-
-       /**
-        * Get the replication position of this slave
-        *
-        * @return DBMasterPos|bool False if this is not a slave.
-        */
-       public function getSlavePos();
-
-       /**
-        * Get the position of this master
-        *
-        * @return DBMasterPos|bool False if this is not a master
-        */
-       public function getMasterPos();
-
-       /**
-        * @return bool Whether the DB is marked as read-only server-side
-        * @since 1.28
-        */
-       public function serverIsReadOnly();
-
-       /**
-        * Run a callback as soon as the current transaction commits or rolls back.
-        * An error is thrown if no transaction is pending. Queries in the function will run in
-        * AUTO-COMMIT mode unless there are begin() calls. Callbacks must commit any transactions
-        * that they begin.
-        *
-        * This is useful for combining cooperative locks and DB transactions.
-        *
-        * The callback takes one argument:
-        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
-        *
-        * @param callable $callback
-        * @return mixed
-        * @since 1.28
-        */
-       public function onTransactionResolution( callable $callback );
-
-       /**
-        * Run a callback as soon as there is no transaction pending.
-        * If there is a transaction and it is rolled back, then the callback is cancelled.
-        * Queries in the function will run in AUTO-COMMIT mode unless there are begin() calls.
-        * Callbacks must commit any transactions that they begin.
-        *
-        * This is useful for updates to different systems or when separate transactions are needed.
-        * For example, one might want to enqueue jobs into a system outside the database, but only
-        * after the database is updated so that the jobs will see the data when they actually run.
-        * It can also be used for updates that easily cause deadlocks if locks are held too long.
-        *
-        * Updates will execute in the order they were enqueued.
-        *
-        * The callback takes one argument:
-        * How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
-        *
-        * @param callable $callback
-        * @since 1.20
-        */
-       public function onTransactionIdle( callable $callback );
-
-       /**
-        * Run a callback before the current transaction commits or now if there is none.
-        * If there is a transaction and it is rolled back, then the callback is cancelled.
-        * Callbacks must not start nor commit any transactions. If no transaction is active,
-        * then a transaction will wrap the callback.
-        *
-        * This is useful for updates that easily cause deadlocks if locks are held too long
-        * but where atomicity is strongly desired for these updates and some related updates.
-        *
-        * Updates will execute in the order they were enqueued.
-        *
-        * @param callable $callback
-        * @since 1.22
-        */
-       public function onTransactionPreCommitOrIdle( callable $callback );
-
-       /**
-        * Begin an atomic section of statements
-        *
-        * If a transaction has been started already, just keep track of the given
-        * section name to make sure the transaction is not committed pre-maturely.
-        * This function can be used in layers (with sub-sections), so use a stack
-        * to keep track of the different atomic sections. If there is no transaction,
-        * start one implicitly.
-        *
-        * The goal of this function is to create an atomic section of SQL queries
-        * without having to start a new transaction if it already exists.
-        *
-        * Atomic sections are more strict than transactions. With transactions,
-        * attempting to begin a new transaction when one is already running results
-        * in MediaWiki issuing a brief warning and doing an implicit commit. All
-        * atomic levels *must* be explicitly closed using IDatabase::endAtomic(),
-        * and any database transactions cannot be began or committed until all atomic
-        * levels are closed. There is no such thing as implicitly opening or closing
-        * an atomic section.
-        *
-        * @since 1.23
-        * @param string $fname
-        * @throws DBError
-        */
-       public function startAtomic( $fname = __METHOD__ );
-
-       /**
-        * Ends an atomic section of SQL statements
-        *
-        * Ends the next section of atomic SQL statements and commits the transaction
-        * if necessary.
-        *
-        * @since 1.23
-        * @see IDatabase::startAtomic
-        * @param string $fname
-        * @throws DBError
-        */
-       public function endAtomic( $fname = __METHOD__ );
-
-       /**
-        * Run a callback to do an atomic set of updates for this database
-        *
-        * The $callback takes the following arguments:
-        *   - This database object
-        *   - The value of $fname
-        *
-        * If any exception occurs in the callback, then rollback() will be called and the error will
-        * be re-thrown. It may also be that the rollback itself fails with an exception before then.
-        * In any case, such errors are expected to terminate the request, without any outside caller
-        * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
-        * atomic section and uncommitted updates, which trashes the current request, requiring an
-        * error to be displayed.
-        *
-        * This can be an alternative to explicit startAtomic()/endAtomic() calls.
-        *
-        * @see DatabaseBase::startAtomic
-        * @see DatabaseBase::endAtomic
-        *
-        * @param string $fname Caller name (usually __METHOD__)
-        * @param callable $callback Callback that issues DB updates
-        * @return mixed $res Result of the callback (since 1.28)
-        * @throws DBError
-        * @throws RuntimeException
-        * @throws UnexpectedValueException
-        * @since 1.27
-        */
-       public function doAtomicSection( $fname, callable $callback );
-
-       /**
-        * Begin a transaction. If a transaction is already in progress,
-        * that transaction will be committed before the new transaction is started.
-        *
-        * Only call this from code with outer transcation scope.
-        * See https://www.mediawiki.org/wiki/Database_transactions for details.
-        * Nesting of transactions is not supported.
-        *
-        * Note that when the DBO_TRX flag is set (which is usually the case for web
-        * requests, but not for maintenance scripts), any previous database query
-        * will have started a transaction automatically.
-        *
-        * Nesting of transactions is not supported. Attempts to nest transactions
-        * will cause a warning, unless the current transaction was started
-        * automatically because of the DBO_TRX flag.
-        *
-        * @param string $fname
-        * @param string $mode A situationally valid IDatabase::TRANSACTION_* constant [optional]
-        * @throws DBError
-        */
-       public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT );
-
-       /**
-        * Commits a transaction previously started using begin().
-        * If no transaction is in progress, a warning is issued.
-        *
-        * Only call this from code with outer transcation scope.
-        * See https://www.mediawiki.org/wiki/Database_transactions for details.
-        * Nesting of transactions is not supported.
-        *
-        * @param string $fname
-        * @param string $flush Flush flag, set to situationally valid IDatabase::FLUSHING_*
-        *   constant to disable warnings about explicitly committing implicit transactions,
-        *   or calling commit when no transaction is in progress.
-        *
-        *   This will trigger an exception if there is an ongoing explicit transaction.
-        *
-        *   Only set the flush flag if you are sure that these warnings are not applicable,
-        *   and no explicit transactions are open.
-        *
-        * @throws DBUnexpectedError
-        */
-       public function commit( $fname = __METHOD__, $flush = '' );
-
-       /**
-        * Rollback a transaction previously started using begin().
-        * If no transaction is in progress, a warning is issued.
-        *
-        * Only call this from code with outer transcation scope.
-        * See https://www.mediawiki.org/wiki/Database_transactions for details.
-        * Nesting of transactions is not supported. If a serious unexpected error occurs,
-        * throwing an Exception is preferrable, using a pre-installed error handler to trigger
-        * rollback (in any case, failure to issue COMMIT will cause rollback server-side).
-        *
-        * @param string $fname
-        * @param string $flush Flush flag, set to a situationally valid IDatabase::FLUSHING_*
-        *   constant to disable warnings about calling rollback when no transaction is in
-        *   progress. This will silently break any ongoing explicit transaction. Only set the
-        *   flush flag if you are sure that it is safe to ignore these warnings in your context.
-        * @throws DBUnexpectedError
-        * @since 1.23 Added $flush parameter
-        */
-       public function rollback( $fname = __METHOD__, $flush = '' );
-
-       /**
-        * List all tables on the database
-        *
-        * @param string $prefix Only show tables with this prefix, e.g. mw_
-        * @param string $fname Calling function name
-        * @throws MWException
-        * @return array
-        */
-       public function listTables( $prefix = null, $fname = __METHOD__ );
-
-       /**
-        * Convert a timestamp in one of the formats accepted by wfTimestamp()
-        * to the format used for inserting into timestamp fields in this DBMS.
-        *
-        * The result is unquoted, and needs to be passed through addQuotes()
-        * before it can be included in raw SQL.
-        *
-        * @param string|int $ts
-        *
-        * @return string
-        */
-       public function timestamp( $ts = 0 );
-
-       /**
-        * Convert a timestamp in one of the formats accepted by wfTimestamp()
-        * to the format used for inserting into timestamp fields in this DBMS. If
-        * NULL is input, it is passed through, allowing NULL values to be inserted
-        * into timestamp fields.
-        *
-        * The result is unquoted, and needs to be passed through addQuotes()
-        * before it can be included in raw SQL.
-        *
-        * @param string|int $ts
-        *
-        * @return string
-        */
-       public function timestampOrNull( $ts = null );
-
-       /**
-        * Ping the server and try to reconnect if it there is no connection
-        *
-        * @return bool Success or failure
-        */
-       public function ping();
-
-       /**
-        * Get slave lag. Currently supported only by MySQL.
-        *
-        * Note that this function will generate a fatal error on many
-        * installations. Most callers should use LoadBalancer::safeGetLag()
-        * instead.
-        *
-        * @return int|bool Database replication lag in seconds or false on error
-        */
-       public function getLag();
-
-       /**
-        * Get the slave lag when the current transaction started
-        * or a general lag estimate if not transaction is active
-        *
-        * This is useful when transactions might use snapshot isolation
-        * (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
-        * is this lag plus transaction duration. If they don't, it is still
-        * safe to be pessimistic. In AUTO-COMMIT mode, this still gives an
-        * indication of the staleness of subsequent reads.
-        *
-        * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of BEGIN)
-        * @since 1.27
-        */
-       public function getSessionLagStatus();
-
-       /**
-        * Return the maximum number of items allowed in a list, or 0 for unlimited.
-        *
-        * @return int
-        */
-       public function maxListLen();
-
-       /**
-        * Some DBMSs have a special format for inserting into blob fields, they
-        * don't allow simple quoted strings to be inserted. To insert into such
-        * a field, pass the data through this function before passing it to
-        * IDatabase::insert().
-        *
-        * @param string $b
-        * @return string
-        */
-       public function encodeBlob( $b );
-
-       /**
-        * Some DBMSs return a special placeholder object representing blob fields
-        * in result objects. Pass the object through this function to return the
-        * original string.
-        *
-        * @param string|Blob $b
-        * @return string
-        */
-       public function decodeBlob( $b );
-
-       /**
-        * Override database's default behavior. $options include:
-        *     'connTimeout' : Set the connection timeout value in seconds.
-        *                     May be useful for very long batch queries such as
-        *                     full-wiki dumps, where a single query reads out over
-        *                     hours or days.
-        *
-        * @param array $options
-        * @return void
-        */
-       public function setSessionOptions( array $options );
-
-       /**
-        * Set variables to be used in sourceFile/sourceStream, in preference to the
-        * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
-        * all. If it's set to false, $GLOBALS will be used.
-        *
-        * @param bool|array $vars Mapping variable name to value.
-        */
-       public function setSchemaVars( $vars );
-
-       /**
-        * Check to see if a named lock is available (non-blocking)
-        *
-        * @param string $lockName Name of lock to poll
-        * @param string $method Name of method calling us
-        * @return bool
-        * @since 1.20
-        */
-       public function lockIsFree( $lockName, $method );
-
-       /**
-        * Acquire a named lock
-        *
-        * Named locks are not related to transactions
-        *
-        * @param string $lockName Name of lock to aquire
-        * @param string $method Name of the calling method
-        * @param int $timeout Acquisition timeout in seconds
-        * @return bool
-        */
-       public function lock( $lockName, $method, $timeout = 5 );
-
-       /**
-        * Release a lock
-        *
-        * Named locks are not related to transactions
-        *
-        * @param string $lockName Name of lock to release
-        * @param string $method Name of the calling method
-        *
-        * @return int Returns 1 if the lock was released, 0 if the lock was not established
-        * by this thread (in which case the lock is not released), and NULL if the named
-        * lock did not exist
-        */
-       public function unlock( $lockName, $method );
-
-       /**
-        * Acquire a named lock, flush any transaction, and return an RAII style unlocker object
-        *
-        * Only call this from outer transcation scope and when only one DB will be affected.
-        * See https://www.mediawiki.org/wiki/Database_transactions for details.
-        *
-        * This is suitiable for transactions that need to be serialized using cooperative locks,
-        * where each transaction can see each others' changes. Any transaction is flushed to clear
-        * out stale REPEATABLE-READ snapshot data. Once the returned object falls out of PHP scope,
-        * the lock will be released unless a transaction is active. If one is active, then the lock
-        * will be released when it either commits or rolls back.
-        *
-        * If the lock acquisition failed, then no transaction flush happens, and null is returned.
-        *
-        * @param string $lockKey Name of lock to release
-        * @param string $fname Name of the calling method
-        * @param int $timeout Acquisition timeout in seconds
-        * @return ScopedCallback|null
-        * @throws DBUnexpectedError
-        * @since 1.27
-        */
-       public function getScopedLockAndFlush( $lockKey, $fname, $timeout );
-
-       /**
-        * Check to see if a named lock used by lock() use blocking queues
-        *
-        * @return bool
-        * @since 1.26
-        */
-       public function namedLocksEnqueue();
-
-       /**
-        * Find out when 'infinity' is. Most DBMSes support this. This is a special
-        * keyword for timestamps in PostgreSQL, and works with CHAR(14) as well
-        * because "i" sorts after all numbers.
-        *
-        * @return string
-        */
-       public function getInfinity();
-
-       /**
-        * Encode an expiry time into the DBMS dependent format
-        *
-        * @param string $expiry Timestamp for expiry, or the 'infinity' string
-        * @return string
-        */
-       public function encodeExpiry( $expiry );
-
-       /**
-        * Decode an expiry time into a DBMS independent format
-        *
-        * @param string $expiry DB timestamp field value for expiry
-        * @param int $format TS_* constant, defaults to TS_MW
-        * @return string
-        */
-       public function decodeExpiry( $expiry, $format = TS_MW );
-
-       /**
-        * Allow or deny "big selects" for this session only. This is done by setting
-        * the sql_big_selects session variable.
-        *
-        * This is a MySQL-specific feature.
-        *
-        * @param bool|string $value True for allow, false for deny, or "default" to
-        *   restore the initial value
-        */
-       public function setBigSelects( $value = true );
-
-       /**
-        * @return bool Whether this DB is read-only
-        * @since 1.27
-        */
-       public function isReadOnly();
-}
diff --git a/includes/db/loadbalancer/LBFactory.php b/includes/db/loadbalancer/LBFactory.php
deleted file mode 100644 (file)
index f560cf1..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-<?php
-/**
- * Generator of database load balancing objects.
- *
- * 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 Database
- */
-
-use MediaWiki\MediaWikiServices;
-use MediaWiki\Services\DestructibleService;
-use Psr\Log\LoggerInterface;
-use MediaWiki\Logger\LoggerFactory;
-
-/**
- * An interface for generating database load balancers
- * @ingroup Database
- */
-abstract class LBFactory implements DestructibleService {
-       /** @var ChronologyProtector */
-       protected $chronProt;
-       /** @var TransactionProfiler */
-       protected $trxProfiler;
-       /** @var LoggerInterface */
-       protected $trxLogger;
-       /** @var BagOStuff */
-       protected $srvCache;
-       /** @var WANObjectCache */
-       protected $wanCache;
-
-       /** @var mixed */
-       protected $ticket;
-       /** @var string|bool Reason all LBs are read-only or false if not */
-       protected $readOnlyReason = false;
-
-       const SHUTDOWN_NO_CHRONPROT = 1; // don't save ChronologyProtector positions (for async code)
-
-       /**
-        * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
-        * @param array $conf
-        * @TODO: inject objects via dependency framework
-        */
-       public function __construct( array $conf ) {
-               if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
-                       $this->readOnlyReason = $conf['readOnlyReason'];
-               }
-               $this->chronProt = $this->newChronologyProtector();
-               $this->trxProfiler = Profiler::instance()->getTransactionProfiler();
-               // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
-               $cache = ObjectCache::getLocalServerInstance();
-               if ( $cache->getQoS( $cache::ATTR_EMULATION ) > $cache::QOS_EMULATION_SQL ) {
-                       $this->srvCache = $cache;
-               } else {
-                       $this->srvCache = new EmptyBagOStuff();
-               }
-               $wCache = ObjectCache::getMainWANInstance();
-               if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
-                       $this->wanCache = $wCache;
-               } else {
-                       $this->wanCache = WANObjectCache::newEmpty();
-               }
-               $this->trxLogger = LoggerFactory::getInstance( 'DBTransaction' );
-               $this->ticket = mt_rand();
-       }
-
-       /**
-        * Disables all load balancers. All connections are closed, and any attempt to
-        * open a new connection will result in a DBAccessError.
-        * @see LoadBalancer::disable()
-        */
-       public function destroy() {
-               $this->shutdown();
-               $this->forEachLBCallMethod( 'disable' );
-       }
-
-       /**
-        * Disables all access to the load balancer, will cause all database access
-        * to throw a DBAccessError
-        */
-       public static function disableBackend() {
-               MediaWikiServices::disableStorageBackend();
-       }
-
-       /**
-        * Get an LBFactory instance
-        *
-        * @deprecated since 1.27, use MediaWikiServices::getDBLoadBalancerFactory() instead.
-        *
-        * @return LBFactory
-        */
-       public static function singleton() {
-               return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
-       }
-
-       /**
-        * Returns the LBFactory class to use and the load balancer configuration.
-        *
-        * @todo instead of this, use a ServiceContainer for managing the different implementations.
-        *
-        * @param array $config (e.g. $wgLBFactoryConf)
-        * @return string Class name
-        */
-       public static function getLBFactoryClass( array $config ) {
-               // For configuration backward compatibility after removing
-               // underscores from class names in MediaWiki 1.23.
-               $bcClasses = [
-                       'LBFactory_Simple' => 'LBFactorySimple',
-                       'LBFactory_Single' => 'LBFactorySingle',
-                       'LBFactory_Multi' => 'LBFactoryMulti',
-                       'LBFactory_Fake' => 'LBFactoryFake',
-               ];
-
-               $class = $config['class'];
-
-               if ( isset( $bcClasses[$class] ) ) {
-                       $class = $bcClasses[$class];
-                       wfDeprecated(
-                               '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
-                               '1.23'
-                       );
-               }
-
-               return $class;
-       }
-
-       /**
-        * Shut down, close connections and destroy the cached instance.
-        *
-        * @deprecated since 1.27, use LBFactory::destroy()
-        */
-       public static function destroyInstance() {
-               self::singleton()->destroy();
-       }
-
-       /**
-        * Create a new load balancer object. The resulting object will be untracked,
-        * not chronology-protected, and the caller is responsible for cleaning it up.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function newMainLB( $wiki = false );
-
-       /**
-        * Get a cached (tracked) load balancer object.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function getMainLB( $wiki = false );
-
-       /**
-        * Create a new load balancer for external storage. The resulting object will be
-        * untracked, not chronology-protected, and the caller is responsible for
-        * cleaning it up.
-        *
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract protected function newExternalLB( $cluster, $wiki = false );
-
-       /**
-        * Get a cached (tracked) load balancer for external storage
-        *
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function &getExternalLB( $cluster, $wiki = false );
-
-       /**
-        * Execute a function for each tracked load balancer
-        * The callback is called with the load balancer as the first parameter,
-        * and $params passed as the subsequent parameters.
-        *
-        * @param callable $callback
-        * @param array $params
-        */
-       abstract public function forEachLB( $callback, array $params = [] );
-
-       /**
-        * Prepare all tracked load balancers for shutdown
-        * @param integer $flags Supports SHUTDOWN_* flags
-        * STUB
-        */
-       public function shutdown( $flags = 0 ) {
-       }
-
-       /**
-        * Call a method of each tracked load balancer
-        *
-        * @param string $methodName
-        * @param array $args
-        */
-       private function forEachLBCallMethod( $methodName, array $args = [] ) {
-               $this->forEachLB(
-                       function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
-                               call_user_func_array( [ $loadBalancer, $methodName ], $args );
-                       },
-                       [ $methodName, $args ]
-               );
-       }
-
-       /**
-        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
-        *
-        * The DBO_TRX setting will be reverted to the default in each of these methods:
-        *   - commitMasterChanges()
-        *   - rollbackMasterChanges()
-        *   - commitAll()
-        * This allows for custom transaction rounds from any outer transaction scope.
-        *
-        * @param string $fname
-        * @since 1.28
-        */
-       public function beginMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
-       }
-
-       /**
-        * Commit on all connections. Done for two reasons:
-        * 1. To commit changes to the masters.
-        * 2. To release the snapshot on all connections, master and slave.
-        * @param string $fname Caller name
-        * @param array $options Options map:
-        *   - maxWriteDuration: abort if more than this much time was spent in write queries
-        */
-       public function commitAll( $fname = __METHOD__, array $options = [] ) {
-               $this->commitMasterChanges( $fname, $options );
-               $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
-       }
-
-       /**
-        * Commit changes on all master connections
-        * @param string $fname Caller name
-        * @param array $options Options map:
-        *   - maxWriteDuration: abort if more than this much time was spent in write queries
-        * @throws Exception
-        */
-       public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
-               // Perform all pre-commit callbacks, aborting on failure
-               $this->forEachLBCallMethod( 'runMasterPreCommitCallbacks' );
-               // Perform all pre-commit checks, aborting on failure
-               $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
-               // Log the DBs and methods involved in multi-DB transactions
-               $this->logIfMultiDbTransaction();
-               // Actually perform the commit on all master DB connections
-               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
-               // Run all post-commit callbacks
-               /** @var Exception $e */
-               $e = null; // first callback exception
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$e ) {
-                       $ex = $lb->runMasterPostCommitCallbacks();
-                       $e = $e ?: $ex;
-               } );
-               // Commit any dangling DBO_TRX transactions from callbacks on one DB to another DB
-               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
-               // Throw any last post-commit callback error
-               if ( $e instanceof Exception ) {
-                       throw $e;
-               }
-       }
-
-       /**
-        * Rollback changes on all master connections
-        * @param string $fname Caller name
-        * @since 1.23
-        */
-       public function rollbackMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
-       }
-
-       /**
-        * Log query info if multi DB transactions are going to be committed now
-        */
-       private function logIfMultiDbTransaction() {
-               $callersByDB = [];
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
-                       $masterName = $lb->getServerName( $lb->getWriterIndex() );
-                       $callers = $lb->pendingMasterChangeCallers();
-                       if ( $callers ) {
-                               $callersByDB[$masterName] = $callers;
-                       }
-               } );
-
-               if ( count( $callersByDB ) >= 2 ) {
-                       $dbs = implode( ', ', array_keys( $callersByDB ) );
-                       $msg = "Multi-DB transaction [{$dbs}]:\n";
-                       foreach ( $callersByDB as $db => $callers ) {
-                               $msg .= "$db: " . implode( '; ', $callers ) . "\n";
-                       }
-                       $this->trxLogger->info( $msg );
-               }
-       }
-
-       /**
-        * Determine if any master connection has pending changes
-        * @return bool
-        * @since 1.23
-        */
-       public function hasMasterChanges() {
-               $ret = false;
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->hasMasterChanges();
-               } );
-
-               return $ret;
-       }
-
-       /**
-        * Detemine if any lagged slave connection was used
-        * @since 1.27
-        * @return bool
-        */
-       public function laggedSlaveUsed() {
-               $ret = false;
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->laggedSlaveUsed();
-               } );
-
-               return $ret;
-       }
-
-       /**
-        * Determine if any master connection has pending/written changes from this request
-        * @return bool
-        * @since 1.27
-        */
-       public function hasOrMadeRecentMasterChanges() {
-               $ret = false;
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->hasOrMadeRecentMasterChanges();
-               } );
-               return $ret;
-       }
-
-       /**
-        * Waits for the slave DBs to catch up to the current master position
-        *
-        * Use this when updating very large numbers of rows, as in maintenance scripts,
-        * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
-        *
-        * By default this waits on all DB clusters actually used in this request.
-        * This makes sense when lag being waiting on is caused by the code that does this check.
-        * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
-        * that were not changed since the last wait check. To forcefully wait on a specific cluster
-        * for a given wiki, use the 'wiki' parameter. To forcefully wait on an "external" cluster,
-        * use the "cluster" parameter.
-        *
-        * Never call this function after a large DB write that is *still* in a transaction.
-        * It only makes sense to call this after the possible lag inducing changes were committed.
-        *
-        * @param array $opts Optional fields that include:
-        *   - wiki : wait on the load balancer DBs that handles the given wiki
-        *   - cluster : wait on the given external load balancer DBs
-        *   - timeout : Max wait time. Default: ~60 seconds
-        *   - ifWritesSince: Only wait if writes were done since this UNIX timestamp
-        * @throws DBReplicationWaitError If a timeout or error occured waiting on a DB cluster
-        * @since 1.27
-        */
-       public function waitForReplication( array $opts = [] ) {
-               $opts += [
-                       'wiki' => false,
-                       'cluster' => false,
-                       'timeout' => 60,
-                       'ifWritesSince' => null
-               ];
-
-               // Figure out which clusters need to be checked
-               /** @var LoadBalancer[] $lbs */
-               $lbs = [];
-               if ( $opts['cluster'] !== false ) {
-                       $lbs[] = $this->getExternalLB( $opts['cluster'] );
-               } elseif ( $opts['wiki'] !== false ) {
-                       $lbs[] = $this->getMainLB( $opts['wiki'] );
-               } else {
-                       $this->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
-                               $lbs[] = $lb;
-                       } );
-                       if ( !$lbs ) {
-                               return; // nothing actually used
-                       }
-               }
-
-               // Get all the master positions of applicable DBs right now.
-               // This can be faster since waiting on one cluster reduces the
-               // time needed to wait on the next clusters.
-               $masterPositions = array_fill( 0, count( $lbs ), false );
-               foreach ( $lbs as $i => $lb ) {
-                       if ( $lb->getServerCount() <= 1 ) {
-                               // Bug 27975 - Don't try to wait for slaves if there are none
-                               // Prevents permission error when getting master position
-                               continue;
-                       } elseif ( $opts['ifWritesSince']
-                               && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
-                       ) {
-                               continue; // no writes since the last wait
-                       }
-                       $masterPositions[$i] = $lb->getMasterPos();
-               }
-
-               $failed = [];
-               foreach ( $lbs as $i => $lb ) {
-                       if ( $masterPositions[$i] ) {
-                               // The DBMS may not support getMasterPos() or the whole
-                               // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
-                               if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
-                                       $failed[] = $lb->getServerName( $lb->getWriterIndex() );
-                               }
-                       }
-               }
-
-               if ( $failed ) {
-                       throw new DBReplicationWaitError(
-                               "Could not wait for slaves to catch up to " .
-                               implode( ', ', $failed )
-                       );
-               }
-       }
-
-       /**
-        * Get a token asserting that no transaction writes are active
-        *
-        * @param string $fname Caller name (e.g. __METHOD__)
-        * @return mixed A value to pass to commitAndWaitForReplication()
-        * @since 1.28
-        */
-       public function getEmptyTransactionTicket( $fname ) {
-               if ( $this->hasMasterChanges() ) {
-                       $this->trxLogger->error( __METHOD__ . ": $fname does not have outer scope." );
-                       return null;
-               }
-
-               return $this->ticket;
-       }
-
-       /**
-        * Convenience method for safely running commitMasterChanges()/waitForReplication()
-        *
-        * This will commit and wait unless $ticket indicates it is unsafe to do so
-        *
-        * @param string $fname Caller name (e.g. __METHOD__)
-        * @param mixed $ticket Result of getOuterTransactionScopeTicket()
-        * @param array $opts Options to waitForReplication()
-        * @throws DBReplicationWaitError
-        * @since 1.28
-        */
-       public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
-               if ( $ticket !== $this->ticket ) {
-                       $logger = LoggerFactory::getInstance( 'DBPerformance' );
-                       $logger->error( __METHOD__ . ": cannot commit; $fname does not have outer scope." );
-                       return;
-               }
-
-               $this->commitMasterChanges( $fname );
-               $this->waitForReplication( $opts );
-       }
-
-       /**
-        * Disable the ChronologyProtector for all load balancers
-        *
-        * This can be called at the start of special API entry points
-        *
-        * @since 1.27
-        */
-       public function disableChronologyProtection() {
-               $this->chronProt->setEnabled( false );
-       }
-
-       /**
-        * @return ChronologyProtector
-        */
-       protected function newChronologyProtector() {
-               $request = RequestContext::getMain()->getRequest();
-               $chronProt = new ChronologyProtector(
-                       ObjectCache::getMainStashInstance(),
-                       [
-                               'ip' => $request->getIP(),
-                               'agent' => $request->getHeader( 'User-Agent' )
-                       ]
-               );
-               if ( PHP_SAPI === 'cli' ) {
-                       $chronProt->setEnabled( false );
-               } elseif ( $request->getHeader( 'ChronologyProtection' ) === 'false' ) {
-                       // Request opted out of using position wait logic. This is useful for requests
-                       // done by the job queue or background ETL that do not have a meaningful session.
-                       $chronProt->setWaitEnabled( false );
-               }
-
-               return $chronProt;
-       }
-
-       /**
-        * @param ChronologyProtector $cp
-        */
-       protected function shutdownChronologyProtector( ChronologyProtector $cp ) {
-               // Get all the master positions needed
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( $cp ) {
-                       $cp->shutdownLB( $lb );
-               } );
-               // Write them to the stash
-               $unsavedPositions = $cp->shutdown();
-               // If the positions failed to write to the stash, at least wait on local datacenter
-               // slaves to catch up before responding. Even if there are several DCs, this increases
-               // the chance that the user will see their own changes immediately afterwards. As long
-               // as the sticky DC cookie applies (same domain), this is not even an issue.
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( $unsavedPositions ) {
-                       $masterName = $lb->getServerName( $lb->getWriterIndex() );
-                       if ( isset( $unsavedPositions[$masterName] ) ) {
-                               $lb->waitForAll( $unsavedPositions[$masterName] );
-                       }
-               } );
-       }
-
-       /**
-        * Close all open database connections on all open load balancers.
-        * @since 1.28
-        */
-       public function closeAll() {
-               $this->forEachLBCallMethod( 'closeAll', [] );
-       }
-
-}
-
-/**
- * Exception class for attempted DB access
- */
-class DBAccessError extends MWException {
-       public function __construct() {
-               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
-                       "This is not allowed, because database access has been disabled." );
-       }
-}
-
-/**
- * Exception class for replica DB wait timeouts
- */
-class DBReplicationWaitError extends Exception {
-}
diff --git a/includes/db/loadbalancer/LBFactoryFake.php b/includes/db/loadbalancer/LBFactoryFake.php
deleted file mode 100644 (file)
index 33ee250..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-/**
- * Generator of database load balancing objects.
- *
- * 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 Database
- */
-
-/**
- * LBFactory class that throws an error on any attempt to use it.
- * This will typically be done via wfGetDB().
- * Call LBFactory::disableBackend() to start using this, and
- * LBFactory::enableBackend() to return to normal behavior
- */
-class LBFactoryFake extends LBFactory {
-       public function newMainLB( $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function getMainLB( $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       protected function newExternalLB( $cluster, $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function &getExternalLB( $cluster, $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function forEachLB( $callback, array $params = [] ) {
-       }
-}
diff --git a/includes/db/loadbalancer/LBFactoryMW.php b/includes/db/loadbalancer/LBFactoryMW.php
new file mode 100644 (file)
index 0000000..16faeb7
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * 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 Database
+ */
+
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Services\DestructibleService;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * Legacy MediaWiki-specific class for generating database load balancers
+ * @ingroup Database
+ */
+abstract class LBFactoryMW extends LBFactory implements DestructibleService {
+       /** @noinspection PhpMissingParentConstructorInspection */
+       /**
+        * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
+        * @param array $conf
+        * @TODO: inject objects via dependency framework
+        */
+       public function __construct( array $conf ) {
+               global $wgCommandLineMode, $wgSQLMode, $wgDBmysql5, $wgDBname, $wgDBprefix;
+
+               $defaults = [
+                       'localDomain' => new DatabaseDomain( $wgDBname, null, $wgDBprefix ),
+                       'hostname' => wfHostname(),
+                       'trxProfiler' => Profiler::instance()->getTransactionProfiler(),
+                       'replLogger' => LoggerFactory::getInstance( 'DBReplication' ),
+                       'queryLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
+                       'connLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
+                       'perfLogger' => LoggerFactory::getInstance( 'DBPerformance' ),
+                       'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
+               ];
+               // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
+               $sCache = ObjectCache::getLocalServerInstance();
+               if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) {
+                       $defaults['srvCache'] = $sCache;
+               }
+               $cCache = ObjectCache::getLocalClusterInstance();
+               if ( $cCache->getQoS( $cCache::ATTR_EMULATION ) > $cCache::QOS_EMULATION_SQL ) {
+                       $defaults['memCache'] = $cCache;
+               }
+               $wCache = ObjectCache::getMainWANInstance();
+               if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
+                       $defaults['wanCache'] = $wCache;
+               }
+
+               $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
+               $this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : $wgCommandLineMode;
+
+               if ( isset( $conf['serverTemplate'] ) ) { // LBFactoryMulti
+                       $conf['serverTemplate']['sqlMode'] = $wgSQLMode;
+                       $conf['serverTemplate']['utf8Mode'] = $wgDBmysql5;
+               }
+
+               parent::__construct( $conf + $defaults );
+       }
+
+       /**
+        * Returns the LBFactory class to use and the load balancer configuration.
+        *
+        * @todo instead of this, use a ServiceContainer for managing the different implementations.
+        *
+        * @param array $config (e.g. $wgLBFactoryConf)
+        * @return string Class name
+        */
+       public static function getLBFactoryClass( array $config ) {
+               // For configuration backward compatibility after removing
+               // underscores from class names in MediaWiki 1.23.
+               $bcClasses = [
+                       'LBFactory_Simple' => 'LBFactorySimple',
+                       'LBFactory_Single' => 'LBFactorySingle',
+                       'LBFactory_Multi' => 'LBFactoryMulti'
+               ];
+
+               $class = $config['class'];
+
+               if ( isset( $bcClasses[$class] ) ) {
+                       $class = $bcClasses[$class];
+                       wfDeprecated(
+                               '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
+                               '1.23'
+                       );
+               }
+
+               return $class;
+       }
+
+       /**
+        * @return bool
+        * @since 1.27
+        * @deprecated Since 1.28; use laggedReplicaUsed()
+        */
+       public function laggedSlaveUsed() {
+               return $this->laggedReplicaUsed();
+       }
+
+       protected function newChronologyProtector() {
+               $request = RequestContext::getMain()->getRequest();
+               $chronProt = new ChronologyProtector(
+                       ObjectCache::getMainStashInstance(),
+                       [
+                               'ip' => $request->getIP(),
+                               'agent' => $request->getHeader( 'User-Agent' ),
+                       ],
+                       $request->getFloat( 'cpPosTime', $request->getCookie( 'cpPosTime', '' ) )
+               );
+               if ( PHP_SAPI === 'cli' ) {
+                       $chronProt->setEnabled( false );
+               } elseif ( $request->getHeader( 'ChronologyProtection' ) === 'false' ) {
+                       // Request opted out of using position wait logic. This is useful for requests
+                       // done by the job queue or background ETL that do not have a meaningful session.
+                       $chronProt->setWaitEnabled( false );
+               }
+
+               return $chronProt;
+       }
+
+       /**
+        * Append ?cpPosTime parameter to a URL for ChronologyProtector purposes if needed
+        *
+        * Note that unlike cookies, this works accross domains
+        *
+        * @param string $url
+        * @param float $time UNIX timestamp just before shutdown() was called
+        * @return string
+        * @since 1.28
+        */
+       public function appendPreShutdownTimeAsQuery( $url, $time ) {
+               $usedCluster = 0;
+               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$usedCluster ) {
+                       $usedCluster |= ( $lb->getServerCount() > 1 );
+               } );
+
+               if ( !$usedCluster ) {
+                       return $url; // no master/replica clusters touched
+               }
+
+               return wfAppendQuery( $url, [ 'cpPosTime' => $time ] );
+       }
+}
index 4b9cccc..95bc8f4 100644 (file)
@@ -83,7 +83,7 @@
  *
  * @ingroup Database
  */
-class LBFactoryMulti extends LBFactory {
+class LBFactoryMulti extends LBFactoryMW {
        /** @var array A map of database names to section names */
        private $sectionsByDB;
 
@@ -164,7 +164,7 @@ class LBFactoryMulti extends LBFactory {
 
        /**
         * @param array $conf
-        * @throws MWException
+        * @throws InvalidArgumentException
         */
        public function __construct( array $conf ) {
                parent::__construct( $conf );
@@ -178,7 +178,7 @@ class LBFactoryMulti extends LBFactory {
 
                foreach ( $required as $key ) {
                        if ( !isset( $conf[$key] ) ) {
-                               throw new MWException( __CLASS__ . ": $key is required in configuration" );
+                               throw new InvalidArgumentException( __CLASS__ . ": $key is required in configuration" );
                        }
                        $this->$key = $conf[$key];
                }
@@ -254,7 +254,6 @@ class LBFactoryMulti extends LBFactory {
                $section = $this->getSectionForWiki( $wiki );
                if ( !isset( $this->mainLBs[$section] ) ) {
                        $lb = $this->newMainLB( $wiki );
-                       $lb->parentInfo( [ 'id' => "main-$section" ] );
                        $this->chronProt->initLB( $lb );
                        $this->mainLBs[$section] = $lb;
                }
@@ -265,12 +264,12 @@ class LBFactoryMulti extends LBFactory {
        /**
         * @param string $cluster
         * @param bool|string $wiki
-        * @throws MWException
+        * @throws InvalidArgumentException
         * @return LoadBalancer
         */
        protected function newExternalLB( $cluster, $wiki = false ) {
                if ( !isset( $this->externalLoads[$cluster] ) ) {
-                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
+                       throw new InvalidArgumentException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
                }
                $template = $this->serverTemplate;
                if ( isset( $this->externalTemplateOverrides ) ) {
@@ -293,10 +292,9 @@ class LBFactoryMulti extends LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                if ( !isset( $this->extLBs[$cluster] ) ) {
                        $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( [ 'id' => "ext-$cluster" ] );
                        $this->chronProt->initLB( $this->extLBs[$cluster] );
                }
 
@@ -313,14 +311,17 @@ class LBFactoryMulti extends LBFactory {
         * @return LoadBalancer
         */
        private function newLoadBalancer( $template, $loads, $groupLoads, $readOnlyReason ) {
-               return new LoadBalancer( [
-                       'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
-                       'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler,
-                       'srvCache' => $this->srvCache,
-                       'wanCache' => $this->wanCache
-               ] );
+               $lb = new LoadBalancer( array_merge(
+                       $this->baseLoadBalancerParams(),
+                       [
+                               'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
+                               'loadMonitor' => $this->loadMonitorClass,
+                               'readOnlyReason' => $readOnlyReason
+                       ]
+               ) );
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
        }
 
        /**
@@ -349,7 +350,7 @@ class LBFactoryMulti extends LBFactory {
                                }
                                $master = false;
                        } else {
-                               $serverInfo['slave'] = true;
+                               $serverInfo['replica'] = true;
                        }
                        if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
                                $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
@@ -418,11 +419,4 @@ class LBFactoryMulti extends LBFactory {
                        call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
                }
        }
-
-       public function shutdown( $flags = 0 ) {
-               if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
-                       $this->shutdownChronologyProtector( $this->chronProt );
-               }
-               $this->commitMasterChanges( __METHOD__ ); // sanity
-       }
 }
index 3702c8b..d937ddb 100644 (file)
@@ -24,7 +24,7 @@
 /**
  * A simple single-master LBFactory that gets its configuration from the b/c globals
  */
-class LBFactorySimple extends LBFactory {
+class LBFactorySimple extends LBFactoryMW {
        /** @var LoadBalancer */
        private $mainLB;
        /** @var LoadBalancer[] */
@@ -46,7 +46,7 @@ class LBFactorySimple extends LBFactory {
         * @return LoadBalancer
         */
        public function newMainLB( $wiki = false ) {
-               global $wgDBservers;
+               global $wgDBservers, $wgDBprefix, $wgDBmwschema, $wgSQLMode, $wgDBmysql5;
 
                if ( is_array( $wgDBservers ) ) {
                        $servers = $wgDBservers;
@@ -54,9 +54,15 @@ class LBFactorySimple extends LBFactory {
                                if ( $i == 0 ) {
                                        $server['master'] = true;
                                } else {
-                                       $server['slave'] = true;
+                                       $server['replica'] = true;
                                }
-                               $server += [ 'flags' => DBO_DEFAULT ];
+                               $server += [
+                                       'schema' => $wgDBmwschema,
+                                       'tablePrefix' => $wgDBprefix,
+                                       'flags' => DBO_DEFAULT,
+                                       'sqlMode' => $wgSQLMode,
+                                       'utf8Mode' => $wgDBmysql5
+                               ];
                        }
                } else {
                        global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
@@ -78,10 +84,14 @@ class LBFactorySimple extends LBFactory {
                                'user' => $wgDBuser,
                                'password' => $wgDBpassword,
                                'dbname' => $wgDBname,
+                               'schema' => $wgDBmwschema,
+                               'tablePrefix' => $wgDBprefix,
                                'type' => $wgDBtype,
                                'load' => 1,
                                'flags' => $flags,
-                               'master' => true
+                               'master' => true,
+                               'sqlMode' => $wgSQLMode,
+                               'utf8Mode' => $wgDBmysql5
                        ] ];
                }
 
@@ -95,7 +105,6 @@ class LBFactorySimple extends LBFactory {
        public function getMainLB( $wiki = false ) {
                if ( !isset( $this->mainLB ) ) {
                        $this->mainLB = $this->newMainLB( $wiki );
-                       $this->mainLB->parentInfo( [ 'id' => 'main' ] );
                        $this->chronProt->initLB( $this->mainLB );
                }
 
@@ -103,15 +112,15 @@ class LBFactorySimple extends LBFactory {
        }
 
        /**
-        * @throws MWException
         * @param string $cluster
         * @param bool|string $wiki
         * @return LoadBalancer
+        * @throws InvalidArgumentException
         */
        protected function newExternalLB( $cluster, $wiki = false ) {
                global $wgExternalServers;
                if ( !isset( $wgExternalServers[$cluster] ) ) {
-                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
+                       throw new InvalidArgumentException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
                }
 
                return $this->newLoadBalancer( $wgExternalServers[$cluster] );
@@ -122,10 +131,9 @@ class LBFactorySimple extends LBFactory {
         * @param bool|string $wiki
         * @return array
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                if ( !isset( $this->extLBs[$cluster] ) ) {
                        $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( [ 'id' => "ext-$cluster" ] );
                        $this->chronProt->initLB( $this->extLBs[$cluster] );
                }
 
@@ -133,14 +141,16 @@ class LBFactorySimple extends LBFactory {
        }
 
        private function newLoadBalancer( array $servers ) {
-               return new LoadBalancer( [
-                       'servers' => $servers,
-                       'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $this->readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler,
-                       'srvCache' => $this->srvCache,
-                       'wanCache' => $this->wanCache
-               ] );
+               $lb = new LoadBalancer( array_merge(
+                       $this->baseLoadBalancerParams(),
+                       [
+                               'servers' => $servers,
+                               'loadMonitor' => $this->loadMonitorClass,
+                       ]
+               ) );
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
        }
 
        /**
@@ -159,11 +169,4 @@ class LBFactorySimple extends LBFactory {
                        call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
                }
        }
-
-       public function shutdown( $flags = 0 ) {
-               if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
-                       $this->shutdownChronologyProtector( $this->chronProt );
-               }
-               $this->commitMasterChanges( __METHOD__ ); // sanity
-       }
 }
index 14c1c28..3937dfd 100644 (file)
@@ -30,17 +30,12 @@ class LBFactorySingle extends LBFactory {
 
        /**
         * @param array $conf An associative array with one member:
-        *  - connection: The DatabaseBase connection object
+        *  - connection: The IDatabase connection object
         */
        public function __construct( array $conf ) {
                parent::__construct( $conf );
 
-               $this->lb = new LoadBalancerSingle( [
-                       'readOnlyReason' => $this->readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler,
-                       'srvCache' => $this->srvCache,
-                       'wanCache' => $this->wanCache
-               ] + $conf );
+               $this->lb = new LoadBalancerSingle( array_merge( $this->baseLoadBalancerParams(), $conf ) );
        }
 
        /**
@@ -73,7 +68,7 @@ class LBFactorySingle extends LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancerSingle
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                return $this->lb;
        }
 
@@ -90,7 +85,7 @@ class LBFactorySingle extends LBFactory {
  * Helper class for LBFactorySingle.
  */
 class LoadBalancerSingle extends LoadBalancer {
-       /** @var DatabaseBase */
+       /** @var IDatabase */
        private $db;
 
        /**
@@ -123,7 +118,7 @@ class LoadBalancerSingle extends LoadBalancer {
         * @param string $server
         * @param bool $dbNameOverride
         *
-        * @return DatabaseBase
+        * @return IDatabase
         */
        protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
                return $this->db;
diff --git a/includes/db/loadbalancer/LoadBalancer.php b/includes/db/loadbalancer/LoadBalancer.php
deleted file mode 100644 (file)
index 65cd3b3..0000000
+++ /dev/null
@@ -1,1579 +0,0 @@
-<?php
-/**
- * Database load balancing.
- *
- * 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 Database
- */
-
-/**
- * Database load balancing object
- *
- * @todo document
- * @ingroup Database
- */
-class LoadBalancer {
-       /** @var array[] Map of (server index => server config array) */
-       private $mServers;
-       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
-       private $mConns;
-       /** @var array Map of (server index => weight) */
-       private $mLoads;
-       /** @var array[] Map of (group => server index => weight) */
-       private $mGroupLoads;
-       /** @var bool Whether to disregard slave lag as a factor in slave selection */
-       private $mAllowLagged;
-       /** @var integer Seconds to spend waiting on slave lag to resolve */
-       private $mWaitTimeout;
-       /** @var array LBFactory information */
-       private $mParentInfo;
-
-       /** @var string The LoadMonitor subclass name */
-       private $mLoadMonitorClass;
-       /** @var LoadMonitor */
-       private $mLoadMonitor;
-       /** @var BagOStuff */
-       private $srvCache;
-       /** @var WANObjectCache */
-       private $wanCache;
-
-       /** @var bool|DatabaseBase Database connection that caused a problem */
-       private $mErrorConnection;
-       /** @var integer The generic (not query grouped) slave index (of $mServers) */
-       private $mReadIndex;
-       /** @var bool|DBMasterPos False if not set */
-       private $mWaitForPos;
-       /** @var bool Whether the generic reader fell back to a lagged slave */
-       private $laggedSlaveMode = false;
-       /** @var bool Whether the generic reader fell back to a lagged slave */
-       private $slavesDownMode = false;
-       /** @var string The last DB selection or connection error */
-       private $mLastError = 'Unknown error';
-       /** @var string|bool Reason the LB is read-only or false if not */
-       private $readOnlyReason = false;
-       /** @var integer Total connections opened */
-       private $connsOpened = 0;
-
-       /** @var TransactionProfiler */
-       protected $trxProfiler;
-
-       /** @var integer Warn when this many connection are held */
-       const CONN_HELD_WARN_THRESHOLD = 10;
-       /** @var integer Default 'max lag' when unspecified */
-       const MAX_LAG = 10;
-       /** @var integer Max time to wait for a slave to catch up (e.g. ChronologyProtector) */
-       const POS_WAIT_TIMEOUT = 10;
-       /** @var integer Seconds to cache master server read-only status */
-       const TTL_CACHE_READONLY = 5;
-
-       /**
-        * @var boolean
-        */
-       private $disabled = false;
-
-       /**
-        * @param array $params Array with keys:
-        *  - servers : Required. Array of server info structures.
-        *  - loadMonitor : Name of a class used to fetch server lag and load.
-        *  - readOnlyReason : Reason the master DB is read-only if so [optional]
-        *  - srvCache : BagOStuff object [optional]
-        *  - wanCache : WANObjectCache object [optional]
-        * @throws MWException
-        */
-       public function __construct( array $params ) {
-               if ( !isset( $params['servers'] ) ) {
-                       throw new MWException( __CLASS__ . ': missing servers parameter' );
-               }
-               $this->mServers = $params['servers'];
-               $this->mWaitTimeout = self::POS_WAIT_TIMEOUT;
-
-               $this->mReadIndex = -1;
-               $this->mWriteIndex = -1;
-               $this->mConns = [
-                       'local' => [],
-                       'foreignUsed' => [],
-                       'foreignFree' => [] ];
-               $this->mLoads = [];
-               $this->mWaitForPos = false;
-               $this->mErrorConnection = false;
-               $this->mAllowLagged = false;
-
-               if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
-                       $this->readOnlyReason = $params['readOnlyReason'];
-               }
-
-               if ( isset( $params['loadMonitor'] ) ) {
-                       $this->mLoadMonitorClass = $params['loadMonitor'];
-               } else {
-                       $master = reset( $params['servers'] );
-                       if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
-                               $this->mLoadMonitorClass = 'LoadMonitorMySQL';
-                       } else {
-                               $this->mLoadMonitorClass = 'LoadMonitorNull';
-                       }
-               }
-
-               foreach ( $params['servers'] as $i => $server ) {
-                       $this->mLoads[$i] = $server['load'];
-                       if ( isset( $server['groupLoads'] ) ) {
-                               foreach ( $server['groupLoads'] as $group => $ratio ) {
-                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
-                                               $this->mGroupLoads[$group] = [];
-                                       }
-                                       $this->mGroupLoads[$group][$i] = $ratio;
-                               }
-                       }
-               }
-
-               if ( isset( $params['srvCache'] ) ) {
-                       $this->srvCache = $params['srvCache'];
-               } else {
-                       $this->srvCache = new EmptyBagOStuff();
-               }
-               if ( isset( $params['wanCache'] ) ) {
-                       $this->wanCache = $params['wanCache'];
-               } else {
-                       $this->wanCache = WANObjectCache::newEmpty();
-               }
-               if ( isset( $params['trxProfiler'] ) ) {
-                       $this->trxProfiler = $params['trxProfiler'];
-               } else {
-                       $this->trxProfiler = new TransactionProfiler();
-               }
-       }
-
-       /**
-        * Get a LoadMonitor instance
-        *
-        * @return LoadMonitor
-        */
-       private function getLoadMonitor() {
-               if ( !isset( $this->mLoadMonitor ) ) {
-                       $class = $this->mLoadMonitorClass;
-                       $this->mLoadMonitor = new $class( $this );
-               }
-
-               return $this->mLoadMonitor;
-       }
-
-       /**
-        * Get or set arbitrary data used by the parent object, usually an LBFactory
-        * @param mixed $x
-        * @return mixed
-        */
-       public function parentInfo( $x = null ) {
-               return wfSetVar( $this->mParentInfo, $x );
-       }
-
-       /**
-        * @param array $loads
-        * @param bool|string $wiki Wiki to get non-lagged for
-        * @param int $maxLag Restrict the maximum allowed lag to this many seconds
-        * @return bool|int|string
-        */
-       private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = self::MAX_LAG ) {
-               $lags = $this->getLagTimes( $wiki );
-
-               # Unset excessively lagged servers
-               foreach ( $lags as $i => $lag ) {
-                       if ( $i != 0 ) {
-                               $maxServerLag = $maxLag;
-                               if ( isset( $this->mServers[$i]['max lag'] ) ) {
-                                       $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
-                               }
-
-                               $host = $this->getServerName( $i );
-                               if ( $lag === false ) {
-                                       wfDebugLog( 'replication', "Server $host (#$i) is not replicating?" );
-                                       unset( $loads[$i] );
-                               } elseif ( $lag > $maxServerLag ) {
-                                       wfDebugLog( 'replication', "Server $host (#$i) has >= $lag seconds of lag" );
-                                       unset( $loads[$i] );
-                               }
-                       }
-               }
-
-               # Find out if all the slaves with non-zero load are lagged
-               $sum = 0;
-               foreach ( $loads as $load ) {
-                       $sum += $load;
-               }
-               if ( $sum == 0 ) {
-                       # No appropriate DB servers except maybe the master and some slaves with zero load
-                       # Do NOT use the master
-                       # Instead, this function will return false, triggering read-only mode,
-                       # and a lagged slave will be used instead.
-                       return false;
-               }
-
-               if ( count( $loads ) == 0 ) {
-                       return false;
-               }
-
-               # Return a random representative of the remainder
-               return ArrayUtils::pickRandom( $loads );
-       }
-
-       /**
-        * Get the index of the reader connection, which may be a slave
-        * This takes into account load ratios and lag times. It should
-        * always return a consistent index during a given invocation
-        *
-        * Side effect: opens connections to databases
-        * @param string|bool $group Query group, or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @throws MWException
-        * @return bool|int|string
-        */
-       public function getReaderIndex( $group = false, $wiki = false ) {
-               global $wgDBtype;
-
-               # @todo FIXME: For now, only go through all this for mysql databases
-               if ( $wgDBtype != 'mysql' ) {
-                       return $this->getWriterIndex();
-               }
-
-               if ( count( $this->mServers ) == 1 ) {
-                       # Skip the load balancing if there's only one server
-                       return 0;
-               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
-                       # Shortcut if generic reader exists already
-                       return $this->mReadIndex;
-               }
-
-               # Find the relevant load array
-               if ( $group !== false ) {
-                       if ( isset( $this->mGroupLoads[$group] ) ) {
-                               $nonErrorLoads = $this->mGroupLoads[$group];
-                       } else {
-                               # No loads for this group, return false and the caller can use some other group
-                               wfDebugLog( 'connect', __METHOD__ . ": no loads for group $group\n" );
-
-                               return false;
-                       }
-               } else {
-                       $nonErrorLoads = $this->mLoads;
-               }
-
-               if ( !count( $nonErrorLoads ) ) {
-                       throw new MWException( "Empty server array given to LoadBalancer" );
-               }
-
-               # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
-               $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
-
-               $laggedSlaveMode = false;
-
-               # No server found yet
-               $i = false;
-               $conn = false;
-               # First try quickly looking through the available servers for a server that
-               # meets our criteria
-               $currentLoads = $nonErrorLoads;
-               while ( count( $currentLoads ) ) {
-                       if ( $this->mAllowLagged || $laggedSlaveMode ) {
-                               $i = ArrayUtils::pickRandom( $currentLoads );
-                       } else {
-                               $i = false;
-                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
-                                       # ChronologyProtecter causes mWaitForPos to be set via sessions.
-                                       # This triggers doWait() after connect, so it's especially good to
-                                       # avoid lagged servers so as to avoid just blocking in that method.
-                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
-                                       # Aim for <= 1 second of waiting (being too picky can backfire)
-                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki, $ago + 1 );
-                               }
-                               if ( $i === false ) {
-                                       # Any server with less lag than it's 'max lag' param is preferable
-                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki );
-                               }
-                               if ( $i === false && count( $currentLoads ) != 0 ) {
-                                       # All slaves lagged. Switch to read-only mode
-                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
-                                       $i = ArrayUtils::pickRandom( $currentLoads );
-                                       $laggedSlaveMode = true;
-                               }
-                       }
-
-                       if ( $i === false ) {
-                               # pickRandom() returned false
-                               # This is permanent and means the configuration or the load monitor
-                               # wants us to return false.
-                               wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
-
-                               return false;
-                       }
-
-                       $serverName = $this->getServerName( $i );
-                       wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: $serverName..." );
-
-                       $conn = $this->openConnection( $i, $wiki );
-                       if ( !$conn ) {
-                               wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
-                               unset( $nonErrorLoads[$i] );
-                               unset( $currentLoads[$i] );
-                               $i = false;
-                               continue;
-                       }
-
-                       // Decrement reference counter, we are finished with this connection.
-                       // It will be incremented for the caller later.
-                       if ( $wiki !== false ) {
-                               $this->reuseConnection( $conn );
-                       }
-
-                       # Return this server
-                       break;
-               }
-
-               # If all servers were down, quit now
-               if ( !count( $nonErrorLoads ) ) {
-                       wfDebugLog( 'connect', "All servers down" );
-               }
-
-               if ( $i !== false ) {
-                       # Slave connection successful
-                       # Wait for the session master pos for a short time
-                       if ( $this->mWaitForPos && $i > 0 ) {
-                               if ( !$this->doWait( $i ) ) {
-                                       $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
-                               }
-                       }
-                       if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
-                               $this->mReadIndex = $i;
-                               # Record if the generic reader index is in "lagged slave" mode
-                               if ( $laggedSlaveMode ) {
-                                       $this->laggedSlaveMode = true;
-                               }
-                       }
-                       $serverName = $this->getServerName( $i );
-                       wfDebugLog( 'connect', __METHOD__ .
-                               ": using server $serverName for group '$group'\n" );
-               }
-
-               return $i;
-       }
-
-       /**
-        * Set the master wait position
-        * If a DB_SLAVE connection has been opened already, waits
-        * Otherwise sets a variable telling it to wait if such a connection is opened
-        * @param DBMasterPos $pos
-        */
-       public function waitFor( $pos ) {
-               $this->mWaitForPos = $pos;
-               $i = $this->mReadIndex;
-
-               if ( $i > 0 ) {
-                       if ( !$this->doWait( $i ) ) {
-                               $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
-                               $this->laggedSlaveMode = true;
-                       }
-               }
-       }
-
-       /**
-        * Set the master wait position and wait for a "generic" slave to catch up to it
-        *
-        * This can be used a faster proxy for waitForAll()
-        *
-        * @param DBMasterPos $pos
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool Success (able to connect and no timeouts reached)
-        * @since 1.26
-        */
-       public function waitForOne( $pos, $timeout = null ) {
-               $this->mWaitForPos = $pos;
-
-               $i = $this->mReadIndex;
-               if ( $i <= 0 ) {
-                       // Pick a generic slave if there isn't one yet
-                       $readLoads = $this->mLoads;
-                       unset( $readLoads[$this->getWriterIndex()] ); // slaves only
-                       $readLoads = array_filter( $readLoads ); // with non-zero load
-                       $i = ArrayUtils::pickRandom( $readLoads );
-               }
-
-               if ( $i > 0 ) {
-                       $ok = $this->doWait( $i, true, $timeout );
-               } else {
-                       $ok = true; // no applicable loads
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Set the master wait position and wait for ALL slaves to catch up to it
-        * @param DBMasterPos $pos
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool Success (able to connect and no timeouts reached)
-        */
-       public function waitForAll( $pos, $timeout = null ) {
-               $this->mWaitForPos = $pos;
-               $serverCount = count( $this->mServers );
-
-               $ok = true;
-               for ( $i = 1; $i < $serverCount; $i++ ) {
-                       if ( $this->mLoads[$i] > 0 ) {
-                               $ok = $this->doWait( $i, true, $timeout ) && $ok;
-                       }
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get any open connection to a given server index, local or foreign
-        * Returns false if there is no connection open
-        *
-        * @param int $i
-        * @return DatabaseBase|bool False on failure
-        */
-       public function getAnyOpenConnection( $i ) {
-               foreach ( $this->mConns as $conns ) {
-                       if ( !empty( $conns[$i] ) ) {
-                               return reset( $conns[$i] );
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Wait for a given slave to catch up to the master pos stored in $this
-        * @param int $index Server index
-        * @param bool $open Check the server even if a new connection has to be made
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool
-        */
-       protected function doWait( $index, $open = false, $timeout = null ) {
-               $close = false; // close the connection afterwards
-
-               // Check if we already know that the DB has reached this point
-               $server = $this->getServerName( $index );
-               $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server );
-               /** @var DBMasterPos $knownReachedPos */
-               $knownReachedPos = $this->srvCache->get( $key );
-               if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
-                       wfDebugLog( 'replication', __METHOD__ .
-                               ": slave $server known to be caught up (pos >= $knownReachedPos).\n" );
-                       return true;
-               }
-
-               // Find a connection to wait on, creating one if needed and allowed
-               $conn = $this->getAnyOpenConnection( $index );
-               if ( !$conn ) {
-                       if ( !$open ) {
-                               wfDebugLog( 'replication', __METHOD__ . ": no connection open for $server\n" );
-
-                               return false;
-                       } else {
-                               $conn = $this->openConnection( $index, '' );
-                               if ( !$conn ) {
-                                       wfDebugLog( 'replication', __METHOD__ . ": failed to connect to $server\n" );
-
-                                       return false;
-                               }
-                               // Avoid connection spam in waitForAll() when connections
-                               // are made just for the sake of doing this lag check.
-                               $close = true;
-                       }
-               }
-
-               wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
-               $timeout = $timeout ?: $this->mWaitTimeout;
-               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
-
-               if ( $result == -1 || is_null( $result ) ) {
-                       // Timed out waiting for slave, use master instead
-                       $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
-                       wfDebugLog( 'replication', "$msg\n" );
-                       wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
-                       $ok = false;
-               } else {
-                       wfDebugLog( 'replication', __METHOD__ . ": Done\n" );
-                       $ok = true;
-                       // Remember that the DB reached this point
-                       $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
-               }
-
-               if ( $close ) {
-                       $this->closeConnection( $conn );
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get a connection by index
-        * This is the main entry point for this class.
-        *
-        * @param int $i Server index
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        *
-        * @throws MWException
-        * @return DatabaseBase
-        */
-       public function getConnection( $i, $groups = [], $wiki = false ) {
-               if ( $i === null || $i === false ) {
-                       throw new MWException( 'Attempt to call ' . __METHOD__ .
-                               ' with invalid server index' );
-               }
-
-               if ( $wiki === wfWikiID() ) {
-                       $wiki = false;
-               }
-
-               $groups = ( $groups === false || $groups === [] )
-                       ? [ false ] // check one "group": the generic pool
-                       : (array)$groups;
-
-               $masterOnly = ( $i == DB_MASTER || $i == $this->getWriterIndex() );
-               $oldConnsOpened = $this->connsOpened; // connections open now
-
-               if ( $i == DB_MASTER ) {
-                       $i = $this->getWriterIndex();
-               } else {
-                       # Try to find an available server in any the query groups (in order)
-                       foreach ( $groups as $group ) {
-                               $groupIndex = $this->getReaderIndex( $group, $wiki );
-                               if ( $groupIndex !== false ) {
-                                       $i = $groupIndex;
-                                       break;
-                               }
-                       }
-               }
-
-               # Operation-based index
-               if ( $i == DB_SLAVE ) {
-                       $this->mLastError = 'Unknown error'; // reset error string
-                       # Try the general server pool if $groups are unavailable.
-                       $i = in_array( false, $groups, true )
-                               ? false // don't bother with this if that is what was tried above
-                               : $this->getReaderIndex( false, $wiki );
-                       # Couldn't find a working server in getReaderIndex()?
-                       if ( $i === false ) {
-                               $this->mLastError = 'No working slave server: ' . $this->mLastError;
-
-                               return $this->reportConnectionError();
-                       }
-               }
-
-               # Now we have an explicit index into the servers array
-               $conn = $this->openConnection( $i, $wiki );
-               if ( !$conn ) {
-                       return $this->reportConnectionError();
-               }
-
-               # Profile any new connections that happen
-               if ( $this->connsOpened > $oldConnsOpened ) {
-                       $host = $conn->getServer();
-                       $dbname = $conn->getDBname();
-                       $trxProf = Profiler::instance()->getTransactionProfiler();
-                       $trxProf->recordConnection( $host, $dbname, $masterOnly );
-               }
-
-               if ( $masterOnly ) {
-                       # Make master-requested DB handles inherit any read-only mode setting
-                       $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $wiki, $conn ) );
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Mark a foreign connection as being available for reuse under a different
-        * DB name or prefix. This mechanism is reference-counted, and must be called
-        * the same number of times as getConnection() to work.
-        *
-        * @param DatabaseBase $conn
-        * @throws MWException
-        */
-       public function reuseConnection( $conn ) {
-               $serverIndex = $conn->getLBInfo( 'serverIndex' );
-               $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
-               if ( $serverIndex === null || $refCount === null ) {
-                       wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
-                       /**
-                        * This can happen in code like:
-                        *   foreach ( $dbs as $db ) {
-                        *     $conn = $lb->getConnection( DB_SLAVE, [], $db );
-                        *     ...
-                        *     $lb->reuseConnection( $conn );
-                        *   }
-                        * When a connection to the local DB is opened in this way, reuseConnection()
-                        * should be ignored
-                        */
-                       return;
-               }
-
-               $dbName = $conn->getDBname();
-               $prefix = $conn->tablePrefix();
-               if ( strval( $prefix ) !== '' ) {
-                       $wiki = "$dbName-$prefix";
-               } else {
-                       $wiki = $dbName;
-               }
-               if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
-                       throw new MWException( __METHOD__ . ": connection not found, has " .
-                               "the connection been freed already?" );
-               }
-               $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
-               if ( $refCount <= 0 ) {
-                       $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
-                       unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
-                       wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
-               } else {
-                       wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
-               }
-       }
-
-       /**
-        * Get a database connection handle reference
-        *
-        * The handle's methods wrap simply wrap those of a DatabaseBase handle
-        *
-        * @see LoadBalancer::getConnection() for parameter information
-        *
-        * @param int $db
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DBConnRef
-        */
-       public function getConnectionRef( $db, $groups = [], $wiki = false ) {
-               return new DBConnRef( $this, $this->getConnection( $db, $groups, $wiki ) );
-       }
-
-       /**
-        * Get a database connection handle reference without connecting yet
-        *
-        * The handle's methods wrap simply wrap those of a DatabaseBase handle
-        *
-        * @see LoadBalancer::getConnection() for parameter information
-        *
-        * @param int $db
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DBConnRef
-        */
-       public function getLazyConnectionRef( $db, $groups = [], $wiki = false ) {
-               return new DBConnRef( $this, [ $db, $groups, $wiki ] );
-       }
-
-       /**
-        * Open a connection to the server given by the specified index
-        * Index must be an actual index into the array.
-        * If the server is already open, returns it.
-        *
-        * On error, returns false, and the connection which caused the
-        * error will be available via $this->mErrorConnection.
-        *
-        * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
-        *
-        * @param int $i Server index
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DatabaseBase|bool Returns false on errors
-        */
-       public function openConnection( $i, $wiki = false ) {
-               if ( $wiki !== false ) {
-                       $conn = $this->openForeignConnection( $i, $wiki );
-               } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
-                       $conn = $this->mConns['local'][$i][0];
-               } else {
-                       $server = $this->mServers[$i];
-                       $server['serverIndex'] = $i;
-                       $conn = $this->reallyOpenConnection( $server, false );
-                       $serverName = $this->getServerName( $i );
-                       if ( $conn->isOpen() ) {
-                               wfDebugLog( 'connect', "Connected to database $i at $serverName\n" );
-                               $this->mConns['local'][$i][0] = $conn;
-                       } else {
-                               wfDebugLog( 'connect', "Failed to connect to database $i at $serverName\n" );
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       }
-               }
-
-               if ( $conn && !$conn->isOpen() ) {
-                       // Connection was made but later unrecoverably lost for some reason.
-                       // Do not return a handle that will just throw exceptions on use,
-                       // but let the calling code (e.g. getReaderIndex) try another server.
-                       // See DatabaseMyslBase::ping() for how this can happen.
-                       $this->mErrorConnection = $conn;
-                       $conn = false;
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Open a connection to a foreign DB, or return one if it is already open.
-        *
-        * Increments a reference count on the returned connection which locks the
-        * connection to the requested wiki. This reference count can be
-        * decremented by calling reuseConnection().
-        *
-        * If a connection is open to the appropriate server already, but with the wrong
-        * database, it will be switched to the right database and returned, as long as
-        * it has been freed first with reuseConnection().
-        *
-        * On error, returns false, and the connection which caused the
-        * error will be available via $this->mErrorConnection.
-        *
-        * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
-        *
-        * @param int $i Server index
-        * @param string $wiki Wiki ID to open
-        * @return DatabaseBase
-        */
-       private function openForeignConnection( $i, $wiki ) {
-               list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
-               if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
-                       // Reuse an already-used connection
-                       $conn = $this->mConns['foreignUsed'][$i][$wiki];
-                       wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
-               } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
-                       // Reuse a free connection for the same wiki
-                       $conn = $this->mConns['foreignFree'][$i][$wiki];
-                       unset( $this->mConns['foreignFree'][$i][$wiki] );
-                       $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                       wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
-               } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
-                       // Reuse a connection from another wiki
-                       $conn = reset( $this->mConns['foreignFree'][$i] );
-                       $oldWiki = key( $this->mConns['foreignFree'][$i] );
-
-                       // The empty string as a DB name means "don't care".
-                       // DatabaseMysqlBase::open() already handle this on connection.
-                       if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
-                               $this->mLastError = "Error selecting database $dbName on server " .
-                                       $conn->getServer() . " from client host " . wfHostname() . "\n";
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       } else {
-                               $conn->tablePrefix( $prefix );
-                               unset( $this->mConns['foreignFree'][$i][$oldWiki] );
-                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                               wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
-                       }
-               } else {
-                       // Open a new connection
-                       $server = $this->mServers[$i];
-                       $server['serverIndex'] = $i;
-                       $server['foreignPoolRefCount'] = 0;
-                       $server['foreign'] = true;
-                       $conn = $this->reallyOpenConnection( $server, $dbName );
-                       if ( !$conn->isOpen() ) {
-                               wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       } else {
-                               $conn->tablePrefix( $prefix );
-                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                               wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
-                       }
-               }
-
-               // Increment reference count
-               if ( $conn ) {
-                       $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
-                       $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Test if the specified index represents an open connection
-        *
-        * @param int $index Server index
-        * @access private
-        * @return bool
-        */
-       private function isOpen( $index ) {
-               if ( !is_integer( $index ) ) {
-                       return false;
-               }
-
-               return (bool)$this->getAnyOpenConnection( $index );
-       }
-
-       /**
-        * Really opens a connection. Uncached.
-        * Returns a Database object whether or not the connection was successful.
-        * @access private
-        *
-        * @param array $server
-        * @param bool $dbNameOverride
-        * @throws MWException
-        * @return DatabaseBase
-        */
-       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
-               if ( $this->disabled ) {
-                       throw new DBAccessError();
-               }
-
-               if ( !is_array( $server ) ) {
-                       throw new MWException( 'You must update your load-balancing configuration. ' .
-                               'See DefaultSettings.php entry for $wgDBservers.' );
-               }
-
-               if ( $dbNameOverride !== false ) {
-                       $server['dbname'] = $dbNameOverride;
-               }
-
-               // Let the handle know what the cluster master is (e.g. "db1052")
-               $masterName = $this->getServerName( 0 );
-               $server['clusterMasterHost'] = $masterName;
-
-               // Log when many connection are made on requests
-               if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
-                       wfDebugLog( 'DBPerformance', __METHOD__ . ": " .
-                               "{$this->connsOpened}+ connections made (master=$masterName)\n" .
-                               wfBacktrace( true ) );
-               }
-
-               # Create object
-               try {
-                       $db = DatabaseBase::factory( $server['type'], $server );
-               } catch ( DBConnectionError $e ) {
-                       // FIXME: This is probably the ugliest thing I have ever done to
-                       // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
-                       $db = $e->db;
-               }
-
-               $db->setLBInfo( $server );
-               $db->setLazyMasterHandle(
-                       $this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() )
-               );
-               $db->setTransactionProfiler( $this->trxProfiler );
-
-               return $db;
-       }
-
-       /**
-        * @throws DBConnectionError
-        * @return bool
-        */
-       private function reportConnectionError() {
-               $conn = $this->mErrorConnection; // The connection which caused the error
-               $context = [
-                       'method' => __METHOD__,
-                       'last_error' => $this->mLastError,
-               ];
-
-               if ( !is_object( $conn ) ) {
-                       // No last connection, probably due to all servers being too busy
-                       wfLogDBError(
-                               "LB failure with no last connection. Connection error: {last_error}",
-                               $context
-                       );
-
-                       // If all servers were busy, mLastError will contain something sensible
-                       throw new DBConnectionError( null, $this->mLastError );
-               } else {
-                       $context['db_server'] = $conn->getProperty( 'mServer' );
-                       wfLogDBError(
-                               "Connection error: {last_error} ({db_server})",
-                               $context
-                       );
-
-                       // throws DBConnectionError
-                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" );
-               }
-
-               return false; /* not reached */
-       }
-
-       /**
-        * @return int
-        * @since 1.26
-        */
-       public function getWriterIndex() {
-               return 0;
-       }
-
-       /**
-        * Returns true if the specified index is a valid server index
-        *
-        * @param string $i
-        * @return bool
-        */
-       public function haveIndex( $i ) {
-               return array_key_exists( $i, $this->mServers );
-       }
-
-       /**
-        * Returns true if the specified index is valid and has non-zero load
-        *
-        * @param string $i
-        * @return bool
-        */
-       public function isNonZeroLoad( $i ) {
-               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
-       }
-
-       /**
-        * Get the number of defined servers (not the number of open connections)
-        *
-        * @return int
-        */
-       public function getServerCount() {
-               return count( $this->mServers );
-       }
-
-       /**
-        * Get the host name or IP address of the server with the specified index
-        * Prefer a readable name if available.
-        * @param string $i
-        * @return string
-        */
-       public function getServerName( $i ) {
-               if ( isset( $this->mServers[$i]['hostName'] ) ) {
-                       $name = $this->mServers[$i]['hostName'];
-               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
-                       $name = $this->mServers[$i]['host'];
-               } else {
-                       $name = '';
-               }
-
-               return ( $name != '' ) ? $name : 'localhost';
-       }
-
-       /**
-        * Return the server info structure for a given index, or false if the index is invalid.
-        * @param int $i
-        * @return array|bool
-        */
-       public function getServerInfo( $i ) {
-               if ( isset( $this->mServers[$i] ) ) {
-                       return $this->mServers[$i];
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Sets the server info structure for the given index. Entry at index $i
-        * is created if it doesn't exist
-        * @param int $i
-        * @param array $serverInfo
-        */
-       public function setServerInfo( $i, array $serverInfo ) {
-               $this->mServers[$i] = $serverInfo;
-       }
-
-       /**
-        * Get the current master position for chronology control purposes
-        * @return mixed
-        */
-       public function getMasterPos() {
-               # If this entire request was served from a slave without opening a connection to the
-               # master (however unlikely that may be), then we can fetch the position from the slave.
-               $masterConn = $this->getAnyOpenConnection( 0 );
-               if ( !$masterConn ) {
-                       $serverCount = count( $this->mServers );
-                       for ( $i = 1; $i < $serverCount; $i++ ) {
-                               $conn = $this->getAnyOpenConnection( $i );
-                               if ( $conn ) {
-                                       return $conn->getSlavePos();
-                               }
-                       }
-               } else {
-                       return $masterConn->getMasterPos();
-               }
-
-               return false;
-       }
-
-       /**
-        * Disable this load balancer. All connections are closed, and any attempt to
-        * open a new connection will result in a DBAccessError.
-        *
-        * @since 1.27
-        */
-       public function disable() {
-               $this->closeAll();
-               $this->disabled = true;
-       }
-
-       /**
-        * Close all open connections
-        */
-       public function closeAll() {
-               $this->forEachOpenConnection( function ( DatabaseBase $conn ) {
-                       $conn->close();
-               } );
-
-               $this->mConns = [
-                       'local' => [],
-                       'foreignFree' => [],
-                       'foreignUsed' => [],
-               ];
-               $this->connsOpened = 0;
-       }
-
-       /**
-        * Close a connection
-        * Using this function makes sure the LoadBalancer knows the connection is closed.
-        * If you use $conn->close() directly, the load balancer won't update its state.
-        * @param DatabaseBase $conn
-        */
-       public function closeConnection( $conn ) {
-               $done = false;
-               foreach ( $this->mConns as $i1 => $conns2 ) {
-                       foreach ( $conns2 as $i2 => $conns3 ) {
-                               foreach ( $conns3 as $i3 => $candidateConn ) {
-                                       if ( $conn === $candidateConn ) {
-                                               $conn->close();
-                                               unset( $this->mConns[$i1][$i2][$i3] );
-                                               --$this->connsOpened;
-                                               $done = true;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               if ( !$done ) {
-                       $conn->close();
-               }
-       }
-
-       /**
-        * Commit transactions on all open connections
-        * @param string $fname Caller name
-        */
-       public function commitAll( $fname = __METHOD__ ) {
-               $this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
-               } );
-       }
-
-       /**
-        * Perform all pre-commit callbacks that remain part of the atomic transactions
-        * and disable any post-commit callbacks until runMasterPostCommitCallbacks()
-        * @since 1.28
-        */
-       public function runMasterPreCommitCallbacks() {
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
-                       // Any error will cause all DB transactions to be rolled back together.
-                       $conn->runOnTransactionPreCommitCallbacks();
-                       // Defer post-commit callbacks until COMMIT finishes for all DBs.
-                       $conn->setPostCommitCallbackSupression( true );
-               } );
-       }
-
-       /**
-        * Perform all pre-commit checks for things like replication safety
-        * @param array $options Includes:
-        *   - maxWriteDuration : max write query duration time in seconds
-        * @throws DBTransactionError
-        * @since 1.28
-        */
-       public function approveMasterChanges( array $options ) {
-               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $limit ) {
-                       // If atomic sections or explicit transactions are still open, some caller must have
-                       // caught an exception but failed to properly rollback any changes. Detect that and
-                       // throw and error (causing rollback).
-                       if ( $conn->explicitTrxActive() ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       "Explicit transaction still active. A caller may have caught an error."
-                               );
-                       }
-                       // Assert that the time to replicate the transaction will be sane.
-                       // If this fails, then all DB transactions will be rollback back together.
-                       $time = $conn->pendingWriteQueryDuration();
-                       if ( $limit > 0 && $time > $limit ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
-                               );
-                       }
-                       // If a connection sits idle while slow queries execute on another, that connection
-                       // may end up dropped before the commit round is reached. Ping servers to detect this.
-                       if ( $conn->writesOrCallbacksPending() && !$conn->ping() ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       "A connection to the {$conn->getDBname()} database was lost before commit."
-                               );
-                       }
-               } );
-       }
-
-       /**
-        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
-        *
-        * The DBO_TRX setting will be reverted to the default in each of these methods:
-        *   - commitMasterChanges()
-        *   - rollbackMasterChanges()
-        *   - commitAll()
-        * This allows for custom transaction rounds from any outer transaction scope.
-        *
-        * @param string $fname
-        * @since 1.28
-        */
-       public function beginMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       if ( $conn->writesOrCallbacksPending() ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       "Transaction with pending writes still active."
-                               );
-                       } elseif ( $conn->trxLevel() ) {
-                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
-                       }
-                       if ( $conn->getFlag( DBO_DEFAULT ) ) {
-                               // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
-                               // Force DBO_TRX even in CLI mode since a commit round is expected soon.
-                               $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
-                               $conn->onTransactionResolution( function () use ( $conn ) {
-                                       $conn->restoreFlags( $conn::RESTORE_PRIOR );
-                               } );
-                       } else {
-                               // Config has explicitly requested DBO_TRX be either on or off; respect that.
-                               // This is useful for things like blob stores which use auto-commit mode.
-                       }
-               } );
-       }
-
-       /**
-        * Issue COMMIT on all master connections where writes where done
-        * @param string $fname Caller name
-        */
-       public function commitMasterChanges( $fname = __METHOD__ ) {
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
-                       if ( $conn->writesOrCallbacksPending() ) {
-                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
-                       }
-               } );
-       }
-
-       /**
-        * Issue all pending post-commit callbacks
-        * @return Exception|null The first exception or null if there were none
-        * @since 1.28
-        */
-       public function runMasterPostCommitCallbacks() {
-               $e = null; // first exception
-               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( &$e ) {
-                       $conn->setPostCommitCallbackSupression( false );
-                       try {
-                               $conn->runOnTransactionIdleCallbacks( $conn::TRIGGER_COMMIT );
-                       } catch ( Exception $ex ) {
-                               $e = $e ?: $ex;
-                       }
-               } );
-
-               return $e;
-       }
-
-       /**
-        * Issue ROLLBACK only on master, only if queries were done on connection
-        * @param string $fname Caller name
-        * @throws DBExpectedError
-        * @since 1.23
-        */
-       public function rollbackMasterChanges( $fname = __METHOD__ ) {
-               $failedServers = [];
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       try {
-                                               $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
-                                       } catch ( DBError $e ) {
-                                               MWExceptionHandler::logException( $e );
-                                               $failedServers[] = $conn->getServer();
-                                       }
-                               }
-                       }
-               }
-
-               if ( $failedServers ) {
-                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
-                               implode( ', ', array_unique( $failedServers ) ) );
-               }
-       }
-
-       /**
-        * @return bool Whether a master connection is already open
-        * @since 1.24
-        */
-       public function hasMasterConnection() {
-               return $this->isOpen( $this->getWriterIndex() );
-       }
-
-       /**
-        * Determine if there are pending changes in a transaction by this thread
-        * @since 1.23
-        * @return bool
-        */
-       public function hasMasterChanges() {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Get the timestamp of the latest write query done by this thread
-        * @since 1.25
-        * @return float|bool UNIX timestamp or false
-        */
-       public function lastMasterChangeTimestamp() {
-               $lastTime = false;
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               $lastTime = max( $lastTime, $conn->lastDoneWrites() );
-                       }
-               }
-               return $lastTime;
-       }
-
-       /**
-        * Check if this load balancer object had any recent or still
-        * pending writes issued against it by this PHP thread
-        *
-        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
-        * @return bool
-        * @since 1.25
-        */
-       public function hasOrMadeRecentMasterChanges( $age = null ) {
-               $age = ( $age === null ) ? $this->mWaitTimeout : $age;
-
-               return ( $this->hasMasterChanges()
-                       || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
-       }
-
-       /**
-        * Get the list of callers that have pending master changes
-        *
-        * @return array
-        * @since 1.27
-        */
-       public function pendingMasterChangeCallers() {
-               $fnames = [];
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
-                       }
-               }
-
-               return $fnames;
-       }
-
-       /**
-        * @param mixed $value
-        * @return mixed
-        */
-       public function waitTimeout( $value = null ) {
-               return wfSetVar( $this->mWaitTimeout, $value );
-       }
-
-       /**
-        * @note This method will trigger a DB connection if not yet done
-        *
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return bool Whether the generic connection for reads is highly "lagged"
-        */
-       public function getLaggedSlaveMode( $wiki = false ) {
-               // No-op if there is only one DB (also avoids recursion)
-               if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
-                       try {
-                               // See if laggedSlaveMode gets set
-                               $conn = $this->getConnection( DB_SLAVE, false, $wiki );
-                               $this->reuseConnection( $conn );
-                       } catch ( DBConnectionError $e ) {
-                               // Avoid expensive re-connect attempts and failures
-                               $this->slavesDownMode = true;
-                               $this->laggedSlaveMode = true;
-                       }
-               }
-
-               return $this->laggedSlaveMode;
-       }
-
-       /**
-        * @note This method will never cause a new DB connection
-        * @return bool Whether any generic connection used for reads was highly "lagged"
-        * @since 1.27
-        */
-       public function laggedSlaveUsed() {
-               return $this->laggedSlaveMode;
-       }
-
-       /**
-        * @note This method may trigger a DB connection if not yet done
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @param DatabaseBase|null DB master connection; used to avoid loops [optional]
-        * @return string|bool Reason the master is read-only or false if it is not
-        * @since 1.27
-        */
-       public function getReadOnlyReason( $wiki = false, DatabaseBase $conn = null ) {
-               if ( $this->readOnlyReason !== false ) {
-                       return $this->readOnlyReason;
-               } elseif ( $this->getLaggedSlaveMode( $wiki ) ) {
-                       if ( $this->slavesDownMode ) {
-                               return 'The database has been automatically locked ' .
-                                       'until the slave database servers become available';
-                       } else {
-                               return 'The database has been automatically locked ' .
-                                       'while the slave database servers catch up to the master.';
-                       }
-               } elseif ( $this->masterRunningReadOnly( $wiki, $conn ) ) {
-                       return 'The database master is running in read-only mode.';
-               }
-
-               return false;
-       }
-
-       /**
-        * @param string $wiki Wiki ID, or false for the current wiki
-        * @param DatabaseBase|null DB master connectionl used to avoid loops [optional]
-        * @return bool
-        */
-       private function masterRunningReadOnly( $wiki, DatabaseBase $conn = null ) {
-               $cache = $this->wanCache;
-               $masterServer = $this->getServerName( $this->getWriterIndex() );
-
-               return (bool)$cache->getWithSetCallback(
-                       $cache->makeGlobalKey( __CLASS__, 'server-read-only', $masterServer ),
-                       self::TTL_CACHE_READONLY,
-                       function () use ( $wiki, $conn ) {
-                               $this->trxProfiler->setSilenced( true );
-                               try {
-                                       $dbw = $conn ?: $this->getConnection( DB_MASTER, [], $wiki );
-                                       $readOnly = (int)$dbw->serverIsReadOnly();
-                               } catch ( DBError $e ) {
-                                       $readOnly = 0;
-                               }
-                               $this->trxProfiler->setSilenced( false );
-                               return $readOnly;
-                       },
-                       [ 'pcTTL' => $cache::TTL_PROC_LONG, 'busyValue' => 0 ]
-               );
-       }
-
-       /**
-        * Disables/enables lag checks
-        * @param null|bool $mode
-        * @return bool
-        */
-       public function allowLagged( $mode = null ) {
-               if ( $mode === null ) {
-                       return $this->mAllowLagged;
-               }
-               $this->mAllowLagged = $mode;
-
-               return $this->mAllowLagged;
-       }
-
-       /**
-        * @return bool
-        */
-       public function pingAll() {
-               $success = true;
-               $this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( &$success ) {
-                       if ( !$conn->ping() ) {
-                               $success = false;
-                       }
-               } );
-
-               return $success;
-       }
-
-       /**
-        * Call a function with each open connection object
-        * @param callable $callback
-        * @param array $params
-        */
-       public function forEachOpenConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $connsByServer ) {
-                       foreach ( $connsByServer as $serverConns ) {
-                               foreach ( $serverConns as $conn ) {
-                                       $mergedParams = array_merge( [ $conn ], $params );
-                                       call_user_func_array( $callback, $mergedParams );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Call a function with each open connection object to a master
-        * @param callable $callback
-        * @param array $params
-        * @since 1.28
-        */
-       public function forEachOpenMasterConnection( $callback, array $params = [] ) {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $connsByServer ) {
-                       if ( isset( $connsByServer[$masterIndex] ) ) {
-                               /** @var DatabaseBase $conn */
-                               foreach ( $connsByServer[$masterIndex] as $conn ) {
-                                       $mergedParams = array_merge( [ $conn ], $params );
-                                       call_user_func_array( $callback, $mergedParams );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get the hostname and lag time of the most-lagged slave
-        *
-        * This is useful for maintenance scripts that need to throttle their updates.
-        * May attempt to open connections to slaves on the default DB. If there is
-        * no lag, the maximum lag will be reported as -1.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the default database
-        * @return array ( host, max lag, index of max lagged host )
-        */
-       public function getMaxLag( $wiki = false ) {
-               $maxLag = -1;
-               $host = '';
-               $maxIndex = 0;
-
-               if ( $this->getServerCount() <= 1 ) {
-                       return [ $host, $maxLag, $maxIndex ]; // no replication = no lag
-               }
-
-               $lagTimes = $this->getLagTimes( $wiki );
-               foreach ( $lagTimes as $i => $lag ) {
-                       if ( $this->mLoads[$i] > 0 && $lag > $maxLag ) {
-                               $maxLag = $lag;
-                               $host = $this->mServers[$i]['host'];
-                               $maxIndex = $i;
-                       }
-               }
-
-               return [ $host, $maxLag, $maxIndex ];
-       }
-
-       /**
-        * Get an estimate of replication lag (in seconds) for each server
-        *
-        * Results are cached for a short time in memcached/process cache
-        *
-        * Values may be "false" if replication is too broken to estimate
-        *
-        * @param string|bool $wiki
-        * @return int[] Map of (server index => float|int|bool)
-        */
-       public function getLagTimes( $wiki = false ) {
-               if ( $this->getServerCount() <= 1 ) {
-                       return [ 0 => 0 ]; // no replication = no lag
-               }
-
-               # Send the request to the load monitor
-               return $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
-       }
-
-       /**
-        * Get the lag in seconds for a given connection, or zero if this load
-        * balancer does not have replication enabled.
-        *
-        * This should be used in preference to Database::getLag() in cases where
-        * replication may not be in use, since there is no way to determine if
-        * replication is in use at the connection level without running
-        * potentially restricted queries such as SHOW SLAVE STATUS. Using this
-        * function instead of Database::getLag() avoids a fatal error in this
-        * case on many installations.
-        *
-        * @param IDatabase $conn
-        * @return int|bool Returns false on error
-        */
-       public function safeGetLag( IDatabase $conn ) {
-               if ( $this->getServerCount() == 1 ) {
-                       return 0;
-               } else {
-                       return $conn->getLag();
-               }
-       }
-
-       /**
-        * Wait for a slave DB to reach a specified master position
-        *
-        * This will connect to the master to get an accurate position if $pos is not given
-        *
-        * @param IDatabase $conn Slave DB
-        * @param DBMasterPos|bool $pos Master position; default: current position
-        * @param integer $timeout Timeout in seconds
-        * @return bool Success
-        * @since 1.27
-        */
-       public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
-               if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'slave' ) ) {
-                       return true; // server is not a slave DB
-               }
-
-               $pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
-               if ( !( $pos instanceof DBMasterPos ) ) {
-                       return false; // something is misconfigured
-               }
-
-               $result = $conn->masterPosWait( $pos, $timeout );
-               if ( $result == -1 || is_null( $result ) ) {
-                       $msg = __METHOD__ . ": Timed out waiting on {$conn->getServer()} pos {$pos}";
-                       wfDebugLog( 'replication', "$msg\n" );
-                       wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
-                       $ok = false;
-               } else {
-                       wfDebugLog( 'replication', __METHOD__ . ": Done\n" );
-                       $ok = true;
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Clear the cache for slag lag delay times
-        *
-        * This is only used for testing
-        */
-       public function clearLagTimeCache() {
-               $this->getLoadMonitor()->clearCaches();
-       }
-}
diff --git a/includes/db/loadbalancer/LoadMonitor.php b/includes/db/loadbalancer/LoadMonitor.php
deleted file mode 100644 (file)
index e68cf1a..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-/**
- * Database load monitoring.
- *
- * 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 Database
- */
-
-/**
- * An interface for database load monitoring
- *
- * @ingroup Database
- */
-interface LoadMonitor {
-       /**
-        * Construct a new LoadMonitor with a given LoadBalancer parent
-        *
-        * @param LoadBalancer $parent
-        */
-       public function __construct( $parent );
-
-       /**
-        * Perform pre-connection load ratio adjustment.
-        * @param array &$loads
-        * @param string|bool $group The selected query group. Default: false
-        * @param string|bool $wiki Default: false
-        */
-       public function scaleLoads( &$loads, $group = false, $wiki = false );
-
-       /**
-        * Get an estimate of replication lag (in seconds) for each server
-        *
-        * Values may be "false" if replication is too broken to estimate
-        *
-        * @param array $serverIndexes
-        * @param string $wiki
-        *
-        * @return array Map of (server index => float|int|bool)
-        */
-       public function getLagTimes( $serverIndexes, $wiki );
-
-       /**
-        * Clear any process and persistent cache of lag times
-        * @since 1.27
-        */
-       public function clearCaches();
-}
-
-class LoadMonitorNull implements LoadMonitor {
-       public function __construct( $parent ) {
-       }
-
-       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
-       }
-
-       public function getLagTimes( $serverIndexes, $wiki ) {
-               return array_fill_keys( $serverIndexes, 0 );
-       }
-
-       public function clearCaches() {
-
-       }
-}
diff --git a/includes/db/loadbalancer/LoadMonitorMySQL.php b/includes/db/loadbalancer/LoadMonitorMySQL.php
deleted file mode 100644 (file)
index 444c4b4..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-<?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 Database
- */
-
-/**
- * Basic MySQL load monitor with no external dependencies
- * Uses memcached to cache the replication lag for a short time
- *
- * @ingroup Database
- */
-class LoadMonitorMySQL implements LoadMonitor {
-       /** @var LoadBalancer */
-       public $parent;
-       /** @var BagOStuff */
-       protected $srvCache;
-       /** @var BagOStuff */
-       protected $mainCache;
-
-       public function __construct( $parent ) {
-               $this->parent = $parent;
-
-               $this->srvCache = ObjectCache::getLocalServerInstance( 'hash' );
-               $this->mainCache = ObjectCache::getLocalClusterInstance();
-       }
-
-       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
-       }
-
-       public function getLagTimes( $serverIndexes, $wiki ) {
-               if ( count( $serverIndexes ) == 1 && reset( $serverIndexes ) == 0 ) {
-                       # Single server only, just return zero without caching
-                       return [ 0 => 0 ];
-               }
-
-               $key = $this->getLagTimeCacheKey();
-               # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec)
-               $ttl = mt_rand( 4e6, 5e6 ) / 1e6;
-               # Keep keys around longer as fallbacks
-               $staleTTL = 60;
-
-               # (a) Check the local APC cache
-               $value = $this->srvCache->get( $key );
-               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
-                       wfDebugLog( 'replication', __METHOD__ . ": got lag times ($key) from local cache" );
-                       return $value['lagTimes']; // cache hit
-               }
-               $staleValue = $value ?: false;
-
-               # (b) Check the shared cache and backfill APC
-               $value = $this->mainCache->get( $key );
-               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
-                       $this->srvCache->set( $key, $value, $staleTTL );
-                       wfDebugLog( 'replication', __METHOD__ . ": got lag times ($key) from main cache" );
-
-                       return $value['lagTimes']; // cache hit
-               }
-               $staleValue = $value ?: $staleValue;
-
-               # (c) Cache key missing or expired; regenerate and backfill
-               if ( $this->mainCache->lock( $key, 0, 10 ) ) {
-                       # Let this process alone update the cache value
-                       $cache = $this->mainCache;
-                       /** @noinspection PhpUnusedLocalVariableInspection */
-                       $unlocker = new ScopedCallback( function () use ( $cache, $key ) {
-                               $cache->unlock( $key );
-                       } );
-               } elseif ( $staleValue ) {
-                       # Could not acquire lock but an old cache exists, so use it
-                       return $staleValue['lagTimes'];
-               }
-
-               $lagTimes = [];
-               foreach ( $serverIndexes as $i ) {
-                       if ( $i == $this->parent->getWriterIndex() ) {
-                               $lagTimes[$i] = 0; // master always has no lag
-                               continue;
-                       }
-
-                       $conn = $this->parent->getAnyOpenConnection( $i );
-                       if ( $conn ) {
-                               $close = false; // already open
-                       } else {
-                               $conn = $this->parent->openConnection( $i, $wiki );
-                               $close = true; // new connection
-                       }
-
-                       if ( !$conn ) {
-                               $lagTimes[$i] = false;
-                               $host = $this->parent->getServerName( $i );
-                               wfDebugLog( 'replication', __METHOD__ . ": host $host (#$i) is unreachable" );
-                               continue;
-                       }
-
-                       $lagTimes[$i] = $conn->getLag();
-                       if ( $lagTimes[$i] === false ) {
-                               $host = $this->parent->getServerName( $i );
-                               wfDebugLog( 'replication', __METHOD__ . ": host $host (#$i) is not replicating?" );
-                       }
-
-                       if ( $close ) {
-                               # Close the connection to avoid sleeper connections piling up.
-                               # Note that the caller will pick one of these DBs and reconnect,
-                               # which is slightly inefficient, but this only matters for the lag
-                               # time cache miss cache, which is far less common that cache hits.
-                               $this->parent->closeConnection( $conn );
-                       }
-               }
-
-               # Add a timestamp key so we know when it was cached
-               $value = [ 'lagTimes' => $lagTimes, 'timestamp' => microtime( true ) ];
-               $this->mainCache->set( $key, $value, $staleTTL );
-               $this->srvCache->set( $key, $value, $staleTTL );
-               wfDebugLog( 'replication', __METHOD__ . ": re-calculated lag times ($key)" );
-
-               return $value['lagTimes'];
-       }
-
-       public function clearCaches() {
-               $key = $this->getLagTimeCacheKey();
-               $this->srvCache->delete( $key );
-               $this->mainCache->delete( $key );
-       }
-
-       private function getLagTimeCacheKey() {
-               $writerIndex = $this->parent->getWriterIndex();
-               // Lag is per-server, not per-DB, so key on the master DB name
-               return $this->srvCache->makeGlobalKey(
-                       'lag-times', $this->parent->getServerName( $writerIndex )
-               );
-       }
-}
index 8c019d8..dde678f 100644 (file)
@@ -439,7 +439,7 @@ class MWDebug {
 
                if ( $wgDebugComments ) {
                        $html .= "<!-- Debug output:\n" .
-                               htmlspecialchars( implode( "\n", self::$debug ) ) .
+                               htmlspecialchars( implode( "\n", self::$debug ), ENT_NOQUOTES ) .
                                "\n\n-->";
                }
 
index a6b53ec..4cf8313 100644 (file)
@@ -25,9 +25,9 @@ namespace MediaWiki\Logger;
  *
  * Usage:
  * @code
- * $wgMWLoggerDefaultSpi = array(
+ * $wgMWLoggerDefaultSpi = [
  *   'class' => '\\MediaWiki\\Logger\\LegacySpi',
- * );
+ * ];
  * @endcode
  *
  * @see \MediaWiki\Logger\LoggerFactory
index 49ad9e6..f44ff1c 100644 (file)
@@ -39,73 +39,73 @@ use ObjectFactory;
  * global configuration variable used by LoggerFactory to construct its
  * default SPI provider:
  * @code
- * $wgMWLoggerDefaultSpi = array(
+ * $wgMWLoggerDefaultSpi = [
  *   'class' => '\\MediaWiki\\Logger\\MonologSpi',
- *   'args' => array( array(
- *       'loggers' => array(
- *           '@default' => array(
- *               'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ),
- *               'handlers'   => array( 'stream' ),
- *           ),
- *           'runJobs' => array(
- *               'processors' => array( 'wiki', 'psr', 'pid' ),
- *               'handlers'   => array( 'stream' ),
- *           )
- *       ),
- *       'processors' => array(
- *           'wiki' => array(
+ *   'args' => [ [
+ *       'loggers' => [
+ *           '@default' => [
+ *               'processors' => [ 'wiki', 'psr', 'pid', 'uid', 'web' ],
+ *               'handlers'   => [ 'stream' ],
+ *           ],
+ *           'runJobs' => [
+ *               'processors' => [ 'wiki', 'psr', 'pid' ],
+ *               'handlers'   => [ 'stream' ],
+ *           ]
+ *       ],
+ *       'processors' => [
+ *           'wiki' => [
  *               'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
- *           ),
- *           'psr' => array(
+ *           ],
+ *           'psr' => [
  *               'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
- *           ),
- *           'pid' => array(
+ *           ],
+ *           'pid' => [
  *               'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
- *           ),
- *           'uid' => array(
+ *           ],
+ *           'uid' => [
  *               'class' => '\\Monolog\\Processor\\UidProcessor',
- *           ),
- *           'web' => array(
+ *           ],
+ *           'web' => [
  *               'class' => '\\Monolog\\Processor\\WebProcessor',
- *           ),
- *       ),
- *       'handlers' => array(
- *           'stream' => array(
+ *           ],
+ *       ],
+ *       'handlers' => [
+ *           'stream' => [
  *               'class'     => '\\Monolog\\Handler\\StreamHandler',
- *               'args'      => array( 'path/to/your.log' ),
+ *               'args'      => [ 'path/to/your.log' ],
  *               'formatter' => 'line',
- *           ),
- *           'redis' => array(
+ *           ],
+ *           'redis' => [
  *               'class'     => '\\Monolog\\Handler\\RedisHandler',
- *               'args'      => array( function() {
+ *               'args'      => [ function() {
  *                       $redis = new Redis();
  *                       $redis->connect( '127.0.0.1', 6379 );
  *                       return $redis;
  *                   },
  *                   'logstash'
- *               ),
+ *               ],
  *               'formatter' => 'logstash',
  *               'buffer' => true,
- *           ),
- *           'udp2log' => array(
+ *           ],
+ *           'udp2log' => [
  *               'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
- *               'args' => array(
+ *               'args' => [
  *                   'udp://127.0.0.1:8420/mediawiki
- *               ),
+ *               ],
  *               'formatter' => 'line',
- *           ),
- *       ),
- *       'formatters' => array(
- *           'line' => array(
+ *           ],
+ *       ],
+ *       'formatters' => [
+ *           'line' => [
  *               'class' => '\\Monolog\\Formatter\\LineFormatter',
- *            ),
- *            'logstash' => array(
+ *            ],
+ *            'logstash' => [
  *                'class' => '\\Monolog\\Formatter\\LogstashFormatter',
- *                'args'  => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ),
- *            ),
- *       ),
- *   ) ),
- * );
+ *                'args'  => [ 'mediawiki', php_uname( 'n' ), null, '', 1 ],
+ *            ],
+ *       ],
+ *   ] ],
+ * ];
  * @endcode
  *
  * @see https://github.com/Seldaek/monolog
index f92ff7d..82308d0 100644 (file)
@@ -28,9 +28,9 @@ use Psr\Log\NullLogger;
  *
  * Usage:
  *
- *     $wgMWLoggerDefaultSpi = array(
+ *     $wgMWLoggerDefaultSpi = [
  *         'class' => '\\MediaWiki\\Logger\\NullSpi',
- *     );
+ *     ];
  *
  * @see \MediaWiki\Logger\LoggerFactory
  * @since 1.25
index a348719..6585575 100644 (file)
@@ -24,7 +24,7 @@ class AtomicSectionUpdate implements DeferrableUpdate, DeferrableCallback {
                $this->callback = $callback;
 
                if ( $this->dbw->trxLevel() ) {
-                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname );
                }
        }
 
index d26cf9d..d61dec2 100644 (file)
@@ -23,7 +23,7 @@ class AutoCommitUpdate implements DeferrableUpdate, DeferrableCallback {
                $this->callback = $callback;
 
                if ( $this->dbw->trxLevel() ) {
-                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname );
                }
        }
 
index 281ac24..d2d8bd7 100644 (file)
 /**
  * Abstract base class for update jobs that do something with some secondary
  * data extracted from article.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- *       a transaction will automatically be wrapped around the update. If need be,
- *       subclasses can override the beginTransaction() and commitTransaction() methods.
  */
 abstract class DataUpdate implements DeferrableUpdate {
        /** @var mixed Result from LBFactory::getEmptyTransactionTicket() */
@@ -45,113 +41,16 @@ abstract class DataUpdate implements DeferrableUpdate {
                $this->ticket = $ticket;
        }
 
-       /**
-        * Begin an appropriate transaction, if any.
-        * This default implementation does nothing.
-        */
-       public function beginTransaction() {
-               // noop
-       }
-
-       /**
-        * Commit the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
-        */
-       public function commitTransaction() {
-               // noop
-       }
-
-       /**
-        * Abort / roll back the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
-        */
-       public function rollbackTransaction() {
-               // noop
-       }
-
        /**
         * Convenience method, calls doUpdate() on every DataUpdate in the array.
         *
-        * This methods supports transactions logic by first calling beginTransaction()
-        * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
-        * then calling commitTransaction() on each update. If an error occurs,
-        * rollbackTransaction() will be called on any update object that had beginTransaction()
-        * called but not yet commitTransaction().
-        *
-        * This allows for limited transactional logic across multiple backends for storing
-        * secondary data.
-        *
-        * @param DataUpdate[] $updates A list of DataUpdate instances
-        * @param string $mode Use "enqueue" to use the job queue when possible [Default: run]
-        * @throws Exception|null
-        */
-       public static function runUpdates( array $updates, $mode = 'run' ) {
-               if ( $mode === 'enqueue' ) {
-                       // When possible, push updates as jobs instead of calling doUpdate()
-                       $updates = self::enqueueUpdates( $updates );
-               }
-
-               if ( !count( $updates ) ) {
-                       return; // nothing to do
-               }
-
-               $open_transactions = [];
-               $exception = null;
-
-               try {
-                       // begin transactions
-                       foreach ( $updates as $update ) {
-                               $update->beginTransaction();
-                               $open_transactions[] = $update;
-                       }
-
-                       // do work
-                       foreach ( $updates as $update ) {
-                               $update->doUpdate();
-                       }
-
-                       // commit transactions
-                       while ( count( $open_transactions ) > 0 ) {
-                               $trans = array_pop( $open_transactions );
-                               $trans->commitTransaction();
-                       }
-               } catch ( Exception $ex ) {
-                       $exception = $ex;
-                       wfDebug( "Caught exception, will rethrow after rollback: " .
-                               $ex->getMessage() . "\n" );
-               }
-
-               // rollback remaining transactions
-               while ( count( $open_transactions ) > 0 ) {
-                       $trans = array_pop( $open_transactions );
-                       $trans->rollbackTransaction();
-               }
-
-               if ( $exception ) {
-                       throw $exception; // rethrow after cleanup
-               }
-       }
-
-       /**
-        * Enqueue jobs for every DataUpdate that support enqueueUpdate()
-        * and return the remaining DataUpdate objects (those that do not)
-        *
         * @param DataUpdate[] $updates A list of DataUpdate instances
-        * @return DataUpdate[]
-        * @since 1.27
+        * @throws Exception
+        * @deprecated Since 1.28 Use DeferredUpdates::execute()
         */
-       protected static function enqueueUpdates( array $updates ) {
-               $remaining = [];
-
+       public static function runUpdates( array $updates ) {
                foreach ( $updates as $update ) {
-                       if ( $update instanceof EnqueueableDataUpdate ) {
-                               $spec = $update->getAsJobSpecification();
-                               JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
-                       } else {
-                               $remaining[] = $update;
-                       }
+                       $update->doUpdate();
                }
-
-               return $remaining;
        }
 }
index ee14e1a..d24ebde 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class for managing the deferred updates
  * In web request mode, deferred updates can be run at the end of the request, either before or
  * after the HTTP response has been sent. In either case, they run after the DB commit step. If
  * an update runs after the response is sent, it will not block clients. If sent before, it will
- * run synchronously. If such an update works via queueing, it will be more likely to complete by
- * the time the client makes their next request after this one.
+ * run synchronously. These two modes are defined via PRESEND and POSTSEND constants, the latter
+ * being the default for addUpdate() and addCallableUpdate().
  *
- * In CLI mode, updates are only deferred until the current wiki has no DB write transaction
- * active within this request.
+ * Updates that work through this system will be more likely to complete by the time the client
+ * makes their next request after this one than with the JobQueue system.
  *
- * When updates are deferred, they use a FIFO queue (one for pre-send and one for post-send).
+ * In CLI mode, updates run immediately if no DB writes are pending. Otherwise, they run when:
+ *   - a) Any waitForReplication() call if no writes are pending on any DB
+ *   - b) A commit happens on Maintenance::getDB( DB_MASTER ) if no writes are pending on any DB
+ *   - c) EnqueueableDataUpdate tasks may enqueue on commit of Maintenance::getDB( DB_MASTER )
+ *   - d) At the completion of Maintenance::execute()
+ *
+ * When updates are deferred, they go into one two FIFO "top-queues" (one for pre-send and one
+ * for post-send). Updates enqueued *during* doUpdate() of a "top" update go into the "sub-queue"
+ * for that update. After that method finishes, the sub-queue is run until drained. This continues
+ * for each top-queue job until the entire top queue is drained. This happens for the pre-send
+ * top-queue, and later on, the post-send top-queue, in execute().
  *
  * @since 1.19
  */
@@ -42,22 +53,43 @@ class DeferredUpdates {
        /** @var DeferrableUpdate[] Updates to be deferred until after request end */
        private static $postSendUpdates = [];
 
-       const ALL = 0; // all updates
+       const ALL = 0; // all updates; in web requests, use only after flushing the output buffer
        const PRESEND = 1; // for updates that should run before flushing output buffer
        const POSTSEND = 2; // for updates that should run after flushing output buffer
 
+       const BIG_QUEUE_SIZE = 100;
+
+       /** @var array|null Information about the current execute() call or null if not running */
+       private static $executeContext;
+
        /**
-        * Add an update to the deferred list
+        * Add an update to the deferred list to be run later by execute()
+        *
+        * In CLI mode, callback magic will also be used to run updates when safe
         *
         * @param DeferrableUpdate $update Some object that implements doUpdate()
-        * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
         */
-       public static function addUpdate( DeferrableUpdate $update, $type = self::POSTSEND ) {
-               if ( $type === self::PRESEND ) {
+       public static function addUpdate( DeferrableUpdate $update, $stage = self::POSTSEND ) {
+               global $wgCommandLineMode;
+
+               // This is a sub-DeferredUpdate, run it right after its parent update
+               if ( self::$executeContext && self::$executeContext['stage'] >= $stage ) {
+                       self::$executeContext['subqueue'][] = $update;
+                       return;
+               }
+
+               if ( $stage === self::PRESEND ) {
                        self::push( self::$preSendUpdates, $update );
                } else {
                        self::push( self::$postSendUpdates, $update );
                }
+
+               // Try to run the updates now if in CLI mode and no transaction is active.
+               // This covers scripts that don't/barely use the DB but make updates to other stores.
+               if ( $wgCommandLineMode ) {
+                       self::tryOpportunisticExecute( 'run' );
+               }
        }
 
        /**
@@ -67,34 +99,38 @@ class DeferredUpdates {
         * @see MWCallableUpdate::__construct()
         *
         * @param callable $callable
-        * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
         * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28)
         */
        public static function addCallableUpdate(
-               $callable, $type = self::POSTSEND, IDatabase $dbw = null
+               $callable, $stage = self::POSTSEND, IDatabase $dbw = null
        ) {
-               self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $type );
+               self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $stage );
        }
 
        /**
         * Do any deferred updates and clear the list
         *
         * @param string $mode Use "enqueue" to use the job queue when possible [Default: "run"]
-        * @param integer $type DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
         */
-       public static function doUpdates( $mode = 'run', $type = self::ALL ) {
-               if ( $type === self::ALL || $type == self::PRESEND ) {
-                       self::execute( self::$preSendUpdates, $mode );
+       public static function doUpdates( $mode = 'run', $stage = self::ALL ) {
+               $stageEffective = ( $stage === self::ALL ) ? self::POSTSEND : $stage;
+
+               if ( $stage === self::ALL || $stage === self::PRESEND ) {
+                       self::execute( self::$preSendUpdates, $mode, $stageEffective );
                }
 
-               if ( $type === self::ALL || $type == self::POSTSEND ) {
-                       self::execute( self::$postSendUpdates, $mode );
+               if ( $stage === self::ALL || $stage == self::POSTSEND ) {
+                       self::execute( self::$postSendUpdates, $mode, $stageEffective );
                }
        }
 
+       /**
+        * @param DeferrableUpdate[] $queue
+        * @param DeferrableUpdate $update
+        */
        private static function push( array &$queue, DeferrableUpdate $update ) {
-               global $wgCommandLineMode;
-
                if ( $update instanceof MergeableUpdate ) {
                        $class = get_class( $update ); // fully-qualified class
                        if ( isset( $queue[$class] ) ) {
@@ -107,78 +143,175 @@ class DeferredUpdates {
                } else {
                        $queue[] = $update;
                }
-
-               // CLI scripts may forget to periodically flush these updates,
-               // so try to handle that rather than OOMing and losing them entirely.
-               // Try to run the updates as soon as there is no current wiki transaction.
-               static $waitingOnTrx = false; // de-duplicate callback
-               if ( $wgCommandLineMode && !$waitingOnTrx ) {
-                       $lb = wfGetLB();
-                       $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
-                       // Do the update as soon as there is no transaction
-                       if ( $dbw && $dbw->trxLevel() ) {
-                               $waitingOnTrx = true;
-                               $dbw->onTransactionIdle( function() use ( &$waitingOnTrx ) {
-                                       DeferredUpdates::doUpdates();
-                                       $waitingOnTrx = false;
-                               } );
-                       } else {
-                               self::doUpdates();
-                       }
-               }
        }
 
-       public static function execute( array &$queue, $mode ) {
-               $stats = \MediaWiki\MediaWikiServices::getInstance()->getStatsdDataFactory();
+       /**
+        * Immediately run/queue a list of updates
+        *
+        * @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects
+        * @param string $mode Use "enqueue" to use the job queue when possible
+        * @param integer $stage Class constant (PRESEND, POSTSEND) (since 1.28)
+        * @throws ErrorPageError Happens on top-level calls
+        * @throws Exception Happens on second-level calls
+        */
+       protected static function execute( array &$queue, $mode, $stage ) {
+               $services = MediaWikiServices::getInstance();
+               $stats = $services->getStatsdDataFactory();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $method = RequestContext::getMain()->getRequest()->getMethod();
 
-               $updates = $queue; // snapshot of queue
-               // Keep doing rounds of updates until none get enqueued
-               while ( count( $updates ) ) {
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+
+               /** @var ErrorPageError $reportableError */
+               $reportableError = null;
+               /** @var DeferrableUpdate[] $updates Snapshot of queue */
+               $updates = $queue;
+
+               // Keep doing rounds of updates until none get enqueued...
+               while ( $updates ) {
                        $queue = []; // clear the queue
-                       /** @var DataUpdate[] $dataUpdates */
-                       $dataUpdates = [];
-                       /** @var DeferrableUpdate[] $otherUpdates */
-                       $otherUpdates = [];
-                       foreach ( $updates as $update ) {
-                               if ( $update instanceof DataUpdate ) {
-                                       $dataUpdates[] = $update;
+
+                       if ( $mode === 'enqueue' ) {
+                               try {
+                                       // Push enqueuable updates to the job queue and get the rest
+                                       $updates = self::enqueueUpdates( $updates );
+                               } catch ( Exception $e ) {
+                                       // Let other updates have a chance to run if this failed
+                                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+                               }
+                       }
+
+                       // Order will be DataUpdate followed by generic DeferrableUpdate tasks
+                       $updatesByType = [ 'data' => [], 'generic' => [] ];
+                       foreach ( $updates as $du ) {
+                               if ( $du instanceof DataUpdate ) {
+                                       $du->setTransactionTicket( $ticket );
+                                       $updatesByType['data'][] = $du;
                                } else {
-                                       $otherUpdates[] = $update;
+                                       $updatesByType['generic'][] = $du;
                                }
 
-                               $name = $update instanceof DeferrableCallback
-                                       ? get_class( $update ) . '-' . $update->getOrigin()
-                                       : get_class( $update );
+                               $name = ( $du instanceof DeferrableCallback )
+                                       ? get_class( $du ) . '-' . $du->getOrigin()
+                                       : get_class( $du );
                                $stats->increment( 'deferred_updates.' . $method . '.' . $name );
                        }
 
-                       // Delegate DataUpdate execution to the DataUpdate class
-                       try {
-                               DataUpdate::runUpdates( $dataUpdates, $mode );
-                       } catch ( Exception $e ) {
-                               // Let the other updates occur if these had to rollback
-                               MWExceptionHandler::logException( $e );
-                       }
-                       // Execute the non-DataUpdate tasks
-                       foreach ( $otherUpdates as $update ) {
-                               try {
-                                       $update->doUpdate();
-                                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
-                               } catch ( Exception $e ) {
-                                       // We don't want exceptions thrown during deferred updates to
-                                       // be reported to the user since the output is already sent
-                                       if ( !$e instanceof ErrorPageError ) {
-                                               MWExceptionHandler::logException( $e );
+                       // Execute all remaining tasks...
+                       foreach ( $updatesByType as $updatesForType ) {
+                               foreach ( $updatesForType as $update ) {
+                                       self::$executeContext = [
+                                               'update' => $update,
+                                               'stage' => $stage,
+                                               'subqueue' => []
+                                       ];
+                                       /** @var DeferrableUpdate $update */
+                                       $guiError = self::runUpdate( $update, $lbFactory, $stage );
+                                       $reportableError = $reportableError ?: $guiError;
+                                       // Do the subqueue updates for $update until there are none
+                                       while ( self::$executeContext['subqueue'] ) {
+                                               $subUpdate = reset( self::$executeContext['subqueue'] );
+                                               $firstKey = key( self::$executeContext['subqueue'] );
+                                               unset( self::$executeContext['subqueue'][$firstKey] );
+
+                                               $guiError = self::runUpdate( $subUpdate, $lbFactory, $stage );
+                                               $reportableError = $reportableError ?: $guiError;
                                        }
-                                       // Make sure incomplete transactions are not committed and end any
-                                       // open atomic sections so that other DB updates have a chance to run
-                                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
+                                       self::$executeContext = null;
                                }
                        }
 
                        $updates = $queue; // new snapshot of queue (check for new entries)
                }
+
+               if ( $reportableError ) {
+                       throw $reportableError; // throw the first of any GUI errors
+               }
+       }
+
+       /**
+        * @param DeferrableUpdate $update
+        * @param LBFactory $lbFactory
+        * @param integer $stage
+        * @return ErrorPageError|null
+        */
+       private static function runUpdate( DeferrableUpdate $update, LBFactory $lbFactory, $stage ) {
+               $guiError = null;
+               try {
+                       $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+                       $lbFactory->beginMasterChanges( $fnameTrxOwner );
+                       $update->doUpdate();
+                       $lbFactory->commitMasterChanges( $fnameTrxOwner );
+               } catch ( Exception $e ) {
+                       // Reporting GUI exceptions does not work post-send
+                       if ( $e instanceof ErrorPageError && $stage === self::PRESEND ) {
+                               $guiError = $e;
+                       }
+                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+               }
+
+               return $guiError;
+       }
+
+       /**
+        * Run all deferred updates immediately if there are no DB writes active
+        *
+        * If $mode is 'run' but there are busy databates, EnqueueableDataUpdate
+        * tasks will be enqueued anyway for the sake of progress.
+        *
+        * @param string $mode Use "enqueue" to use the job queue when possible
+        * @return bool Whether updates were allowed to run
+        * @since 1.28
+        */
+       public static function tryOpportunisticExecute( $mode = 'run' ) {
+               // execute() loop is already running
+               if ( self::$executeContext ) {
+                       return false;
+               }
+
+               // Avoiding running updates without them having outer scope
+               if ( !self::getBusyDbConnections() ) {
+                       self::doUpdates( $mode );
+                       return true;
+               }
+
+               if ( self::pendingUpdatesCount() >= self::BIG_QUEUE_SIZE ) {
+                       // If we cannot run the updates with outer transaction context, try to
+                       // at least enqueue all the updates that support queueing to job queue
+                       self::$preSendUpdates = self::enqueueUpdates( self::$preSendUpdates );
+                       self::$postSendUpdates = self::enqueueUpdates( self::$postSendUpdates );
+               }
+
+               return !self::pendingUpdatesCount();
+       }
+
+       /**
+        * Enqueue a job for each EnqueueableDataUpdate item and return the other items
+        *
+        * @param DeferrableUpdate[] $updates A list of deferred update instances
+        * @return DeferrableUpdate[] Remaining updates that do not support being queued
+        */
+       private static function enqueueUpdates( array $updates ) {
+               $remaining = [];
+
+               foreach ( $updates as $update ) {
+                       if ( $update instanceof EnqueueableDataUpdate ) {
+                               $spec = $update->getAsJobSpecification();
+                               JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
+                       } else {
+                               $remaining[] = $update;
+                       }
+               }
+
+               return $remaining;
+       }
+
+       /**
+        * @return integer Number of enqueued updates
+        * @since 1.28
+        */
+       public static function pendingUpdatesCount() {
+               return count( self::$preSendUpdates ) + count( self::$postSendUpdates );
        }
 
        /**
@@ -189,4 +322,22 @@ class DeferredUpdates {
                self::$preSendUpdates = [];
                self::$postSendUpdates = [];
        }
+
+       /**
+        * @return IDatabase[] Connection where commit() cannot be called yet
+        */
+       private static function getBusyDbConnections() {
+               $connsBusy = [];
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->forEachLB( function ( LoadBalancer $lb ) use ( &$connsBusy ) {
+                       $lb->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$connsBusy ) {
+                               if ( $conn->writesOrCallbacksPending() || $conn->explicitTrxActive() ) {
+                                       $connsBusy[] = $conn;
+                               }
+                       } );
+               } );
+
+               return $connsBusy;
+       }
 }
index 47f2b21..4159166 100644 (file)
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
+
 /**
  * Update object handling the cleanup of links tables after a page was deleted.
  **/
-class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
        /** @var WikiPage */
        protected $page;
        /** @var integer */
@@ -30,6 +32,9 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        /** @var string */
        protected $timestamp;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * @param WikiPage $page Page we are updating
         * @param integer|null $pageId ID of the page we are updating [optional]
@@ -37,7 +42,7 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
         * @throws MWException
         */
        function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
-               parent::__construct( false ); // no implicit transaction
+               parent::__construct();
 
                $this->page = $page;
                if ( $pageId ) {
@@ -52,23 +57,28 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        }
 
        public function doUpdate() {
-               $config = RequestContext::getMain()->getConfig();
+               $services = MediaWikiServices::getInstance();
+               $config = $services->getMainConfig();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $batchSize = $config->get( 'UpdateRowsPerQuery' );
-               $factory = wfGetLBFactory();
 
                // Page may already be deleted, so don't just getId()
                $id = $this->pageId;
-               // Make sure all links update threads see the changes of each other.
-               // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = LinksUpdate::acquirePageLock( $this->mDb, $id );
+
+               if ( $this->ticket ) {
+                       // Make sure all links update threads see the changes of each other.
+                       // This handles the case when updates have to batched into several COMMITs.
+                       $scopedLock = LinksUpdate::acquirePageLock( $this->getDB(), $id );
+               }
 
                $title = $this->page->getTitle();
+               $dbw = $this->getDB(); // convenience
 
                // Delete restrictions for it
-               $this->mDb->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
+               $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
 
                // Fix category table counts
-               $cats = $this->mDb->selectFieldValues(
+               $cats = $dbw->selectFieldValues(
                        'categorylinks',
                        'cl_to',
                        [ 'cl_from' => $id ],
@@ -78,8 +88,8 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                foreach ( $catBatches as $catBatch ) {
                        $this->page->updateCategoryCounts( [], $catBatch, $id );
                        if ( count( $catBatches ) > 1 ) {
-                               $factory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                );
                        }
                }
@@ -87,19 +97,19 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                // Refresh the category table entry if it seems to have no pages. Check
                // master for the most up-to-date cat_pages count.
                if ( $title->getNamespace() === NS_CATEGORY ) {
-                       $row = $this->mDb->selectRow(
+                       $row = $dbw->selectRow(
                                'category',
                                [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
                                [ 'cat_title' => $title->getDBkey(), 'cat_pages <= 0' ],
                                __METHOD__
                        );
                        if ( $row ) {
-                               $cat = Category::newFromRow( $row, $title )->refreshCounts();
+                               Category::newFromRow( $row, $title )->refreshCounts();
                        }
                }
 
                // If using cascading deletes, we can skip some explicit deletes
-               if ( !$this->mDb->cascadingDeletes() ) {
+               if ( !$dbw->cascadingDeletes() ) {
                        // Delete outgoing links
                        $this->batchDeleteByPK(
                                'pagelinks',
@@ -144,14 +154,14 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                $batchSize
                        );
                        // Delete any redirect entry or page props entries
-                       $this->mDb->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
-                       $this->mDb->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
+                       $dbw->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
+                       $dbw->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
                }
 
                // If using cleanup triggers, we can skip some manual deletes
-               if ( !$this->mDb->cleanupTriggers() ) {
+               if ( !$dbw->cleanupTriggers() ) {
                        // Find recentchanges entries to clean up...
-                       $rcIdsForTitle = $this->mDb->selectFieldValues(
+                       $rcIdsForTitle = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [
@@ -159,11 +169,11 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                        'rc_namespace' => $title->getNamespace(),
                                        'rc_title' => $title->getDBkey(),
                                        'rc_timestamp < ' .
-                                               $this->mDb->addQuotes( $this->mDb->timestamp( $this->timestamp ) )
+                                               $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) )
                                ],
                                __METHOD__
                        );
-                       $rcIdsForPage = $this->mDb->selectFieldValues(
+                       $rcIdsForPage = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [ 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ],
@@ -173,31 +183,33 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                        // T98706: delete by PK to avoid lock contention with RC delete log insertions
                        $rcIdBatches = array_chunk( array_merge( $rcIdsForTitle, $rcIdsForPage ), $batchSize );
                        foreach ( $rcIdBatches as $rcIdBatch ) {
-                               $this->mDb->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
+                               $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
                                if ( count( $rcIdBatches ) > 1 ) {
-                                       $factory->commitAndWaitForReplication(
-                                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                                       $lbFactory->commitAndWaitForReplication(
+                                               __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                        );
                                }
                        }
                }
 
-               // Commit and release the lock
+               // Commit and release the lock (if set)
                ScopedCallback::consume( $scopedLock );
        }
 
        private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
-               $dbw = $this->mDb; // convenience
-               $factory = wfGetLBFactory();
+               $services = MediaWikiServices::getInstance();
+               $lbFactory = $services->getDBLoadBalancerFactory();
+               $dbw = $this->getDB(); // convenience
+
                $res = $dbw->select( $table, $pk, $conds, __METHOD__ );
 
                $pkDeleteConds = [];
                foreach ( $res as $row ) {
-                       $pkDeleteConds[] = $this->mDb->makeList( (array)$row, LIST_AND );
+                       $pkDeleteConds[] = $dbw->makeList( (array)$row, LIST_AND );
                        if ( count( $pkDeleteConds ) >= $bSize ) {
                                $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
-                               $factory->commitAndWaitForReplication(
-                                       __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
                                );
                                $pkDeleteConds = [];
                        }
@@ -208,9 +220,17 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                }
        }
 
+       protected function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'deleteLinks',
                                [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
index 5e02c5c..d18349b 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * Class the manages updates of *_link tables as well as similar extension-managed tables
  *
@@ -27,7 +29,7 @@
  *
  * See docs/deferred.txt
  */
-class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        // @todo make members protected, but make sure extensions don't break
 
        /** @var int Page ID of the article linked from */
@@ -79,11 +81,24 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         */
        private $linkDeletions = null;
 
+       /**
+        * @var null|array Added properties if calculated.
+        */
+       private $propertyInsertions = null;
+
+       /**
+        * @var null|array Deleted properties if calculated.
+        */
+       private $propertyDeletions = null;
+
        /**
         * @var User|null
         */
        private $user;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * Constructor
         *
@@ -93,8 +108,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @throws MWException
         */
        function __construct( Title $title, ParserOutput $parserOutput, $recursive = true ) {
-               // Implicit transactions are disabled as they interfere with batching
-               parent::__construct( false );
+               parent::__construct();
 
                $this->mTitle = $title;
                $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
@@ -148,19 +162,24 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @note: this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
         */
        public function doUpdate() {
-               // Make sure all links update threads see the changes of each other.
-               // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = self::acquirePageLock( $this->mDb, $this->mId );
+               if ( $this->ticket ) {
+                       // Make sure all links update threads see the changes of each other.
+                       // This handles the case when updates have to batched into several COMMITs.
+                       $scopedLock = self::acquirePageLock( $this->getDB(), $this->mId );
+               }
 
                Hooks::run( 'LinksUpdate', [ &$this ] );
                $this->doIncrementalUpdate();
 
-               // Commit and release the lock
+               // Commit and release the lock (if set)
                ScopedCallback::consume( $scopedLock );
                // Run post-commit hooks without DBO_TRX
-               $this->mDb->onTransactionIdle( function() {
-                       Hooks::run( 'LinksUpdateComplete', [ &$this ] );
-               } );
+               $this->getDB()->onTransactionIdle(
+                       function () {
+                               Hooks::run( 'LinksUpdateComplete', [ &$this ] );
+                       },
+                       __METHOD__
+               );
        }
 
        /**
@@ -234,12 +253,13 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                # Page properties
                $existing = $this->getExistingProperties();
-               $propertiesDeletes = $this->getPropertyDeletions( $existing );
-               $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
+               $this->propertyDeletions = $this->getPropertyDeletions( $existing );
+               $this->incrTableUpdate( 'page_props', 'pp', $this->propertyDeletions,
                        $this->getPropertyInsertions( $existing ) );
 
                # Invalidate the necessary pages
-               $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
+               $this->propertyInsertions = array_diff_assoc( $this->mProperties, $existing );
+               $changed = $this->propertyDeletions + $this->propertyInsertions;
                $this->invalidateProperties( $changed );
 
                # Refresh links of all pages including this page
@@ -304,7 +324,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $cats
         */
        function invalidateCategories( $cats ) {
-               PurgeJobUtils::invalidatePages( $this->mDb, NS_CATEGORY, array_keys( $cats ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_CATEGORY, array_keys( $cats ) );
        }
 
        /**
@@ -323,7 +343,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $images
         */
        function invalidateImageDescriptions( $images ) {
-               PurgeJobUtils::invalidatePages( $this->mDb, NS_FILE, array_keys( $images ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_FILE, array_keys( $images ) );
        }
 
        /**
@@ -334,8 +354,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $insertions Rows to insert
         */
        private function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
-               $bSize = RequestContext::getMain()->getConfig()->get( 'UpdateRowsPerQuery' );
-               $factory = wfGetLBFactory();
+               $services = MediaWikiServices::getInstance();
+               $bSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
+               $factory = $services->getDBLoadBalancerFactory();
 
                if ( $table === 'page_props' ) {
                        $fromField = 'pp_page';
@@ -367,7 +388,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        foreach ( $deletionBatches as $deletionBatch ) {
                                $deleteWheres[] = [
                                        $fromField => $this->mId,
-                                       $this->mDb->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
+                                       $this->getDB()->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
                                ];
                        }
                } else {
@@ -386,17 +407,17 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                foreach ( $deleteWheres as $deleteWhere ) {
-                       $this->mDb->delete( $table, $deleteWhere, __METHOD__ );
+                       $this->getDB()->delete( $table, $deleteWhere, __METHOD__ );
                        $factory->commitAndWaitForReplication(
-                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
                        );
                }
 
                $insertBatches = array_chunk( $insertions, $bSize );
                foreach ( $insertBatches as $insertBatch ) {
-                       $this->mDb->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
+                       $this->getDB()->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
                        $factory->commitAndWaitForReplication(
-                               __METHOD__, $this->ticket, [ 'wiki' => $this->mDb->getWikiID() ]
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
                        );
                }
 
@@ -483,7 +504,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                foreach ( $diffs as $url => $dummy ) {
                        foreach ( wfMakeUrlIndexes( $url ) as $index ) {
                                $arr[] = [
-                                       'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
+                                       'el_id' => $this->getDB()->nextSequenceValue( 'externallinks_el_id_seq' ),
                                        'el_from' => $this->mId,
                                        'el_to' => $url,
                                        'el_index' => $index,
@@ -529,7 +550,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                                'cl_from' => $this->mId,
                                'cl_to' => $name,
                                'cl_sortkey' => $sortkey,
-                               'cl_timestamp' => $this->mDb->timestamp(),
+                               'cl_timestamp' => $this->getDB()->timestamp(),
                                'cl_sortkey_prefix' => $prefix,
                                'cl_collation' => $wgCategoryCollation,
                                'cl_type' => $type,
@@ -767,8 +788,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingLinks() {
-               $res = $this->mDb->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
-                       [ 'pl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
+                       [ 'pl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->pl_namespace] ) ) {
@@ -786,8 +807,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingTemplates() {
-               $res = $this->mDb->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
-                       [ 'tl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
+                       [ 'tl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->tl_namespace] ) ) {
@@ -805,8 +826,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingImages() {
-               $res = $this->mDb->select( 'imagelinks', [ 'il_to' ],
-                       [ 'il_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'imagelinks', [ 'il_to' ],
+                       [ 'il_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->il_to] = 1;
@@ -821,8 +842,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingExternals() {
-               $res = $this->mDb->select( 'externallinks', [ 'el_to' ],
-                       [ 'el_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'externallinks', [ 'el_to' ],
+                       [ 'el_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->el_to] = 1;
@@ -837,8 +858,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingCategories() {
-               $res = $this->mDb->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
-                       [ 'cl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
+                       [ 'cl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->cl_to] = $row->cl_sortkey_prefix;
@@ -854,8 +875,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingInterlangs() {
-               $res = $this->mDb->select( 'langlinks', [ 'll_lang', 'll_title' ],
-                       [ 'll_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'langlinks', [ 'll_lang', 'll_title' ],
+                       [ 'll_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->ll_lang] = $row->ll_title;
@@ -868,9 +889,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * Get an array of existing inline interwiki links, as a 2-D array
         * @return array (prefix => array(dbkey => 1))
         */
-       protected function getExistingInterwikis() {
-               $res = $this->mDb->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
-                       [ 'iwl_from' => $this->mId ], __METHOD__, $this->mOptions );
+       private function getExistingInterwikis() {
+               $res = $this->getDB()->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
+                       [ 'iwl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->iwl_prefix] ) ) {
@@ -888,8 +909,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array Array of property names and values
         */
        private function getExistingProperties() {
-               $res = $this->mDb->select( 'page_props', [ 'pp_propname', 'pp_value' ],
-                       [ 'pp_page' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'page_props', [ 'pp_propname', 'pp_value' ],
+                       [ 'pp_page' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->pp_propname] = $row->pp_value;
@@ -1016,21 +1037,52 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                return $result;
        }
 
+       /**
+        * Fetch page properties added by this LinksUpdate.
+        * Only available after the update is complete.
+        * @since 1.28
+        * @return null|array
+        */
+       public function getAddedProperties() {
+               return $this->propertyInsertions;
+       }
+
+       /**
+        * Fetch page properties removed by this LinksUpdate.
+        * Only available after the update is complete.
+        * @since 1.28
+        * @return null|array
+        */
+       public function getRemovedProperties() {
+               return $this->propertyDeletions;
+       }
+
        /**
         * Update links table freshness
         */
-       protected function updateLinksTimestamp() {
+       private function updateLinksTimestamp() {
                if ( $this->mId ) {
                        // The link updates made here only reflect the freshness of the parser output
                        $timestamp = $this->mParserOutput->getCacheTime();
-                       $this->mDb->update( 'page',
-                               [ 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ],
+                       $this->getDB()->update( 'page',
+                               [ 'page_links_updated' => $this->getDB()->timestamp( $timestamp ) ],
                                [ 'page_id' => $this->mId ],
                                __METHOD__
                        );
                }
        }
 
+       /**
+        * @return IDatabase
+        */
+       private function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                if ( $this->user ) {
                        $userInfo = [
@@ -1048,7 +1100,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'refreshLinksPrioritized',
                                [
index 47b162c..5247e97 100644 (file)
@@ -19,7 +19,7 @@ class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback {
                $this->fname = $fname;
 
                if ( $dbw && $dbw->trxLevel() ) {
-                       $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+                       $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname );
                }
        }
 
index 30aae15..ab4a609 100644 (file)
  *
  * @file
  */
+use Wikimedia\Assert\Assert;
 
 /**
  * Class for handling updates to the site_stats table
  */
-class SiteStatsUpdate implements DeferrableUpdate {
+class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
        /** @var int */
        protected $edits = 0;
-
        /** @var int */
        protected $pages = 0;
-
        /** @var int */
        protected $articles = 0;
-
        /** @var int */
        protected $users = 0;
-
        /** @var int */
        protected $images = 0;
 
+       private static $counters = [ 'edits', 'pages', 'articles', 'users', 'images' ];
+
        // @todo deprecate this constructor
        function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
                $this->edits = $edits;
@@ -45,6 +44,15 @@ class SiteStatsUpdate implements DeferrableUpdate {
                $this->users = $users;
        }
 
+       public function merge( MergeableUpdate $update ) {
+               /** @var SiteStatsUpdate $update */
+               Assert::parameterType( __CLASS__, $update, '$update' );
+
+               foreach ( self::$counters as $field ) {
+                       $this->$field += $update->$field;
+               }
+       }
+
        /**
         * @param array $deltas
         * @return SiteStatsUpdate
@@ -52,8 +60,7 @@ class SiteStatsUpdate implements DeferrableUpdate {
        public static function factory( array $deltas ) {
                $update = new self( 0, 0, 0 );
 
-               $fields = [ 'views', 'edits', 'pages', 'articles', 'users', 'images' ];
-               foreach ( $fields as $field ) {
+               foreach ( self::$counters as $field ) {
                        if ( isset( $deltas[$field] ) && $deltas[$field] ) {
                                $update->$field = $deltas[$field];
                        }
@@ -133,7 +140,7 @@ class SiteStatsUpdate implements DeferrableUpdate {
         */
        public static function cacheUpdate( $dbw ) {
                global $wgActiveUserDays;
-               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+               $dbr = wfGetDB( DB_REPLICA, 'vslow' );
                # Get non-bot users than did some recent action other than making accounts.
                # If account creation is included, the number gets inflated ~20+ fold on enwiki.
                $activeUsers = $dbr->selectField(
index ff06915..25e8841 100644 (file)
  */
 
 /**
- * Abstract base class for update jobs that put some secondary data extracted
- * from article content into the database.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- *       a transaction will automatically be wrapped around the update. Starting another
- *       one would break the outer transaction bracket. If need be, subclasses can override
- *       the beginTransaction() and commitTransaction() methods.
+ * @deprecated Since 1.28 Use DataUpdate directly, injecting the database
  */
 abstract class SqlDataUpdate extends DataUpdate {
        /** @var IDatabase Database connection reference */
        protected $mDb;
-
        /** @var array SELECT options to be used (array) */
        protected $mOptions = [];
 
-       /** @var bool Whether a transaction is open on this object (internal use only!) */
-       private $mHasTransaction;
-
-       /** @var bool Whether this update should be wrapped in a transaction */
-       protected $mUseTransaction;
-
-       /**
-        * Constructor
-        *
-        * @param bool $withTransaction Whether this update should be wrapped in a
-        *   transaction (default: true). A transaction is only started if no
-        *   transaction is already in progress, see beginTransaction() for details.
-        */
-       public function __construct( $withTransaction = true ) {
+       public function __construct() {
                parent::__construct();
 
                $this->mDb = wfGetLB()->getLazyConnectionRef( DB_MASTER );
-
-               $this->mWithTransaction = $withTransaction;
-               $this->mHasTransaction = false;
-       }
-
-       /**
-        * Begin a database transaction, if $withTransaction was given as true in
-        * the constructor for this SqlDataUpdate.
-        *
-        * Because nested transactions are not supported by the Database class,
-        * this implementation checks Database::trxLevel() and only opens a
-        * transaction if none is already active.
-        */
-       public function beginTransaction() {
-               if ( !$this->mWithTransaction ) {
-                       return;
-               }
-
-               // NOTE: nested transactions are not supported, only start a transaction if none is open
-               if ( $this->mDb->trxLevel() === 0 ) {
-                       $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
-                       $this->mHasTransaction = true;
-               }
-       }
-
-       /**
-        * Commit the database transaction started via beginTransaction (if any).
-        */
-       public function commitTransaction() {
-               if ( $this->mHasTransaction ) {
-                       $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
-                       $this->mHasTransaction = false;
-               }
-       }
-
-       /**
-        * Abort the database transaction started via beginTransaction (if any).
-        */
-       public function abortTransaction() {
-               if ( $this->mHasTransaction ) { // XXX: actually... maybe always?
-                       $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
-                       $this->mHasTransaction = false;
-               }
        }
 }
index baec396..1537535 100644 (file)
@@ -180,7 +180,7 @@ class DifferenceEngine extends ContextSource {
         */
        public function deletedLink( $id ) {
                if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow( 'archive', '*',
                                [ 'ar_rev_id' => $id ],
                                __METHOD__ );
@@ -528,7 +528,7 @@ class DifferenceEngine extends ContextSource {
                        RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
                ) {
                        // Look for an unpatrolled change corresponding to this diff
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                        $change = RecentChange::newFromConds(
                                [
                                        'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
@@ -695,10 +695,10 @@ class DifferenceEngine extends ContextSource {
        }
 
        /**
-        * Add style sheets and supporting JS for diff display.
+        * Add style sheets for diff display.
         */
        public function showDiffStyle() {
-               $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' );
+               $this->getOutput()->addModuleStyles( 'mediawiki.diff.styles' );
        }
 
        /**
@@ -1338,7 +1338,7 @@ class DifferenceEngine extends ContextSource {
                }
 
                // Load tags information for both revisions
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $this->mOldid !== false ) {
                        $this->mOldTags = $dbr->selectField(
                                'tag_summary',
index 0a174fe..5496cb6 100644 (file)
@@ -71,37 +71,7 @@ class MWException extends Exception {
         * @return string|null String to output or null if any hook has been called
         */
        public function runHooks( $name, $args = [] ) {
-               global $wgExceptionHooks;
-
-               if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) {
-                       return null; // Just silently ignore
-               }
-
-               if ( !array_key_exists( $name, $wgExceptionHooks ) ||
-                       !is_array( $wgExceptionHooks[$name] )
-               ) {
-                       return null;
-               }
-
-               $hooks = $wgExceptionHooks[$name];
-               $callargs = array_merge( [ $this ], $args );
-
-               foreach ( $hooks as $hook ) {
-                       if (
-                               is_string( $hook ) ||
-                               ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) )
-                       ) {
-                               // 'function' or [ 'class', 'hook' ]
-                               $result = call_user_func_array( $hook, $callargs );
-                       } else {
-                               $result = null;
-                       }
-
-                       if ( is_string( $result ) ) {
-                               return $result;
-                       }
-               }
-               return null;
+               return MWExceptionRenderer::runHooks( $this, $name, $args );
        }
 
        /**
@@ -229,20 +199,7 @@ class MWException extends Exception {
         * It will be either HTML or plain text based on isCommandLine().
         */
        public function report() {
-               global $wgMimeType;
-
-               if ( defined( 'MW_API' ) ) {
-                       // Unhandled API exception, we can't be sure that format printer is alive
-                       self::header( 'MediaWiki-API-Error: internal_api_error_' . get_class( $this ) );
-                       wfHttpError( 500, 'Internal Server Error', $this->getText() );
-               } elseif ( self::isCommandLine() ) {
-                       MWExceptionHandler::printError( $this->getText() );
-               } else {
-                       self::statusHeader( 500 );
-                       self::header( "Content-Type: $wgMimeType; charset=utf-8" );
-
-                       $this->reportHTML();
-               }
+               MWExceptionRenderer::output( $this, MWExceptionRenderer::AS_PRETTY );
        }
 
        /**
index 7d8b244..8359846 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Handler class for MWExceptions
@@ -59,71 +60,14 @@ class MWExceptionHandler {
         * @param Exception|Throwable $e
         */
        protected static function report( $e ) {
-               global $wgShowExceptionDetails;
-
-               $cmdLine = MWException::isCommandLine();
-
-               if ( $e instanceof MWException ) {
-                       try {
-                               // Try and show the exception prettily, with the normal skin infrastructure
-                               $e->report();
-                       } catch ( Exception $e2 ) {
-                               // Exception occurred from within exception handler
-                               // Show a simpler message for the original exception,
-                               // don't try to invoke report()
-                               $message = "MediaWiki internal error.\n\n";
-
-                               if ( $wgShowExceptionDetails ) {
-                                       $message .= 'Original exception: ' . self::getLogMessage( $e ) .
-                                               "\nBacktrace:\n" . self::getRedactedTraceAsString( $e ) .
-                                               "\n\nException caught inside exception handler: " . self::getLogMessage( $e2 ) .
-                                               "\nBacktrace:\n" . self::getRedactedTraceAsString( $e2 );
-                               } else {
-                                       $message .= "Exception caught inside exception handler.\n\n" .
-                                               "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " .
-                                               "to show detailed debugging information.";
-                               }
-
-                               $message .= "\n";
-
-                               if ( $cmdLine ) {
-                                       self::printError( $message );
-                               } else {
-                                       echo nl2br( htmlspecialchars( $message ) ) . "\n";
-                               }
-                       }
-               } else {
-                       if ( !$wgShowExceptionDetails ) {
-                               $message = self::getPublicLogMessage( $e );
-                       } else {
-                               $message = self::getLogMessage( $e ) .
-                                       "\nBacktrace:\n" .
-                                       self::getRedactedTraceAsString( $e ) . "\n";
-                       }
-
-                       if ( $cmdLine ) {
-                               self::printError( $message );
-                       } else {
-                               echo nl2br( htmlspecialchars( $message ) ) . "\n";
-                       }
-
-               }
-       }
-
-       /**
-        * Print a message, if possible to STDERR.
-        * Use this in command line mode only (see isCommandLine)
-        *
-        * @param string $message Failure text
-        */
-       public static function printError( $message ) {
-               # NOTE: STDERR may not be available, especially if php-cgi is used from the
-               # command line (bug #15602). Try to produce meaningful output anyway. Using
-               # echo may corrupt output to STDOUT though.
-               if ( defined( 'STDERR' ) ) {
-                       fwrite( STDERR, $message );
-               } else {
-                       echo $message;
+               try {
+                       // Try and show the exception prettily, with the normal skin infrastructure
+                       MWExceptionRenderer::output( $e, MWExceptionRenderer::AS_PRETTY );
+               } catch ( Exception $e2 ) {
+                       // Exception occurred from within exception handler
+                       // Show a simpler message for the original exception,
+                       // don't try to invoke report()
+                       MWExceptionRenderer::output( $e, MWExceptionRenderer::AS_PRETTY, $e2 );
                }
        }
 
@@ -136,16 +80,16 @@ class MWExceptionHandler {
         * @param Exception|Throwable $e
         */
        public static function rollbackMasterChangesAndLog( $e ) {
-               $factory = wfGetLBFactory();
-               if ( $factory->hasMasterChanges() ) {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               if ( $lbFactory->hasMasterChanges() ) {
                        $logger = LoggerFactory::getInstance( 'Bug56269' );
                        $logger->warning(
                                'Exception thrown with an uncommited database transaction: ' .
                                self::getLogMessage( $e ),
                                self::getLogContext( $e )
                        );
-                       $factory->rollbackMasterChanges( __METHOD__ );
                }
+               $lbFactory->rollbackMasterChanges( __METHOD__ );
        }
 
        /**
diff --git a/includes/exception/MWExceptionRenderer.php b/includes/exception/MWExceptionRenderer.php
new file mode 100644 (file)
index 0000000..bb7a01f
--- /dev/null
@@ -0,0 +1,406 @@
+<?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
+ * @author Aaron Schulz
+ */
+
+/**
+ * Class to expose exceptions to the client (API bots, users, admins using CLI scripts)
+ * @since 1.28
+ */
+class MWExceptionRenderer {
+       const AS_RAW = 1; // show as text
+       const AS_PRETTY = 2; // show as HTML
+
+       /**
+        * @param Exception $e Original exception
+        * @param integer $mode MWExceptionExposer::AS_* constant
+        * @param Exception|null $eNew New exception from attempting to show the first
+        */
+       public static function output( Exception $e, $mode, Exception $eNew = null ) {
+               global $wgMimeType;
+
+               if ( $e instanceof DBConnectionError ) {
+                       self::reportOutageHTML( $e );
+                       return;
+               }
+
+               if ( defined( 'MW_API' ) ) {
+                       // Unhandled API exception, we can't be sure that format printer is alive
+                       self::header( 'MediaWiki-API-Error: internal_api_error_' . get_class( $e ) );
+                       wfHttpError( 500, 'Internal Server Error', self::getText( $e ) );
+               } elseif ( self::isCommandLine() ) {
+                       self::printError( self::getText( $e ) );
+               } elseif ( $mode === self::AS_PRETTY ) {
+                       self::statusHeader( 500 );
+                       self::header( "Content-Type: $wgMimeType; charset=utf-8" );
+                       self::reportHTML( $e );
+               } else {
+                       if ( $eNew ) {
+                               $message = "MediaWiki internal error.\n\n";
+                               if ( self::showBackTrace( $e ) ) {
+                                       $message .= 'Original exception: ' .
+                                               MWExceptionHandler::getLogMessage( $e ) .
+                                               "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $e ) .
+                                               "\n\nException caught inside exception handler: " .
+                                                       MWExceptionHandler::getLogMessage( $eNew ) .
+                                               "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $eNew );
+                               } else {
+                                       $message .= "Exception caught inside exception handler.\n\n" .
+                                               "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " .
+                                               "to show detailed debugging information.";
+                               }
+                               $message .= "\n";
+                       } else {
+                               if ( self::showBackTrace( $e ) ) {
+                                       $message = MWExceptionHandler::getLogMessage( $e ) .
+                                               "\nBacktrace:\n" .
+                                               MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
+                               } else {
+                                       $message = MWExceptionHandler::getPublicLogMessage( $e );
+                               }
+                       }
+                       if ( self::isCommandLine() ) {
+                               self::printError( $message );
+                       } else {
+                               echo nl2br( htmlspecialchars( $message ) ) . "\n";
+                       }
+               }
+       }
+
+       /**
+        * Run hook to allow extensions to modify the text of the exception
+        *
+        * Called by MWException for b/c
+        *
+        * @param Exception $e
+        * @param string $name Class name of the exception
+        * @param array $args Arguments to pass to the callback functions
+        * @return string|null String to output or null if any hook has been called
+        */
+       public static function runHooks( Exception $e, $name, $args = [] ) {
+               global $wgExceptionHooks;
+
+               if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) {
+                       return null; // Just silently ignore
+               }
+
+               if ( !array_key_exists( $name, $wgExceptionHooks ) ||
+                       !is_array( $wgExceptionHooks[$name] )
+               ) {
+                       return null;
+               }
+
+               $hooks = $wgExceptionHooks[$name];
+               $callargs = array_merge( [ $e ], $args );
+
+               foreach ( $hooks as $hook ) {
+                       if (
+                               is_string( $hook ) ||
+                               ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) )
+                       ) {
+                               // 'function' or [ 'class', 'hook' ]
+                               $result = call_user_func_array( $hook, $callargs );
+                       } else {
+                               $result = null;
+                       }
+
+                       if ( is_string( $result ) ) {
+                               return $result;
+                       }
+               }
+
+               return null;
+       }
+
+       /**
+        * @param Exception $e
+        * @return bool Should the exception use $wgOut to output the error?
+        */
+       private static function useOutputPage( Exception $e ) {
+               // Can the extension use the Message class/wfMessage to get i18n-ed messages?
+               foreach ( $e->getTrace() as $frame ) {
+                       if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) {
+                               return false;
+                       }
+               }
+
+               return (
+                       !empty( $GLOBALS['wgFullyInitialised'] ) &&
+                       !empty( $GLOBALS['wgOut'] ) &&
+                       !defined( 'MEDIAWIKI_INSTALL' )
+               );
+       }
+
+       /**
+        * Output the exception report using HTML
+        *
+        * @param Exception $e
+        */
+       private static function reportHTML( Exception $e ) {
+               global $wgOut, $wgSitename;
+
+               if ( self::useOutputPage( $e ) ) {
+                       if ( $e instanceof MWException ) {
+                               $wgOut->prepareErrorPage( $e->getPageTitle() );
+                       } elseif ( $e instanceof DBReadOnlyError ) {
+                               $wgOut->prepareErrorPage( self::msg( 'readonly', 'Database is locked' ) );
+                       } elseif ( $e instanceof DBExpectedError ) {
+                               $wgOut->prepareErrorPage( self::msg( 'databaseerror', 'Database error' ) );
+                       } else {
+                               $wgOut->prepareErrorPage( self::msg( 'internalerror', 'Internal error' ) );
+                       }
+
+                       $hookResult = self::runHooks( $e, get_class( $e ) );
+                       if ( $hookResult ) {
+                               $wgOut->addHTML( $hookResult );
+                       } else {
+                               // Show any custom GUI message before the details
+                               if ( $e instanceof MessageSpecifier ) {
+                                       $wgOut->addHtml( Message::newFromSpecifier( $e )->escaped() );
+                               }
+                               $wgOut->addHTML( self::getHTML( $e ) );
+                       }
+
+                       $wgOut->output();
+               } else {
+                       self::header( 'Content-Type: text/html; charset=utf-8' );
+                       $pageTitle = self::msg( 'internalerror', 'Internal error' );
+                       echo "<!DOCTYPE html>\n" .
+                               '<html><head>' .
+                               // Mimick OutputPage::setPageTitle behaviour
+                               '<title>' .
+                               htmlspecialchars( self::msg( 'pagetitle', "$1 - $wgSitename", $pageTitle ) ) .
+                               '</title>' .
+                               '<style>body { font-family: sans-serif; margin: 0; padding: 0.5em 2em; }</style>' .
+                               "</head><body>\n";
+
+                       $hookResult = self::runHooks( $e, get_class( $e ) . 'Raw' );
+                       if ( $hookResult ) {
+                               echo $hookResult;
+                       } else {
+                               echo self::getHTML( $e );
+                       }
+
+                       echo "</body></html>\n";
+               }
+       }
+
+       /**
+        * If $wgShowExceptionDetails is true, return a HTML message with a
+        * backtrace to the error, otherwise show a message to ask to set it to true
+        * to show that information.
+        *
+        * @param Exception $e
+        * @return string Html to output
+        */
+       public static function getHTML( Exception $e ) {
+               if ( self::showBackTrace( $e ) ) {
+                       $html = "<div class=\"errorbox\"><p>" .
+                               nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $e ) ) ) .
+                               '</p><p>Backtrace:</p><p>' .
+                               nl2br( htmlspecialchars( MWExceptionHandler::getRedactedTraceAsString( $e ) ) ) .
+                               "</p></div>\n";
+               } else {
+                       $logId = WebRequest::getRequestId();
+                       $html = "<div class=\"errorbox\">" .
+                               '[' . $logId . '] ' .
+                               gmdate( 'Y-m-d H:i:s' ) . ": " .
+                               self::msg( "internalerror-fatal-exception",
+                                       "Fatal exception of type $1",
+                                       get_class( $e ),
+                                       $logId,
+                                       MWExceptionHandler::getURL()
+                               ) . "</div>\n" .
+                       "<!-- Set \$wgShowExceptionDetails = true; " .
+                       "at the bottom of LocalSettings.php to show detailed " .
+                       "debugging information. -->";
+               }
+
+               return $html;
+       }
+
+       /**
+        * Get a message from i18n
+        *
+        * @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
+        * @return string Message with arguments replaced
+        */
+       private static function msg( $key, $fallback /*[, params...] */ ) {
+               $args = array_slice( func_get_args(), 2 );
+               try {
+                       return wfMessage( $key, $args )->text();
+               } catch ( Exception $e ) {
+                       return wfMsgReplaceArgs( $fallback, $args );
+               }
+       }
+
+       /**
+        * @param Exception $e
+        * @return string
+        */
+       private function getText( Exception $e ) {
+               if ( self::showBackTrace( $e ) ) {
+                       return MWExceptionHandler::getLogMessage( $e ) .
+                               "\nBacktrace:\n" .
+                               MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
+               } else {
+                       return "Set \$wgShowExceptionDetails = true; " .
+                               "in LocalSettings.php to show detailed debugging information.\n";
+               }
+       }
+
+       /**
+        * @param Exception $e
+        * @return bool
+        */
+       private static function showBackTrace( Exception $e ) {
+               global $wgShowExceptionDetails, $wgShowDBErrorBacktrace;
+
+               return (
+                       $wgShowExceptionDetails &&
+                       ( !( $e instanceof DBError ) || $wgShowDBErrorBacktrace )
+               );
+       }
+
+       /**
+        * @return bool
+        */
+       private static function isCommandLine() {
+               return !empty( $GLOBALS['wgCommandLineMode'] );
+       }
+
+       /**
+        * @param string $header
+        */
+       private static function header( $header ) {
+               if ( !headers_sent() ) {
+                       header( $header );
+               }
+       }
+
+       /**
+        * @param integer $code
+        */
+       private static function statusHeader( $code ) {
+               if ( !headers_sent() ) {
+                       HttpStatus::header( $code );
+               }
+       }
+
+       /**
+        * Print a message, if possible to STDERR.
+        * Use this in command line mode only (see isCommandLine)
+        *
+        * @param string $message Failure text
+        */
+       private static function printError( $message ) {
+               // NOTE: STDERR may not be available, especially if php-cgi is used from the
+               // command line (bug #15602). Try to produce meaningful output anyway. Using
+               // echo may corrupt output to STDOUT though.
+               if ( defined( 'STDERR' ) ) {
+                       fwrite( STDERR, $message );
+               } else {
+                       echo $message;
+               }
+       }
+
+       /**
+        * @param Exception $e
+        */
+       private static function reportOutageHTML( Exception $e ) {
+               global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
+
+               $sorry = htmlspecialchars( self::msg(
+                       'dberr-problems',
+                       'Sorry! This site is experiencing technical difficulties.'
+               ) );
+               $again = htmlspecialchars( self::msg(
+                       'dberr-again',
+                       'Try waiting a few minutes and reloading.'
+               ) );
+
+               if ( $wgShowHostnames || $wgShowSQLErrors ) {
+                       $info = str_replace(
+                               '$1',
+                               Html::element( 'span', [ 'dir' => 'ltr' ], htmlspecialchars( $e->getMessage() ) ),
+                               htmlspecialchars( self::msg( 'dberr-info', '($1)' ) )
+                       );
+               } else {
+                       $info = htmlspecialchars( self::msg(
+                               'dberr-info-hidden',
+                               '(Cannot access the database)'
+                       ) );
+               }
+
+               MessageCache::singleton()->disable(); // no DB access
+
+               $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
+
+               if ( $wgShowDBErrorBacktrace ) {
+                       $html .= '<p>Backtrace:</p><pre>' .
+                               htmlspecialchars( $e->getTraceAsString() ) . '</pre>';
+               }
+
+               $html .= '<hr />';
+               $html .= self::googleSearchForm();
+
+               echo $html;
+       }
+
+       /**
+        * @return string
+        */
+       private static function googleSearchForm() {
+               global $wgSitename, $wgCanonicalServer, $wgRequest;
+
+               $usegoogle = htmlspecialchars( self::msg(
+                       'dberr-usegoogle',
+                       'You can try searching via Google in the meantime.'
+               ) );
+               $outofdate = htmlspecialchars( self::msg(
+                       'dberr-outofdate',
+                       'Note that their indexes of our content may be out of date.'
+               ) );
+               $googlesearch = htmlspecialchars( self::msg( 'searchbutton', 'Search' ) );
+               $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
+               $server = htmlspecialchars( $wgCanonicalServer );
+               $sitename = htmlspecialchars( $wgSitename );
+               $trygoogle = <<<EOT
+<div style="margin: 1.5em">$usegoogle<br />
+<small>$outofdate</small>
+</div>
+<form method="get" action="//www.google.com/search" id="googlesearch">
+       <input type="hidden" name="domains" value="$server" />
+       <input type="hidden" name="num" value="50" />
+       <input type="hidden" name="ie" value="UTF-8" />
+       <input type="hidden" name="oe" value="UTF-8" />
+       <input type="text" name="q" size="31" maxlength="255" value="$search" />
+       <input type="submit" name="btnG" value="$googlesearch" />
+       <p>
+               <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
+               <label><input type="radio" name="sitesearch" value="" />WWW</label>
+       </p>
+</form>
+EOT;
+               return $trygoogle;
+       }
+}
diff --git a/includes/exception/TimestampException.php b/includes/exception/TimestampException.php
deleted file mode 100644 (file)
index b9c0c35..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-/**
- * @since 1.20
- */
-class TimestampException extends MWException {
-}
index b7c3489..43c5b09 100644 (file)
@@ -62,7 +62,7 @@ class UserNotLoggedIn extends ErrorPageError {
         * @param string $titleMsg A message key to set the page title.
         *        Optional, default: 'exception-nologin'
         * @param array $params Parameters to wfMessage().
-        *        Optional, default: array()
+        *        Optional, default: []
         */
        public function __construct(
                $reasonMsg = 'exception-nologin-text',
index b454577..2eae279 100644 (file)
@@ -112,7 +112,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
        }
 
        /**
-        * Get a slave database connection for the specified cluster
+        * Get a replica DB connection for the specified cluster
         *
         * @param string $cluster Cluster name
         * @return IDatabase
@@ -130,7 +130,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
                        wfDebug( "writable external store\n" );
                }
 
-               $db = $lb->getConnection( DB_SLAVE, [], $wiki );
+               $db = $lb->getConnection( DB_REPLICA, [], $wiki );
                $db->clearFlag( DBO_TRX ); // sanity
 
                return $db;
@@ -264,7 +264,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
        }
 
        /**
-        * Helper function for self::batchFetchBlobs for merging master/slave results
+        * Helper function for self::batchFetchBlobs for merging master/replica DB results
         * @param array &$ret Current self::batchFetchBlobs return value
         * @param array &$ids Map from blob_id to requested itemIDs
         * @param mixed $res DB result from Database::select
index 10183f4..ed36a1f 100644 (file)
@@ -241,31 +241,31 @@ abstract class FileBackend {
         *
         * a) Create a new file in storage with the contents of a string
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     );
+        *     ]
         * @endcode
         *
         * b) Copy a file system file into storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'store',
         *         'src'                 => <file system path, FSFile, or TempFSFile>,
         *         'dst'                 => <storage path>,
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * c) Copy a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
@@ -273,12 +273,12 @@ abstract class FileBackend {
         *         'overwriteSame'       => <boolean>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * d) Move a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
@@ -286,32 +286,32 @@ abstract class FileBackend {
         *         'overwriteSame'       => <boolean>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * e) Delete a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
-        *     )
+        *     ]
         * @endcode
         *
         * f) Update metadata for a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'describe',
         *         'src'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map>
-        *     )
+        *     ]
         * @endcode
         *
         * g) Do nothing (no-op)
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'null',
-        *     )
+        *     ]
         * @endcode
         *
         * Boolean flags for operations (operation-specific):
@@ -513,69 +513,69 @@ abstract class FileBackend {
         *
         * a) Create a new file in storage with the contents of a string
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * b) Copy a file system file into storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'store',
         *         'src'                 => <file system path, FSFile, or TempFSFile>,
         *         'dst'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * c) Copy a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * d) Move a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * e) Delete a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
-        *     )
+        *     ]
         * @endcode
         *
         * f) Update metadata for a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'describe',
         *         'src'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map>
-        *     )
+        *     ]
         * @endcode
         *
         * g) Do nothing (no-op)
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'null',
-        *     )
+        *     ]
         * @endcode
         *
         * @par Boolean flags for operations (operation-specific):
@@ -1034,7 +1034,7 @@ abstract class FileBackend {
         *
         * Write operations should *never* be done on this file as some backends
         * may do internal tracking or may be instances of FileBackendMultiWrite.
-        * In that later case, there are copies of the file that must stay in sync.
+        * In that latter case, there are copies of the file that must stay in sync.
         * Additionally, further calls to this function may return the same file.
         *
         * @param array $params Parameters include:
index a29119c..bc4d81d 100644 (file)
@@ -1443,7 +1443,7 @@ abstract class FileBackendStore extends FileBackend {
        /**
         * Like resolveStoragePath() except null values are returned if
         * the container is sharded and the shard could not be determined
-        * or if the path ends with '/'. The later case is illegal for FS
+        * or if the path ends with '/'. The latter case is illegal for FS
         * backends and can confuse listings for object store backends.
         *
         * This function is used when resolving paths that must be valid
@@ -1702,8 +1702,8 @@ abstract class FileBackendStore extends FileBackend {
                if ( $path === null ) {
                        return; // invalid storage path
                }
-               $age = time() - wfTimestamp( TS_UNIX, $val['mtime'] );
-               $ttl = min( 7 * 86400, max( 300, floor( .1 * $age ) ) );
+               $mtime = wfTimestamp( TS_UNIX, $val['mtime'] );
+               $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, .1 );
                $key = $this->fileCacheKey( $path );
                // Set the cache unless it is currently salted.
                $this->memCache->set( $key, $val, $ttl );
index 567a298..a3cb3b1 100644 (file)
@@ -103,17 +103,17 @@ abstract class LockManager {
         */
        final public function lockByType( array $pathsByType, $timeout = 0 ) {
                $pathsByType = $this->normalizePathsByType( $pathsByType );
-               $msleep = [ 0, 50, 100, 300, 500 ]; // retry backoff times
-               $start = microtime( true );
-               do {
-                       $status = $this->doLockByType( $pathsByType );
-                       $elapsed = microtime( true ) - $start;
-                       if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
-                               break; // success, timeout, or clock set back
-                       }
-                       usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
-                       $elapsed = microtime( true ) - $start;
-               } while ( $elapsed < $timeout && $elapsed >= 0 );
+
+               $status = null;
+               $loop = new WaitConditionLoop(
+                       function () use ( &$status, $pathsByType ) {
+                               $status = $this->doLockByType( $pathsByType );
+
+                               return $status->isOK() ?: WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
+               $loop->invoke();
 
                return $status;
        }
index cb5266a..2f17e27 100644 (file)
@@ -276,6 +276,7 @@ class MemcLockManager extends QuorumLockManager {
         * @return MemcachedBagOStuff|null
         */
        protected function getCache( $lockSrv ) {
+               /** @var BagOStuff $memc */
                $memc = null;
                if ( isset( $this->bagOStuffs[$lockSrv] ) ) {
                        $memc = $this->bagOStuffs[$lockSrv];
@@ -337,20 +338,21 @@ class MemcLockManager extends QuorumLockManager {
                // Try to quickly loop to acquire the keys, but back off after a few rounds.
                // This reduces memcached spam, especially in the rare case where a server acquires
                // some lock keys and dies without releasing them. Lock keys expire after a few minutes.
-               $rounds = 0;
-               $start = microtime( true );
-               do {
-                       if ( ( ++$rounds % 4 ) == 0 ) {
-                               usleep( 1000 * 50 ); // 50 ms
-                       }
-                       foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
-                               if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
-                                       $lockedKeys[] = $key;
-                               } else {
-                                       continue; // acquire in order
+               $loop = new WaitConditionLoop(
+                       function () use ( $memc, $keys, &$lockedKeys ) {
+                               foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
+                                       if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
+                                               $lockedKeys[] = $key;
+                                       }
                                }
-                       }
-               } while ( count( $lockedKeys ) < count( $keys ) && ( microtime( true ) - $start ) <= 3 );
+
+                               return array_diff( $keys, $lockedKeys )
+                                       ? WaitConditionLoop::CONDITION_CONTINUE
+                                       : true;
+                       },
+                       3.0 // timeout
+               );
+               $loop->invoke();
 
                if ( count( $lockedKeys ) != count( $keys ) ) {
                        $this->releaseMutexes( $memc, $lockedKeys ); // failed; release what was locked
index aec337e..596dbde 100644 (file)
@@ -27,7 +27,7 @@
  * @brief Proxy backend that manages file layout rewriting for FileRepo.
  *
  * LocalRepo may be configured to store files under their title names or by SHA-1.
- * This acts as a shim in the later case, providing backwards compatability for
+ * This acts as a shim in the latter case, providing backwards compatability for
  * most callers. All "public"/"deleted" zone files actually go in an "original"
  * container and are never changed.
  *
@@ -94,7 +94,7 @@ class FileBackendDBRepoWrapper extends FileBackend {
         * @return array Translated paths in same order
         */
        public function getBackendPaths( array $paths, $latest = true ) {
-               $db = $this->getDB( $latest ? DB_MASTER : DB_SLAVE );
+               $db = $this->getDB( $latest ? DB_MASTER : DB_REPLICA );
 
                // @TODO: batching
                $resolved = [];
index 4ab913d..b8b1cf6 100644 (file)
@@ -482,8 +482,8 @@ class FileRepo {
         * @param array $items An array of titles, or an array of findFile() options with
         *    the "title" option giving the title. Example:
         *
-        *     $findItem = array( 'title' => $title, 'private' => true );
-        *     $findBatch = array( $findItem );
+        *     $findItem = [ 'title' => $title, 'private' => true ];
+        *     $findBatch = [ $findItem ];
         *     $repo->findFiles( $findBatch );
         *
         *    No title should appear in $items twice, as the result use titles as keys
index b48191f..645a59b 100644 (file)
@@ -28,13 +28,13 @@ use MediaWiki\Logger\LoggerFactory;
  *
  * Example config:
  *
- * $wgForeignFileRepos[] = array(
+ * $wgForeignFileRepos[] = [
  *   'class'                  => 'ForeignAPIRepo',
  *   'name'                   => 'shared',
  *   'apibase'                => 'https://en.wikipedia.org/w/api.php',
  *   'fetchDescription'       => true, // Optional
  *   'descriptionCacheExpiry' => 3600,
- * );
+ * ];
  *
  * @ingroup FileRepo
  */
@@ -63,8 +63,8 @@ class ForeignAPIRepo extends FileRepo {
        /** @var array */
        protected $mFileExists = [];
 
-       /** @var array */
-       private $mQueryCache = [];
+       /** @var string */
+       private $mApiBase;
 
        /**
         * @param array|null $info
@@ -397,7 +397,8 @@ class ForeignAPIRepo extends FileRepo {
                        }
                        /* There is a new Commons file, or existing thumbnail older than a month */
                }
-               $thumb = self::httpGet( $foreignUrl );
+
+               $thumb = self::httpGet( $foreignUrl, 'default', [], $mtime );
                if ( !$thumb ) {
                        wfDebug( __METHOD__ . " Could not download thumb\n" );
 
@@ -413,7 +414,11 @@ class ForeignAPIRepo extends FileRepo {
                        return $foreignUrl;
                }
                $knownThumbUrls[$sizekey] = $localUrl;
-               $cache->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
+
+               $ttl = $mtime
+                       ? $cache->adaptiveTTL( $mtime, $this->apiThumbCacheExpiry )
+                       : $this->apiThumbCacheExpiry;
+               $cache->set( $key, $knownThumbUrls, $ttl );
                wfDebug( __METHOD__ . " got local thumb $localUrl, saving to cache \n" );
 
                return $localUrl;
@@ -506,9 +511,12 @@ class ForeignAPIRepo extends FileRepo {
         * @param string $url
         * @param string $timeout
         * @param array $options
+        * @param integer|bool &$mtime Resulting Last-Modified UNIX timestamp if received
         * @return bool|string
         */
-       public static function httpGet( $url, $timeout = 'default', $options = [] ) {
+       public static function httpGet(
+               $url, $timeout = 'default', $options = [], &$mtime = false
+       ) {
                $options['timeout'] = $timeout;
                /* Http::get */
                $url = wfExpandUrl( $url, PROTO_HTTP );
@@ -524,6 +532,9 @@ class ForeignAPIRepo extends FileRepo {
                $status = $req->execute();
 
                if ( $status->isOK() ) {
+                       $mtime = wfTimestampOrNull( TS_UNIX, $req->getResponseHeader( 'Last-Modified' ) );
+                       $mtime = $mtime ?: false;
+
                        return $req->getContent();
                } else {
                        $logger = LoggerFactory::getInstance( 'http' );
@@ -531,6 +542,7 @@ class ForeignAPIRepo extends FileRepo {
                                $status->getWikiText( false, false, 'en' ),
                                [ 'caller' => 'ForeignAPIRepo::httpGet' ]
                        );
+
                        return false;
                }
        }
@@ -548,7 +560,7 @@ class ForeignAPIRepo extends FileRepo {
         * @param string $target Used in cache key creation, mostly
         * @param array $query The query parameters for the API request
         * @param int $cacheTTL Time to live for the memcached caching
-        * @return null
+        * @return string|null
         */
        public function httpGetCached( $target, $query, $cacheTTL = 3600 ) {
                if ( $this->mApiBase ) {
@@ -557,28 +569,23 @@ class ForeignAPIRepo extends FileRepo {
                        $url = $this->makeUrl( $query, 'api' );
                }
 
-               if ( !isset( $this->mQueryCache[$url] ) ) {
-                       $data = ObjectCache::getMainWANInstance()->getWithSetCallback(
-                               $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) ),
-                               $cacheTTL,
-                               function () use ( $url ) {
-                                       return ForeignAPIRepo::httpGet( $url );
+               $cache = ObjectCache::getMainWANInstance();
+               return $cache->getWithSetCallback(
+                       $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) ),
+                       $cacheTTL,
+                       function ( $curValue, &$ttl ) use ( $url, $cache ) {
+                               $html = self::httpGet( $url, 'default', [], $mtime );
+                               if ( $html !== false ) {
+                                       $ttl = $mtime ? $cache->adaptiveTTL( $mtime, $ttl ) : $ttl;
+                               } else {
+                                       $ttl = $cache->adaptiveTTL( $mtime, $ttl );
+                                       $html = null; // caches negatives
                                }
-                       );
 
-                       if ( !$data ) {
-                               return null;
-                       }
-
-                       if ( count( $this->mQueryCache ) > 100 ) {
-                               // Keep the cache from growing infinitely
-                               $this->mQueryCache = [];
-                       }
-
-                       $this->mQueryCache[$url] = $data;
-               }
-
-               return $this->mQueryCache[$url];
+                               return $html;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
index a59ca34..f8b1ed9 100644 (file)
@@ -63,7 +63,7 @@ class ForeignDBViaLBRepo extends LocalRepo {
         * @return IDatabase
         */
        function getSlaveDB() {
-               return wfGetDB( DB_SLAVE, [], $this->wiki );
+               return wfGetDB( DB_REPLICA, [], $this->wiki );
        }
 
        /**
index eaec151..7b40a7b 100644 (file)
@@ -453,11 +453,11 @@ class LocalRepo extends FileRepo {
        }
 
        /**
-        * Get a connection to the slave DB
+        * Get a connection to the replica DB
         * @return DatabaseBase
         */
        function getSlaveDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
@@ -469,7 +469,7 @@ class LocalRepo extends FileRepo {
        }
 
        /**
-        * Get a callback to get a DB handle given an index (DB_SLAVE/DB_MASTER)
+        * Get a callback to get a DB handle given an index (DB_REPLICA/DB_MASTER)
         * @return Closure
         */
        protected function getDBFactory() {
@@ -500,9 +500,12 @@ class LocalRepo extends FileRepo {
        function invalidateImageRedirect( Title $title ) {
                $key = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
                if ( $key ) {
-                       $this->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
-                               ObjectCache::getMainWANInstance()->delete( $key );
-                       } );
+                       $this->getMasterDB()->onTransactionPreCommitOrIdle(
+                               function () use ( $key ) {
+                                       ObjectCache::getMainWANInstance()->delete( $key );
+                               },
+                               __METHOD__
+                       );
                }
        }
 
index ca1ea84..d1e683a 100644 (file)
@@ -177,7 +177,7 @@ class ArchivedFile {
 
                if ( !$this->title || $this->title->getNamespace() == NS_FILE ) {
                        $this->dataLoaded = true; // set it here, to have also true on miss
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow(
                                'filearchive',
                                self::selectFields(),
index 40141c9..618272c 100644 (file)
 
 use \MediaWiki\Logger\LoggerFactory;
 
-/**
- * Bump this number when serialized cache records may be incompatible.
- */
-define( 'MW_FILE_VERSION', 9 );
-
 /**
  * Class to represent a local file in the wiki's own database
  *
@@ -46,6 +41,8 @@ define( 'MW_FILE_VERSION', 9 );
  * @ingroup FileAbstraction
  */
 class LocalFile extends File {
+       const VERSION = 10; // cache version
+
        const CACHE_FIELD_MAX_LEN = 1000;
 
        /** @var bool Does the file exist on disk? (loadFromXxx) */
@@ -240,77 +237,71 @@ class LocalFile extends File {
         * @return string|bool
         */
        function getCacheKey() {
-               $hashedName = md5( $this->getName() );
-
-               return $this->repo->getSharedCacheKey( 'file', $hashedName );
+               return $this->repo->getSharedCacheKey( 'file', sha1( $this->getName() ) );
        }
 
        /**
-        * Try to load file metadata from memcached. Returns true on success.
-        * @return bool
+        * Try to load file metadata from memcached, falling back to the database
         */
        private function loadFromCache() {
                $this->dataLoaded = false;
                $this->extraDataLoaded = false;
-               $key = $this->getCacheKey();
 
+               $key = $this->getCacheKey();
                if ( !$key ) {
-                       return false;
-               }
+                       $this->loadFromDB( self::READ_NORMAL );
 
-               $cache = ObjectCache::getMainWANInstance();
-               $cachedValues = $cache->get( $key );
-
-               // Check if the key existed and belongs to this version of MediaWiki
-               if ( is_array( $cachedValues ) && $cachedValues['version'] == MW_FILE_VERSION ) {
-                       $this->fileExists = $cachedValues['fileExists'];
-                       if ( $this->fileExists ) {
-                               $this->setProps( $cachedValues );
-                       }
-                       $this->dataLoaded = true;
-                       $this->extraDataLoaded = true;
-                       foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                               $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
-                       }
+                       return;
                }
 
-               return $this->dataLoaded;
-       }
-
-       /**
-        * Save the file metadata to memcached
-        */
-       private function saveToCache() {
-               $this->load();
+               $cache = ObjectCache::getMainWANInstance();
+               $cachedValues = $cache->getWithSetCallback(
+                       $key,
+                       $cache::TTL_WEEK,
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+                               $setOpts += Database::getCacheSetOptions( $this->repo->getSlaveDB() );
+
+                               $this->loadFromDB( self::READ_NORMAL );
+
+                               $fields = $this->getCacheFields( '' );
+                               $cacheVal['fileExists'] = $this->fileExists;
+                               if ( $this->fileExists ) {
+                                       foreach ( $fields as $field ) {
+                                               $cacheVal[$field] = $this->$field;
+                                       }
+                               }
+                               // Strip off excessive entries from the subset of fields that can become large.
+                               // If the cache value gets to large it will not fit in memcached and nothing will
+                               // get cached at all, causing master queries for any file access.
+                               foreach ( $this->getLazyCacheFields( '' ) as $field ) {
+                                       if ( isset( $cacheVal[$field] )
+                                               && strlen( $cacheVal[$field] ) > 100 * 1024
+                                       ) {
+                                               unset( $cacheVal[$field] ); // don't let the value get too big
+                                       }
+                               }
 
-               $key = $this->getCacheKey();
-               if ( !$key ) {
-                       return;
-               }
+                               if ( $this->fileExists ) {
+                                       $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->timestamp ), $ttl );
+                               } else {
+                                       $ttl = $cache::TTL_DAY;
+                               }
 
-               $fields = $this->getCacheFields( '' );
-               $cacheVal = [ 'version' => MW_FILE_VERSION ];
-               $cacheVal['fileExists'] = $this->fileExists;
+                               return $cacheVal;
+                       },
+                       [ 'version' => self::VERSION ]
+               );
 
+               $this->fileExists = $cachedValues['fileExists'];
                if ( $this->fileExists ) {
-                       foreach ( $fields as $field ) {
-                               $cacheVal[$field] = $this->$field;
-                       }
+                       $this->setProps( $cachedValues );
                }
 
-               // Strip off excessive entries from the subset of fields that can become large.
-               // If the cache value gets to large it will not fit in memcached and nothing will
-               // get cached at all, causing master queries for any file access.
+               $this->dataLoaded = true;
+               $this->extraDataLoaded = true;
                foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                       if ( isset( $cacheVal[$field] ) && strlen( $cacheVal[$field] ) > 100 * 1024 ) {
-                               unset( $cacheVal[$field] ); // don't let the value get too big
-                       }
+                       $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
                }
-
-               // Cache presence for 1 week and negatives for 1 day
-               $ttl = $this->fileExists ? 86400 * 7 : 86400;
-               $opts = Database::getCacheSetOptions( $this->repo->getSlaveDB() );
-               ObjectCache::getMainWANInstance()->set( $key, $cacheVal, $ttl, $opts );
        }
 
        /**
@@ -322,9 +313,12 @@ class LocalFile extends File {
                        return;
                }
 
-               $this->repo->getMasterDB()->onTransactionPreCommitOrIdle( function() use ( $key ) {
-                       ObjectCache::getMainWANInstance()->delete( $key );
-               } );
+               $this->repo->getMasterDB()->onTransactionPreCommitOrIdle(
+                       function () use ( $key ) {
+                               ObjectCache::getMainWANInstance()->delete( $key );
+                       },
+                       __METHOD__
+               );
        }
 
        /**
@@ -440,16 +434,18 @@ class LocalFile extends File {
        private function loadFieldsWithTimestamp( $dbr, $fname ) {
                $fieldMap = false;
 
-               $row = $dbr->selectRow( 'image', $this->getLazyCacheFields( 'img_' ),
-                       [ 'img_name' => $this->getName(), 'img_timestamp' => $this->getTimestamp() ],
-                       $fname );
+               $row = $dbr->selectRow( 'image', $this->getLazyCacheFields( 'img_' ), [
+                               'img_name' => $this->getName(),
+                               'img_timestamp' => $dbr->timestamp( $this->getTimestamp() )
+                       ], $fname );
                if ( $row ) {
                        $fieldMap = $this->unprefixRow( $row, 'img_' );
                } else {
                        # File may have been uploaded over in the meantime; check the old versions
-                       $row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ),
-                               [ 'oi_name' => $this->getName(), 'oi_timestamp' => $this->getTimestamp() ],
-                               $fname );
+                       $row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ), [
+                                       'oi_name' => $this->getName(),
+                                       'oi_timestamp' => $dbr->timestamp( $this->getTimestamp() )
+                               ], $fname );
                        if ( $row ) {
                                $fieldMap = $this->unprefixRow( $row, 'oi_' );
                        }
@@ -545,12 +541,13 @@ class LocalFile extends File {
         */
        function load( $flags = 0 ) {
                if ( !$this->dataLoaded ) {
-                       if ( ( $flags & self::READ_LATEST ) || !$this->loadFromCache() ) {
+                       if ( $flags & self::READ_LATEST ) {
                                $this->loadFromDB( $flags );
-                               $this->saveToCache();
+                       } else {
+                               $this->loadFromCache();
                        }
-                       $this->dataLoaded = true;
                }
+
                if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
                        // @note: loads on name/timestamp to reduce race condition problems
                        $this->loadExtraFromDB();
@@ -766,7 +763,7 @@ class LocalFile extends File {
 
                if ( $type == 'text' ) {
                        return $this->user_text;
-               } elseif ( $type == 'id' ) {
+               } else { // id
                        return (int)$this->user;
                }
        }
@@ -1621,7 +1618,9 @@ class LocalFile extends File {
                        $sha1 = $repo->isVirtualUrl( $srcPath )
                                ? $repo->getFileSha1( $srcPath )
                                : FSFile::getSha1Base36FromPath( $srcPath );
-                       $dst = $repo->getBackend()->getPathForSHA1( $sha1 );
+                       /** @var FileBackendDBRepoWrapper $wrapperBackend */
+                       $wrapperBackend = $repo->getBackend();
+                       $dst = $wrapperBackend->getPathForSHA1( $sha1 );
                        $status = $repo->quickImport( $src, $dst );
                        if ( $flags & File::DELETE_SOURCE ) {
                                unlink( $srcPath );
@@ -2006,12 +2005,15 @@ class LocalFile extends File {
                        }
                        // Release the lock *after* commit to avoid row-level contention.
                        // Make sure it triggers on rollback() as well as commit() (T132921).
-                       $dbw->onTransactionResolution( function () use ( $logger ) {
-                               $status = $this->releaseFileLock();
-                               if ( !$status->isGood() ) {
-                                       $logger->error( "Failed to unlock '{file}'", [ 'file' => $this->name ] );
-                               }
-                       } );
+                       $dbw->onTransactionResolution(
+                               function () use ( $logger ) {
+                                       $status = $this->releaseFileLock();
+                                       if ( !$status->isGood() ) {
+                                               $logger->error( "Failed to unlock '{file}'", [ 'file' => $this->name ] );
+                                       }
+                               },
+                               __METHOD__
+                       );
                        // Callers might care if the SELECT snapshot is safely fresh
                        $this->lockedOwnTrx = $makesTransaction;
                }
@@ -2492,6 +2494,7 @@ class LocalFileRestoreBatch {
         * @return FileRepoStatus
         */
        public function execute() {
+               /** @var Language */
                global $wgLang;
 
                $repo = $this->file->getRepo();
index 3c88594..567e692 100644 (file)
@@ -1014,7 +1014,8 @@ class HTMLForm extends ContextSource {
                $this->getOutput()->addModuleStyles( 'mediawiki.htmlform.styles' );
 
                $html = ''
-                       . $this->getErrors( $submitResult )
+                       . $this->getErrorsOrWarnings( $submitResult, 'error' )
+                       . $this->getErrorsOrWarnings( $submitResult, 'warning' )
                        . $this->getHeaderText()
                        . $this->getBody()
                        . $this->getHiddenFields()
@@ -1230,23 +1231,46 @@ class HTMLForm extends ContextSource {
         *
         * @param string|array|Status $errors
         *
+        * @deprecated since 1.28, use getErrorsOrWarnings() instead
+        *
         * @return string
         */
        public function getErrors( $errors ) {
-               if ( $errors instanceof Status ) {
-                       if ( $errors->isOK() ) {
-                               $errorstr = '';
+               wfDeprecated( __METHOD__ );
+               return $this->getErrorsOrWarnings( $errors, 'error' );
+       }
+
+       /**
+        * Returns a formatted list of errors or warnings from the given elements.
+        *
+        * @param string|array|Status $elements The set of errors/warnings to process.
+        * @param string $elementsType Should warnings or errors be returned.  This is meant
+        *      for Status objects, all other valid types are always considered as errors.
+        * @return string
+        */
+       public function getErrorsOrWarnings( $elements, $elementsType ) {
+               if ( !in_array( $elementsType, [ 'error', 'warning' ], true ) ) {
+                       throw new DomainException( $elementsType . ' is not a valid type.' );
+               }
+               $elementstr = false;
+               if ( $elements instanceof Status ) {
+                       list( $errorStatus, $warningStatus ) = $elements->splitByErrorType();
+                       $status = $elementsType === 'error' ? $errorStatus : $warningStatus;
+                       if ( $status->isGood() ) {
+                               $elementstr = '';
                        } else {
-                               $errorstr = $this->getOutput()->parse( $errors->getWikiText() );
+                               $elementstr = $this->getOutput()->parse(
+                                       $status->getWikiText()
+                               );
                        }
-               } elseif ( is_array( $errors ) ) {
-                       $errorstr = $this->formatErrors( $errors );
-               } else {
-                       $errorstr = $errors;
+               } elseif ( is_array( $elements ) && $elementsType === 'error' ) {
+                       $elementstr = $this->formatErrors( $elements );
+               } elseif ( $elementsType === 'error' ) {
+                       $elementstr = $elements;
                }
 
-               return $errorstr
-                       ? Html::rawElement( 'div', [ 'class' => 'error' ], $errorstr )
+               return $elementstr
+                       ? Html::rawElement( 'div', [ 'class' => $elementsType ], $elementstr )
                        : '';
        }
 
index 0b22727..bbd3473 100644 (file)
@@ -26,6 +26,7 @@
  */
 class OOUIHTMLForm extends HTMLForm {
        private $oouiErrors;
+       private $oouiWarnings;
 
        public function __construct( $descriptor, $context = null, $messagePrefix = '' ) {
                parent::__construct( $descriptor, $context, $messagePrefix );
@@ -185,28 +186,34 @@ class OOUIHTMLForm extends HTMLForm {
        }
 
        /**
-        * @param string|array|Status $err
+        * @param string|array|Status $elements
+        * @param string $elementsType
         * @return string
         */
-       function getErrors( $err ) {
-               if ( !$err ) {
+       function getErrorsOrWarnings( $elements, $elementsType ) {
+               if ( !in_array( $elementsType, [ 'error', 'warning' ] ) ) {
+                       throw new DomainException( $elementsType . ' is not a valid type.' );
+               }
+               if ( !$elements ) {
                        $errors = [];
-               } elseif ( $err instanceof Status ) {
-                       if ( $err->isOK() ) {
+               } elseif ( $elements instanceof Status ) {
+                       if ( $elements->isGood() ) {
                                $errors = [];
                        } else {
-                               $errors = $err->getErrorsByType( 'error' );
+                               $errors = $elements->getErrorsByType( $elementsType );
                                foreach ( $errors as &$error ) {
                                        // Input:  array( 'message' => 'foo', 'errors' => array( 'a', 'b', 'c' ) )
                                        // Output: array( 'foo', 'a', 'b', 'c' )
                                        $error = array_merge( [ $error['message'] ], $error['params'] );
                                }
                        }
-               } else {
-                       $errors = $err;
+               } elseif ( $elementsType === 'errors' )  {
+                       $errors = $elements;
                        if ( !is_array( $errors ) ) {
                                $errors = [ $errors ];
                        }
+               } else {
+                       $errors = [];
                }
 
                foreach ( $errors as &$error ) {
@@ -215,7 +222,11 @@ class OOUIHTMLForm extends HTMLForm {
                }
 
                // Used in getBody()
-               $this->oouiErrors = $errors;
+               if ( $elementsType === 'error' ) {
+                       $this->oouiErrors = $errors;
+               } else {
+                       $this->oouiWarnings = $errors;
+               }
                return '';
        }
 
@@ -236,7 +247,10 @@ class OOUIHTMLForm extends HTMLForm {
                        if ( $this->oouiErrors ) {
                                $classes[] = 'mw-htmlform-ooui-header-errors';
                        }
-                       if ( $this->mHeader || $this->oouiErrors ) {
+                       if ( $this->oouiWarnings ) {
+                               $classes[] = 'mw-htmlform-ooui-header-warnings';
+                       }
+                       if ( $this->mHeader || $this->oouiErrors || $this->oouiWarnings ) {
                                // if there's no header, don't create an (empty) LabelWidget, simply use a placeholder
                                if ( $this->mHeader ) {
                                        $element = new OOUI\LabelWidget( [ 'label' => new OOUI\HtmlSnippet( $this->mHeader ) ] );
@@ -249,6 +263,7 @@ class OOUIHTMLForm extends HTMLForm {
                                                [
                                                        'align' => 'top',
                                                        'errors' => $this->oouiErrors,
+                                                       'notices' => $this->oouiWarnings,
                                                        'classes' => $classes,
                                                ]
                                        )
index f9f035d..42c2fdf 100644 (file)
@@ -27,7 +27,7 @@ class HTMLRadioField extends HTMLFormField {
                }
 
                if ( !is_string( $value ) && !is_int( $value ) ) {
-                       return false;
+                       return $this->msg( 'htmlform-required' )->parse();
                }
 
                $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
index 406667e..d1a9bc5 100644 (file)
@@ -377,7 +377,7 @@ class WikiImporter {
                // Update article count statistics (T42009)
                // The normal counting logic in WikiPage->doEditUpdates() is designed for
                // one-revision-at-a-time editing, not bulk imports. In this situation it
-               // suffers from issues of slave lag. We let WikiPage handle the total page
+               // suffers from issues of replica DB lag. We let WikiPage handle the total page
                // and revision count, and we implement our own custom logic for the
                // article (content page) count.
                $page = WikiPage::factory( $title );
index 701403e..ded2bd8 100644 (file)
@@ -192,7 +192,7 @@ abstract class DatabaseInstaller {
                $this->db->begin( __METHOD__ );
 
                $error = $this->db->sourceFile(
-                       call_user_func( [ $this->db, $sourceFileMethod ] )
+                       call_user_func( [ $this, $sourceFileMethod ], $this->db )
                );
                if ( $error !== true ) {
                        $this->db->reportQueryError( $error, 0, '', __METHOD__ );
@@ -227,6 +227,47 @@ abstract class DatabaseInstaller {
                return $this->stepApplySourceFile( 'getUpdateKeysPath', 'updates', false );
        }
 
+       /**
+        * Return a path to the DBMS-specific SQL file if it exists,
+        * otherwise default SQL file
+        *
+        * @param IDatabase $db
+        * @param string $filename
+        * @return string
+        */
+       private function getSqlFilePath( $db, $filename ) {
+               global $IP;
+
+               $dbmsSpecificFilePath = "$IP/maintenance/" . $db->getType() . "/$filename";
+               if ( file_exists( $dbmsSpecificFilePath ) ) {
+                       return $dbmsSpecificFilePath;
+               } else {
+                       return "$IP/maintenance/$filename";
+               }
+       }
+
+       /**
+        * Return a path to the DBMS-specific schema file,
+        * otherwise default to tables.sql
+        *
+        * @param IDatabase $db
+        * @return string
+        */
+       public function getSchemaPath( $db ) {
+               return $this->getSqlFilePath( $db, 'tables.sql' );
+       }
+
+       /**
+        * Return a path to the DBMS-specific update key file,
+        * otherwise default to update-keys.sql
+        *
+        * @param IDatabase $db
+        * @return string
+        */
+       public function getUpdateKeysPath( $db ) {
+               return $this->getSqlFilePath( $db, 'update-keys.sql' );
+       }
+
        /**
         * Create the tables for each extension the user enabled
         * @return Status
index 86b2f3b..0e4b098 100644 (file)
@@ -659,7 +659,7 @@ abstract class DatabaseUpdater {
                $this->output( "$msg ..." );
 
                if ( !$isFullPath ) {
-                       $path = $this->db->patchPath( $path );
+                       $path = $this->patchPath( $this->db, $path );
                }
                if ( $this->fileHandle !== null ) {
                        $this->copyFile( $path );
@@ -671,6 +671,26 @@ abstract class DatabaseUpdater {
                return true;
        }
 
+       /**
+        * Get the full path of a patch file. Originally based on archive()
+        * from updaters.inc. Keep in mind this always returns a patch, as
+        * it fails back to MySQL if no DB-specific patch can be found
+        *
+        * @param IDatabase $db
+        * @param string $patch The name of the patch, like patch-something.sql
+        * @return string Full path to patch file
+        */
+       public function patchPath( IDatabase $db, $patch ) {
+               global $IP;
+
+               $dbType = $db->getType();
+               if ( file_exists( "$IP/maintenance/$dbType/archives/$patch" ) ) {
+                       return "$IP/maintenance/$dbType/archives/$patch";
+               } else {
+                       return "$IP/maintenance/archives/$patch";
+               }
+       }
+
        /**
         * Add a new table to the database
         *
@@ -1078,7 +1098,7 @@ abstract class DatabaseUpdater {
                global $wgProfiler;
 
                if ( !$this->doTable( 'profiling' ) ) {
-                       return true;
+                       return;
                }
 
                $profileToDb = false;
index d508d76..eafb9d4 100644 (file)
@@ -1437,10 +1437,10 @@ abstract class Installer {
 
        /**
         * Get an array of install steps. Should always be in the format of
-        * array(
+        * [
         *   'name'     => 'someuniquename',
-        *   'callback' => array( $obj, 'method' ),
-        * )
+        *   'callback' => [ $obj, 'method' ],
+        * ]
         * There must be a config-install-$name message defined per step, which will
         * be shown on install.
         *
@@ -1724,7 +1724,7 @@ abstract class Installer {
         * Add an installation step following the given step.
         *
         * @param callable $callback A valid installation callback array, in this form:
-        *    array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
+        *    [ 'name' => 'some-unique-name', 'callback' => [ $obj, 'function' ] ];
         * @param string $findStep The step to find. Omit to put the step at the beginning
         */
        public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
index 1d7c7f2..a9e3e85 100644 (file)
@@ -98,7 +98,7 @@ class LocalSettingsGenerator {
         * For $wgGroupPermissions, set a given ['group']['permission'] value.
         * @param string $group Group name
         * @param array $rightsArr An array of permissions, in the form of:
-        *   array( 'right' => true, 'right2' => false )
+        *   [ 'right' => true, 'right2' => false ]
         */
        public function setGroupRights( $group, $rightsArr ) {
                $this->groupPermissions[$group] = $rightsArr;
index 6989969..65af086 100644 (file)
@@ -155,7 +155,6 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ],
 
                        // 1.15
-                       [ 'doUniquePlTlIl' ],
                        [ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
                        [ 'addTable', 'tag_summary', 'patch-tag_summary.sql' ],
                        [ 'addTable', 'valid_tag', 'patch-valid_tag.sql' ],
@@ -287,7 +286,8 @@ class MysqlUpdater extends DatabaseUpdater {
                        // 1.28
                        [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
                                'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
-                       [ 'doRevisionPageRevIndexNonUnique' ]
+                       [ 'doRevisionPageRevIndexNonUnique' ],
+                       [ 'doNonUniquePlTlIl' ],
                ];
        }
 
@@ -974,24 +974,24 @@ class MysqlUpdater extends DatabaseUpdater {
                return true;
        }
 
-       protected function doUniquePlTlIl() {
+       protected function doNonUniquePlTlIl() {
                $info = $this->db->indexInfo( 'pagelinks', 'pl_namespace' );
-               if ( is_array( $info ) && !$info[0]->Non_unique ) {
-                       $this->output( "...pl_namespace, tl_namespace, il_to indices are already UNIQUE.\n" );
+               if ( is_array( $info ) && $info[0]->Non_unique ) {
+                       $this->output( "...pl_namespace, tl_namespace, il_to indices are already non-UNIQUE.\n" );
 
                        return true;
                }
                if ( $this->skipSchema ) {
                        $this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
-                               "and il_to indices UNIQUE).\n" );
+                               "and il_to indices non-UNIQUE).\n" );
 
                        return false;
                }
 
                return $this->applyPatch(
-                       'patch-pl-tl-il-unique.sql',
+                       'patch-pl-tl-il-nonunique.sql',
                        false,
-                       'Making pl_namespace, tl_namespace and il_to indices UNIQUE'
+                       'Making pl_namespace, tl_namespace and il_to indices non-UNIQUE'
                );
        }
 
index ea96cfe..cd9574c 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "Xuacu",
                        "Fitoschido",
-                       "Enolp"
+                       "Enolp",
+                       "Crucifunked"
                ]
        },
        "config-desc": "L'instalador pa MediaWiki",
        "config-outdated-sqlite": "'''Avisu:''' tien SQLite $1, que ye inferior a la versión mínima necesaria $2. SQLite nun tará disponible.",
        "config-no-fts3": "'''Avisu:''' SQLite ta compiláu ensin el [//sqlite.org/fts3.html módulu FTS3]; les funciones de gueta nun tarán disponibles nesti sistema.",
        "config-pcre-old": "<strong>Fatal:</strong> Ríquese PCRE $1 o posterior.\nEl binariu de PHP ta enllazáu con PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Más información].",
+       "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-xcache": "[http://xcache.lighttpd.net/ XCache] ta instaláu",
        "config-apc": "[http://www.php.net/apc APC] ta instaláu",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] ta instaláu",
+       "config-no-cache-apcu": "<strong>Warning:</strong> Non pudo atopase[http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nEl caxé d'oxetos nun ta activáu.",
+       "config-mod-security": "<strong>Alvertencia:</strong> El to servidor web tien activáu [http://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  [http://modsecurity.org/documentation/ mod_security documentation] o contacta col alministrador del to servidor si atopes erros aleatorios.",
        "config-diff3-bad": "Nun s'alcontró GNU diff3.",
        "config-git": "Alcontróse'l software de control de versiones Git: <code>$1</code>.",
        "config-git-bad": "Nun s'alcontró el software de control de versiones Git.",
+       "config-imagemagick": "ImageMagick atopáu: <code>$1</code>.\nLa miniaturización d'imaxes habilitaráse si habilites les cargues.",
+       "config-gd": "Atopóse una biblioteca de gráficos GD integrada.\nLa miniaturización d'imaxes habilitaráse si habilites les xubíes.",
+       "config-no-scaling": "Nun s'atopó la biblioteca GD o ImageMagik.\nVa desactivase la miniaturización d'imaxes.",
+       "config-no-uri": "<strong>Erru:</strong> non pudo determinase el URI actual. Atayóse la instalación.",
+       "config-no-cli-uri": "<strong>Alvertencia:</strong> Nun s'especificó  <code>--scriptpath</code>, úsase'l valor predetermináu <code>$1</code>.",
+       "config-using-server": "Utilizando'l nome de servidor \"<nowiki>$1</nowiki>\".",
+       "config-using-uri": "Utilizando la URL del servidor \"<nowiki>$1$2</nowiki>\".",
+       "config-uploads-not-safe": "<strong>Alvertencia:</strong> El to directoriu predetermináu pa les cargues <code>$1</code> ye vulnerable a la execución de scripts arbitrarios.\nAnque MediaWiki comprueba tolos archivos cargaos por si hubiera amenaces de seguridá, ye altamente recomendable [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security close this security vulnerability] enantes d'activar les cargues.",
+       "config-no-cli-uploads-check": "<strong>Alvertencia:</strong> el to directoriu predetermináu pa cargues <code>$1</code> nun tá comprobáu contra la vulnerabilidá d'execución arbitraria de scripts mientres la instalación per llínea de comandos.",
+       "config-brokenlibxml": "El sistema tien una combinación de versiones de PHP y de libxml2 que ye pocu confiable y puede provocar corrupción oculta nos datos de MediaWiki y otres aplicaciones web. Actualiza a libxml2 2.7.3 o posterior ([https://bugs.php.net/bug.php?díi=45996 bug reportáu con PHP]). Instalación albortada.",
+       "config-suhosin-max-value-length": "Suhosin ta instaláu y llinda el parámetru <code>length</code> GET a $1 bytes.\nEl componente ResourceLoader (xestor de recursos) de MediaWiki va trabayar nesta llende, pero eso va perxudicar el rendimientu.\nSi ye posible, tendríes d'establecer <code>suhosin.get.max_value_length</code> nel valor 1024 o superior en <code>php.ini</code> y establecer <code>$wgResourceLoaderMaxQueryLength</code> nel mesmu valor en <code>LocalSettings.php</code>.",
        "config-db-type": "Tipu de base de datos:",
+       "config-db-host": "Servidor de la base de datos:",
+       "config-db-host-help": "Si'l to servidor de base de datos ta n'otru servidor, escribe'l nome del equipu o la so dirección IP equí.\n\nSi tas utilizando alojamiento web compartíu, el to provisor tendría de date'l nome correctu del servidor na so documentación.\n\nSi vas instalar nun servidor Windows y a utilizar MySQL, l'usu de \"localhost\" como nome del servidor puede nun #funcionar. Si ye asina, intenta poner \"127.0.0.1\" como dirección IP local.\n\nSi utilices PostgreSQL, dexa esti campu vacío pa conectase al traviés d'un socket de Unix.",
+       "config-db-host-oracle": "TNS de la base de datos:",
+       "config-db-host-oracle-help": "Escribe un [http://download.oracle.com/docs/cd/B28359_01/network.111/b28317/tnsnames.htm nome de conexón local] válidu; un archivu tnsnames.ora ten de ser visible pa esta instalación.<br />Si tas utilizando biblioteques de veceru 10g o más recién tamién puedes utilizar el métodu de asignación de nomes [http://download.oracle.com/docs/cd/Y11882_01/network.112/y10836/naming.htm Easy Connect].",
+       "config-db-wiki-settings": "Identifica esta wiki",
        "config-db-name": "Nome de base de datos:",
+       "config-db-name-help": "Escueye un nome qu'identifique la to wiki. Nun tien de contener espacios. \nSi tas utilizando agospiamientu web compartíu, el to provisor va date un nome específicu de base de datos por que lu utilices, o bien va dexate crear bases de datos al traviés d'un panel de control.",
+       "config-db-name-oracle": "Esquema de la base de datos:",
        "config-db-install-account": "Cuenta d'usuariu pa la instalación",
        "config-db-username": "Nome d'usuariu de base de datos:",
        "config-db-password": "Contraseña de base de datos:",
index 3c6d530..fb23d01 100644 (file)
@@ -41,6 +41,7 @@
        "config-restart": "হ্যাঁ, পুনরায় চালু করুন",
        "config-env-php": "পিএইচপি $1 ইন্সটল করা হয়েছে।",
        "config-env-hhvm": "HHVM $1 ইনস্টল করা হয়েছে।",
+       "config-memory-raised": "পিএইচপির <code>memory_limit</code> হচ্ছে $1, বৃদ্ধি পেয়ে $2 হয়েছে।",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] ইনস্টল করা হয়েছে",
        "config-apc": "[http://www.php.net/apc এপিসি] ইনস্টল হয়েছে",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] ইনস্টল করা হয়েছে",
@@ -72,6 +73,8 @@
        "config-missing-db-host": "আপনাকে অবশ্যই \"{{int:config-db-host}}\"-এর জন্য একটি মান প্রবেশ করাতে হবে।",
        "config-missing-db-server-oracle": "আপনাকে অবশ্যই \"{{int:config-db-host-oracle}}\"-এর জন্য একটি মান প্রবেশ করাতে হবে।",
        "config-connection-error": "$1।\n\n\nদয়া করে প্রস্তাবকারী, ব্যবহারকারী নাম ও পাসওয়ার্ড দেখুন এবং পুনরায় চেষ্টা করুন।",
+       "config-sqlite-readonly": "ফাইল <code>$1</code> লিখনযোগ্য নয়।",
+       "config-sqlite-cant-create-db": "ডাটাবেজ ফাইল <code>$1</code> তৈরি করা যায়নি।",
        "config-regenerate": "LocalSettings.php পুনরূত্পাদিত করুন →",
        "config-mysql-engine": "সংরক্ষণ ইঞ্জিন:",
        "config-mysql-innodb": "ইনোডিবি",
        "config-license-cc-choose": "একটি স্বনির্ধারিত ক্রিয়েটিভ কমন্স লাইসেন্ট নির্বাচন করুন",
        "config-email-settings": "ই-মেইল সেটিংস",
        "config-email-user": "ব্যবহারকারী-থেকে-ব্যবহারকারী ই-মেইল সুবিধা সক্রিয় করো",
+       "config-email-usertalk": "ব্যবহারকারী আলাপ পাতার বিজ্ঞপ্তি সক্রিয় করো",
        "config-upload-settings": "চিত্র এবং ফাইল আপলোড",
        "config-upload-enable": "ফাইল আপলোড সক্রিয় করো",
        "config-upload-deleted": "অপসারণকৃত ফাইলের ডিরেক্টরি:",
        "config-logo": "লোগো ইউআরএল:",
+       "config-advanced-settings": "উন্নত কনফিগারেশন",
        "config-memcached-servers": "মেমক্যাশেকৃত সার্ভারসমূহ:",
        "config-extensions": "এক্সটেনশন",
        "config-skins": "আবরণ",
        "config-install-tables": "টেবিল তৈরি",
        "config-install-keys": "গোপন কি তৈরি",
        "config-help": "সাহায্য",
+       "config-help-tooltip": "প্রসারিত করতে ক্লিক করুন",
        "mainpagetext": "<strong>মিডিয়াউইকি ইনস্টল করা হয়েছে।</strong>",
        "mainpagedocfooter": "কীভাবে উইকি সফটওয়্যারটি ব্যবহারকার করবেন, তা জানতে [https://meta.wikimedia.org/wiki/Help:Contents ব্যবহারকারী সহায়িকা] দেখুন।\n\n== কোথা থেকে শুরু করবেন ==\n\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings কনফিগারেশন সেটিংস তালিকা]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ প্রশ্নোত্তরে মিডিয়াউইকি]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce মিডিয়াউইকি মুক্তির মেইলিং লিস্ট]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources আপনার ভাষার জন্য মিডিয়াউইকি স্থানীয়করণ করুন]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam আপনার উইকিতে স্প্যামের সাথে লড়াই করার উপায় সম্পর্কে জানুন]"
 }
index 2cd7bb8..583b80c 100644 (file)
@@ -51,6 +51,6 @@
        "config-email-settings": "ڕێکخستنەکانی ئیمەیڵ",
        "config-install-step-done": "کرا",
        "config-help": "یارمەتی",
-       "mainpagetext": "'''میدیاویکی بە سەرکەوتوویی دامەزرا.'''",
-       "mainpagedocfooter": "لە [https://meta.wikimedia.org/wiki/Help:Contents ڕێنوێنیی بەکارھێنەران] بۆ زانیاری سەبارەت بە بەکارھێنانی نەرمامێری ویکی کەڵک وەربگرە.\n\n== دەستپێکردن ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings پێرستی ڕێکخستنەکانی شێوەپێدان]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسیارە دووپاتکراوەکانی میدیاویکی (MediaWiki FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce پێرستی ئیمەیلی وەشانەکانی میدیاویکی]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources خۆماڵیکردنی ویکیمیدیا بۆ زمانەکەت]"
+       "mainpagetext": "<strong>میدیاویکی بە سەرکەوتوویی دامەزرا.</strong>",
+       "mainpagedocfooter": "لە [https://meta.wikimedia.org/wiki/Help:Contents ڕێنوێنیی بەکارھێنەران] بۆ زانیاری سەبارەت بە بەکارھێنانی نەرمامێری ویکی کەڵک وەربگرە.\n\n== دەستپێکردن ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings پێرستی ڕێکخستنەکانی شێوەپێدان]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسیارە دووپاتکراوەکانی میدیاویکی (MediaWiki FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce پێرستی ئیمەیلی وەشانەکانی میدیاویکی]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources خۆماڵیکردنی ویکیمیدیا بۆ زمانەکەت]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam فێربە چۆن ڕووبەڕووى ئیمەیڵە بێزارکەرەکانی ویکییەکەت دەبیتەوە]"
 }
index b55b3c7..e704141 100644 (file)
        "config-mysql-myisam": "MyISAM",
        "config-mysql-myisam-dep": "<strong>Warnung:</strong> Es wurde MyISAM als Speichersubsystem für das Datenbanksystem MySQL ausgewählt. Aus folgenden Gründen wird es nicht für den Einsatz mit MediaWiki empfohlen:\n* Es unterstützt aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen.\n* Es ist anfälliger für Datenprobleme.\n* Es wird von MediaWiki nicht immer adäquat unterstützt.\n\nSofern die vorhandene MySQL-Installation das Speichersubsystem InnoDB unterstützt, wird deren Verwendung eindringlich empfohlen.\nSofern sie es nicht unterstützt, sollte nunmehr eine entsprechende Aktualisierung in Erwägung gezogen werden.",
        "config-mysql-only-myisam-dep": "<strong>Warnung:</strong> MyISAM ist das einzige verfügbare Speichersubsystem für das Datenbanksystem MySQL auf diesem Server. Es wird nicht für die Verwendung mit MediaWiki empfohlen, da es\n* aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen unterstützt,\n* anfälliger für Datenprobleme ist und\n* von MediaWiki nicht immer adäquat unterstützt wird.\n\nDeine MySQL-Installation unterstützt nicht das Speichersubsystem InnoDB. Eine Aktualisierung wird nunmehr empfohlen.",
-       "config-mysql-engine-help": "'''InnoDB''' ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n'''MyISAM''' ist in Einzelnutzerumgebungen sowie bei schreibgeschützten Wikis schneller.\nBei MyISAM-Datenbanken treten tendenziell häufiger Fehler auf als bei InnoDB-Datenbanken.",
+       "config-mysql-engine-help": "<strong>InnoDB</strong> als Speichersubsystem für das Datenbanksystem MySQL ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n<strong>MyISAM</strong> als Speichersubsystem für das Datenbanksystem MySQL ist hingegen in Einzelnutzerumgebungen oder bei schreibgeschützten Wikis schneller.\nDatenbanken, die MyISAM verwenden, sind indes tendenziell fehleranfälliger als solche, die InnoDB verwenden.",
        "config-mysql-charset": "Datenbankzeichensatz:",
        "config-mysql-binary": "binär",
        "config-mysql-utf8": "UTF-8",
index b29e3bd..6fa270a 100644 (file)
        "config-page-language": "Zıwan",
        "config-page-welcome": "Şıma xeyr ameyê MediaWiki!",
        "config-page-dbconnect": "Database rê grêdey",
+       "config-page-upgrade": "Est bıyaye versiyoni berz kı",
+       "config-page-dbsettings": "Sazê Database",
        "config-page-name": "Name",
        "config-page-options": "Weçinegi",
-       "config-page-install": "Barine",
+       "config-page-install": "Bar ke",
        "config-page-complete": "Temamyayo",
+       "config-page-restart": "Barkerdışi fına ser kı",
        "config-page-readme": "Mı bıwane",
        "config-page-copying": "Kopyayeno",
        "config-page-upgradedoc": "Berzkerdış",
        "config-restart": "E, fına dest pekê",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki keye]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Şınasiya Karberi]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Şınasiya İdarekaran]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Peşti]\n----\n* <doclink href=Readme>Mı buwanê</doclink>\n* <doclink href=ReleaseNotes>Notê elaqeyıni</doclink>\n* <doclink href=Copying>Kopyakerdış</doclink>\n* <doclink href=UpgradeDoc>Zêdekerdış</doclink>",
        "config-env-php": "PHP $1 i biyo saz.",
+       "config-env-hhvm": "HHVM $1 saz bi ya.",
        "config-db-type": "Database tipe:",
        "config-db-host": "Database host:",
        "config-db-host-oracle": "Database TNS:",
index 1d230b2..3e7f8f3 100644 (file)
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
-       "config-support-info": "MediaWiki supporte 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 le support.",
+       "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-mysql-url}} MySQL] est le premier choix pour MediaWiki et est le mieux pris en charge. MediaWiki fonctionne aussi avec [{{int:version-db-mariadb-url}} MariaDB] et [{{int:version-db-percona-url}} Percona Server], qui sont compatibles avec MySQL. ([http://www.php.net/manual/en/mysqli.installation.php Comment compiler PHP avec le support MySQL])",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] est un système de base de données populaire et ''open source'' qui peut être une alternative à MySQL. Son support peut contenir quelques bogues mineurs et n'est pas recommandé dans un environnement de production.  ([http://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec le support de PostgreSQL])",
-       "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien supporté. ([http://www.php.net/manual/en/pdo.installation.php Comment compiler PHP avec le support de SQLite], en utilisant PDO)",
+       "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien pris en charge. ([http://www.php.net/manual/fr/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. ([http://www.php.net/manual/en/oci8.installation.php Comment compiler PHP avec le support OCI8])",
        "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] est une base de données commerciale d’entreprise pour Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php Comment compiler PHP avec le support de SQLSRV])",
        "config-header-mysql": "Paramètres de MySQL",
        "config-mysql-engine": "Moteur de stockage :",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n'est pas recommandé pour une utilisation avec MediaWiki, parce que:\n * il supporte à peine la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le codebase MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL supporte InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne supporte pas les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
-       "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il supporte très peu les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne supporte pas InnoDB ; il est peut-être temps de la mettre à jour.",
-       "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il supporte bien les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule. \nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d'InnoDB.",
+       "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n’est pas recommandé pour une utilisation avec MediaWiki, parce que :\n * il prend à peine en charge la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le code de base MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL prenden charge InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne prend pas en charge les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
+       "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il prend très peu en charge les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne prend pas en charge InnoDB ; il est peut-être temps de la mettre à jour.",
+       "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il prend bien en charge les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule.\nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d’InnoDB.",
        "config-mysql-charset": "Jeu de caractères de la base de données :",
        "config-mysql-binary": "Binaire",
        "config-mysql-utf8": "UTF-8",
        "config-help": "aide",
        "config-help-tooltip": "cliquer pour agrandir",
        "config-nofile": "Le fichier « $1 » est introuvable. A-t-il été supprimé ?",
-       "config-extension-link": "Saviez-vous que votre wiki supporte [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
+       "config-extension-link": "Saviez-vous que votre wiki prend en charge [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
        "mainpagetext": "<strong>MediaWiki a été installé.</strong>",
        "mainpagedocfooter": "Consultez le [https://meta.wikimedia.org/wiki/Help:Contents/fr Guide de l’utilisateur] pour plus d’informations sur l’utilisation de ce logiciel de wiki.\n\n== Pour démarrer ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste des paramètres de configuration]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/fr Questions courantes sur MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Liste de discussion sur les distributions de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Adaptez MediaWiki dans votre langue]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Apprendre comment lutter contre le pourriel dans votre wiki]"
 }
index 2ebd0b3..4c68cf9 100644 (file)
        "config-subscribe": "Subscribe al [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce lista de diffusion pro annuncios de nove versiones].",
        "config-subscribe-help": "Isto es un lista de e-mail a basse volumine pro annuncios de nove versiones, includente importante annuncios de securitate.\nTu deberea subscriber a illo e actualisar tu installation de MediaWiki quando nove versiones es editate.",
        "config-subscribe-noemail": "Tu tentava abonar te al lista de diffusion pro annunciamento de nove versiones sin fornir un adresse de e-mail.\nPer favor specifica un adresse de e-mail si tu vole abonar te al lista de diffusion.",
+       "config-pingback": "Divider datos sur iste installation con le disveloppatores de MediaWiki.",
+       "config-pingback-help": "Si tu selige option, MediaWiki inviara periodicamente a https://www.mediawiki.org certe datos basic sur iste installation de MediaWiki. Iste datos include, per exemplo, le typo de systema, le version de PHP, e le programma de base de datos seligite. Le Fundation Wikimedia divide iste datos con le disveloppatores de MediaWiki pro adjutar a guidar le effortios de disveloppamento futur. Le sequente datos concernente iste systema essera inviate:\n<pre>$1</pre>",
        "config-almost-done": "Tu ha quasi finite!\nTu pote ora saltar le configuration remanente e installar le wiki immediatemente.",
        "config-optional-continue": "Pone me plus questiones.",
        "config-optional-skip": "Isto me es jam tediose. Simplemente installa le wiki.",
diff --git a/includes/installer/i18n/lij.json b/includes/installer/i18n/lij.json
new file mode 100644 (file)
index 0000000..3670744
--- /dev/null
@@ -0,0 +1,54 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Giromin Cangiaxo"
+               ]
+       },
+       "config-desc": "Programma de installaçion pe MediaWiki",
+       "config-title": "Installaçion de MediaWiki $1",
+       "config-information": "Informaçioin",
+       "config-localsettings-upgrade": "L'è stæto rilevou un file <code>LocalSettings.php</code>.\nPe aggiornâ questa installaçion, se prega de insei o valô de <code>$wgUpgradeKey</code> inta casella chì de sotta.\nO poei atrovâ in <code>LocalSettings.php</code>.",
+       "config-localsettings-cli-upgrade": "L 'è stæto rilevou un file <code>LocalSettings.php</code>.\nPe aggiornâ questa installaçion, eseguî <code>update.php</code>",
+       "config-localsettings-key": "Ciave de aggiornamento:",
+       "config-localsettings-badkey": "A ciave d'agiornamento che t'hæ fornio a no l'è corretta.",
+       "config-upgrade-key-missing": "L'è stæto rilevou un'installaçion existente de MediaWiki.\nPe aggiornâ quest'installaçion, se prega de insei a seguente riga inta parte infeiô do to <code>LocalSettings.php</code>:\n\n$1",
+       "config-localsettings-incomplete": "O file <code>LocalSettings.php</code> existente pâ ese incompleto.\nA variabile $1 a no l'è impostâ.\nCangia <code>LocalSettings.php</code> de moddo che questa variabile a segge impostâ e fanni clic insce \"{{int:Config-continue}}\".",
+       "config-localsettings-connection-error": "S'è veificou un errô durante a connescion a-o database doeuviando e impostaçioin specificæ in <code>LocalSettings.php</code>. Se prega de coreze queste impostaçioin e riprovâ.\n\n$1",
+       "config-session-error": "Errô inte l'avvio da sescion: $1",
+       "config-session-expired": "I dæti da sescion pan ese descheiti.\nE sescioin son configuæ pe 'na duata de $1.\nTi poeu aomentâla impostando <code>session.gc_maxlifetime</code> into file php.ini.\nRiavvia o processo d'installaçion.",
+       "config-no-session": "I dæti da sescion son andæti persci!\nControlla o to file php.ini e aseguite che <code>session.save_path</code> o l'è impostou insce 'na directory apropiâ.",
+       "config-your-language": "A to lengua:",
+       "config-your-language-help": "Seleçion-a una lengua da doeuviâ  durante o processo d'installaçion.",
+       "config-wiki-language": "A lengua do wiki:",
+       "config-wiki-language-help": "Seleçion-a a lengua ch'a saiâ prevalentemente doeuviâ into wiki.",
+       "config-back": "inderê",
+       "config-continue": "Continnoa →",
+       "config-page-language": "Lengua",
+       "config-page-welcome": "Benvegnui a MediaWiki!",
+       "config-page-dbconnect": "Connescion a-o database",
+       "config-page-upgrade": "Agiornamento de l'installaçion existente",
+       "config-page-dbsettings": "Impostaçioin do database",
+       "config-page-name": "Nomme",
+       "config-page-options": "Opçioin",
+       "config-page-install": "Installa",
+       "config-page-complete": "Completa!",
+       "config-page-restart": "Riavvio installaçion",
+       "config-page-readme": "Lezime",
+       "config-page-releasenotes": "Notte de verscion",
+       "config-page-copying": "Copia",
+       "config-page-upgradedoc": "Aggiornamento",
+       "config-page-existingwiki": "Wiki existenti",
+       "config-help-restart": "Ti voeu scassâ tutti i dæti sarvæ che ti t'hæ inseio e riavviâ o processo de installaçion?",
+       "config-restart": "Scì, riavvia",
+       "config-welcome": "=== Controllo de l'ambiente ===\nSaiâ eseguio di controlli de base pe vedde se questo ambiente o l'è adatto pe l'installaçion de MediaWiki.\nRegordite de includde queste informaçioin se ti domandi ascistença insce comme completâ l'installaçion.",
+       "config-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma o l'è un software libero; ti poeu redistriboîlo e/ò modificâlo segondo i termi da GNU General Public License, comme pubbricâ da-a Free Software Foundation; ò a verscion 2 da Liçença ò (a proppia scelta) qualunque verscion succesciva.\n\nQuesto programma o l'è distribuio inta sperança ch'o segge utile, ma SENSA ARCUNA GARANTIA; sença manco a garantia impliçita de NEGOSSIABILITÆ o de APPRICABILITÆ PE UN PARTICOL SCOPO.\nS'amie a GNU General Public License pe maggioî dettaggi.\n\nQuesto programma o dev'ese distribuio insemme a <doclink href=Copying>una copia da GNU General Public License</doclink>; in caxo contraio, se ne poeu otegnî un-a scrivendo a-a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppù [http://www.gnu.org/copyleft/gpl.html lezila inta ræ'].",
+       "config-sidebar": "* [https://www.mediawiki.org Paggina prinçipâ MediaWiki]\n* [https://www.mediawiki.org/wiki/Agiutto:Guidda a-i contegnui pe utenti]\n* [https://www.mediawiki.org/wiki/Manoâ:Guidda ai contegnui per admin]\n* [https://www.mediawiki.org/wiki/Manoâ:FAQ FAQ]\n----\n* <doclink href=Readme>Lezime</doclink>\n* <doclink href=ReleaseNotes>Notte de verscion</doclink>\n* <doclink href=Copying>Copie</doclink>\n* <doclink href=UpgradeDoc>Aggiornamenti</doclink>",
+       "config-env-good": "L'ambiente o l'è stæto controllou.\nL'è poscibile installâ MediaWiki.",
+       "config-env-bad": "L'ambiente o l'è stæto controllou.\nNon l'è poscibbile installâ MediaWiki.",
+       "config-env-php": "PHP $1 o l'è installou.",
+       "config-env-hhvm": "HHVM $1 o l'è installou.",
+       "config-unicode-using-intl": "Adoeuvia [http://pecl.php.net/intl l'estenscion PECL intl] pe-a normalizzaçion Unicode.",
+       "config-unicode-pure-php-warning": "'''Attençion:''' [http://pecl.php.net/intl l'estenscion PECL intl] a no l'è disponibile pe gestî a normalizzaçion Unicode, quindi se torna a-a lenta implementaçion in PHP puo.\nSe ti esegui un scito a ato traffego, ti doviesci leze arcun-e conscidiaçioin in sciâ [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations normalizzaçion Unicode].",
+       "config-unicode-update-warning": "'''Attençion:''' a verscion installaa do gestô pe-a normalizzaçion Unicode a l'adoeuvia una vegia verscion da libraia [http://site.icu-project.org/ do progetto ICU].\nTi doviesci [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations aggiornâ] se ti voeu doeuviâ l'Unicode.",
+       "config-no-db": "Imposcibile trovâ un driver adatto pe-o database! L'è necessaio installâ un driver pe PHP.\n{{PLURAL:$2|O seguente formato de database o l'è supportou|I seguenti formati de database son supportæ}}: $1.\n\nSe ti compilli PHP aotonomamente, riconfiguilo attivando un client database, presempio utilizzando <code>./configure --with-mysqli</code>.\nQualoa t'avesci installou PHP pe mezo de 'n pacchetto Debian ò Ubuntu, alloa ti devi installâ o pacchetto <code>php5-mysql</code> ascì."
+}
index fca2977..7c50e82 100644 (file)
@@ -6,13 +6,14 @@
                        "सरोज कुमार ढकाल",
                        "Ganesh Paudel",
                        "बिप्लब आनन्द",
-                       "Nirjal stha"
+                       "Nirjal stha",
+                       "राम प्रसाद जोशी"
                ]
        },
        "config-desc": "मेडियाविकिको लागि स्थापक",
        "config-title": "मेडिया विकि $1 स्थापना",
        "config-information": "जानकारी",
-       "config-localsettings-badkey": "तपाà¤\87लà¥\87 à¤¦à¤¿à¤¨à¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\95à¥\81नà¥\8dà¤\9cà¥\80 à¤\97लत à¤\9b ।",
+       "config-localsettings-badkey": "तपाà¤\88à¤\82लà¥\87 à¤¦à¤¿à¤\8fà¤\95à¥\8b à¤\95à¥\81à¤\82à¤\9cà¥\80 à¤®à¤¿à¤²à¥\87न ।",
        "config-your-language": "तपाईंको भाषा:",
        "config-your-language-help": "इन्स्टल गर्दा उपयोग गर्ने भाषा छान्नुहोस् ।",
        "config-wiki-language": "विकि भाषाहरू",
index 914618b..6ef4dd3 100644 (file)
        "config-continue": "ಮುಂದುವರೆಸಾಲೆ →",
        "config-page-language": "ಬಾಸೆ",
        "config-page-welcome": "ಮಾಧ್ಯಮವಿಕಿಗ್ ಸ್ವಾಗತ",
-       "config-page-dbconnect": "ದತà³\8dತಾà²\82ಶಸà²\82à²\9aಯà²\97à³\8d à²¸à²\82ಪರà³\8dà²\95ಕೊರ್ಲೆ",
+       "config-page-dbconnect": "ದತà³\8dತಾà²\82ಸà³\8a à²¸à²\82à²\9aಯà³\8aà²\97à³\8d à²\95à³\8aಲಿà²\95à³\86ಕೊರ್ಲೆ",
        "config-page-name": "ಪುದರ್",
        "config-page-options": "ಆಯ್ಕೆಲು",
        "config-page-install": "ಸ್ಥಾಪಿಸಾಲೆ",
-       "config-page-complete": "ಪà³\82ರà³\8dಣ!",
-       "config-page-readme": "à²\8eನನà³\8d à²\93ದà³\81ಲà³\87",
-       "config-page-releasenotes": "ಬà³\81ಡà³\81à²\97à³\8aಡà³\86ದ à²\9fಿಪà³\8dಪಣಿಲà³\81",
+       "config-page-complete": "ಮà³\81à²\97ಿà²\82ಡà³\8d!",
+       "config-page-readme": "à²\8eನನà³\8d à²\93ದà³\81ಲà³\86",
+       "config-page-releasenotes": "ಬುಡುಗಡೆದ ಟಿಪ್ಪಣಿಲು",
        "config-page-copying": "ನಕಲ್ ಮಲ್ತೊಂದುಂಡು",
        "config-page-upgradedoc": "ಪರಿಷ್ಕರಣೆ ಆವೊಂದುಂಡು",
-       "config-page-existingwiki": "ಪà³\8dರಸà³\8dತà³\81ತ ವಿಕಿ",
-       "config-restart": "ಸರಿ,à²\95à³\81ಡ à²ªà³\8dರಾರà²\82ಭ ಮಲ್ಪುಲೆ",
+       "config-page-existingwiki": "à²\87ತà³\8dತà³\86ದ ವಿಕಿ",
+       "config-restart": "ಸರಿ,à²\95à³\81ಡ à²¸à³\81ರà³\81 ಮಲ್ಪುಲೆ",
        "config-db-type": "ದತ್ತಾಂಶಸಂಚಯ ಮಾದರಿ:",
        "config-db-host-oracle": "ದತ್ತಾಂಶಸಂಚಯ TNS:",
        "config-db-wiki-settings": "ಈ ವಿಕಿಯನ್ನು ಗುರುತಿಸಾಲೆ",
index 08aa8c1..365231c 100644 (file)
        "config-subscribe-help": "thông báo an ninh.\nBạn nên đồng ý với nó và cập nhật bản cài đặt MediaWiki của bạn khi phiên bản mới xuất hiện.",
        "config-subscribe-noemail": "Bạn đã cố gắng để đăng ký vào danh sách thư thông báo phát hành mà không cung cấp một địa chỉ thư điện tử nào cả.\nVui lòng cung cấp một địa chỉ thư điện tử nếu bạn muốn đăng ký vào danh sách thư.",
        "config-pingback": "Chia sẻ dữ liệu về bản cài đặt này với nhóm phát triển MediaWiki.",
+       "config-pingback-help": "Nếu bạn kích hoạt chức năng này, MediaWiki sẽ thỉnh thoảng gửi cho https://www.mediawiki.org/ dữ liệu cơ bản về bản cài đặt MediaWiki này. Dữ liệu này có chẳng hạn loại máy, phiên bản PHP, và phía sau cơ sở dữ liệu đã chọn. Quỹ Wikimedia chia sẻ dữ liệu này với các nhà phát triển MediaWiki để giúp họ trong việc phát triển phần mềm vào tương lai. Dữ liệu sau sẽ được gửi từ hệ thống này:\n<pre>$1</pre>",
        "config-almost-done": "Bạn gần như đã hoàn tất!\nBây giờ bạn có thể bỏ qua cấu hình còn lại và cài đặt wiki ngay bây giờ.",
        "config-optional-continue": "Hỏi tôi về thêm chi tiết.",
        "config-optional-skip": "Chán quá, cài đặt wiki rỗi.",
index 6ac165a..0e107b3 100644 (file)
@@ -37,7 +37,7 @@ use WANObjectCache;
  * and tree storage backends (SQL, CDB, and plain PHP arrays).
  *
  * All information is loaded on creation when called by $this->fetch( $prefix ).
- * All work is done on slave, because this should *never* change (except during
+ * All work is done on replica DB, because this should *never* change (except during
  * schema updates etc, which aren't wiki-related)
  *
  * @since 1.28
@@ -282,7 +282,7 @@ class ClassicInterwikiLookup implements InterwikiLookup {
                        $this->objectCache->makeKey( 'interwiki', $prefix ),
                        $this->objectCacheExpiry,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix ) {
-                               $dbr = wfGetDB( DB_SLAVE ); // TODO: inject LoadBalancer
+                               $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -395,7 +395,7 @@ class ClassicInterwikiLookup implements InterwikiLookup {
         * @return array[] Interwiki rows
         */
        private function getAllPrefixesDB( $local ) {
-               $db = wfGetDB( DB_SLAVE ); // TODO: inject DB LoadBalancer
+               $db = wfGetDB( DB_REPLICA ); // TODO: inject DB LoadBalancer
 
                $where = [];
 
index 459910a..d0a7719 100644 (file)
@@ -26,7 +26,7 @@ use Interwiki;
 /**
  * Service interface for looking up Interwiki records.
  *
- * @singe 1.28
+ * @since 1.28
  */
 interface InterwikiLookup {
 
index d64be3c..020a684 100644 (file)
@@ -553,7 +553,7 @@ abstract class JobQueue {
        }
 
        /**
-        * Wait for any slaves or backup servers to catch up.
+        * Wait for any replica DBs or backup servers to catch up.
         *
         * This does nothing for certain queue classes.
         *
index 3a1da13..856cdfd 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  * @author Aaron Schulz
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class to handle job queues stored in the DB
@@ -185,7 +186,8 @@ class JobQueueDB extends JobQueue {
                $dbw->onTransactionIdle(
                        function () use ( $dbw, $jobs, $flags, $method ) {
                                $this->doBatchPushInternal( $dbw, $jobs, $flags, $method );
-                       }
+                       },
+                       __METHOD__
                );
        }
 
@@ -236,7 +238,7 @@ class JobQueueDB extends JobQueue {
                        }
                        // Build the full list of job rows to insert
                        $rows = array_merge( $rowList, array_values( $rowSet ) );
-                       // Insert the job rows in chunks to avoid slave lag...
+                       // Insert the job rows in chunks to avoid replica DB lag...
                        foreach ( array_chunk( $rows, 50 ) as $rowBatch ) {
                                $dbw->insert( 'job', $rowBatch, $method );
                        }
@@ -493,15 +495,18 @@ class JobQueueDB extends JobQueue {
                // jobs to become no-ops without any actual jobs that made them redundant.
                $dbw = $this->getMasterDB();
                $cache = $this->dupCache;
-               $dbw->onTransactionIdle( function () use ( $cache, $params, $key, $dbw ) {
-                       $timestamp = $cache->get( $key ); // current last timestamp of this job
-                       if ( $timestamp && $timestamp >= $params['rootJobTimestamp'] ) {
-                               return true; // a newer version of this root job was enqueued
-                       }
+               $dbw->onTransactionIdle(
+                       function () use ( $cache, $params, $key, $dbw ) {
+                               $timestamp = $cache->get( $key ); // current last timestamp of this job
+                               if ( $timestamp && $timestamp >= $params['rootJobTimestamp'] ) {
+                                       return true; // a newer version of this root job was enqueued
+                               }
 
-                       // Update the timestamp of the last root job started at the location...
-                       return $cache->set( $key, $params['rootJobTimestamp'], JobQueueDB::ROOTJOB_TTL );
-               } );
+                               // Update the timestamp of the last root job started at the location...
+                               return $cache->set( $key, $params['rootJobTimestamp'], JobQueueDB::ROOTJOB_TTL );
+                       },
+                       __METHOD__
+               );
 
                return true;
        }
@@ -526,7 +531,8 @@ class JobQueueDB extends JobQueue {
         * @return void
         */
        protected function doWaitForBackups() {
-               wfWaitForSlaves( false, $this->wiki, $this->cluster ?: false );
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->waitForReplication( [ 'wiki' => $this->wiki, 'cluster' => $this->cluster ] );
        }
 
        /**
@@ -732,7 +738,7 @@ class JobQueueDB extends JobQueue {
         */
        protected function getSlaveDB() {
                try {
-                       return $this->getDB( DB_SLAVE );
+                       return $this->getDB( DB_REPLICA );
                } catch ( DBConnectionError $e ) {
                        throw new JobQueueConnectionError( "DBConnectionError:" . $e->getMessage() );
                }
@@ -751,13 +757,14 @@ class JobQueueDB extends JobQueue {
        }
 
        /**
-        * @param int $index (DB_SLAVE/DB_MASTER)
+        * @param int $index (DB_REPLICA/DB_MASTER)
         * @return DBConnRef
         */
        protected function getDB( $index ) {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $lb = ( $this->cluster !== false )
-                       ? wfGetLBFactory()->getExternalLB( $this->cluster, $this->wiki )
-                       : wfGetLB( $this->wiki );
+                       ? $lbFactory->getExternalLB( $this->cluster, $this->wiki )
+                       : $lbFactory->getMainLB( $this->wiki );
 
                return $lb->getConnectionRef( $index, [], $this->wiki );
        }
index a4b3241..71d68d9 100644 (file)
@@ -120,6 +120,8 @@ class JobQueueGroup {
         * @return void
         */
        public function push( $jobs ) {
+               global $wgJobTypesExcludedFromDefaultQueue;
+
                $jobs = is_array( $jobs ) ? $jobs : [ $jobs ];
                if ( !count( $jobs ) ) {
                        return;
@@ -142,6 +144,20 @@ class JobQueueGroup {
                                $this->cache->clear( 'queues-ready' );
                        }
                }
+
+               $cache = ObjectCache::getLocalClusterInstance();
+               $cache->set(
+                       $cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', self::TYPE_ANY ),
+                       'true',
+                       15
+               );
+               if ( array_diff( array_keys( $jobsByType ), $wgJobTypesExcludedFromDefaultQueue ) ) {
+                       $cache->set(
+                               $cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', self::TYPE_DEFAULT ),
+                               'true',
+                               15
+                       );
+               }
        }
 
        /**
@@ -255,7 +271,7 @@ class JobQueueGroup {
        }
 
        /**
-        * Wait for any slaves or backup queue servers to catch up.
+        * Wait for any replica DBs or backup queue servers to catch up.
         *
         * This does nothing for certain queue classes.
         *
@@ -298,8 +314,8 @@ class JobQueueGroup {
         * @since 1.23
         */
        public function queuesHaveJobs( $type = self::TYPE_ANY ) {
-               $key = wfMemcKey( 'jobqueue', 'queueshavejobs', $type );
                $cache = ObjectCache::getLocalClusterInstance();
+               $key = $cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', $type );
 
                $value = $cache->get( $key );
                if ( $value === false ) {
index 5f48dca..ed3aa9a 100644 (file)
@@ -23,6 +23,7 @@
 
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Logger\LoggerFactory;
+use Liuggio\StatsdClient\Factory\StatsdDataFactory;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 
@@ -42,7 +43,7 @@ class JobRunner implements LoggerAwareInterface {
        protected $logger;
 
        const MAX_ALLOWED_LAG = 3; // abort if more than this much DB lag is present
-       const LAG_CHECK_PERIOD = 1.0; // check slave lag this many seconds
+       const LAG_CHECK_PERIOD = 1.0; // check replica DB lag this many seconds
        const ERROR_BACKOFF_TTL = 1; // seconds to back off a queue due to errors
 
        /**
@@ -114,19 +115,20 @@ class JobRunner implements LoggerAwareInterface {
                        $response['reached'] = 'read-only';
                        return $response;
                }
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                // Bail out if there is too much DB lag.
                // This check should not block as we want to try other wiki queues.
-               list( , $maxLag ) = wfGetLB( wfWikiID() )->getMaxLag();
+               list( , $maxLag ) = $lbFactory->getMainLB( wfWikiID() )->getMaxLag();
                if ( $maxLag >= self::MAX_ALLOWED_LAG ) {
-                       $response['reached'] = 'slave-lag-limit';
+                       $response['reached'] = 'replica-lag-limit';
                        return $response;
                }
 
                // Flush any pending DB writes for sanity
-               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $lbFactory->commitAll( __METHOD__ );
 
-               // Catch huge single updates that lead to slave lag
+               // Catch huge single updates that lead to replica DB lag
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
                $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
                $trxProfiler->setExpectations( $wgTrxProfilerLimits['JobRunner'], __METHOD__ );
@@ -137,11 +139,11 @@ class JobRunner implements LoggerAwareInterface {
                $wait = 'wait'; // block to read backoffs the first time
 
                $group = JobQueueGroup::singleton();
-               $stats = RequestContext::getMain()->getStats();
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
                $jobsPopped = 0;
                $timeMsTotal = 0;
                $startTime = microtime( true ); // time since jobs started running
-               $lastCheckTime = 1; // timestamp of last slave check
+               $lastCheckTime = 1; // timestamp of last replica DB check
                do {
                        // Sync the persistent backoffs with concurrent runners
                        $backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
@@ -159,6 +161,7 @@ class JobRunner implements LoggerAwareInterface {
                        } else {
                                $job = $group->pop( $type ); // job from a single queue
                        }
+                       $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
 
                        if ( $job ) { // found a job
                                ++$jobsPopped;
@@ -178,8 +181,7 @@ class JobRunner implements LoggerAwareInterface {
                                        $backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
                                }
 
-                               $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
-                               $info = $this->executeJob( $job, $stats, $popTime );
+                               $info = $this->executeJob( $job, $lbFactory, $stats, $popTime );
                                if ( $info['status'] !== false || !$job->allowRetries() ) {
                                        $group->ack( $job ); // succeeded or job cannot be retried
                                        $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
@@ -210,7 +212,7 @@ class JobRunner implements LoggerAwareInterface {
                                        break;
                                }
 
-                               // Don't let any of the main DB slaves get backed up.
+                               // Don't let any of the main DB replica DBs get backed up.
                                // This only waits for so long before exiting and letting
                                // other wikis in the farm (on different masters) get a chance.
                                $timePassed = microtime( true ) - $lastCheckTime;
@@ -221,12 +223,12 @@ class JobRunner implements LoggerAwareInterface {
                                                        'timeout' => self::MAX_ALLOWED_LAG
                                                ] );
                                        } catch ( DBReplicationWaitError $e ) {
-                                               $response['reached'] = 'slave-lag-limit';
+                                               $response['reached'] = 'replica-lag-limit';
                                                break;
                                        }
                                        $lastCheckTime = microtime( true );
                                }
-                               // Don't let any queue slaves/backups fall behind
+                               // Don't let any queue replica DBs/backups fall behind
                                if ( $jobsPopped > 0 && ( $jobsPopped % 100 ) == 0 ) {
                                        $group->waitForBackups();
                                }
@@ -252,32 +254,32 @@ class JobRunner implements LoggerAwareInterface {
 
        /**
         * @param Job $job
-        * @param BufferingStatsdDataFactory $stats
+        * @param LBFactory $lbFactory
+        * @param StatsdDataFactory $stats
         * @param float $popTime
         * @return array Map of status/error/timeMs
         */
-       private function executeJob( Job $job, $stats, $popTime ) {
+       private function executeJob( Job $job, LBFactory $lbFactory, $stats, $popTime ) {
                $jType = $job->getType();
                $msg = $job->toString() . " STARTING";
                $this->logger->debug( $msg );
                $this->debugCallback( $msg );
-               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
 
                // Run the job...
                $rssStart = $this->getMaxRssKb();
                $jobStartTime = microtime( true );
                try {
+                       $fnameTrxOwner = get_class( $job ) . '::run'; // give run() outer scope
+                       $lbFactory->beginMasterChanges( $fnameTrxOwner );
                        $status = $job->run();
                        $error = $job->getLastError();
-                       $this->commitMasterChanges( $job );
-
+                       $this->commitMasterChanges( $lbFactory, $job, $fnameTrxOwner );
+                       // Run any deferred update tasks; doUpdates() manages transactions itself
                        DeferredUpdates::doUpdates();
-                       $this->commitMasterChanges( $job );
                } catch ( Exception $e ) {
                        MWExceptionHandler::rollbackMasterChangesAndLog( $e );
                        $status = false;
                        $error = get_class( $e ) . ': ' . $e->getMessage();
-                       MWExceptionHandler::logException( $e );
                }
                // Always attempt to call teardown() even if Job throws exception.
                try {
@@ -288,10 +290,10 @@ class JobRunner implements LoggerAwareInterface {
 
                // Commit all outstanding connections that are in a transaction
                // to get a fresh repeatable read snapshot on every connection.
-               // Note that jobs are still responsible for handling slave lag.
-               $lbFactory->commitAll( __METHOD__ );
+               // Note that jobs are still responsible for handling replica DB lag.
+               $lbFactory->flushReplicaSnapshots( __METHOD__ );
                // Clear out title cache data from prior snapshots
-               LinkCache::singleton()->clear();
+               MediaWikiServices::getInstance()->getLinkCache()->clear();
                $timeMs = intval( ( microtime( true ) - $jobStartTime ) * 1000 );
                $rssEnd = $this->getMaxRssKb();
 
@@ -490,34 +492,42 @@ class JobRunner implements LoggerAwareInterface {
        /**
         * Issue a commit on all masters who are currently in a transaction and have
         * made changes to the database. It also supports sometimes waiting for the
-        * local wiki's slaves to catch up. See the documentation for
+        * local wiki's replica DBs to catch up. See the documentation for
         * $wgJobSerialCommitThreshold for more.
         *
+        * @param LBFactory $lbFactory
         * @param Job $job
+        * @param string $fnameTrxOwner
         * @throws DBError
         */
-       private function commitMasterChanges( Job $job ) {
+       private function commitMasterChanges( LBFactory $lbFactory, Job $job, $fnameTrxOwner ) {
                global $wgJobSerialCommitThreshold;
 
-               $lb = wfGetLB( wfWikiID() );
+               $time = false;
+               $lb = $lbFactory->getMainLB( wfWikiID() );
                if ( $wgJobSerialCommitThreshold !== false && $lb->getServerCount() > 1 ) {
                        // Generally, there is one master connection to the local DB
                        $dbwSerial = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
+                       // We need natively blocking fast locks
+                       if ( $dbwSerial && $dbwSerial->namedLocksEnqueue() ) {
+                               $time = $dbwSerial->pendingWriteQueryDuration( $dbwSerial::ESTIMATE_DB_APPLY );
+                               if ( $time < $wgJobSerialCommitThreshold ) {
+                                       $dbwSerial = false;
+                               }
+                       } else {
+                               $dbwSerial = false;
+                       }
                } else {
+                       // There are no replica DBs or writes are all to foreign DB (we don't handle that)
                        $dbwSerial = false;
                }
 
-               if ( !$dbwSerial
-                       || !$dbwSerial->namedLocksEnqueue()
-                       || $dbwSerial->pendingWriteQueryDuration() < $wgJobSerialCommitThreshold
-               ) {
-                       // Writes are all to foreign DBs, named locks don't form queues,
-                       // or $wgJobSerialCommitThreshold is not reached; commit changes now
-                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
+               if ( !$dbwSerial ) {
+                       $lbFactory->commitMasterChanges( $fnameTrxOwner );
                        return;
                }
 
-               $ms = intval( 1000 * $dbwSerial->pendingWriteQueryDuration() );
+               $ms = intval( 1000 * $time );
                $msg = $job->toString() . " COMMIT ENQUEUED [{$ms}ms of writes]";
                $this->logger->info( $msg );
                $this->debugCallback( $msg );
@@ -527,16 +537,18 @@ class JobRunner implements LoggerAwareInterface {
                        // This will trigger a rollback in the main loop
                        throw new DBError( $dbwSerial, "Timed out waiting on commit queue." );
                }
-               // Wait for the slave DBs to catch up
+               $unlocker = new ScopedCallback( function () use ( $dbwSerial ) {
+                       $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+               } );
+
+               // Wait for the replica DBs to catch up
                $pos = $lb->getMasterPos();
                if ( $pos ) {
                        $lb->waitForAll( $pos );
                }
 
                // Actually commit the DB master changes
-               wfGetLBFactory()->commitMasterChanges( __METHOD__ );
-
-               // Release the lock
-               $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+               $lbFactory->commitMasterChanges( $fnameTrxOwner );
+               ScopedCallback::consume( $unlocker );
        }
 }
index b561021..a52ff06 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Job to add recent change entries mentioning category membership changes
@@ -32,6 +33,9 @@
  * @since 1.27
  */
 class CategoryMembershipChangeJob extends Job {
+       /** @var integer|null */
+       private $ticket;
+
        const ENQUEUE_FUDGE_SEC = 60;
 
        public function __construct( Title $title, array $params ) {
@@ -42,29 +46,34 @@ class CategoryMembershipChangeJob extends Job {
        }
 
        public function run() {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lb = $lbFactory->getMainLB();
+               $dbw = $lb->getConnection( DB_MASTER );
+
+               $this->ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+
                $page = WikiPage::newFromID( $this->params['pageId'], WikiPage::READ_LATEST );
                if ( !$page ) {
                        $this->setLastError( "Could not find page #{$this->params['pageId']}" );
                        return false; // deleted?
                }
 
-               $dbw = wfGetDB( DB_MASTER );
                // Use a named lock so that jobs for this page see each others' changes
                $lockKey = "CategoryMembershipUpdates:{$page->getId()}";
-               $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 10 );
+               $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 3 );
                if ( !$scopedLock ) {
                        $this->setLastError( "Could not acquire lock '$lockKey'" );
                        return false;
                }
 
-               $dbr = wfGetDB( DB_SLAVE, [ 'recentchanges' ] );
-               // Wait till the slave is caught up so that jobs for this page see each others' changes
-               if ( !wfGetLB()->safeWaitForMasterPos( $dbr ) ) {
-                       $this->setLastError( "Timed out while waiting for slave to catch up" );
+               $dbr = $lb->getConnection( DB_REPLICA, [ 'recentchanges' ] );
+               // Wait till the replica DB is caught up so that jobs for this page see each others' changes
+               if ( !$lb->safeWaitForMasterPos( $dbr ) ) {
+                       $this->setLastError( "Timed out while waiting for replica DB to catch up" );
                        return false;
                }
                // Clear any stale REPEATABLE-READ snapshot
-               wfGetLBFactory()->commitAll( __METHOD__ );
+               $dbr->flushSnapshot( __METHOD__ );
 
                $cutoffUnix = wfTimestamp( TS_UNIX, $this->params['revTimestamp'] );
                // Using ENQUEUE_FUDGE_SEC handles jobs inserted out of revision order due to the delay
@@ -119,18 +128,21 @@ class CategoryMembershipChangeJob extends Job {
 
                // Apply all category updates in revision timestamp order
                foreach ( $res as $row ) {
-                       $this->notifyUpdatesForRevision( $page, Revision::newFromRow( $row ) );
+                       $this->notifyUpdatesForRevision( $lbFactory, $page, Revision::newFromRow( $row ) );
                }
 
                return true;
        }
 
        /**
+        * @param LBFactory $lbFactory
         * @param WikiPage $page
         * @param Revision $newRev
         * @throws MWException
         */
-       protected function notifyUpdatesForRevision( WikiPage $page, Revision $newRev ) {
+       protected function notifyUpdatesForRevision(
+               LBFactory $lbFactory, WikiPage $page, Revision $newRev
+       ) {
                $config = RequestContext::getMain()->getConfig();
                $title = $page->getTitle();
 
@@ -156,10 +168,6 @@ class CategoryMembershipChangeJob extends Job {
                        return; // nothing to do
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $factory = wfGetLBFactory();
-               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
-
                $catMembChange = new CategoryMembershipChange( $title, $newRev );
                $catMembChange->checkTemplateLinks();
 
@@ -170,7 +178,7 @@ class CategoryMembershipChangeJob extends Job {
                        $categoryTitle = Title::makeTitle( NS_CATEGORY, $categoryName );
                        $catMembChange->triggerCategoryAddedNotification( $categoryTitle );
                        if ( $insertCount++ && ( $insertCount % $batchSize ) == 0 ) {
-                               $factory->commitAndWaitForReplication( __METHOD__, $ticket );
+                               $lbFactory->commitAndWaitForReplication( __METHOD__, $this->ticket );
                        }
                }
 
@@ -178,7 +186,7 @@ class CategoryMembershipChangeJob extends Job {
                        $categoryTitle = Title::makeTitle( NS_CATEGORY, $categoryName );
                        $catMembChange->triggerCategoryRemovedNotification( $categoryTitle );
                        if ( $insertCount++ && ( $insertCount++ % $batchSize ) == 0 ) {
-                               $factory->commitAndWaitForReplication( __METHOD__, $ticket );
+                               $lbFactory->commitAndWaitForReplication( __METHOD__, $this->ticket );
                        }
                }
        }
index 8d565bd..5c0f89f 100644 (file)
@@ -59,7 +59,7 @@ class DeleteLinksJob extends Job {
 
                $update = new LinksDeletionUpdate( $page, $pageId, $timestamp );
                $update->setTransactionTicket( $factory->getEmptyTransactionTicket( __METHOD__ ) );
-               DataUpdate::runUpdates( [ $update ] );
+               $update->doUpdate();
 
                return true;
        }
index 26d3c5c..80826fe 100644 (file)
@@ -31,7 +31,7 @@
  * @code
  * $ php maintenance/eval.php
  * > $queue = JobQueueGroup::singleton();
- * > $job = new NullJob( Title::newMainPage(), array( 'lives' => 10 ) );
+ * > $job = new NullJob( Title::newMainPage(), [ 'lives' => 10 ] );
  * > $queue->push( $job );
  * @endcode
  * You can then confirm the job has been enqueued by using the showJobs.php
index 2fd3899..0e90674 100644 (file)
@@ -19,6 +19,7 @@
  * @author Aaron Schulz
  * @ingroup JobQueue
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Job for pruning recent changes
@@ -81,7 +82,7 @@ class RecentChangesUpdateJob extends Job {
                        return; // already in progress
                }
 
-               $factory = wfGetLBFactory();
+               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
                do {
@@ -93,7 +94,7 @@ class RecentChangesUpdateJob extends Job {
                        );
                        if ( $rcIds ) {
                                $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIds ], __METHOD__ );
-                               // There might be more, so try waiting for slaves
+                               // There might be more, so try waiting for replica DBs
                                try {
                                        $factory->commitAndWaitForReplication(
                                                __METHOD__, $ticket, [ 'timeout' => 3 ]
@@ -119,109 +120,112 @@ class RecentChangesUpdateJob extends Job {
                $dbw = wfGetDB( DB_MASTER );
                // JobRunner uses DBO_TRX, but doesn't call begin/commit itself;
                // onTransactionIdle() will run immediately since there is no trx.
-               $dbw->onTransactionIdle( function() use ( $dbw, $days, $window ) {
-                       $factory = wfGetLBFactory();
-                       $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
-                       // Avoid disconnect/ping() cycle that makes locks fall off
-                       $dbw->setSessionOptions( [ 'connTimeout' => 900 ] );
-
-                       $lockKey = wfWikiID() . '-activeusers';
-                       if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
-                               return; // exclusive update (avoids duplicate entries)
-                       }
-
-                       $nowUnix = time();
-                       // Get the last-updated timestamp for the cache
-                       $cTime = $dbw->selectField( 'querycache_info',
-                               'qci_timestamp',
-                               [ 'qci_type' => 'activeusers' ]
-                       );
-                       $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
-
-                       // Pick the date range to fetch from. This is normally from the last
-                       // update to till the present time, but has a limited window for sanity.
-                       // If the window is limited, multiple runs are need to fully populate it.
-                       $sTimestamp = max( $cTimeUnix, $nowUnix - $days * 86400 );
-                       $eTimestamp = min( $sTimestamp + $window, $nowUnix );
-
-                       // Get all the users active since the last update
-                       $res = $dbw->select(
-                               [ 'recentchanges' ],
-                               [ 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ],
-                               [
-                                       'rc_user > 0', // actual accounts
-                                       'rc_type != ' . $dbw->addQuotes( RC_EXTERNAL ), // no wikidata
-                                       'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
-                                       'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
-                                       'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
-                               ],
-                               __METHOD__,
-                               [
-                                       'GROUP BY' => [ 'rc_user_text' ],
-                                       'ORDER BY' => 'NULL' // avoid filesort
-                               ]
-                       );
-                       $names = [];
-                       foreach ( $res as $row ) {
-                               $names[$row->rc_user_text] = $row->lastedittime;
-                       }
-
-                       // Rotate out users that have not edited in too long (according to old data set)
-                       $dbw->delete( 'querycachetwo',
-                               [
-                                       'qcc_type' => 'activeusers',
-                                       'qcc_value < ' . $dbw->addQuotes( $nowUnix - $days * 86400 ) // TS_UNIX
-                               ],
-                               __METHOD__
-                       );
+               $dbw->onTransactionIdle(
+                       function () use ( $dbw, $days, $window ) {
+                               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
+                               // Avoid disconnect/ping() cycle that makes locks fall off
+                               $dbw->setSessionOptions( [ 'connTimeout' => 900 ] );
+
+                               $lockKey = wfWikiID() . '-activeusers';
+                               if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
+                                       return; // exclusive update (avoids duplicate entries)
+                               }
 
-                       // Find which of the recently active users are already accounted for
-                       if ( count( $names ) ) {
-                               $res = $dbw->select( 'querycachetwo',
-                                       [ 'user_name' => 'qcc_title' ],
+                               $nowUnix = time();
+                               // Get the last-updated timestamp for the cache
+                               $cTime = $dbw->selectField( 'querycache_info',
+                                       'qci_timestamp',
+                                       [ 'qci_type' => 'activeusers' ]
+                               );
+                               $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
+
+                               // Pick the date range to fetch from. This is normally from the last
+                               // update to till the present time, but has a limited window for sanity.
+                               // If the window is limited, multiple runs are need to fully populate it.
+                               $sTimestamp = max( $cTimeUnix, $nowUnix - $days * 86400 );
+                               $eTimestamp = min( $sTimestamp + $window, $nowUnix );
+
+                               // Get all the users active since the last update
+                               $res = $dbw->select(
+                                       [ 'recentchanges' ],
+                                       [ 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ],
                                        [
-                                               'qcc_type' => 'activeusers',
-                                               'qcc_namespace' => NS_USER,
-                                               'qcc_title' => array_keys( $names ) ],
-                                       __METHOD__
+                                               'rc_user > 0', // actual accounts
+                                               'rc_type != ' . $dbw->addQuotes( RC_EXTERNAL ), // no wikidata
+                                               'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
+                                               'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
+                                               'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
+                                       ],
+                                       __METHOD__,
+                                       [
+                                               'GROUP BY' => [ 'rc_user_text' ],
+                                               'ORDER BY' => 'NULL' // avoid filesort
+                                       ]
                                );
+                               $names = [];
                                foreach ( $res as $row ) {
-                                       unset( $names[$row->user_name] );
+                                       $names[$row->rc_user_text] = $row->lastedittime;
                                }
-                       }
 
-                       // Insert the users that need to be added to the list
-                       if ( count( $names ) ) {
-                               $newRows = [];
-                               foreach ( $names as $name => $lastEditTime ) {
-                                       $newRows[] = [
+                               // Rotate out users that have not edited in too long (according to old data set)
+                               $dbw->delete( 'querycachetwo',
+                                       [
                                                'qcc_type' => 'activeusers',
-                                               'qcc_namespace' => NS_USER,
-                                               'qcc_title' => $name,
-                                               'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
-                                               'qcc_namespacetwo' => 0, // unused
-                                               'qcc_titletwo' => '' // unused
-                                       ];
+                                               'qcc_value < ' . $dbw->addQuotes( $nowUnix - $days * 86400 ) // TS_UNIX
+                                       ],
+                                       __METHOD__
+                               );
+
+                               // Find which of the recently active users are already accounted for
+                               if ( count( $names ) ) {
+                                       $res = $dbw->select( 'querycachetwo',
+                                               [ 'user_name' => 'qcc_title' ],
+                                               [
+                                                       'qcc_type' => 'activeusers',
+                                                       'qcc_namespace' => NS_USER,
+                                                       'qcc_title' => array_keys( $names ) ],
+                                               __METHOD__
+                                       );
+                                       foreach ( $res as $row ) {
+                                               unset( $names[$row->user_name] );
+                                       }
                                }
-                               foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
-                                       $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
-                                       $factory->commitAndWaitForReplication( __METHOD__, $ticket );
+
+                               // Insert the users that need to be added to the list
+                               if ( count( $names ) ) {
+                                       $newRows = [];
+                                       foreach ( $names as $name => $lastEditTime ) {
+                                               $newRows[] = [
+                                                       'qcc_type' => 'activeusers',
+                                                       'qcc_namespace' => NS_USER,
+                                                       'qcc_title' => $name,
+                                                       'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
+                                                       'qcc_namespacetwo' => 0, // unused
+                                                       'qcc_titletwo' => '' // unused
+                                               ];
+                                       }
+                                       foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
+                                               $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
+                                               $factory->commitAndWaitForReplication( __METHOD__, $ticket );
+                                       }
                                }
-                       }
 
-                       // If a transaction was already started, it might have an old
-                       // snapshot, so kludge the timestamp range back as needed.
-                       $asOfTimestamp = min( $eTimestamp, (int)$dbw->trxTimestamp() );
+                               // If a transaction was already started, it might have an old
+                               // snapshot, so kludge the timestamp range back as needed.
+                               $asOfTimestamp = min( $eTimestamp, (int)$dbw->trxTimestamp() );
 
-                       // Touch the data freshness timestamp
-                       $dbw->replace( 'querycache_info',
-                               [ 'qci_type' ],
-                               [ 'qci_type' => 'activeusers',
-                                       'qci_timestamp' => $dbw->timestamp( $asOfTimestamp ) ], // not always $now
-                               __METHOD__
-                       );
+                               // Touch the data freshness timestamp
+                               $dbw->replace( 'querycache_info',
+                                       [ 'qci_type' ],
+                                       [ 'qci_type' => 'activeusers',
+                                               'qci_timestamp' => $dbw->timestamp( $asOfTimestamp ) ], // not always $now
+                                       __METHOD__
+                               );
 
-                       $dbw->unlock( $lockKey, __METHOD__ );
-               } );
+                               $dbw->unlock( $lockKey, __METHOD__ );
+                       },
+                       __METHOD__
+               );
        }
 }
index 9cdb161..5f33ae0 100644 (file)
@@ -40,7 +40,7 @@ class RefreshLinksJob extends Job {
        const PARSE_THRESHOLD_SEC = 1.0;
        /** @var integer Lag safety margin when comparing root job times to last-refresh times */
        const CLOCK_FUDGE = 10;
-       /** @var integer How many seconds to wait for slaves to catch up */
+       /** @var integer How many seconds to wait for replica DBs to catch up */
        const LAG_WAIT_TIMEOUT = 15;
 
        function __construct( Title $title, array $params ) {
@@ -83,12 +83,13 @@ class RefreshLinksJob extends Job {
 
                // Job to update all (or a range of) backlink pages for a page
                if ( !empty( $this->params['recursive'] ) ) {
-                       // When the base job branches, wait for the slaves to catch up to the master.
+                       // When the base job branches, wait for the replica DBs to catch up to the master.
                        // From then on, we know that any template changes at the time the base job was
                        // enqueued will be reflected in backlink page parses when the leaf jobs run.
                        if ( !isset( $params['range'] ) ) {
                                try {
-                                       wfGetLBFactory()->waitForReplication( [
+                                       $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                                       $lbFactory->waitForReplication( [
                                                'wiki'    => wfWikiID(),
                                                'timeout' => self::LAG_WAIT_TIMEOUT
                                        ] );
@@ -128,13 +129,18 @@ class RefreshLinksJob extends Job {
         * @return bool
         */
        protected function runForTitle( Title $title ) {
-               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+               $services = MediaWikiServices::getInstance();
+               $stats = $services->getStatsdDataFactory();
+               $lbFactory = $services->getDBLoadBalancerFactory();
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
 
                $page = WikiPage::factory( $title );
                $page->loadPageData( WikiPage::READ_LATEST );
 
                // Serialize links updates by page ID so they see each others' changes
-               $scopedLock = LinksUpdate::acquirePageLock( wfGetDB( DB_MASTER ), $page->getId(), 'job' );
+               $dbw = $lbFactory->getMainLB()->getConnection( DB_MASTER );
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scopedLock = LinksUpdate::acquirePageLock( $dbw, $page->getId(), 'job' );
                // Get the latest ID *after* acquirePageLock() flushed the transaction.
                // This is used to detect edits/moves after loadPageData() but before the scope lock.
                // The works around the chicken/egg problem of determining the scope lock key.
@@ -182,7 +188,7 @@ class RefreshLinksJob extends Job {
 
                        $skewedTimestamp = $this->params['rootJobTimestamp'];
                        if ( $opportunistic ) {
-                               // Neither clock skew nor DB snapshot/slave lag matter much for such
+                               // Neither clock skew nor DB snapshot/replica DB lag matter much for such
                                // updates; focus on reusing the (often recently updated) cache
                        } else {
                                // For transclusion updates, the template changes must be reflected
@@ -241,10 +247,7 @@ class RefreshLinksJob extends Job {
                        $parserOutput
                );
 
-               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
-               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                foreach ( $updates as $key => $update ) {
-                       $update->setTransactionTicket( $ticket );
                        // FIXME: This code probably shouldn't be here?
                        // Needed by things like Echo notifications which need
                        // to know which user caused the links update
@@ -263,7 +266,10 @@ class RefreshLinksJob extends Job {
                        }
                }
 
-               DataUpdate::runUpdates( $updates );
+               foreach ( $updates as $update ) {
+                       $update->setTransactionTicket( $ticket );
+                       $update->doUpdate();
+               }
 
                InfoAction::invalidateCache( $title );
 
index 329bc23..d76d866 100644 (file)
@@ -20,6 +20,8 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
+
 class PurgeJobUtils {
        /**
         * Invalidate the cache of a list of pages from a single namespace.
@@ -34,38 +36,45 @@ class PurgeJobUtils {
                        return;
                }
 
-               $dbw->onTransactionPreCommitOrIdle( function() use ( $dbw, $namespace, $dbkeys ) {
-                       // Determine which pages need to be updated.
-                       // This is necessary to prevent the job queue from smashing the DB with
-                       // large numbers of concurrent invalidations of the same page.
-                       $now = $dbw->timestamp();
-                       $ids = $dbw->selectFieldValues(
-                               'page',
-                               'page_id',
-                               [
-                                       'page_namespace' => $namespace,
-                                       'page_title' => $dbkeys,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ],
-                               __METHOD__
-                       );
+               $dbw->onTransactionIdle(
+                       function () use ( $dbw, $namespace, $dbkeys ) {
+                               $services = MediaWikiServices::getInstance();
+                               $lbFactory = $services->getDBLoadBalancerFactory();
+                               // Determine which pages need to be updated.
+                               // This is necessary to prevent the job queue from smashing the DB with
+                               // large numbers of concurrent invalidations of the same page.
+                               $now = $dbw->timestamp();
+                               $ids = $dbw->selectFieldValues(
+                                       'page',
+                                       'page_id',
+                                       [
+                                               'page_namespace' => $namespace,
+                                               'page_title' => $dbkeys,
+                                               'page_touched < ' . $dbw->addQuotes( $now )
+                                       ],
+                                       __METHOD__
+                               );
 
-                       if ( $ids === [] ) {
-                               return;
-                       }
+                               if ( !$ids ) {
+                                       return;
+                               }
 
-                       // Do the update.
-                       // We still need the page_touched condition, in case the row has changed since
-                       // the non-locking select above.
-                       $dbw->update(
-                               'page',
-                               [ 'page_touched' => $now ],
-                               [
-                                       'page_id' => $ids,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ],
-                               __METHOD__
-                       );
-               } );
+                               $batchSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
+                               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+                               foreach ( array_chunk( $ids, $batchSize ) as $idBatch ) {
+                                       $dbw->update(
+                                               'page',
+                                               [ 'page_touched' => $now ],
+                                               [
+                                                       'page_id' => $idBatch,
+                                                       'page_touched < ' . $dbw->addQuotes( $now ) // handle races
+                                               ],
+                                               __METHOD__
+                                       );
+                                       $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+                               }
+                       },
+                       __METHOD__
+               );
        }
 }
index 2370ed3..2f5a454 100644 (file)
@@ -84,13 +84,15 @@ class MapCacheLRU {
         * If the item is already set, it will be pushed to the top of the cache.
         *
         * @param string $key
-        * @return mixed
+        * @return mixed Returns null if the key was not found
         */
        public function get( $key ) {
                if ( !array_key_exists( $key, $this->cache ) ) {
                        return null;
                }
+
                $this->ping( $key );
+
                return $this->cache[$key];
        }
 
@@ -102,6 +104,29 @@ class MapCacheLRU {
                return array_keys( $this->cache );
        }
 
+       /**
+        * Get an item with the given key, producing and setting it if not found.
+        *
+        * If the callback returns false, then nothing is stored.
+        *
+        * @since 1.28
+        * @param string $key
+        * @param callable $callback Callback that will produce the value
+        * @return mixed The cached value if found or the result of $callback otherwise
+        */
+       public function getWithSetCallback( $key, callable $callback ) {
+               if ( $this->has( $key ) ) {
+                       $value = $this->get( $key );
+               } else {
+                       $value = call_user_func( $callback );
+                       if ( $value !== false ) {
+                               $this->set( $key, $value );
+                       }
+               }
+
+               return $value;
+       }
+
        /**
         * Clear one or several cache entries, or all cache entries
         *
index 2e780c9..dd1976c 100644 (file)
@@ -30,6 +30,19 @@ use Liuggio\StatsdClient\Entity\StatsdDataInterface;
  * @since 1.26
  */
 class SamplingStatsdClient extends StatsdClient {
+       protected $samplingRates = [];
+
+       /**
+        * Sampling rates as an associative array of patterns and rates.
+        * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+        * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+        * @param array $samplingRates
+        * @since 1.28
+        */
+       public function setSamplingRates( array $samplingRates ) {
+               $this->samplingRates = $samplingRates;
+       }
+
        /**
         * Sets sampling rate for all items in $data.
         * The sample rate specified in a StatsdData entity overrides the sample rate specified here.
@@ -37,11 +50,18 @@ class SamplingStatsdClient extends StatsdClient {
         * {@inheritDoc}
         */
        public function appendSampleRate( $data, $sampleRate = 1 ) {
-               if ( $sampleRate < 1 ) {
-                       array_walk( $data, function( $item ) use ( $sampleRate ) {
+               $samplingRates = $this->samplingRates;
+               if ( !$samplingRates && $sampleRate !== 1 ) {
+                       $samplingRates = [ '*' => $sampleRate ];
+               }
+               if ( $samplingRates ) {
+                       array_walk( $data, function( $item ) use ( $samplingRates ) {
                                /** @var $item StatsdData */
-                               if ( $item->getSampleRate() === 1 ) {
-                                       $item->setSampleRate( $sampleRate );
+                               foreach ( $samplingRates as $pattern => $rate ) {
+                                       if ( fnmatch( $pattern, $item->getKey(), FNM_NOESCAPE ) ) {
+                                               $item->setSampleRate( $item->getSampleRate() * $rate );
+                                               break;
+                                       }
                                }
                        } );
                }
@@ -74,9 +94,7 @@ class SamplingStatsdClient extends StatsdClient {
                }
 
                // add sampling
-               if ( $sampleRate < 1 ) {
-                       $data = $this->appendSampleRate( $data, $sampleRate );
-               }
+               $data = $this->appendSampleRate( $data, $sampleRate );
                $data = $this->sampleData( $data );
 
                $data = array_map( 'strval', $data );
index 1d23f9d..7f41f55 100644 (file)
@@ -79,6 +79,34 @@ class StatusValue {
                return $result;
        }
 
+       /**
+        * Splits this StatusValue object into two new StatusValue objects, one which contains only
+        * the error messages, and one that contains the warnings, only. The returned array is
+        * defined as:
+        * array(
+        *      0 => object(StatusValue) # the StatusValue with error messages, only
+        *      1 => object(StatusValue) # The StatusValue with warning messages, only
+        * )
+        *
+        * @return array
+        */
+       public function splitByErrorType() {
+               $errorsOnlyStatusValue = clone $this;
+               $warningsOnlyStatusValue = clone $this;
+               $warningsOnlyStatusValue->ok = true;
+
+               $errorsOnlyStatusValue->errors = $warningsOnlyStatusValue->errors = [];
+               foreach ( $this->errors as $item ) {
+                       if ( $item['type'] === 'warning' ) {
+                               $warningsOnlyStatusValue->errors[] = $item;
+                       } else {
+                               $errorsOnlyStatusValue->errors[] = $item;
+                       }
+               };
+
+               return [ $errorsOnlyStatusValue, $warningsOnlyStatusValue ];
+       }
+
        /**
         * Returns whether the operation completed and didn't have any error or
         * warnings
@@ -246,8 +274,8 @@ class StatusValue {
         * Note, due to the lack of tools for comparing IStatusMessage objects, this
         * function will not work when using such an object as the search parameter.
         *
-        * @param IStatusMessage|string $source Message key or object to search for
-        * @param IStatusMessage|string $dest Replacement message key or object
+        * @param MessageSpecifier|string $source Message key or object to search for
+        * @param MessageSpecifier|string $dest Replacement message key or object
         * @return bool Return true if the replacement was done, false otherwise.
         */
        public function replaceMessage( $source, $dest ) {
diff --git a/includes/libs/WaitConditionLoop.php b/includes/libs/WaitConditionLoop.php
new file mode 100644 (file)
index 0000000..969e86e
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * Wait loop that reaches a condition or times out.
+ *
+ * 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 Aaron Schulz
+ */
+
+/**
+ * Wait loop that reaches a condition or times out
+ * @since 1.28
+ */
+class WaitConditionLoop {
+       /** @var callable */
+       private $condition;
+       /** @var callable[] */
+       private $busyCallbacks = [];
+       /** @var float Seconds */
+       private $timeout;
+       /** @var float Seconds */
+       private $lastWaitTime;
+       /** @var integer|null */
+       private $rusageMode;
+
+       const CONDITION_REACHED = 1;
+       const CONDITION_CONTINUE = 0; // evaluates as falsey
+       const CONDITION_FAILED = -1;
+       const CONDITION_TIMED_OUT = -2;
+       const CONDITION_ABORTED = -3;
+
+       /**
+        * @param callable $condition Callback that returns a WaitConditionLoop::CONDITION_ constant
+        * @param float $timeout Timeout in seconds
+        * @param array &$busyCallbacks List of callbacks to do useful work (by reference)
+        */
+       public function __construct( callable $condition, $timeout = 5.0, &$busyCallbacks = [] ) {
+               $this->condition = $condition;
+               $this->timeout = $timeout;
+               $this->busyCallbacks =& $busyCallbacks;
+
+               if ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
+                       $this->rusageMode = 2; // RUSAGE_THREAD
+               } elseif ( function_exists( 'getrusage' ) ) {
+                       $this->rusageMode = 0; // RUSAGE_SELF
+               }
+       }
+
+       /**
+        * Invoke the loop and continue until either:
+        *   - a) The condition callback returns neither CONDITION_CONTINUE nor false
+        *   - b) The timeout is reached
+        * This a condition callback can return true (stop) or false (continue) for convenience.
+        * In such cases, the halting result of "true" will be converted to CONDITION_REACHED.
+        *
+        * If $timeout is 0, then only the condition callback will be called (no busy callbacks),
+        * and this will immediately return CONDITION_FAILED if the condition was not met.
+        *
+        * Exceptions in callbacks will be caught and the callback will be swapped with
+        * one that simply rethrows that exception back to the caller when invoked.
+        *
+        * @return integer WaitConditionLoop::CONDITION_* constant
+        * @throws Exception Any error from the condition callback
+        */
+       public function invoke() {
+               $elapsed = 0.0; // seconds
+               $sleepUs = 0; // microseconds to sleep each time
+               $lastCheck = false;
+               $finalResult = self::CONDITION_TIMED_OUT;
+               do {
+                       $checkStartTime = $this->getWallTime();
+                       // Check if the condition is met yet
+                       $realStart = $this->getWallTime();
+                       $cpuStart = $this->getCpuTime();
+                       $checkResult = call_user_func( $this->condition );
+                       $cpu = $this->getCpuTime() - $cpuStart;
+                       $real = $this->getWallTime() - $realStart;
+                       // Exit if the condition is reached, and error occurs, or this is non-blocking
+                       if ( $this->timeout <= 0 ) {
+                               $finalResult = $checkResult ? self::CONDITION_REACHED : self::CONDITION_FAILED;
+                               break;
+                       } elseif ( (int)$checkResult !== self::CONDITION_CONTINUE ) {
+                               if ( is_int( $checkResult ) ) {
+                                       $finalResult = $checkResult;
+                               } else {
+                                       $finalResult = self::CONDITION_REACHED;
+                               }
+                               break;
+                       } elseif ( $lastCheck ) {
+                               break; // timeout reached
+                       }
+                       // Detect if condition callback seems to block or if justs burns CPU
+                       $conditionUsesInterrupts = ( $real > 0.100 && $cpu <= $real * .03 );
+                       if ( !$this->popAndRunBusyCallback() && !$conditionUsesInterrupts ) {
+                               // 10 queries = 10(10+100)/2 ms = 550ms, 14 queries = 1050ms
+                               $sleepUs = min( $sleepUs + 10 * 1e3, 1e6 ); // stop incrementing at ~1s
+                               $this->usleep( $sleepUs );
+                       }
+                       $checkEndTime = $this->getWallTime();
+                       // The max() protects against the clock getting set back
+                       $elapsed += max( $checkEndTime - $checkStartTime, 0.010 );
+                       // Do not let slow callbacks timeout without checking the condition one more time
+                       $lastCheck = ( $elapsed >= $this->timeout );
+               } while ( true );
+
+               $this->lastWaitTime = $elapsed;
+
+               return $finalResult;
+       }
+
+       /**
+        * @return float Seconds
+        */
+       public function getLastWaitTime() {
+               return $this->lastWaitTime;
+       }
+
+       /**
+        * @param integer $microseconds
+        */
+       protected function usleep( $microseconds ) {
+               usleep( $microseconds );
+       }
+
+       /**
+        * @return float
+        */
+       protected function getWallTime() {
+               return microtime( true );
+       }
+
+       /**
+        * @return float Returns 0.0 if not supported (Windows on PHP < 7)
+        */
+       protected function getCpuTime() {
+               if ( $this->rusageMode === null ) {
+                       return microtime( true ); // assume worst case (all time is CPU)
+               }
+
+               $ru = getrusage( $this->rusageMode );
+               $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
+               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
+
+               return $time;
+       }
+
+       /**
+        * Run one of the callbacks that does work ahead of time for another caller
+        *
+        * @return bool Whether a callback was executed
+        */
+       private function popAndRunBusyCallback() {
+               if ( $this->busyCallbacks ) {
+                       reset( $this->busyCallbacks );
+                       $key = key( $this->busyCallbacks );
+                       /** @var callable $workCallback */
+                       $workCallback =& $this->busyCallbacks[$key];
+                       try {
+                               $workCallback();
+                       } catch ( Exception $e ) {
+                               $workCallback = function () use ( $e ) {
+                                       throw $e;
+                               };
+                       }
+                       unset( $this->busyCallbacks[$key] ); // consume
+
+                       return true;
+               }
+
+               return false;
+       }
+}
index 5472e83..cd79b67 100644 (file)
@@ -45,31 +45,29 @@ use Psr\Log\NullLogger;
 abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /** @var array[] Lock tracking */
        protected $locks = [];
-
        /** @var integer ERR_* class constant */
        protected $lastError = self::ERR_NONE;
-
        /** @var string */
        protected $keyspace = 'local';
-
        /** @var LoggerInterface */
        protected $logger;
-
        /** @var callback|null */
        protected $asyncHandler;
+       /** @var integer Seconds */
+       protected $syncTimeout;
 
        /** @var bool */
        private $debugMode = false;
-
        /** @var array */
        private $duplicateKeyLookups = [];
-
        /** @var bool */
        private $reportDupes = false;
-
        /** @var bool */
        private $dupeTrackScheduled = false;
 
+       /** @var callable[] */
+       protected $busyCallbacks = [];
+
        /** @var integer[] Map of (ATTR_* class constant => QOS_* class constant) */
        protected $attrMap = [];
 
@@ -94,6 +92,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *      In CLI mode, it should run the task immediately.
         *   - reportDupes: Whether to emit warning log messages for all keys that were
         *      requested more than once (requires an asyncHandler).
+        *   - syncTimeout: How long to wait with WRITE_SYNC in seconds.
         * @param array $params
         */
        public function __construct( array $params = [] ) {
@@ -114,6 +113,8 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
                        $this->reportDupes = true;
                }
+
+               $this->syncTimeout = isset( $params['syncTimeout'] ) ? $params['syncTimeout'] : 3;
        }
 
        /**
@@ -409,35 +410,21 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                }
 
                $expiry = min( $expiry ?: INF, self::TTL_DAY );
-
-               $this->clearLastError();
-               $timestamp = microtime( true ); // starting UNIX timestamp
-               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
-                       $locked = true;
-               } elseif ( $this->getLastError() || $timeout <= 0 ) {
-                       $locked = false; // network partition or non-blocking
-               } else {
-                       // Estimate the RTT (us); use 1ms minimum for sanity
-                       $uRTT = max( 1e3, ceil( 1e6 * ( microtime( true ) - $timestamp ) ) );
-                       $sleep = 2 * $uRTT; // rough time to do get()+set()
-
-                       $attempts = 0; // failed attempts
-                       do {
-                               if ( ++$attempts >= 3 && $sleep <= 5e5 ) {
-                                       // Exponentially back off after failed attempts to avoid network spam.
-                                       // About 2*$uRTT*(2^n-1) us of "sleep" happen for the next n attempts.
-                                       $sleep *= 2;
-                               }
-                               usleep( $sleep ); // back off
+               $loop = new WaitConditionLoop(
+                       function () use ( $key, $timeout, $expiry ) {
                                $this->clearLastError();
-                               $locked = $this->add( "{$key}:lock", 1, $expiry );
-                               if ( $this->getLastError() ) {
-                                       $locked = false; // network partition
-                                       break;
+                               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
+                                       return true; // locked!
+                               } elseif ( $this->getLastError() ) {
+                                       return WaitConditionLoop::CONDITION_ABORTED; // network partition?
                                }
-                       } while ( !$locked && ( microtime( true ) - $timestamp ) < $timeout );
-               }
 
+                               return WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
+
+               $locked = ( $loop->invoke() === $loop::CONDITION_REACHED );
                if ( $locked ) {
                        $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
                }
@@ -642,6 +629,30 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                $this->lastError = $err;
        }
 
+       /**
+        * Let a callback be run to avoid wasting time on special blocking calls
+        *
+        * The callbacks may or may not be called ever, in any particular order.
+        * They are likely to be invoked when something WRITE_SYNC is used used.
+        * They should follow a caching pattern as shown below, so that any code
+        * using the word will get it's result no matter what happens.
+        * @code
+        *     $result = null;
+        *     $workCallback = function () use ( &$result ) {
+        *         if ( !$result ) {
+        *             $result = ....
+        *         }
+        *         return $result;
+        *     }
+        * @endcode
+        *
+        * @param callable $workCallback
+        * @since 1.28
+        */
+       public function addBusyCallback( callable $workCallback ) {
+               $this->busyCallbacks[] = $workCallback;
+       }
+
        /**
         * Modify a cache update operation array for EventRelayer::notify()
         *
index e70a51f..74bf4b5 100644 (file)
@@ -42,6 +42,8 @@ class CachedBagOStuff extends HashBagOStuff {
         * @param array $params Parameters for HashBagOStuff
         */
        function __construct( BagOStuff $backend, $params = [] ) {
+               unset( $params['reportDupes'] ); // useless here
+
                parent::__construct( $params );
 
                $this->backend = $backend;
index 62c4fa5..0e09f16 100644 (file)
@@ -47,6 +47,12 @@ interface IExpiringStore {
        // Medium attributes constants related to emulation or media type
        const ATTR_EMULATION = 1;
        const QOS_EMULATION_SQL = 1;
+       // Medium attributes constants related to replica consistency
+       const ATTR_SYNCWRITES = 2; // SYNC_WRITES flag support
+       const QOS_SYNCWRITES_NONE = 1; // replication only supports eventual consistency or less
+       const QOS_SYNCWRITES_BE = 2; // best effort synchronous with limited retries
+       const QOS_SYNCWRITES_QC = 3; // write quorum applied directly to state machines where R+W > N
+       const QOS_SYNCWRITES_SS = 4; // strict-serializable, nodes refuse reads if possible stale
        // Generic "unknown" value that is useful for comparisons (e.g. always good enough)
        const QOS_UNKNOWN = INF;
 }
index 5967441..6973392 100644 (file)
@@ -30,6 +30,12 @@ class MemcachedBagOStuff extends BagOStuff {
        /** @var MemcachedClient|Memcached */
        protected $client;
 
+       function __construct( array $params ) {
+               parent::__construct( $params );
+
+               $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_BE; // unreliable
+       }
+
        /**
         * Fill in some defaults for missing keys in $params.
         *
diff --git a/includes/libs/objectcache/MemcachedPeclBagOStuff.php b/includes/libs/objectcache/MemcachedPeclBagOStuff.php
new file mode 100644 (file)
index 0000000..5983c1b
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * Object caching using memcached.
+ *
+ * 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 Cache
+ */
+
+/**
+ * A wrapper class for the PECL memcached client
+ *
+ * @ingroup Cache
+ */
+class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
+
+       /**
+        * Constructor
+        *
+        * Available parameters are:
+        *   - servers:             The list of IP:port combinations holding the memcached servers.
+        *   - persistent:          Whether to use a persistent connection
+        *   - compress_threshold:  The minimum size an object must be before it is compressed
+        *   - timeout:             The read timeout in microseconds
+        *   - connect_timeout:     The connect timeout in seconds
+        *   - retry_timeout:       Time in seconds to wait before retrying a failed connect attempt
+        *   - server_failure_limit:  Limit for server connect failures before it is removed
+        *   - serializer:          May be either "php" or "igbinary". Igbinary produces more compact
+        *                          values, but serialization is much slower unless the php.ini option
+        *                          igbinary.compact_strings is off.
+        *   - use_binary_protocol  Whether to enable the binary protocol (default is ASCII) (boolean)
+        * @param array $params
+        * @throws InvalidArgumentException
+        */
+       function __construct( $params ) {
+               parent::__construct( $params );
+               $params = $this->applyDefaultParams( $params );
+
+               if ( $params['persistent'] ) {
+                       // The pool ID must be unique to the server/option combination.
+                       // The Memcached object is essentially shared for each pool ID.
+                       // We can only reuse a pool ID if we keep the config consistent.
+                       $this->client = new Memcached( md5( serialize( $params ) ) );
+                       if ( count( $this->client->getServerList() ) ) {
+                               $this->logger->debug( __METHOD__ . ": persistent Memcached object already loaded." );
+                               return; // already initialized; don't add duplicate servers
+                       }
+               } else {
+                       $this->client = new Memcached;
+               }
+
+               if ( $params['use_binary_protocol'] ) {
+                       $this->client->setOption( Memcached::OPT_BINARY_PROTOCOL, true );
+               }
+
+               if ( isset( $params['retry_timeout'] ) ) {
+                       $this->client->setOption( Memcached::OPT_RETRY_TIMEOUT, $params['retry_timeout'] );
+               }
+
+               if ( isset( $params['server_failure_limit'] ) ) {
+                       $this->client->setOption( Memcached::OPT_SERVER_FAILURE_LIMIT, $params['server_failure_limit'] );
+               }
+
+               // The compression threshold is an undocumented php.ini option for some
+               // reason. There's probably not much harm in setting it globally, for
+               // compatibility with the settings for the PHP client.
+               ini_set( 'memcached.compression_threshold', $params['compress_threshold'] );
+
+               // Set timeouts
+               $this->client->setOption( Memcached::OPT_CONNECT_TIMEOUT, $params['connect_timeout'] * 1000 );
+               $this->client->setOption( Memcached::OPT_SEND_TIMEOUT, $params['timeout'] );
+               $this->client->setOption( Memcached::OPT_RECV_TIMEOUT, $params['timeout'] );
+               $this->client->setOption( Memcached::OPT_POLL_TIMEOUT, $params['timeout'] / 1000 );
+
+               // Set libketama mode since it's recommended by the documentation and
+               // is as good as any. There's no way to configure libmemcached to use
+               // hashes identical to the ones currently in use by the PHP client, and
+               // even implementing one of the libmemcached hashes in pure PHP for
+               // forwards compatibility would require MemcachedClient::get_sock() to be
+               // rewritten.
+               $this->client->setOption( Memcached::OPT_LIBKETAMA_COMPATIBLE, true );
+
+               // Set the serializer
+               switch ( $params['serializer'] ) {
+                       case 'php':
+                               $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP );
+                               break;
+                       case 'igbinary':
+                               if ( !Memcached::HAVE_IGBINARY ) {
+                                       throw new InvalidArgumentException(
+                                               __CLASS__ . ': the igbinary extension is not available ' .
+                                               'but igbinary serialization was requested.'
+                                       );
+                               }
+                               $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY );
+                               break;
+                       default:
+                               throw new InvalidArgumentException(
+                                       __CLASS__ . ': invalid value for serializer parameter'
+                               );
+               }
+               $servers = [];
+               foreach ( $params['servers'] as $host ) {
+                       if ( preg_match( '/^\[(.+)\]:(\d+)$/', $host, $m ) ) {
+                               $servers[] = [ $m[1], (int)$m[2] ]; // (ip, port)
+                       } elseif ( preg_match( '/^([^:]+):(\d+)$/', $host, $m ) ) {
+                               $servers[] = [ $m[1], (int)$m[2] ]; // (ip or path, port)
+                       } else {
+                               $servers[] = [ $host, false ]; // (ip or path, port)
+                       }
+               }
+               $this->client->addServers( $servers );
+       }
+
+       protected function applyDefaultParams( $params ) {
+               $params = parent::applyDefaultParams( $params );
+
+               if ( !isset( $params['use_binary_protocol'] ) ) {
+                       $params['use_binary_protocol'] = false;
+               }
+
+               if ( !isset( $params['serializer'] ) ) {
+                       $params['serializer'] = 'php';
+               }
+
+               return $params;
+       }
+
+       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
+               $this->debugLog( "get($key)" );
+               $result = $this->client->get( $this->validateKeyEncoding( $key ), null, $casToken );
+               $result = $this->checkResult( $key, $result );
+               return $result;
+       }
+
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               $this->debugLog( "set($key)" );
+               return $this->checkResult( $key, parent::set( $key, $value, $exptime ) );
+       }
+
+       protected function cas( $casToken, $key, $value, $exptime = 0 ) {
+               $this->debugLog( "cas($key)" );
+               return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) );
+       }
+
+       public function delete( $key ) {
+               $this->debugLog( "delete($key)" );
+               $result = parent::delete( $key );
+               if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) {
+                       // "Not found" is counted as success in our interface
+                       return true;
+               } else {
+                       return $this->checkResult( $key, $result );
+               }
+       }
+
+       public function add( $key, $value, $exptime = 0 ) {
+               $this->debugLog( "add($key)" );
+               return $this->checkResult( $key, parent::add( $key, $value, $exptime ) );
+       }
+
+       public function incr( $key, $value = 1 ) {
+               $this->debugLog( "incr($key)" );
+               $result = $this->client->increment( $key, $value );
+               return $this->checkResult( $key, $result );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               $this->debugLog( "decr($key)" );
+               $result = $this->client->decrement( $key, $value );
+               return $this->checkResult( $key, $result );
+       }
+
+       /**
+        * Check the return value from a client method call and take any necessary
+        * action. Returns the value that the wrapper function should return. At
+        * present, the return value is always the same as the return value from
+        * the client, but some day we might find a case where it should be
+        * different.
+        *
+        * @param string $key The key used by the caller, or false if there wasn't one.
+        * @param mixed $result The return value
+        * @return mixed
+        */
+       protected function checkResult( $key, $result ) {
+               if ( $result !== false ) {
+                       return $result;
+               }
+               switch ( $this->client->getResultCode() ) {
+                       case Memcached::RES_SUCCESS:
+                               break;
+                       case Memcached::RES_DATA_EXISTS:
+                       case Memcached::RES_NOTSTORED:
+                       case Memcached::RES_NOTFOUND:
+                               $this->debugLog( "result: " . $this->client->getResultMessage() );
+                               break;
+                       default:
+                               $msg = $this->client->getResultMessage();
+                               $logCtx = [];
+                               if ( $key !== false ) {
+                                       $server = $this->client->getServerByKey( $key );
+                                       $logCtx['memcached-server'] = "{$server['host']}:{$server['port']}";
+                                       $logCtx['memcached-key'] = $key;
+                                       $msg = "Memcached error for key \"{memcached-key}\" on server \"{memcached-server}\": $msg";
+                               } else {
+                                       $msg = "Memcached error: $msg";
+                               }
+                               $this->logger->error( $msg, $logCtx );
+                               $this->setLastError( BagOStuff::ERR_UNEXPECTED );
+               }
+               return $result;
+       }
+
+       public function getMulti( array $keys, $flags = 0 ) {
+               $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
+               foreach ( $keys as $key ) {
+                       $this->validateKeyEncoding( $key );
+               }
+               $result = $this->client->getMulti( $keys ) ?: [];
+               return $this->checkResult( false, $result );
+       }
+
+       /**
+        * @param array $data
+        * @param int $exptime
+        * @return bool
+        */
+       public function setMulti( array $data, $exptime = 0 ) {
+               $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' );
+               foreach ( array_keys( $data ) as $key ) {
+                       $this->validateKeyEncoding( $key );
+               }
+               $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
+               return $this->checkResult( false, $result );
+       }
+
+       public function changeTTL( $key, $expiry = 0 ) {
+               $this->debugLog( "touch($key)" );
+               $result = $this->client->touch( $key, $expiry );
+               return $this->checkResult( $key, $result );
+       }
+}
index 9fc3fe1..ae91be5 100644 (file)
@@ -51,6 +51,8 @@ class RESTBagOStuff extends BagOStuff {
                }
                // Make sure URL ends with /
                $this->url = rtrim( $params['url'], '/' ) . '/';
+               // Default config, R+W > N; no locks on reads though; writes go straight to state-machine
+               $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_QC;
        }
 
        /**
index f2ba9de..3bc7ae2 100644 (file)
@@ -22,7 +22,7 @@
 
 /**
  * A cache class that directs writes to one set of servers and reads to
- * another. This assumes that the servers used for reads are setup to slave
+ * another. This assumes that the servers used for reads are setup to replica DB
  * those that writes go to. This can easily be used with redis for example.
  *
  * In the WAN scenario (e.g. multi-datacenter case), this is useful when
@@ -42,7 +42,7 @@ class ReplicatedBagOStuff extends BagOStuff {
         *   - writeFactory : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
         *                    This object will be used for writes (e.g. the master DB).
         *   - readFactory  : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
-        *                    This object will be used for reads (e.g. a slave DB).
+        *                    This object will be used for reads (e.g. a replica DB).
         *
         * @param array $params
         * @throws InvalidArgumentException
@@ -59,12 +59,13 @@ class ReplicatedBagOStuff extends BagOStuff {
                                __METHOD__ . ': the "readFactory" parameter is required' );
                }
 
+               $opts = [ 'reportDupes' => false ]; // redundant
                $this->writeStore = ( $params['writeFactory'] instanceof BagOStuff )
                        ? $params['writeFactory']
-                       : ObjectFactory::getObjectFromSpec( $params['writeFactory'] );
+                       : ObjectFactory::getObjectFromSpec( $opts + $params['writeFactory'] );
                $this->readStore = ( $params['readFactory'] instanceof BagOStuff )
                        ? $params['readFactory']
-                       : ObjectFactory::getObjectFromSpec( $params['readFactory'] );
+                       : ObjectFactory::getObjectFromSpec( $opts + $params['readFactory'] );
                $this->attrMap = $this->mergeFlagMaps( [ $this->readStore, $this->writeStore ] );
        }
 
index c40c819..5f6e324 100644 (file)
@@ -76,8 +76,8 @@ use Psr\Log\NullLogger;
 class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** @var BagOStuff The local datacenter cache */
        protected $cache;
-       /** @var HashBagOStuff Script instance PHP cache */
-       protected $procCache;
+       /** @var HashBagOStuff[] Map of group PHP instance caches */
+       protected $processCaches = [];
        /** @var string Purge channel name */
        protected $purgeChannel;
        /** @var EventRelayer Bus that handles purge broadcasts */
@@ -104,6 +104,15 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** Default time-since-expiry on a miss that makes a key "hot" */
        const LOCK_TSE = 1;
 
+       /** Never consider performing "popularity" refreshes until a key reaches this age */
+       const AGE_NEW = 60;
+       /** The time length of the "popularity" refresh window for hot keys */
+       const HOT_TTR = 900;
+       /** Hits/second for a refresh to be expected within the "popularity" window */
+       const HIT_RATE_HIGH = 1;
+       /** Seconds to ramp up to the "popularity" refresh chance after a key is no longer new */
+       const RAMPUP_TTL = 30;
+
        /** Idiom for getWithSetCallback() callbacks to avoid calling set() */
        const TTL_UNCACHEABLE = -1;
        /** Idiom for getWithSetCallback() callbacks to 'lockTSE' logic */
@@ -112,6 +121,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        const TTL_LAGGED = 30;
        /** Idiom for delete() for "no hold-off" */
        const HOLDOFF_NONE = 0;
+       /** Idiom for getWithSetCallback() for "no minimum required as-of timestamp" */
+       const MIN_TIMESTAMP_NONE = 0.0;
 
        /** Tiny negative float to use when CTL comes up >= 0 due to clock skew */
        const TINY_NEGATIVE = -0.000001;
@@ -145,7 +156,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        const VFLD_DATA = 'WOC:d'; // key to the value of versioned data
        const VFLD_VERSION = 'WOC:v'; // key to the version of the value present
 
-       const MAX_PC_KEYS = 1000; // max keys to keep in process cache
+       const PC_PRIMARY = 'primary:1000'; // process cache name and max key count
 
        const DEFAULT_PURGE_CHANNEL = 'wancache-purge';
 
@@ -158,7 +169,6 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         */
        public function __construct( array $params ) {
                $this->cache = $params['cache'];
-               $this->procCache = new HashBagOStuff( [ 'maxKeys' => self::MAX_PC_KEYS ] );
                $this->purgeChannel = isset( $params['channels']['purge'] )
                        ? $params['channels']['purge']
                        : self::DEFAULT_PURGE_CHANNEL;
@@ -364,7 +374,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Example usage:
         * @code
-        *     $dbr = wfGetDB( DB_SLAVE );
+        *     $dbr = wfGetDB( DB_REPLICA );
         *     $setOpts = Database::getCacheSetOptions( $dbr );
         *     // Fetch the row from the DB
         *     $row = $dbr->selectRow( ... );
@@ -377,22 +387,30 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param integer $ttl Seconds to live. Special values are:
         *   - WANObjectCache::TTL_INDEFINITE: Cache forever
         * @param array $opts Options map:
-        *   - lag     : Seconds of slave lag. Typically, this is either the slave lag
-        *               before the data was read or, if applicable, the slave lag before
-        *               the snapshot-isolated transaction the data was read from started.
-        *               Default: 0 seconds
-        *   - since   : UNIX timestamp of the data in $value. Typically, this is either
-        *               the current time the data was read or (if applicable) the time when
-        *               the snapshot-isolated transaction the data was read from started.
-        *               Default: 0 seconds
+        *   - lag : Seconds of replica DB lag. Typically, this is either the replica DB lag
+        *      before the data was read or, if applicable, the replica DB lag before
+        *      the snapshot-isolated transaction the data was read from started.
+        *      Use false to indicate that replication is not running.
+        *      Default: 0 seconds
+        *   - since : UNIX timestamp of the data in $value. Typically, this is either
+        *      the current time the data was read or (if applicable) the time when
+        *      the snapshot-isolated transaction the data was read from started.
+        *      Default: 0 seconds
         *   - pending : Whether this data is possibly from an uncommitted write transaction.
-        *               Generally, other threads should not see values from the future and
-        *               they certainly should not see ones that ended up getting rolled back.
-        *               Default: false
+        *      Generally, other threads should not see values from the future and
+        *      they certainly should not see ones that ended up getting rolled back.
+        *      Default: false
         *   - lockTSE : if excessive replication/snapshot lag is detected, then store the value
-        *               with this TTL and flag it as stale. This is only useful if the reads for
-        *               this key use getWithSetCallback() with "lockTSE" set.
-        *               Default: WANObjectCache::TSE_NONE
+        *      with this TTL and flag it as stale. This is only useful if the reads for
+        *      this key use getWithSetCallback() with "lockTSE" set.
+        *      Default: WANObjectCache::TSE_NONE
+        *   - staleTTL : Seconds to keep the key around if it is stale. The get()/getMulti()
+        *      methods return such stale values with a $curTTL of 0, and getWithSetCallback()
+        *      will call the regeneration callback in such cases, passing in the old value
+        *      and its as-of time to the callback. This is useful if adaptiveTTL() is used
+        *      on the old value's as-of time when it is verified as still being correct.
+        *      Default: 0.
+        * @note Options added in 1.28: staleTTL
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
@@ -400,6 +418,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
                $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
+               $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
                if ( !empty( $opts['pending'] ) ) {
@@ -441,7 +460,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                : $wrapped;
                };
 
-               return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl, 1 );
+               return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 );
        }
 
        /**
@@ -566,7 +585,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * Keys using it via get(), getMulti(), or getWithSetCallback() will
         * be invalidated. It is treated as being HOLDOFF_TTL seconds in the future
         * by those methods to avoid race conditions where dependent keys get updated
-        * with stale values (e.g. from a DB slave).
+        * with stale values (e.g. from a DB replica DB).
         *
         * This is typically useful for keys with hardcoded names or in some cases
         * dynamically generated names where a low number of combinations exist.
@@ -635,6 +654,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *   - $oldValue : current cache value or false if not present
         *   - &$ttl : a reference to the TTL which can be altered
         *   - &$setOpts : a reference to options for set() which can be altered
+        *   - $oldAsOf : generation UNIX timestamp of $oldValue or null if not present (since 1.28)
         *
         * It is strongly recommended to set the 'lag' and 'since' fields to avoid race conditions
         * that can cause stale values to get stuck at keys. Usually, callbacks ignore the current
@@ -660,8 +680,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache::TTL_MINUTE,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return $dbr->selectRow( ... );
@@ -678,8 +698,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache::TTL_DAY,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return CatConfig::newFromRow( $dbr->selectRow( ... ) );
@@ -705,8 +725,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
         *             // Determine new value from the DB
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return CatState::newFromResults( $dbr->select( ... ) );
@@ -732,8 +752,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         10,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             // Start off with the last cached list
@@ -765,9 +785,6 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *   - checkKeys: List of "check" keys. The key at $key will be seen as invalid when either
         *      touchCheckKey() or resetCheckKey() is called on any of these keys.
         *      Default: [].
-        *   - lowTTL: Consider pre-emptive updates when the current TTL (seconds) of the key is less
-        *      than this. It becomes more likely over time, becoming certain once the key is expired.
-        *      Default: WANObjectCache::LOW_TTL.
         *   - lockTSE: If the key is tombstoned or expired (by checkKeys) less than this many seconds
         *      ago, then try to have a single thread handle cache regeneration at any given time.
         *      Other threads will try to use stale values if possible. If, on miss, the time since
@@ -784,26 +801,53 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *   - pcTTL: Process cache the value in this PHP instance for this many seconds. This avoids
         *      network I/O when a key is read several times. This will not cache when the callback
         *      returns false, however. Note that any purges will not be seen while process cached;
-        *      since the callback should use slave DBs and they may be lagged or have snapshot
+        *      since the callback should use replica DBs and they may be lagged or have snapshot
         *      isolation anyway, this should not typically matter.
         *      Default: WANObjectCache::TTL_UNCACHEABLE.
+        *   - pcGroup: Process cache group to use instead of the primary one. If set, this must be
+        *      of the format ALPHANUMERIC_NAME:MAX_KEY_SIZE, e.g. "mydata:10". Use this for storing
+        *      large values, small yet numerous values, or some values with a high cost of eviction.
+        *      It is generally preferable to use a class constant when setting this value.
+        *      This has no effect unless pcTTL is used.
+        *      Default: WANObjectCache::PC_PRIMARY.
         *   - version: Integer version number. This allows for callers to make breaking changes to
         *      how values are stored while maintaining compatability and correct cache purges. New
         *      versions are stored alongside older versions concurrently. Avoid storing class objects
         *      however, as this reduces compatibility (due to serialization).
         *      Default: null.
+        *   - minAsOf: Reject values if they were generated before this UNIX timestamp.
+        *      This is useful if the source of a key is suspected of having possibly changed
+        *      recently, and the caller wants any such changes to be reflected.
+        *      Default: WANObjectCache::MIN_TIMESTAMP_NONE.
+        *   - hotTTR: Expected time-till-refresh for keys that average ~1 hit/second.
+        *      This should be greater than "ageNew". Keys with higher hit rates will regenerate
+        *      more often. This is useful when a popular key is changed but the cache purge was
+        *      delayed or lost. Seldom used keys are rarely affected by this setting, unless an
+        *      extremely low "hotTTR" value is passed in.
+        *      Default: WANObjectCache::HOT_TTR.
+        *   - lowTTL: Consider pre-emptive updates when the current TTL (seconds) of the key is less
+        *      than this. It becomes more likely over time, becoming certain once the key is expired.
+        *      Default: WANObjectCache::LOW_TTL.
+        *   - ageNew: Consider popularity refreshes only once a key reaches this age in seconds.
+        *      Default: WANObjectCache::AGE_NEW.
         * @return mixed Value found or written to the key
+        * @note Options added in 1.28: version, busyValue, hotTTR, ageNew, pcGroup, minAsOf
         * @note Callable type hints are not used to avoid class-autoloading
         */
        final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [] ) {
                $pcTTL = isset( $opts['pcTTL'] ) ? $opts['pcTTL'] : self::TTL_UNCACHEABLE;
 
                // Try the process cache if enabled
-               $value = ( $pcTTL >= 0 ) ? $this->procCache->get( $key ) : false;
+               if ( $pcTTL >= 0 ) {
+                       $group = isset( $opts['pcGroup'] ) ? $opts['pcGroup'] : self::PC_PRIMARY;
+                       $procCache = $this->getProcessCache( $group );
+                       $value = $procCache->get( $key );
+               } else {
+                       $procCache = false;
+                       $value = false;
+               }
 
                if ( $value === false ) {
-                       unset( $opts['minTime'] ); // not a public feature
-
                        // Fetch the value over the network
                        if ( isset( $opts['version'] ) ) {
                                $version = $opts['version'];
@@ -811,7 +855,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                $cur = $this->doGetWithSetCallback(
                                        $key,
                                        $ttl,
-                                       function ( $oldValue, &$ttl, &$setOpts ) use ( $callback, $version ) {
+                                       function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
+                                       use ( $callback, $version ) {
                                                if ( is_array( $oldValue )
                                                        && array_key_exists( self::VFLD_DATA, $oldValue )
                                                ) {
@@ -822,7 +867,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                                }
 
                                                return [
-                                                       self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts ),
+                                                       self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts, $oldAsOf ),
                                                        self::VFLD_VERSION => $version
                                                ];
                                        },
@@ -840,7 +885,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                                $ttl,
                                                $callback,
                                                // Regenerate value if not newer than $key
-                                               [ 'version' => null, 'minTime' => $asOf ] + $opts
+                                               [ 'version' => null, 'minAsOf' => $asOf ] + $opts
                                        );
                                }
                        } else {
@@ -848,8 +893,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        }
 
                        // Update the process cache if enabled
-                       if ( $pcTTL >= 0 && $value !== false ) {
-                               $this->procCache->set( $key, $value, $pcTTL );
+                       if ( $procCache && $value !== false ) {
+                               $procCache->set( $key, $value, $pcTTL );
                        }
                }
 
@@ -864,8 +909,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param string $key
         * @param integer $ttl
         * @param callback $callback
-        * @param array $opts Options map for getWithSetCallback() which also includes:
-        *   - minTime: Treat values older than this UNIX timestamp as not existing. Default: null.
+        * @param array $opts Options map for getWithSetCallback()
         * @param float &$asOf Cache generation timestamp of returned value [returned]
         * @return mixed
         * @note Callable type hints are not used to avoid class-autoloading
@@ -875,7 +919,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
                $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : [];
                $busyValue = isset( $opts['busyValue'] ) ? $opts['busyValue'] : null;
-               $minTime = isset( $opts['minTime'] ) ? $opts['minTime'] : 0.0;
+               $popWindow = isset( $opts['hotTTR'] ) ? $opts['hotTTR'] : self::HOT_TTR;
+               $ageNew = isset( $opts['ageNew'] ) ? $opts['ageNew'] : self::AGE_NEW;
+               $minTime = isset( $opts['minAsOf'] ) ? $opts['minAsOf'] : self::MIN_TIMESTAMP_NONE;
                $versioned = isset( $opts['version'] );
 
                // Get the current key value
@@ -887,7 +933,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                if ( $value !== false
                        && $curTTL > 0
                        && $this->isValid( $value, $versioned, $asOf, $minTime )
-                       && !$this->worthRefresh( $curTTL, $lowTTL )
+                       && !$this->worthRefreshExpiring( $curTTL, $lowTTL )
+                       && !$this->worthRefreshPopular( $asOf, $ageNew, $popWindow )
                ) {
                        return $value;
                }
@@ -937,13 +984,13 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
                // Generate the new value from the callback...
                $setOpts = [];
-               $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts ] );
-               $asOf = microtime( true );
+               $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts, $asOf ] );
                // When delete() is called, writes are write-holed by the tombstone,
                // so use a special INTERIM key to pass the new value around threads.
                if ( ( $isTombstone && $lockTSE > 0 ) && $value !== false && $ttl >= 0 ) {
                        $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
-                       $wrapped = $this->wrap( $value, $tempTTL, $asOf );
+                       $newAsOf = microtime( true );
+                       $wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
                        // Avoid using set() to avoid pointless mcrouter broadcasting
                        $this->cache->merge(
                                self::INTERIM_KEY_PREFIX . $key,
@@ -995,7 +1042,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         */
        final public function getLastError() {
                if ( $this->lastRelayError ) {
-                       // If the cache and the relayer failed, focus on the later.
+                       // If the cache and the relayer failed, focus on the latter.
                        // An update not making it to the relayer means it won't show up
                        // in other DCs (nor will consistent re-hashing see up-to-date values).
                        // On the other hand, if just the cache update failed, then it should
@@ -1030,7 +1077,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @since 1.27
         */
        public function clearProcessCache() {
-               $this->procCache->clear();
+               $this->processCaches = [];
        }
 
        /**
@@ -1042,6 +1089,43 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                return $this->cache->getQoS( $flag );
        }
 
+       /**
+        * Get a TTL that is higher for objects that have not changed recently
+        *
+        * This is useful for keys that get explicit purges and DB or purge relay
+        * lag is a potential concern (especially how it interacts with CDN cache)
+        *
+        * Example usage:
+        * @code
+        *     // Last-modified time of page
+        *     $mtime = wfTimestamp( TS_UNIX, $page->getTimestamp() );
+        *     // Get adjusted TTL. If $mtime is 3600 seconds ago and $minTTL/$factor left at
+        *     // defaults, then $ttl is 3600 * .2 = 720. If $minTTL was greater than 720, then
+        *     // $ttl would be $minTTL. If $maxTTL was smaller than 720, $ttl would be $maxTTL.
+        *     $ttl = $cache->adaptiveTTL( $mtime, $cache::TTL_DAY );
+        * @endcode
+        *
+        * @param integer|float $mtime UNIX timestamp
+        * @param integer $maxTTL Maximum TTL (seconds)
+        * @param integer $minTTL Minimum TTL (seconds); Default: 30
+        * @param float $factor Value in the range (0,1); Default: .2
+        * @return integer Adaptive TTL
+        * @since 1.28
+        */
+       public function adaptiveTTL( $mtime, $maxTTL, $minTTL = 30, $factor = .2 ) {
+               if ( is_float( $mtime ) || ctype_digit( $mtime ) ) {
+                       $mtime = (int)$mtime; // handle fractional seconds and string integers
+               }
+
+               if ( !is_int( $mtime ) || $mtime <= 0 ) {
+                       return $minTTL; // no last-modified time provided
+               }
+
+               $age = time() - $mtime;
+
+               return (int)min( $maxTTL, max( $minTTL, $factor * $age ) );
+       }
+
        /**
         * Do the actual async bus purge of a key
         *
@@ -1114,7 +1198,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param float $lowTTL Consider a refresh when $curTTL is less than this
         * @return bool
         */
-       protected function worthRefresh( $curTTL, $lowTTL ) {
+       protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
                if ( $curTTL >= $lowTTL ) {
                        return false;
                } elseif ( $curTTL <= 0 ) {
@@ -1126,6 +1210,40 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
        }
 
+       /**
+        * Check if a key is due for randomized regeneration due to its popularity
+        *
+        * This is used so that popular keys can preemptively refresh themselves for higher
+        * consistency (especially in the case of purge loss/delay). Unpopular keys can remain
+        * in cache with their high nominal TTL. This means popular keys keep good consistency,
+        * whether the data changes frequently or not, and long-tail keys get to stay in cache
+        * and get hits too. Similar to worthRefreshExpiring(), randomization is used.
+        *
+        * @param float $asOf UNIX timestamp of the value
+        * @param integer $ageNew Age of key when this might recommend refreshing (seconds)
+        * @param integer $timeTillRefresh Age of key when it should be refreshed if popular (seconds)
+        * @return bool
+        */
+       protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh ) {
+               $age = microtime( true ) - $asOf;
+               $timeOld = $age - $ageNew;
+               if ( $timeOld <= 0 ) {
+                       return false;
+               }
+
+               // Lifecycle is: new, ramp-up refresh chance, full refresh chance
+               $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::RAMPUP_TTL / 2, 1 );
+               // P(refresh) * (# hits in $refreshWindowSec) = (expected # of refreshes)
+               // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1
+               // P(refresh) = 1/($refreshWindowSec * $popularHitsPerSec)
+               $chance = 1 / ( self::HIT_RATE_HIGH * $refreshWindowSec );
+
+               // Ramp up $chance from 0 to its nominal value over RAMPUP_TTL seconds to avoid stampedes
+               $chance *= ( $timeOld <= self::RAMPUP_TTL ) ? $timeOld / self::RAMPUP_TTL : 1;
+
+               return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
+       }
+
        /**
         * Check whether $value is appropriately versioned and not older than $minTime (if set)
         *
@@ -1249,4 +1367,17 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        protected function makePurgeValue( $timestamp, $holdoff ) {
                return self::PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
        }
+
+       /**
+        * @param string $group
+        * @return HashBagOStuff
+        */
+       protected function getProcessCache( $group ) {
+               if ( !isset( $this->processCaches[$group] ) ) {
+                       list( , $n ) = explode( ':', $group );
+                       $this->processCaches[$group] = new HashBagOStuff( [ 'maxKeys' => (int)$n ] );
+               }
+
+               return $this->processCaches[$group];
+       }
 }
index 19cc66a..6996ce5 100644 (file)
@@ -65,6 +65,13 @@ class WinCacheBagOStuff extends BagOStuff {
        }
 
        public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
-               return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
+               if ( wincache_lock( $key ) ) { // optimize with FIFO lock
+                       $ok = $this->mergeViaLock( $key, $callback, $exptime, $attempts, $flags );
+                       wincache_unlock( $key );
+               } else {
+                       $ok = false;
+               }
+
+               return $ok;
        }
 }
diff --git a/includes/libs/rdbms/TransactionProfiler.php b/includes/libs/rdbms/TransactionProfiler.php
new file mode 100644 (file)
index 0000000..5c9976d
--- /dev/null
@@ -0,0 +1,329 @@
+<?php
+/**
+ * Transaction profiling for contention
+ *
+ * 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
+ * @author Aaron Schulz
+ */
+
+use Psr\Log\LoggerInterface;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\NullLogger;
+
+/**
+ * Helper class that detects high-contention DB queries via profiling calls
+ *
+ * This class is meant to work with a DatabaseBase object, which manages queries
+ *
+ * @since 1.24
+ */
+class TransactionProfiler implements LoggerAwareInterface {
+       /** @var float Seconds */
+       protected $dbLockThreshold = 3.0;
+       /** @var float Seconds */
+       protected $eventThreshold = .25;
+       /** @var bool */
+       protected $silenced = false;
+
+       /** @var array transaction ID => (write start time, list of DBs involved) */
+       protected $dbTrxHoldingLocks = [];
+       /** @var array transaction ID => list of (query name, start time, end time) */
+       protected $dbTrxMethodTimes = [];
+
+       /** @var array */
+       protected $hits = [
+               'writes'      => 0,
+               'queries'     => 0,
+               'conns'       => 0,
+               'masterConns' => 0
+       ];
+       /** @var array */
+       protected $expect = [
+               'writes'         => INF,
+               'queries'        => INF,
+               'conns'          => INF,
+               'masterConns'    => INF,
+               'maxAffected'    => INF,
+               'readQueryTime'  => INF,
+               'writeQueryTime' => INF
+       ];
+       /** @var array */
+       protected $expectBy = [];
+
+       /**
+        * @var LoggerInterface
+        */
+       private $logger;
+
+       public function __construct() {
+               $this->setLogger( new NullLogger() );
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
+       /**
+        * @param bool $value
+        * @since 1.28
+        */
+       public function setSilenced( $value ) {
+               $this->silenced = $value;
+       }
+
+       /**
+        * Set performance expectations
+        *
+        * With conflicting expectations, the most narrow ones will be used
+        *
+        * @param string $event (writes,queries,conns,mConns)
+        * @param integer $value Maximum count of the event
+        * @param string $fname Caller
+        * @since 1.25
+        */
+       public function setExpectation( $event, $value, $fname ) {
+               $this->expect[$event] = isset( $this->expect[$event] )
+                       ? min( $this->expect[$event], $value )
+                       : $value;
+               if ( $this->expect[$event] == $value ) {
+                       $this->expectBy[$event] = $fname;
+               }
+       }
+
+       /**
+        * Set multiple performance expectations
+        *
+        * With conflicting expectations, the most narrow ones will be used
+        *
+        * @param array $expects Map of (event => limit)
+        * @param $fname
+        * @since 1.26
+        */
+       public function setExpectations( array $expects, $fname ) {
+               foreach ( $expects as $event => $value ) {
+                       $this->setExpectation( $event, $value, $fname );
+               }
+       }
+
+       /**
+        * Reset performance expectations and hit counters
+        *
+        * @since 1.25
+        */
+       public function resetExpectations() {
+               foreach ( $this->hits as &$val ) {
+                       $val = 0;
+               }
+               unset( $val );
+               foreach ( $this->expect as &$val ) {
+                       $val = INF;
+               }
+               unset( $val );
+               $this->expectBy = [];
+       }
+
+       /**
+        * Mark a DB as having been connected to with a new handle
+        *
+        * Note that there can be multiple connections to a single DB.
+        *
+        * @param string $server DB server
+        * @param string $db DB name
+        * @param bool $isMaster
+        */
+       public function recordConnection( $server, $db, $isMaster ) {
+               // Report when too many connections happen...
+               if ( $this->hits['conns']++ == $this->expect['conns'] ) {
+                       $this->reportExpectationViolated( 'conns', "[connect to $server ($db)]" );
+               }
+               if ( $isMaster && $this->hits['masterConns']++ == $this->expect['masterConns'] ) {
+                       $this->reportExpectationViolated( 'masterConns', "[connect to $server ($db)]" );
+               }
+       }
+
+       /**
+        * Mark a DB as in a transaction with one or more writes pending
+        *
+        * Note that there can be multiple connections to a single DB.
+        *
+        * @param string $server DB server
+        * @param string $db DB name
+        * @param string $id ID string of transaction
+        */
+       public function transactionWritingIn( $server, $db, $id ) {
+               $name = "{$server} ({$db}) (TRX#$id)";
+               if ( isset( $this->dbTrxHoldingLocks[$name] ) ) {
+                       $this->logger->info( "Nested transaction for '$name' - out of sync." );
+               }
+               $this->dbTrxHoldingLocks[$name] = [
+                       'start' => microtime( true ),
+                       'conns' => [], // all connections involved
+               ];
+               $this->dbTrxMethodTimes[$name] = [];
+
+               foreach ( $this->dbTrxHoldingLocks as $name => &$info ) {
+                       // Track all DBs in transactions for this transaction
+                       $info['conns'][$name] = 1;
+               }
+       }
+
+       /**
+        * Register the name and time of a method for slow DB trx detection
+        *
+        * This assumes that all queries are synchronous (non-overlapping)
+        *
+        * @param string $query Function name or generalized SQL
+        * @param float $sTime Starting UNIX wall time
+        * @param bool $isWrite Whether this is a write query
+        * @param integer $n Number of affected rows
+        */
+       public function recordQueryCompletion( $query, $sTime, $isWrite = false, $n = 0 ) {
+               $eTime = microtime( true );
+               $elapsed = ( $eTime - $sTime );
+
+               if ( $isWrite && $n > $this->expect['maxAffected'] ) {
+                       $this->logger->info(
+                               "Query affected $n row(s):\n" . $query . "\n" .
+                               ( new RuntimeException() )->getTraceAsString() );
+               }
+
+               // Report when too many writes/queries happen...
+               if ( $this->hits['queries']++ == $this->expect['queries'] ) {
+                       $this->reportExpectationViolated( 'queries', $query );
+               }
+               if ( $isWrite && $this->hits['writes']++ == $this->expect['writes'] ) {
+                       $this->reportExpectationViolated( 'writes', $query );
+               }
+               // Report slow queries...
+               if ( !$isWrite && $elapsed > $this->expect['readQueryTime'] ) {
+                       $this->reportExpectationViolated( 'readQueryTime', $query, $elapsed );
+               }
+               if ( $isWrite && $elapsed > $this->expect['writeQueryTime'] ) {
+                       $this->reportExpectationViolated( 'writeQueryTime', $query, $elapsed );
+               }
+
+               if ( !$this->dbTrxHoldingLocks ) {
+                       // Short-circuit
+                       return;
+               } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
+                       // Not an important query nor slow enough
+                       return;
+               }
+
+               foreach ( $this->dbTrxHoldingLocks as $name => $info ) {
+                       $lastQuery = end( $this->dbTrxMethodTimes[$name] );
+                       if ( $lastQuery ) {
+                               // Additional query in the trx...
+                               $lastEnd = $lastQuery[2];
+                               if ( $sTime >= $lastEnd ) { // sanity check
+                                       if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
+                                               // Add an entry representing the time spent doing non-queries
+                                               $this->dbTrxMethodTimes[$name][] = [ '...delay...', $lastEnd, $sTime ];
+                                       }
+                                       $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
+                               }
+                       } else {
+                               // First query in the trx...
+                               if ( $sTime >= $info['start'] ) { // sanity check
+                                       $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Mark a DB as no longer in a transaction
+        *
+        * This will check if locks are possibly held for longer than
+        * needed and log any affected transactions to a special DB log.
+        * Note that there can be multiple connections to a single DB.
+        *
+        * @param string $server DB server
+        * @param string $db DB name
+        * @param string $id ID string of transaction
+        * @param float $writeTime Time spent in write queries
+        */
+       public function transactionWritingOut( $server, $db, $id, $writeTime = 0.0 ) {
+               $name = "{$server} ({$db}) (TRX#$id)";
+               if ( !isset( $this->dbTrxMethodTimes[$name] ) ) {
+                       $this->logger->info( "Detected no transaction for '$name' - out of sync." );
+                       return;
+               }
+
+               $slow = false;
+
+               // Warn if too much time was spend writing...
+               if ( $writeTime > $this->expect['writeQueryTime'] ) {
+                       $this->reportExpectationViolated(
+                               'writeQueryTime',
+                               "[transaction $id writes to {$server} ({$db})]",
+                               $writeTime
+                       );
+                       $slow = true;
+               }
+               // Fill in the last non-query period...
+               $lastQuery = end( $this->dbTrxMethodTimes[$name] );
+               if ( $lastQuery ) {
+                       $now = microtime( true );
+                       $lastEnd = $lastQuery[2];
+                       if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
+                               $this->dbTrxMethodTimes[$name][] = [ '...delay...', $lastEnd, $now ];
+                       }
+               }
+               // Check for any slow queries or non-query periods...
+               foreach ( $this->dbTrxMethodTimes[$name] as $info ) {
+                       $elapsed = ( $info[2] - $info[1] );
+                       if ( $elapsed >= $this->dbLockThreshold ) {
+                               $slow = true;
+                               break;
+                       }
+               }
+               if ( $slow ) {
+                       $dbs = implode( ', ', array_keys( $this->dbTrxHoldingLocks[$name]['conns'] ) );
+                       $msg = "Sub-optimal transaction on DB(s) [{$dbs}]:\n";
+                       foreach ( $this->dbTrxMethodTimes[$name] as $i => $info ) {
+                               list( $query, $sTime, $end ) = $info;
+                               $msg .= sprintf( "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), $query );
+                       }
+                       $this->logger->info( $msg );
+               }
+               unset( $this->dbTrxHoldingLocks[$name] );
+               unset( $this->dbTrxMethodTimes[$name] );
+       }
+
+       /**
+        * @param string $expect
+        * @param string $query
+        * @param string|float|int $actual [optional]
+        */
+       protected function reportExpectationViolated( $expect, $query, $actual = null ) {
+               if ( $this->silenced ) {
+                       return;
+               }
+
+               $n = $this->expect[$expect];
+               $by = $this->expectBy[$expect];
+               $actual = ( $actual !== null ) ? " (actual: $actual)" : "";
+
+               $this->logger->info(
+                       "Expectation ($expect <= $n) by $by not met$actual:\n$query\n" .
+                       ( new RuntimeException() )->getTraceAsString()
+               );
+       }
+}
diff --git a/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php b/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php
new file mode 100644 (file)
index 0000000..b102f0f
--- /dev/null
@@ -0,0 +1,326 @@
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * 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 Database
+ */
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * Class for ensuring a consistent ordering of events as seen by the user, despite replication.
+ * Kind of like Hawking's [[Chronology Protection Agency]].
+ */
+class ChronologyProtector implements LoggerAwareInterface{
+       /** @var BagOStuff */
+       protected $store;
+       /** @var LoggerInterface */
+       protected $logger;
+
+       /** @var string Storage key name */
+       protected $key;
+       /** @var string Hash of client parameters */
+       protected $clientId;
+       /** @var float|null Minimum UNIX timestamp of 1+ expected startup positions */
+       protected $waitForPosTime;
+       /** @var int Max seconds to wait on positions to appear */
+       protected $waitForPosTimeout = self::POS_WAIT_TIMEOUT;
+       /** @var bool Whether to no-op all method calls */
+       protected $enabled = true;
+       /** @var bool Whether to check and wait on positions */
+       protected $wait = true;
+
+       /** @var bool Whether the client data was loaded */
+       protected $initialized = false;
+       /** @var DBMasterPos[] Map of (DB master name => position) */
+       protected $startupPositions = [];
+       /** @var DBMasterPos[] Map of (DB master name => position) */
+       protected $shutdownPositions = [];
+       /** @var float[] Map of (DB master name => 1) */
+       protected $shutdownTouchDBs = [];
+
+       /** @var integer Seconds to store positions */
+       const POSITION_TTL = 60;
+       /** @var integer Max time to wait for positions to appear */
+       const POS_WAIT_TIMEOUT = 5;
+
+       /**
+        * @param BagOStuff $store
+        * @param array $client Map of (ip: <IP>, agent: <user-agent>)
+        * @param float $posTime UNIX timestamp
+        * @since 1.27
+        */
+       public function __construct( BagOStuff $store, array $client, $posTime = null ) {
+               $this->store = $store;
+               $this->clientId = md5( $client['ip'] . "\n" . $client['agent'] );
+               $this->key = $store->makeGlobalKey( __CLASS__, $this->clientId );
+               $this->waitForPosTime = $posTime;
+               $this->logger = new \Psr\Log\NullLogger();
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
+       /**
+        * @param bool $enabled Whether to no-op all method calls
+        * @since 1.27
+        */
+       public function setEnabled( $enabled ) {
+               $this->enabled = $enabled;
+       }
+
+       /**
+        * @param bool $enabled Whether to check and wait on positions
+        * @since 1.27
+        */
+       public function setWaitEnabled( $enabled ) {
+               $this->wait = $enabled;
+       }
+
+       /**
+        * Initialise a ILoadBalancer to give it appropriate chronology protection.
+        *
+        * If the stash has a previous master position recorded, this will try to
+        * make sure that the next query to a replica DB of that master will see changes up
+        * to that position by delaying execution. The delay may timeout and allow stale
+        * data if no non-lagged replica DBs are available.
+        *
+        * @param ILoadBalancer $lb
+        * @return void
+        */
+       public function initLB( ILoadBalancer $lb ) {
+               if ( !$this->enabled || $lb->getServerCount() <= 1 ) {
+                       return; // non-replicated setup or disabled
+               }
+
+               $this->initPositions();
+
+               $masterName = $lb->getServerName( $lb->getWriterIndex() );
+               if ( !empty( $this->startupPositions[$masterName] ) ) {
+                       $pos = $this->startupPositions[$masterName];
+                       $this->logger->info( __METHOD__ . ": LB for '$masterName' set to pos $pos\n" );
+                       $lb->waitFor( $pos );
+               }
+       }
+
+       /**
+        * Notify the ChronologyProtector that the ILoadBalancer is about to shut
+        * down. Saves replication positions.
+        *
+        * @param ILoadBalancer $lb
+        * @return void
+        */
+       public function shutdownLB( ILoadBalancer $lb ) {
+               if ( !$this->enabled ) {
+                       return; // not enabled
+               } elseif ( !$lb->hasOrMadeRecentMasterChanges( INF ) ) {
+                       // Only save the position if writes have been done on the connection
+                       return;
+               }
+
+               $masterName = $lb->getServerName( $lb->getWriterIndex() );
+               if ( $lb->getServerCount() > 1 ) {
+                       $pos = $lb->getMasterPos();
+                       $this->logger->info( __METHOD__ . ": LB for '$masterName' has pos $pos\n" );
+                       $this->shutdownPositions[$masterName] = $pos;
+               } else {
+                       $this->logger->info( __METHOD__ . ": DB '$masterName' touched\n" );
+               }
+               $this->shutdownTouchDBs[$masterName] = 1;
+       }
+
+       /**
+        * Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now.
+        * May commit chronology data to persistent storage.
+        *
+        * @param callable|null $workCallback Work to do instead of waiting on syncing positions
+        * @param string $mode One of (sync, async); whether to wait on remote datacenters
+        * @return DBMasterPos[] Empty on success; returns the (db name => position) map on failure
+        */
+       public function shutdown( callable $workCallback = null, $mode = 'sync' ) {
+               if ( !$this->enabled ) {
+                       return [];
+               }
+
+               $store = $this->store;
+               // Some callers might want to know if a user recently touched a DB.
+               // These writes do not need to block on all datacenters receiving them.
+               foreach ( $this->shutdownTouchDBs as $dbName => $unused ) {
+                       $store->set(
+                               $this->getTouchedKey( $this->store, $dbName ),
+                               microtime( true ),
+                               $store::TTL_DAY
+                       );
+               }
+
+               if ( !count( $this->shutdownPositions ) ) {
+                       return []; // nothing to save
+               }
+
+               $this->logger->info( __METHOD__ . ": saving master pos for " .
+                       implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
+               );
+
+               // CP-protected writes should overwhemingly go to the master datacenter, so get DC-local
+               // lock to merge the values. Use a DC-local get() and a synchronous all-DC set(). This
+               // makes it possible for the BagOStuff class to write in parallel to all DCs with one RTT.
+               if ( $store->lock( $this->key, 3 ) ) {
+                       if ( $workCallback ) {
+                               // Let the store run the work before blocking on a replication sync barrier. By the
+                               // time it's done with the work, the barrier should be fast if replication caught up.
+                               $store->addBusyCallback( $workCallback );
+                       }
+                       $ok = $store->set(
+                               $this->key,
+                               self::mergePositions( $store->get( $this->key ), $this->shutdownPositions ),
+                               self::POSITION_TTL,
+                               ( $mode === 'sync' ) ? $store::WRITE_SYNC : 0
+                       );
+                       $store->unlock( $this->key );
+               } else {
+                       $ok = false;
+               }
+
+               if ( !$ok ) {
+                       $bouncedPositions = $this->shutdownPositions;
+                       // Raced out too many times or stash is down
+                       $this->logger->warning( __METHOD__ . ": failed to save master pos for " .
+                               implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
+                       );
+               } elseif ( $mode === 'sync' &&
+                       $store->getQoS( $store::ATTR_SYNCWRITES ) < $store::QOS_SYNCWRITES_BE
+               ) {
+                       // Positions may not be in all datacenters, force LBFactory to play it safe
+                       $this->logger->info( __METHOD__ . ": store may not support synchronous writes." );
+                       $bouncedPositions = $this->shutdownPositions;
+               } else {
+                       $bouncedPositions = [];
+               }
+
+               return $bouncedPositions;
+       }
+
+       /**
+        * @param string $dbName DB master name (e.g. "db1052")
+        * @return float|bool UNIX timestamp when client last touched the DB; false if not on record
+        * @since 1.28
+        */
+       public function getTouched( $dbName ) {
+               return $this->store->get( $this->getTouchedKey( $this->store, $dbName ) );
+       }
+
+       /**
+        * @param BagOStuff $store
+        * @param string $dbName
+        * @return string
+        */
+       private function getTouchedKey( BagOStuff $store, $dbName ) {
+               return $store->makeGlobalKey( __CLASS__, 'mtime', $this->clientId, $dbName );
+       }
+
+       /**
+        * Load in previous master positions for the client
+        */
+       protected function initPositions() {
+               if ( $this->initialized ) {
+                       return;
+               }
+
+               $this->initialized = true;
+               if ( $this->wait ) {
+                       // If there is an expectation to see master positions with a certain min
+                       // timestamp, then block until they appear, or until a timeout is reached.
+                       if ( $this->waitForPosTime > 0.0 ) {
+                               $data = null;
+                               $loop = new WaitConditionLoop(
+                                       function () use ( &$data ) {
+                                               $data = $this->store->get( $this->key );
+
+                                               return ( self::minPosTime( $data ) >= $this->waitForPosTime )
+                                                       ? WaitConditionLoop::CONDITION_REACHED
+                                                       : WaitConditionLoop::CONDITION_CONTINUE;
+                                       },
+                                       $this->waitForPosTimeout
+                               );
+                               $result = $loop->invoke();
+                               $waitedMs = $loop->getLastWaitTime() * 1e3;
+
+                               if ( $result == $loop::CONDITION_REACHED ) {
+                                       $msg = "expected and found pos time {$this->waitForPosTime} ({$waitedMs}ms)";
+                                       $this->logger->debug( $msg );
+                               } else {
+                                       $msg = "expected but missed pos time {$this->waitForPosTime} ({$waitedMs}ms)";
+                                       $this->logger->info( $msg );
+                               }
+                       } else {
+                               $data = $this->store->get( $this->key );
+                       }
+
+                       $this->startupPositions = $data ? $data['positions'] : [];
+                       $this->logger->info( __METHOD__ . ": key is {$this->key} (read)\n" );
+               } else {
+                       $this->startupPositions = [];
+                       $this->logger->info( __METHOD__ . ": key is {$this->key} (unread)\n" );
+               }
+       }
+
+       /**
+        * @param array|bool $data
+        * @return float|null
+        */
+       private static function minPosTime( $data ) {
+               if ( !isset( $data['positions'] ) ) {
+                       return null;
+               }
+
+               $min = null;
+               foreach ( $data['positions'] as $pos ) {
+                       /** @var DBMasterPos $pos */
+                       $min = $min ? min( $pos->asOfTime(), $min ) : $pos->asOfTime();
+               }
+
+               return $min;
+       }
+
+       /**
+        * @param array|bool $curValue
+        * @param DBMasterPos[] $shutdownPositions
+        * @return array
+        */
+       private static function mergePositions( $curValue, array $shutdownPositions ) {
+               /** @var $curPositions DBMasterPos[] */
+               if ( $curValue === false ) {
+                       $curPositions = $shutdownPositions;
+               } else {
+                       $curPositions = $curValue['positions'];
+                       // Use the newest positions for each DB master
+                       foreach ( $shutdownPositions as $db => $pos ) {
+                               if ( !isset( $curPositions[$db] )
+                                       || $pos->asOfTime() > $curPositions[$db]->asOfTime()
+                               ) {
+                                       $curPositions[$db] = $pos;
+                               }
+                       }
+               }
+
+               return [ 'positions' => $curPositions ];
+       }
+}
diff --git a/includes/libs/rdbms/database/DBConnRef.php b/includes/libs/rdbms/database/DBConnRef.php
new file mode 100644 (file)
index 0000000..0d9b692
--- /dev/null
@@ -0,0 +1,594 @@
+<?php
+/**
+ * Helper class to handle automatically marking connections as reusable (via RAII pattern)
+ * as well handling deferring the actual network connection until the handle is used
+ *
+ * @note: proxy methods are defined explicity to avoid interface errors
+ * @ingroup Database
+ * @since 1.22
+ */
+class DBConnRef implements IDatabase {
+       /** @var ILoadBalancer */
+       private $lb;
+
+       /** @var IDatabase|null Live connection handle */
+       private $conn;
+
+       /** @var array|null N-tuple of (server index, group, DatabaseDomain|string) */
+       private $params;
+
+       const FLD_INDEX = 0;
+       const FLD_GROUP = 1;
+       const FLD_DOMAIN = 2;
+
+       /**
+        * @param ILoadBalancer $lb
+        * @param IDatabase|array $conn Connection or (server index, group, DatabaseDomain|string)
+        */
+       public function __construct( ILoadBalancer $lb, $conn ) {
+               $this->lb = $lb;
+               if ( $conn instanceof IDatabase ) {
+                       $this->conn = $conn; // live handle
+               } elseif ( count( $conn ) >= 3 && $conn[self::FLD_DOMAIN] !== false ) {
+                       $this->params = $conn;
+               } else {
+                       throw new InvalidArgumentException( "Missing lazy connection arguments." );
+               }
+       }
+
+       function __call( $name, array $arguments ) {
+               if ( $this->conn === null ) {
+                       list( $db, $groups, $wiki ) = $this->params;
+                       $this->conn = $this->lb->getConnection( $db, $groups, $wiki );
+               }
+
+               return call_user_func_array( [ $this->conn, $name ], $arguments );
+       }
+
+       public function getServerInfo() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function bufferResults( $buffer = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function trxLevel() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function trxTimestamp() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function explicitTrxActive() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function tablePrefix( $prefix = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function dbSchema( $schema = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getLBInfo( $name = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setLBInfo( $name, $value = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setLazyMasterHandle( IDatabase $conn ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function implicitGroupby() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function implicitOrderby() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lastQuery() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function doneWrites() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lastDoneWrites() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function writesPending() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function writesOrCallbacksPending() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function pendingWriteCallers() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function isOpen() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getFlag( $flag ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getProperty( $name ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getDomainID() {
+               if ( $this->conn === null ) {
+                       $domain = $this->params[self::FLD_DOMAIN];
+                       // Avoid triggering a database connection
+                       return $domain instanceof DatabaseDomain ? $domain->getId() : $domain;
+               }
+
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getWikiID() {
+               return $this->getDomainID();
+       }
+
+       public function getType() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function open( $server, $user, $password, $dbName ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function fetchObject( $res ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function fetchRow( $res ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function numRows( $res ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function numFields( $res ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function fieldName( $res, $n ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function insertId() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function dataSeek( $res, $row ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lastErrno() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lastError() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function fieldInfo( $table, $field ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function affectedRows() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getSoftwareLink() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getServerVersion() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function close() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function reportConnectionError( $error = 'Unknown error' ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function freeResult( $res ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectField(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectFieldValues(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function select(
+               $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectSQLText(
+               $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectRow(
+               $table, $vars, $conds, $fname = __METHOD__,
+               $options = [], $join_conds = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function estimateRowCount(
+               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectRowCount(
+               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function fieldExists( $table, $field, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function indexExists( $table, $index, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function tableExists( $table, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function indexUnique( $table, $index ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function makeList( $a, $mode = LIST_COMMA ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function bitNot( $field ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function bitAnd( $fieldLeft, $fieldRight ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function bitOr( $fieldLeft, $fieldRight ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function buildConcat( $stringList ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function buildGroupConcatField(
+               $delim, $table, $field, $conds = '', $join_conds = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function selectDB( $db ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getDBname() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getServer() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function addQuotes( $s ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function buildLike() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function anyChar() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function anyString() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function nextSequenceValue( $seqName ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function upsert(
+               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function deleteJoin(
+               $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function delete( $table, $conds, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function insertSelect(
+               $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__, $insertOptions = [], $selectOptions = []
+       ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function unionSupportsOrderAndLimit() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function unionQueries( $sqls, $all ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function conditional( $cond, $trueVal, $falseVal ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function strreplace( $orig, $old, $new ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getServerUptime() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function wasDeadlock() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function wasLockTimeout() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function wasErrorReissuable() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function wasReadOnlyError() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function masterPosWait( DBMasterPos $pos, $timeout ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getSlavePos() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getMasterPos() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function serverIsReadOnly() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function onTransactionIdle( callable $callback, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setTransactionListener( $name, callable $callback = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function startAtomic( $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function endAtomic( $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function doAtomicSection( $fname, callable $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function commit( $fname = __METHOD__, $flush = '' ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function rollback( $fname = __METHOD__, $flush = '' ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function flushSnapshot( $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function listTables( $prefix = null, $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function timestamp( $ts = 0 ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function timestampOrNull( $ts = null ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function ping( &$rtt = null ) {
+               return func_num_args()
+                       ? $this->__call( __FUNCTION__, [ &$rtt ] )
+                       : $this->__call( __FUNCTION__, [] ); // method cares about null vs missing
+       }
+
+       public function getLag() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getSessionLagStatus() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function maxListLen() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function encodeBlob( $b ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function decodeBlob( $b ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setSessionOptions( array $options ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setSchemaVars( $vars ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lockIsFree( $lockName, $method ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function lock( $lockName, $method, $timeout = 5 ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function unlock( $lockName, $method ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function namedLocksEnqueue() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function getInfinity() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function encodeExpiry( $expiry ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function decodeExpiry( $expiry, $format = TS_MW ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setBigSelects( $value = true ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function isReadOnly() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setTableAliases( array $aliases ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       /**
+        * Clean up the connection when out of scope
+        */
+       function __destruct() {
+               if ( $this->conn !== null ) {
+                       $this->lb->reuseConnection( $this->conn );
+               }
+       }
+}
diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php
new file mode 100644 (file)
index 0000000..4cab119
--- /dev/null
@@ -0,0 +1,3498 @@
+<?php
+/**
+ * @defgroup Database Database
+ *
+ * This file deals with database interface functions
+ * and query specifics/optimisations.
+ *
+ * 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 Database
+ */
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Database abstraction object
+ * @ingroup Database
+ */
+abstract class Database implements IDatabase, LoggerAwareInterface {
+       /** Number of times to re-try an operation in case of deadlock */
+       const DEADLOCK_TRIES = 4;
+       /** Minimum time to wait before retry, in microseconds */
+       const DEADLOCK_DELAY_MIN = 500000;
+       /** Maximum time to wait before retry */
+       const DEADLOCK_DELAY_MAX = 1500000;
+
+       /** How long before it is worth doing a dummy query to test the connection */
+       const PING_TTL = 1.0;
+       const PING_QUERY = 'SELECT 1 AS ping';
+
+       const TINY_WRITE_SEC = .010;
+       const SLOW_WRITE_SEC = .500;
+       const SMALL_WRITE_ROWS = 100;
+
+       /** @var string SQL query */
+       protected $mLastQuery = '';
+       /** @var bool */
+       protected $mDoneWrites = false;
+       /** @var string|bool */
+       protected $mPHPError = false;
+       /** @var string */
+       protected $mServer;
+       /** @var string */
+       protected $mUser;
+       /** @var string */
+       protected $mPassword;
+       /** @var string */
+       protected $mDBname;
+       /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
+       protected $tableAliases = [];
+       /** @var bool Whether this PHP instance is for a CLI script */
+       protected $cliMode;
+       /** @var string Agent name for query profiling */
+       protected $agent;
+
+       /** @var BagOStuff APC cache */
+       protected $srvCache;
+       /** @var LoggerInterface */
+       protected $connLogger;
+       /** @var LoggerInterface */
+       protected $queryLogger;
+       /** @var callback Error logging callback */
+       protected $errorLogger;
+
+       /** @var resource Database connection */
+       protected $mConn = null;
+       /** @var bool */
+       protected $mOpened = false;
+
+       /** @var array[] List of (callable, method name) */
+       protected $mTrxIdleCallbacks = [];
+       /** @var array[] List of (callable, method name) */
+       protected $mTrxPreCommitCallbacks = [];
+       /** @var array[] List of (callable, method name) */
+       protected $mTrxEndCallbacks = [];
+       /** @var callable[] Map of (name => callable) */
+       protected $mTrxRecurringCallbacks = [];
+       /** @var bool Whether to suppress triggering of transaction end callbacks */
+       protected $mTrxEndCallbacksSuppressed = false;
+
+       /** @var string */
+       protected $mTablePrefix = '';
+       /** @var string */
+       protected $mSchema = '';
+       /** @var integer */
+       protected $mFlags;
+       /** @var array */
+       protected $mLBInfo = [];
+       /** @var bool|null */
+       protected $mDefaultBigSelects = null;
+       /** @var array|bool */
+       protected $mSchemaVars = false;
+       /** @var array */
+       protected $mSessionVars = [];
+       /** @var array|null */
+       protected $preparedArgs;
+       /** @var string|bool|null Stashed value of html_errors INI setting */
+       protected $htmlErrors;
+       /** @var string */
+       protected $delimiter = ';';
+       /** @var DatabaseDomain */
+       protected $currentDomain;
+
+       /**
+        * Either 1 if a transaction is active or 0 otherwise.
+        * The other Trx fields may not be meaningfull if this is 0.
+        *
+        * @var int
+        */
+       protected $mTrxLevel = 0;
+       /**
+        * Either a short hexidecimal string if a transaction is active or ""
+        *
+        * @var string
+        * @see DatabaseBase::mTrxLevel
+        */
+       protected $mTrxShortId = '';
+       /**
+        * The UNIX time that the transaction started. Callers can assume that if
+        * snapshot isolation is used, then the data is *at least* up to date to that
+        * point (possibly more up-to-date since the first SELECT defines the snapshot).
+        *
+        * @var float|null
+        * @see DatabaseBase::mTrxLevel
+        */
+       private $mTrxTimestamp = null;
+       /** @var float Lag estimate at the time of BEGIN */
+       private $mTrxReplicaLag = null;
+       /**
+        * Remembers the function name given for starting the most recent transaction via begin().
+        * Used to provide additional context for error reporting.
+        *
+        * @var string
+        * @see DatabaseBase::mTrxLevel
+        */
+       private $mTrxFname = null;
+       /**
+        * Record if possible write queries were done in the last transaction started
+        *
+        * @var bool
+        * @see DatabaseBase::mTrxLevel
+        */
+       private $mTrxDoneWrites = false;
+       /**
+        * Record if the current transaction was started implicitly due to DBO_TRX being set.
+        *
+        * @var bool
+        * @see DatabaseBase::mTrxLevel
+        */
+       private $mTrxAutomatic = false;
+       /**
+        * Array of levels of atomicity within transactions
+        *
+        * @var array
+        */
+       private $mTrxAtomicLevels = [];
+       /**
+        * Record if the current transaction was started implicitly by DatabaseBase::startAtomic
+        *
+        * @var bool
+        */
+       private $mTrxAutomaticAtomic = false;
+       /**
+        * Track the write query callers of the current transaction
+        *
+        * @var string[]
+        */
+       private $mTrxWriteCallers = [];
+       /**
+        * @var float Seconds spent in write queries for the current transaction
+        */
+       private $mTrxWriteDuration = 0.0;
+       /**
+        * @var integer Number of write queries for the current transaction
+        */
+       private $mTrxWriteQueryCount = 0;
+       /**
+        * @var float Like mTrxWriteQueryCount but excludes lock-bound, easy to replicate, queries
+        */
+       private $mTrxWriteAdjDuration = 0.0;
+       /**
+        * @var integer Number of write queries counted in mTrxWriteAdjDuration
+        */
+       private $mTrxWriteAdjQueryCount = 0;
+       /**
+        * @var float RTT time estimate
+        */
+       private $mRTTEstimate = 0.0;
+
+       /** @var array Map of (name => 1) for locks obtained via lock() */
+       private $mNamedLocksHeld = [];
+
+       /** @var IDatabase|null Lazy handle to the master DB this server replicates from */
+       private $lazyMasterHandle;
+
+       /**
+        * @since 1.21
+        * @var resource File handle for upgrade
+        */
+       protected $fileHandle = null;
+
+       /**
+        * @since 1.22
+        * @var string[] Process cache of VIEWs names in the database
+        */
+       protected $allViews = null;
+
+       /** @var float UNIX timestamp */
+       protected $lastPing = 0.0;
+
+       /** @var int[] Prior mFlags values */
+       private $priorFlags = [];
+
+       /** @var Profiler */
+       protected $profiler;
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
+
+       /**
+        * Constructor.
+        *
+        * FIXME: It is possible to construct a Database object with no associated
+        * connection object, by specifying no parameters to __construct(). This
+        * feature is deprecated and should be removed.
+        *
+        * IDatabase classes should not be constructed directly in external
+        * code. DatabaseBase::factory() should be used instead.
+        *
+        * @param array $params Parameters passed from DatabaseBase::factory()
+        */
+       function __construct( array $params ) {
+               $server = $params['host'];
+               $user = $params['user'];
+               $password = $params['password'];
+               $dbName = $params['dbname'];
+               $flags = $params['flags'];
+
+               $this->mSchema = $params['schema'];
+               $this->mTablePrefix = $params['tablePrefix'];
+
+               $this->cliMode = isset( $params['cliMode'] )
+                       ? $params['cliMode']
+                       : ( PHP_SAPI === 'cli' );
+               $this->agent = isset( $params['agent'] )
+                       ? str_replace( '/', '-', $params['agent'] ) // escape for comment
+                       : '';
+
+               $this->mFlags = $flags;
+               if ( $this->mFlags & DBO_DEFAULT ) {
+                       if ( $this->cliMode ) {
+                               $this->mFlags &= ~DBO_TRX;
+                       } else {
+                               $this->mFlags |= DBO_TRX;
+                       }
+               }
+
+               $this->mSessionVars = $params['variables'];
+
+               $this->srvCache = isset( $params['srvCache'] )
+                       ? $params['srvCache']
+                       : new HashBagOStuff();
+
+               $this->profiler = isset( $params['profiler'] )
+                       ? $params['profiler']
+                       : Profiler::instance(); // @TODO: remove global state
+               $this->trxProfiler = isset( $params['trxProfiler'] )
+                       ? $params['trxProfiler']
+                       : new TransactionProfiler();
+               $this->connLogger = isset( $params['connLogger'] )
+                       ? $params['connLogger']
+                       : new \Psr\Log\NullLogger();
+               $this->queryLogger = isset( $params['queryLogger'] )
+                       ? $params['queryLogger']
+                       : new \Psr\Log\NullLogger();
+
+               if ( $user ) {
+                       $this->open( $server, $user, $password, $dbName );
+               }
+
+               $this->currentDomain = ( $this->mDBname != '' )
+                       ? new DatabaseDomain( $this->mDBname, null, $this->mTablePrefix )
+                       : DatabaseDomain::newUnspecified();
+       }
+
+       /**
+        * Given a DB type, construct the name of the appropriate child class of
+        * IDatabase. This is designed to replace all of the manual stuff like:
+        *    $class = 'Database' . ucfirst( strtolower( $dbType ) );
+        * as well as validate against the canonical list of DB types we have
+        *
+        * This factory function is mostly useful for when you need to connect to a
+        * database other than the MediaWiki default (such as for external auth,
+        * an extension, et cetera). Do not use this to connect to the MediaWiki
+        * database. Example uses in core:
+        * @see LoadBalancer::reallyOpenConnection()
+        * @see ForeignDBRepo::getMasterDB()
+        * @see WebInstallerDBConnect::execute()
+        *
+        * @since 1.18
+        *
+        * @param string $dbType A possible DB type
+        * @param array $p An array of options to pass to the constructor.
+        *    Valid options are: host, user, password, dbname, flags, tablePrefix, schema, driver
+        * @return IDatabase|null If the database driver or extension cannot be found
+        * @throws InvalidArgumentException If the database driver or extension cannot be found
+        */
+       final public static function factory( $dbType, $p = [] ) {
+               $canonicalDBTypes = [
+                       'mysql' => [ 'mysqli', 'mysql' ],
+                       'postgres' => [],
+                       'sqlite' => [],
+                       'oracle' => [],
+                       'mssql' => [],
+               ];
+
+               $driver = false;
+               $dbType = strtolower( $dbType );
+               if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
+                       $possibleDrivers = $canonicalDBTypes[$dbType];
+                       if ( !empty( $p['driver'] ) ) {
+                               if ( in_array( $p['driver'], $possibleDrivers ) ) {
+                                       $driver = $p['driver'];
+                               } else {
+                                       throw new InvalidArgumentException( __METHOD__ .
+                                               " type '$dbType' does not support driver '{$p['driver']}'" );
+                               }
+                       } else {
+                               foreach ( $possibleDrivers as $posDriver ) {
+                                       if ( extension_loaded( $posDriver ) ) {
+                                               $driver = $posDriver;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       $driver = $dbType;
+               }
+               if ( $driver === false ) {
+                       throw new InvalidArgumentException( __METHOD__ .
+                               " no viable database extension found for type '$dbType'" );
+               }
+
+               // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema,
+               // and everything else doesn't use a schema (e.g. null)
+               // Although postgres and oracle support schemas, we don't use them (yet)
+               // to maintain backwards compatibility
+               $defaultSchemas = [
+                       'mssql' => 'get from global',
+               ];
+
+               $class = 'Database' . ucfirst( $driver );
+               if ( class_exists( $class ) && is_subclass_of( $class, 'IDatabase' ) ) {
+                       // Resolve some defaults for b/c
+                       $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
+                       $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
+                       $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
+                       $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
+                       $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
+                       $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
+                       $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : '';
+                       if ( !isset( $p['schema'] ) ) {
+                               $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : '';
+                       }
+                       $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
+
+                       $conn = new $class( $p );
+                       if ( isset( $p['connLogger'] ) ) {
+                               $conn->connLogger = $p['connLogger'];
+                       }
+                       if ( isset( $p['queryLogger'] ) ) {
+                               $conn->queryLogger = $p['queryLogger'];
+                       }
+                       if ( isset( $p['errorLogger'] ) ) {
+                               $conn->errorLogger = $p['errorLogger'];
+                       } else {
+                               $conn->errorLogger = function ( Exception $e ) {
+                                       trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_WARNING );
+                               };
+                       }
+               } else {
+                       $conn = null;
+               }
+
+               return $conn;
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->queryLogger = $logger;
+       }
+
+       public function getServerInfo() {
+               return $this->getServerVersion();
+       }
+
+       public function bufferResults( $buffer = null ) {
+               $res = !$this->getFlag( DBO_NOBUFFER );
+               if ( $buffer !== null ) {
+                       $buffer ? $this->clearFlag( DBO_NOBUFFER ) : $this->setFlag( DBO_NOBUFFER );
+               }
+
+               return $res;
+       }
+
+       /**
+        * Turns on (false) or off (true) the automatic generation and sending
+        * of a "we're sorry, but there has been a database error" page on
+        * database errors. Default is on (false). When turned off, the
+        * code should use lastErrno() and lastError() to handle the
+        * situation as appropriate.
+        *
+        * Do not use this function outside of the Database classes.
+        *
+        * @param null|bool $ignoreErrors
+        * @return bool The previous value of the flag.
+        */
+       protected function ignoreErrors( $ignoreErrors = null ) {
+               $res = $this->getFlag( DBO_IGNORE );
+               if ( $ignoreErrors !== null ) {
+                       $ignoreErrors ? $this->setFlag( DBO_IGNORE ) : $this->clearFlag( DBO_IGNORE );
+               }
+
+               return $res;
+       }
+
+       public function trxLevel() {
+               return $this->mTrxLevel;
+       }
+
+       public function trxTimestamp() {
+               return $this->mTrxLevel ? $this->mTrxTimestamp : null;
+       }
+
+       public function tablePrefix( $prefix = null ) {
+               $old = $this->mTablePrefix;
+               if ( $prefix !== null ) {
+                       $this->mTablePrefix = $prefix;
+                       $this->currentDomain = ( $this->mDBname != '' )
+                               ? new DatabaseDomain( $this->mDBname, null, $this->mTablePrefix )
+                               : DatabaseDomain::newUnspecified();
+               }
+
+               return $old;
+       }
+
+       public function dbSchema( $schema = null ) {
+               $old = $this->mSchema;
+               if ( $schema !== null ) {
+                       $this->mSchema = $schema;
+               }
+
+               return $old;
+       }
+
+       /**
+        * Set the filehandle to copy write statements to.
+        *
+        * @param resource $fh File handle
+        */
+       public function setFileHandle( $fh ) {
+               $this->fileHandle = $fh;
+       }
+
+       public function getLBInfo( $name = null ) {
+               if ( is_null( $name ) ) {
+                       return $this->mLBInfo;
+               } else {
+                       if ( array_key_exists( $name, $this->mLBInfo ) ) {
+                               return $this->mLBInfo[$name];
+                       } else {
+                               return null;
+                       }
+               }
+       }
+
+       public function setLBInfo( $name, $value = null ) {
+               if ( is_null( $value ) ) {
+                       $this->mLBInfo = $name;
+               } else {
+                       $this->mLBInfo[$name] = $value;
+               }
+       }
+
+       public function setLazyMasterHandle( IDatabase $conn ) {
+               $this->lazyMasterHandle = $conn;
+       }
+
+       /**
+        * @return IDatabase|null
+        * @see setLazyMasterHandle()
+        * @since 1.27
+        */
+       public function getLazyMasterHandle() {
+               return $this->lazyMasterHandle;
+       }
+
+       /**
+        * @param TransactionProfiler $profiler
+        * @since 1.27
+        */
+       public function setTransactionProfiler( TransactionProfiler $profiler ) {
+               $this->trxProfiler = $profiler;
+       }
+
+       public function implicitGroupby() {
+               return true;
+       }
+
+       public function implicitOrderby() {
+               return true;
+       }
+
+       public function lastQuery() {
+               return $this->mLastQuery;
+       }
+
+       public function doneWrites() {
+               return (bool)$this->mDoneWrites;
+       }
+
+       public function lastDoneWrites() {
+               return $this->mDoneWrites ?: false;
+       }
+
+       public function writesPending() {
+               return $this->mTrxLevel && $this->mTrxDoneWrites;
+       }
+
+       public function writesOrCallbacksPending() {
+               return $this->mTrxLevel && (
+                       $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
+               );
+       }
+
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) {
+               if ( !$this->mTrxLevel ) {
+                       return false;
+               } elseif ( !$this->mTrxDoneWrites ) {
+                       return 0.0;
+               }
+
+               switch ( $type ) {
+                       case self::ESTIMATE_DB_APPLY:
+                               $this->ping( $rtt );
+                               $rttAdjTotal = $this->mTrxWriteAdjQueryCount * $rtt;
+                               $applyTime = max( $this->mTrxWriteAdjDuration - $rttAdjTotal, 0 );
+                               // For omitted queries, make them count as something at least
+                               $omitted = $this->mTrxWriteQueryCount - $this->mTrxWriteAdjQueryCount;
+                               $applyTime += self::TINY_WRITE_SEC * $omitted;
+
+                               return $applyTime;
+                       default: // everything
+                               return $this->mTrxWriteDuration;
+               }
+       }
+
+       public function pendingWriteCallers() {
+               return $this->mTrxLevel ? $this->mTrxWriteCallers : [];
+       }
+
+       protected function pendingWriteAndCallbackCallers() {
+               if ( !$this->mTrxLevel ) {
+                       return [];
+               }
+
+               $fnames = $this->mTrxWriteCallers;
+               foreach ( [
+                       $this->mTrxIdleCallbacks,
+                       $this->mTrxPreCommitCallbacks,
+                       $this->mTrxEndCallbacks
+               ] as $callbacks ) {
+                       foreach ( $callbacks as $callback ) {
+                               $fnames[] = $callback[1];
+                       }
+               }
+
+               return $fnames;
+       }
+
+       public function isOpen() {
+               return $this->mOpened;
+       }
+
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               if ( $remember === self::REMEMBER_PRIOR ) {
+                       array_push( $this->priorFlags, $this->mFlags );
+               }
+               $this->mFlags |= $flag;
+       }
+
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               if ( $remember === self::REMEMBER_PRIOR ) {
+                       array_push( $this->priorFlags, $this->mFlags );
+               }
+               $this->mFlags &= ~$flag;
+       }
+
+       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
+               if ( !$this->priorFlags ) {
+                       return;
+               }
+
+               if ( $state === self::RESTORE_INITIAL ) {
+                       $this->mFlags = reset( $this->priorFlags );
+                       $this->priorFlags = [];
+               } else {
+                       $this->mFlags = array_pop( $this->priorFlags );
+               }
+       }
+
+       public function getFlag( $flag ) {
+               return !!( $this->mFlags & $flag );
+       }
+
+       public function getProperty( $name ) {
+               return $this->$name;
+       }
+
+       public function getDomainID() {
+               return $this->currentDomain->getId();
+       }
+
+       final public function getWikiID() {
+               return $this->getDomainID();
+       }
+
+       /**
+        * Get information about an index into an object
+        * @param string $table Table name
+        * @param string $index Index name
+        * @param string $fname Calling function name
+        * @return mixed Database-specific index description class or false if the index does not exist
+        */
+       abstract function indexInfo( $table, $index, $fname = __METHOD__ );
+
+       /**
+        * Wrapper for addslashes()
+        *
+        * @param string $s String to be slashed.
+        * @return string Slashed string.
+        */
+       abstract function strencode( $s );
+
+       protected function installErrorHandler() {
+               $this->mPHPError = false;
+               $this->htmlErrors = ini_set( 'html_errors', '0' );
+               set_error_handler( [ $this, 'connectionerrorLogger' ] );
+       }
+
+       /**
+        * @return bool|string
+        */
+       protected function restoreErrorHandler() {
+               restore_error_handler();
+               if ( $this->htmlErrors !== false ) {
+                       ini_set( 'html_errors', $this->htmlErrors );
+               }
+               if ( $this->mPHPError ) {
+                       $error = preg_replace( '!\[<a.*</a>\]!', '', $this->mPHPError );
+                       $error = preg_replace( '!^.*?:\s?(.*)$!', '$1', $error );
+
+                       return $error;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * @param int $errno
+        * @param string $errstr
+        */
+       public function connectionerrorLogger( $errno, $errstr ) {
+               $this->mPHPError = $errstr;
+       }
+
+       /**
+        * Create a log context to pass to PSR logging functions.
+        *
+        * @param array $extras Additional data to add to context
+        * @return array
+        */
+       protected function getLogContext( array $extras = [] ) {
+               return array_merge(
+                       [
+                               'db_server' => $this->mServer,
+                               'db_name' => $this->mDBname,
+                               'db_user' => $this->mUser,
+                       ],
+                       $extras
+               );
+       }
+
+       public function close() {
+               if ( $this->mConn ) {
+                       if ( $this->trxLevel() ) {
+                               $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
+                       }
+
+                       $closed = $this->closeConnection();
+                       $this->mConn = false;
+               } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
+                       throw new RuntimeException( "Transaction callbacks still pending." );
+               } else {
+                       $closed = true;
+               }
+               $this->mOpened = false;
+
+               return $closed;
+       }
+
+       /**
+        * Make sure isOpen() returns true as a sanity check
+        *
+        * @throws DBUnexpectedError
+        */
+       protected function assertOpen() {
+               if ( !$this->isOpen() ) {
+                       throw new DBUnexpectedError( $this, "DB connection was already closed." );
+               }
+       }
+
+       /**
+        * Closes underlying database connection
+        * @since 1.20
+        * @return bool Whether connection was closed successfully
+        */
+       abstract protected function closeConnection();
+
+       function reportConnectionError( $error = 'Unknown error' ) {
+               $myError = $this->lastError();
+               if ( $myError ) {
+                       $error = $myError;
+               }
+
+               # New method
+               throw new DBConnectionError( $this, $error );
+       }
+
+       /**
+        * The DBMS-dependent part of query()
+        *
+        * @param string $sql SQL query.
+        * @return ResultWrapper|bool Result object to feed to fetchObject,
+        *   fetchRow, ...; or false on failure
+        */
+       abstract protected function doQuery( $sql );
+
+       /**
+        * Determine whether a query writes to the DB.
+        * Should return true if unsure.
+        *
+        * @param string $sql
+        * @return bool
+        */
+       protected function isWriteQuery( $sql ) {
+               return !preg_match(
+                       '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
+       }
+
+       /**
+        * @param $sql
+        * @return string|null
+        */
+       protected function getQueryVerb( $sql ) {
+               return preg_match( '/^\s*([a-z]+)/i', $sql, $m ) ? strtoupper( $m[1] ) : null;
+       }
+
+       /**
+        * Determine whether a SQL statement is sensitive to isolation level.
+        * A SQL statement is considered transactable if its result could vary
+        * depending on the transaction isolation level. Operational commands
+        * such as 'SET' and 'SHOW' are not considered to be transactable.
+        *
+        * @param string $sql
+        * @return bool
+        */
+       protected function isTransactableQuery( $sql ) {
+               $verb = $this->getQueryVerb( $sql );
+               return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ], true );
+       }
+
+       public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
+               $priorWritesPending = $this->writesOrCallbacksPending();
+               $this->mLastQuery = $sql;
+
+               $isWrite = $this->isWriteQuery( $sql );
+               if ( $isWrite ) {
+                       $reason = $this->getReadOnlyReason();
+                       if ( $reason !== false ) {
+                               throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
+                       }
+                       # Set a flag indicating that writes have been done
+                       $this->mDoneWrites = microtime( true );
+               }
+
+               // Add trace comment to the begin of the sql string, right after the operator.
+               // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598)
+               $commentedSql = preg_replace( '/\s|$/', " /* $fname {$this->agent} */ ", $sql, 1 );
+
+               # Start implicit transactions that wrap the request if DBO_TRX is enabled
+               if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX )
+                       && $this->isTransactableQuery( $sql )
+               ) {
+                       $this->begin( __METHOD__ . " ($fname)", self::TRANSACTION_INTERNAL );
+                       $this->mTrxAutomatic = true;
+               }
+
+               # Keep track of whether the transaction has write queries pending
+               if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) {
+                       $this->mTrxDoneWrites = true;
+                       $this->trxProfiler->transactionWritingIn(
+                               $this->mServer, $this->mDBname, $this->mTrxShortId );
+               }
+
+               if ( $this->getFlag( DBO_DEBUG ) ) {
+                       $this->queryLogger->debug( "{$this->mDBname} {$commentedSql}" );
+               }
+
+               # Avoid fatals if close() was called
+               $this->assertOpen();
+
+               # Send the query to the server
+               $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
+
+               # Try reconnecting if the connection was lost
+               if ( false === $ret && $this->wasErrorReissuable() ) {
+                       $recoverable = $this->canRecoverFromDisconnect( $sql, $priorWritesPending );
+                       # Stash the last error values before anything might clear them
+                       $lastError = $this->lastError();
+                       $lastErrno = $this->lastErrno();
+                       # Update state tracking to reflect transaction loss due to disconnection
+                       $this->handleTransactionLoss();
+                       if ( $this->reconnect() ) {
+                               $msg = __METHOD__ . ": lost connection to {$this->getServer()}; reconnected";
+                               $this->connLogger->warning( $msg );
+                               $this->queryLogger->warning(
+                                       "$msg:\n" . ( new RuntimeException() )->getTraceAsString() );
+
+                               if ( !$recoverable ) {
+                                       # Callers may catch the exception and continue to use the DB
+                                       $this->reportQueryError( $lastError, $lastErrno, $sql, $fname );
+                               } else {
+                                       # Should be safe to silently retry the query
+                                       $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
+                               }
+                       } else {
+                               $msg = __METHOD__ . ": lost connection to {$this->getServer()} permanently";
+                               $this->connLogger->error( $msg );
+                       }
+               }
+
+               if ( false === $ret ) {
+                       # Deadlocks cause the entire transaction to abort, not just the statement.
+                       # http://dev.mysql.com/doc/refman/5.7/en/innodb-error-handling.html
+                       # https://www.postgresql.org/docs/9.1/static/explicit-locking.html
+                       if ( $this->wasDeadlock() ) {
+                               if ( $this->explicitTrxActive() || $priorWritesPending ) {
+                                       $tempIgnore = false; // not recoverable
+                               }
+                               # Update state tracking to reflect transaction loss
+                               $this->handleTransactionLoss();
+                       }
+
+                       $this->reportQueryError(
+                               $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
+               }
+
+               $res = $this->resultObject( $ret );
+
+               return $res;
+       }
+
+       private function doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ) {
+               $isMaster = !is_null( $this->getLBInfo( 'master' ) );
+               # generalizeSQL() will probably cut down the query to reasonable
+               # logging size most of the time. The substr is really just a sanity check.
+               if ( $isMaster ) {
+                       $queryProf = 'query-m: ' . substr( self::generalizeSQL( $sql ), 0, 255 );
+               } else {
+                       $queryProf = 'query: ' . substr( self::generalizeSQL( $sql ), 0, 255 );
+               }
+
+               # Include query transaction state
+               $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
+
+               $startTime = microtime( true );
+               $this->profiler->profileIn( $queryProf );
+               $ret = $this->doQuery( $commentedSql );
+               $this->profiler->profileOut( $queryProf );
+               $queryRuntime = max( microtime( true ) - $startTime, 0.0 );
+
+               unset( $queryProfSection ); // profile out (if set)
+
+               if ( $ret !== false ) {
+                       $this->lastPing = $startTime;
+                       if ( $isWrite && $this->mTrxLevel ) {
+                               $this->updateTrxWriteQueryTime( $sql, $queryRuntime );
+                               $this->mTrxWriteCallers[] = $fname;
+                       }
+               }
+
+               if ( $sql === self::PING_QUERY ) {
+                       $this->mRTTEstimate = $queryRuntime;
+               }
+
+               $this->trxProfiler->recordQueryCompletion(
+                       $queryProf, $startTime, $isWrite, $this->affectedRows()
+               );
+               MWDebug::query( $sql, $fname, $isMaster, $queryRuntime );
+
+               return $ret;
+       }
+
+       /**
+        * Update the estimated run-time of a query, not counting large row lock times
+        *
+        * LoadBalancer can be set to rollback transactions that will create huge replication
+        * lag. It bases this estimate off of pendingWriteQueryDuration(). Certain simple
+        * queries, like inserting a row can take a long time due to row locking. This method
+        * uses some simple heuristics to discount those cases.
+        *
+        * @param string $sql A SQL write query
+        * @param float $runtime Total runtime, including RTT
+        */
+       private function updateTrxWriteQueryTime( $sql, $runtime ) {
+               // Whether this is indicative of replica DB runtime (except for RBR or ws_repl)
+               $indicativeOfReplicaRuntime = true;
+               if ( $runtime > self::SLOW_WRITE_SEC ) {
+                       $verb = $this->getQueryVerb( $sql );
+                       // insert(), upsert(), replace() are fast unless bulky in size or blocked on locks
+                       if ( $verb === 'INSERT' ) {
+                               $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS;
+                       } elseif ( $verb === 'REPLACE' ) {
+                               $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS / 2;
+                       }
+               }
+
+               $this->mTrxWriteDuration += $runtime;
+               $this->mTrxWriteQueryCount += 1;
+               if ( $indicativeOfReplicaRuntime ) {
+                       $this->mTrxWriteAdjDuration += $runtime;
+                       $this->mTrxWriteAdjQueryCount += 1;
+               }
+       }
+
+       private function canRecoverFromDisconnect( $sql, $priorWritesPending ) {
+               # Transaction dropped; this can mean lost writes, or REPEATABLE-READ snapshots.
+               # Dropped connections also mean that named locks are automatically released.
+               # Only allow error suppression in autocommit mode or when the lost transaction
+               # didn't matter anyway (aside from DBO_TRX snapshot loss).
+               if ( $this->mNamedLocksHeld ) {
+                       return false; // possible critical section violation
+               } elseif ( $sql === 'COMMIT' ) {
+                       return !$priorWritesPending; // nothing written anyway? (T127428)
+               } elseif ( $sql === 'ROLLBACK' ) {
+                       return true; // transaction lost...which is also what was requested :)
+               } elseif ( $this->explicitTrxActive() ) {
+                       return false; // don't drop atomocity
+               } elseif ( $priorWritesPending ) {
+                       return false; // prior writes lost from implicit transaction
+               }
+
+               return true;
+       }
+
+       private function handleTransactionLoss() {
+               $this->mTrxLevel = 0;
+               $this->mTrxIdleCallbacks = []; // bug 65263
+               $this->mTrxPreCommitCallbacks = []; // bug 65263
+               try {
+                       // Handle callbacks in mTrxEndCallbacks
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+                       $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
+                       return null;
+               } catch ( Exception $e ) {
+                       // Already logged; move on...
+                       return $e;
+               }
+       }
+
+       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
+               if ( $this->ignoreErrors() || $tempIgnore ) {
+                       $this->queryLogger->debug( "SQL ERROR (ignored): $error\n" );
+               } else {
+                       $sql1line = mb_substr( str_replace( "\n", "\\n", $sql ), 0, 5 * 1024 );
+                       $this->queryLogger->error(
+                               "{fname}\t{db_server}\t{errno}\t{error}\t{sql1line}",
+                               $this->getLogContext( [
+                                       'method' => __METHOD__,
+                                       'errno' => $errno,
+                                       'error' => $error,
+                                       'sql1line' => $sql1line,
+                                       'fname' => $fname,
+                               ] )
+                       );
+                       $this->queryLogger->debug( "SQL ERROR: " . $error . "\n" );
+                       throw new DBQueryError( $this, $error, $errno, $sql, $fname );
+               }
+       }
+
+       public function freeResult( $res ) {
+       }
+
+       public function selectField(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = []
+       ) {
+               if ( $var === '*' ) { // sanity
+                       throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
+               }
+
+               if ( !is_array( $options ) ) {
+                       $options = [ $options ];
+               }
+
+               $options['LIMIT'] = 1;
+
+               $res = $this->select( $table, $var, $cond, $fname, $options );
+               if ( $res === false || !$this->numRows( $res ) ) {
+                       return false;
+               }
+
+               $row = $this->fetchRow( $res );
+
+               if ( $row !== false ) {
+                       return reset( $row );
+               } else {
+                       return false;
+               }
+       }
+
+       public function selectFieldValues(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = []
+       ) {
+               if ( $var === '*' ) { // sanity
+                       throw new DBUnexpectedError( $this, "Cannot use a * field" );
+               } elseif ( !is_string( $var ) ) { // sanity
+                       throw new DBUnexpectedError( $this, "Cannot use an array of fields" );
+               }
+
+               if ( !is_array( $options ) ) {
+                       $options = [ $options ];
+               }
+
+               $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
+               if ( $res === false ) {
+                       return false;
+               }
+
+               $values = [];
+               foreach ( $res as $row ) {
+                       $values[] = $row->$var;
+               }
+
+               return $values;
+       }
+
+       /**
+        * Returns an optional USE INDEX clause to go after the table, and a
+        * string to go at the end of the query.
+        *
+        * @param array $options Associative array of options to be turned into
+        *   an SQL query, valid keys are listed in the function.
+        * @return array
+        * @see DatabaseBase::select()
+        */
+       public function makeSelectOptions( $options ) {
+               $preLimitTail = $postLimitTail = '';
+               $startOpts = '';
+
+               $noKeyOptions = [];
+
+               foreach ( $options as $key => $option ) {
+                       if ( is_numeric( $key ) ) {
+                               $noKeyOptions[$option] = true;
+                       }
+               }
+
+               $preLimitTail .= $this->makeGroupByWithHaving( $options );
+
+               $preLimitTail .= $this->makeOrderBy( $options );
+
+               // if (isset($options['LIMIT'])) {
+               //      $tailOpts .= $this->limitResult('', $options['LIMIT'],
+               //              isset($options['OFFSET']) ? $options['OFFSET']
+               //              : false);
+               // }
+
+               if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
+                       $postLimitTail .= ' FOR UPDATE';
+               }
+
+               if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) {
+                       $postLimitTail .= ' LOCK IN SHARE MODE';
+               }
+
+               if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
+                       $startOpts .= 'DISTINCT';
+               }
+
+               # Various MySQL extensions
+               if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) {
+                       $startOpts .= ' /*! STRAIGHT_JOIN */';
+               }
+
+               if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) {
+                       $startOpts .= ' HIGH_PRIORITY';
+               }
+
+               if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) {
+                       $startOpts .= ' SQL_BIG_RESULT';
+               }
+
+               if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) {
+                       $startOpts .= ' SQL_BUFFER_RESULT';
+               }
+
+               if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) {
+                       $startOpts .= ' SQL_SMALL_RESULT';
+               }
+
+               if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) {
+                       $startOpts .= ' SQL_CALC_FOUND_ROWS';
+               }
+
+               if ( isset( $noKeyOptions['SQL_CACHE'] ) ) {
+                       $startOpts .= ' SQL_CACHE';
+               }
+
+               if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) {
+                       $startOpts .= ' SQL_NO_CACHE';
+               }
+
+               if ( isset( $options['USE INDEX'] ) && is_string( $options['USE INDEX'] ) ) {
+                       $useIndex = $this->useIndexClause( $options['USE INDEX'] );
+               } else {
+                       $useIndex = '';
+               }
+               if ( isset( $options['IGNORE INDEX'] ) && is_string( $options['IGNORE INDEX'] ) ) {
+                       $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] );
+               } else {
+                       $ignoreIndex = '';
+               }
+
+               return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
+       }
+
+       /**
+        * Returns an optional GROUP BY with an optional HAVING
+        *
+        * @param array $options Associative array of options
+        * @return string
+        * @see DatabaseBase::select()
+        * @since 1.21
+        */
+       public function makeGroupByWithHaving( $options ) {
+               $sql = '';
+               if ( isset( $options['GROUP BY'] ) ) {
+                       $gb = is_array( $options['GROUP BY'] )
+                               ? implode( ',', $options['GROUP BY'] )
+                               : $options['GROUP BY'];
+                       $sql .= ' GROUP BY ' . $gb;
+               }
+               if ( isset( $options['HAVING'] ) ) {
+                       $having = is_array( $options['HAVING'] )
+                               ? $this->makeList( $options['HAVING'], LIST_AND )
+                               : $options['HAVING'];
+                       $sql .= ' HAVING ' . $having;
+               }
+
+               return $sql;
+       }
+
+       /**
+        * Returns an optional ORDER BY
+        *
+        * @param array $options Associative array of options
+        * @return string
+        * @see DatabaseBase::select()
+        * @since 1.21
+        */
+       public function makeOrderBy( $options ) {
+               if ( isset( $options['ORDER BY'] ) ) {
+                       $ob = is_array( $options['ORDER BY'] )
+                               ? implode( ',', $options['ORDER BY'] )
+                               : $options['ORDER BY'];
+
+                       return ' ORDER BY ' . $ob;
+               }
+
+               return '';
+       }
+
+       // See IDatabase::select for the docs for this function
+       public function select( $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = [] ) {
+               $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
+
+               return $this->query( $sql, $fname );
+       }
+
+       public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = []
+       ) {
+               if ( is_array( $vars ) ) {
+                       $vars = implode( ',', $this->fieldNamesWithAlias( $vars ) );
+               }
+
+               $options = (array)$options;
+               $useIndexes = ( isset( $options['USE INDEX'] ) && is_array( $options['USE INDEX'] ) )
+                       ? $options['USE INDEX']
+                       : [];
+               $ignoreIndexes = ( isset( $options['IGNORE INDEX'] ) && is_array( $options['IGNORE INDEX'] ) )
+                       ? $options['IGNORE INDEX']
+                       : [];
+
+               if ( is_array( $table ) ) {
+                       $from = ' FROM ' .
+                               $this->tableNamesWithIndexClauseOrJOIN( $table, $useIndexes, $ignoreIndexes, $join_conds );
+               } elseif ( $table != '' ) {
+                       if ( $table[0] == ' ' ) {
+                               $from = ' FROM ' . $table;
+                       } else {
+                               $from = ' FROM ' .
+                                       $this->tableNamesWithIndexClauseOrJOIN( [ $table ], $useIndexes, $ignoreIndexes, [] );
+                       }
+               } else {
+                       $from = '';
+               }
+
+               list( $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ) =
+                       $this->makeSelectOptions( $options );
+
+               if ( !empty( $conds ) ) {
+                       if ( is_array( $conds ) ) {
+                               $conds = $this->makeList( $conds, LIST_AND );
+                       }
+                       $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex WHERE $conds $preLimitTail";
+               } else {
+                       $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex $preLimitTail";
+               }
+
+               if ( isset( $options['LIMIT'] ) ) {
+                       $sql = $this->limitResult( $sql, $options['LIMIT'],
+                               isset( $options['OFFSET'] ) ? $options['OFFSET'] : false );
+               }
+               $sql = "$sql $postLimitTail";
+
+               if ( isset( $options['EXPLAIN'] ) ) {
+                       $sql = 'EXPLAIN ' . $sql;
+               }
+
+               return $sql;
+       }
+
+       public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
+               $options = [], $join_conds = []
+       ) {
+               $options = (array)$options;
+               $options['LIMIT'] = 1;
+               $res = $this->select( $table, $vars, $conds, $fname, $options, $join_conds );
+
+               if ( $res === false ) {
+                       return false;
+               }
+
+               if ( !$this->numRows( $res ) ) {
+                       return false;
+               }
+
+               $obj = $this->fetchObject( $res );
+
+               return $obj;
+       }
+
+       public function estimateRowCount(
+               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
+       ) {
+               $rows = 0;
+               $res = $this->select( $table, [ 'rowcount' => 'COUNT(*)' ], $conds, $fname, $options );
+
+               if ( $res ) {
+                       $row = $this->fetchRow( $res );
+                       $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
+               }
+
+               return $rows;
+       }
+
+       public function selectRowCount(
+               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+       ) {
+               $rows = 0;
+               $sql = $this->selectSQLText( $tables, '1', $conds, $fname, $options, $join_conds );
+               $res = $this->query( "SELECT COUNT(*) AS rowcount FROM ($sql) tmp_count", $fname );
+
+               if ( $res ) {
+                       $row = $this->fetchRow( $res );
+                       $rows = ( isset( $row['rowcount'] ) ) ? (int)$row['rowcount'] : 0;
+               }
+
+               return $rows;
+       }
+
+       /**
+        * Removes most variables from an SQL query and replaces them with X or N for numbers.
+        * It's only slightly flawed. Don't use for anything important.
+        *
+        * @param string $sql A SQL Query
+        *
+        * @return string
+        */
+       protected static function generalizeSQL( $sql ) {
+               # This does the same as the regexp below would do, but in such a way
+               # as to avoid crashing php on some large strings.
+               # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
+
+               $sql = str_replace( "\\\\", '', $sql );
+               $sql = str_replace( "\\'", '', $sql );
+               $sql = str_replace( "\\\"", '', $sql );
+               $sql = preg_replace( "/'.*'/s", "'X'", $sql );
+               $sql = preg_replace( '/".*"/s', "'X'", $sql );
+
+               # All newlines, tabs, etc replaced by single space
+               $sql = preg_replace( '/\s+/', ' ', $sql );
+
+               # All numbers => N,
+               # except the ones surrounded by characters, e.g. l10n
+               $sql = preg_replace( '/-?\d+(,-?\d+)+/s', 'N,...,N', $sql );
+               $sql = preg_replace( '/(?<![a-zA-Z])-?\d+(?![a-zA-Z])/s', 'N', $sql );
+
+               return $sql;
+       }
+
+       public function fieldExists( $table, $field, $fname = __METHOD__ ) {
+               $info = $this->fieldInfo( $table, $field );
+
+               return (bool)$info;
+       }
+
+       public function indexExists( $table, $index, $fname = __METHOD__ ) {
+               if ( !$this->tableExists( $table ) ) {
+                       return null;
+               }
+
+               $info = $this->indexInfo( $table, $index, $fname );
+               if ( is_null( $info ) ) {
+                       return null;
+               } else {
+                       return $info !== false;
+               }
+       }
+
+       public function tableExists( $table, $fname = __METHOD__ ) {
+               $table = $this->tableName( $table );
+               $old = $this->ignoreErrors( true );
+               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
+               $this->ignoreErrors( $old );
+
+               return (bool)$res;
+       }
+
+       public function indexUnique( $table, $index ) {
+               $indexInfo = $this->indexInfo( $table, $index );
+
+               if ( !$indexInfo ) {
+                       return null;
+               }
+
+               return !$indexInfo[0]->Non_unique;
+       }
+
+       /**
+        * Helper for DatabaseBase::insert().
+        *
+        * @param array $options
+        * @return string
+        */
+       protected function makeInsertOptions( $options ) {
+               return implode( ' ', $options );
+       }
+
+       public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
+               # No rows to insert, easy just return now
+               if ( !count( $a ) ) {
+                       return true;
+               }
+
+               $table = $this->tableName( $table );
+
+               if ( !is_array( $options ) ) {
+                       $options = [ $options ];
+               }
+
+               $fh = null;
+               if ( isset( $options['fileHandle'] ) ) {
+                       $fh = $options['fileHandle'];
+               }
+               $options = $this->makeInsertOptions( $options );
+
+               if ( isset( $a[0] ) && is_array( $a[0] ) ) {
+                       $multi = true;
+                       $keys = array_keys( $a[0] );
+               } else {
+                       $multi = false;
+                       $keys = array_keys( $a );
+               }
+
+               $sql = 'INSERT ' . $options .
+                       " INTO $table (" . implode( ',', $keys ) . ') VALUES ';
+
+               if ( $multi ) {
+                       $first = true;
+                       foreach ( $a as $row ) {
+                               if ( $first ) {
+                                       $first = false;
+                               } else {
+                                       $sql .= ',';
+                               }
+                               $sql .= '(' . $this->makeList( $row ) . ')';
+                       }
+               } else {
+                       $sql .= '(' . $this->makeList( $a ) . ')';
+               }
+
+               if ( $fh !== null && false === fwrite( $fh, $sql ) ) {
+                       return false;
+               } elseif ( $fh !== null ) {
+                       return true;
+               }
+
+               return (bool)$this->query( $sql, $fname );
+       }
+
+       /**
+        * Make UPDATE options array for DatabaseBase::makeUpdateOptions
+        *
+        * @param array $options
+        * @return array
+        */
+       protected function makeUpdateOptionsArray( $options ) {
+               if ( !is_array( $options ) ) {
+                       $options = [ $options ];
+               }
+
+               $opts = [];
+
+               if ( in_array( 'IGNORE', $options ) ) {
+                       $opts[] = 'IGNORE';
+               }
+
+               return $opts;
+       }
+
+       /**
+        * Make UPDATE options for the DatabaseBase::update function
+        *
+        * @param array $options The options passed to DatabaseBase::update
+        * @return string
+        */
+       protected function makeUpdateOptions( $options ) {
+               $opts = $this->makeUpdateOptionsArray( $options );
+
+               return implode( ' ', $opts );
+       }
+
+       function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
+               $table = $this->tableName( $table );
+               $opts = $this->makeUpdateOptions( $options );
+               $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
+
+               if ( $conds !== [] && $conds !== '*' ) {
+                       $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
+               }
+
+               return $this->query( $sql, $fname );
+       }
+
+       public function makeList( $a, $mode = LIST_COMMA ) {
+               if ( !is_array( $a ) ) {
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with incorrect parameters' );
+               }
+
+               $first = true;
+               $list = '';
+
+               foreach ( $a as $field => $value ) {
+                       if ( !$first ) {
+                               if ( $mode == LIST_AND ) {
+                                       $list .= ' AND ';
+                               } elseif ( $mode == LIST_OR ) {
+                                       $list .= ' OR ';
+                               } else {
+                                       $list .= ',';
+                               }
+                       } else {
+                               $first = false;
+                       }
+
+                       if ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_numeric( $field ) ) {
+                               $list .= "($value)";
+                       } elseif ( ( $mode == LIST_SET ) && is_numeric( $field ) ) {
+                               $list .= "$value";
+                       } elseif ( ( $mode == LIST_AND || $mode == LIST_OR ) && is_array( $value ) ) {
+                               // Remove null from array to be handled separately if found
+                               $includeNull = false;
+                               foreach ( array_keys( $value, null, true ) as $nullKey ) {
+                                       $includeNull = true;
+                                       unset( $value[$nullKey] );
+                               }
+                               if ( count( $value ) == 0 && !$includeNull ) {
+                                       throw new InvalidArgumentException( __METHOD__ . ": empty input for field $field" );
+                               } elseif ( count( $value ) == 0 ) {
+                                       // only check if $field is null
+                                       $list .= "$field IS NULL";
+                               } else {
+                                       // IN clause contains at least one valid element
+                                       if ( $includeNull ) {
+                                               // Group subconditions to ensure correct precedence
+                                               $list .= '(';
+                                       }
+                                       if ( count( $value ) == 1 ) {
+                                               // Special-case single values, as IN isn't terribly efficient
+                                               // Don't necessarily assume the single key is 0; we don't
+                                               // enforce linear numeric ordering on other arrays here.
+                                               $value = array_values( $value )[0];
+                                               $list .= $field . " = " . $this->addQuotes( $value );
+                                       } else {
+                                               $list .= $field . " IN (" . $this->makeList( $value ) . ") ";
+                                       }
+                                       // if null present in array, append IS NULL
+                                       if ( $includeNull ) {
+                                               $list .= " OR $field IS NULL)";
+                                       }
+                               }
+                       } elseif ( $value === null ) {
+                               if ( $mode == LIST_AND || $mode == LIST_OR ) {
+                                       $list .= "$field IS ";
+                               } elseif ( $mode == LIST_SET ) {
+                                       $list .= "$field = ";
+                               }
+                               $list .= 'NULL';
+                       } else {
+                               if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
+                                       $list .= "$field = ";
+                               }
+                               $list .= $mode == LIST_NAMES ? $value : $this->addQuotes( $value );
+                       }
+               }
+
+               return $list;
+       }
+
+       public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
+               $conds = [];
+
+               foreach ( $data as $base => $sub ) {
+                       if ( count( $sub ) ) {
+                               $conds[] = $this->makeList(
+                                       [ $baseKey => $base, $subKey => array_keys( $sub ) ],
+                                       LIST_AND );
+                       }
+               }
+
+               if ( $conds ) {
+                       return $this->makeList( $conds, LIST_OR );
+               } else {
+                       // Nothing to search for...
+                       return false;
+               }
+       }
+
+       /**
+        * Return aggregated value alias
+        *
+        * @param array $valuedata
+        * @param string $valuename
+        *
+        * @return string
+        */
+       public function aggregateValue( $valuedata, $valuename = 'value' ) {
+               return $valuename;
+       }
+
+       public function bitNot( $field ) {
+               return "(~$field)";
+       }
+
+       public function bitAnd( $fieldLeft, $fieldRight ) {
+               return "($fieldLeft & $fieldRight)";
+       }
+
+       public function bitOr( $fieldLeft, $fieldRight ) {
+               return "($fieldLeft | $fieldRight)";
+       }
+
+       public function buildConcat( $stringList ) {
+               return 'CONCAT(' . implode( ',', $stringList ) . ')';
+       }
+
+       public function buildGroupConcatField(
+               $delim, $table, $field, $conds = '', $join_conds = []
+       ) {
+               $fld = "GROUP_CONCAT($field SEPARATOR " . $this->addQuotes( $delim ) . ')';
+
+               return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
+       }
+
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return $field;
+       }
+
+       public function selectDB( $db ) {
+               # Stub. Shouldn't cause serious problems if it's not overridden, but
+               # if your database engine supports a concept similar to MySQL's
+               # databases you may as well.
+               $this->mDBname = $db;
+
+               return true;
+       }
+
+       public function getDBname() {
+               return $this->mDBname;
+       }
+
+       public function getServer() {
+               return $this->mServer;
+       }
+
+       /**
+        * Format a table name ready for use in constructing an SQL query
+        *
+        * This does two important things: it quotes the table names to clean them up,
+        * and it adds a table prefix if only given a table name with no quotes.
+        *
+        * All functions of this object which require a table name call this function
+        * themselves. Pass the canonical name to such functions. This is only needed
+        * when calling query() directly.
+        *
+        * @note This function does not sanitize user input. It is not safe to use
+        *   this function to escape user input.
+        * @param string $name Database table name
+        * @param string $format One of:
+        *   quoted - Automatically pass the table name through addIdentifierQuotes()
+        *            so that it can be used in a query.
+        *   raw - Do not add identifier quotes to the table name
+        * @return string Full database name
+        */
+       public function tableName( $name, $format = 'quoted' ) {
+               # Skip the entire process when we have a string quoted on both ends.
+               # Note that we check the end so that we will still quote any use of
+               # use of `database`.table. But won't break things if someone wants
+               # to query a database table with a dot in the name.
+               if ( $this->isQuotedIdentifier( $name ) ) {
+                       return $name;
+               }
+
+               # Lets test for any bits of text that should never show up in a table
+               # name. Basically anything like JOIN or ON which are actually part of
+               # SQL queries, but may end up inside of the table value to combine
+               # sql. Such as how the API is doing.
+               # Note that we use a whitespace test rather than a \b test to avoid
+               # any remote case where a word like on may be inside of a table name
+               # surrounded by symbols which may be considered word breaks.
+               if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
+                       return $name;
+               }
+
+               # Split database and table into proper variables.
+               # We reverse the explode so that database.table and table both output
+               # the correct table.
+               $dbDetails = explode( '.', $name, 3 );
+               if ( count( $dbDetails ) == 3 ) {
+                       list( $database, $schema, $table ) = $dbDetails;
+                       # We don't want any prefix added in this case
+                       $prefix = '';
+               } elseif ( count( $dbDetails ) == 2 ) {
+                       list( $database, $table ) = $dbDetails;
+                       # We don't want any prefix added in this case
+                       # In dbs that support it, $database may actually be the schema
+                       # but that doesn't affect any of the functionality here
+                       $prefix = '';
+                       $schema = '';
+               } else {
+                       list( $table ) = $dbDetails;
+                       if ( isset( $this->tableAliases[$table] ) ) {
+                               $database = $this->tableAliases[$table]['dbname'];
+                               $schema = is_string( $this->tableAliases[$table]['schema'] )
+                                       ? $this->tableAliases[$table]['schema']
+                                       : $this->mSchema;
+                               $prefix = is_string( $this->tableAliases[$table]['prefix'] )
+                                       ? $this->tableAliases[$table]['prefix']
+                                       : $this->mTablePrefix;
+                       } else {
+                               $database = '';
+                               $schema = $this->mSchema; # Default schema
+                               $prefix = $this->mTablePrefix; # Default prefix
+                       }
+               }
+
+               # Quote $table and apply the prefix if not quoted.
+               # $tableName might be empty if this is called from Database::replaceVars()
+               $tableName = "{$prefix}{$table}";
+               if ( $format == 'quoted'
+                       && !$this->isQuotedIdentifier( $tableName ) && $tableName !== ''
+               ) {
+                       $tableName = $this->addIdentifierQuotes( $tableName );
+               }
+
+               # Quote $schema and merge it with the table name if needed
+               if ( strlen( $schema ) ) {
+                       if ( $format == 'quoted' && !$this->isQuotedIdentifier( $schema ) ) {
+                               $schema = $this->addIdentifierQuotes( $schema );
+                       }
+                       $tableName = $schema . '.' . $tableName;
+               }
+
+               # Quote $database and merge it with the table name if needed
+               if ( $database !== '' ) {
+                       if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
+                               $database = $this->addIdentifierQuotes( $database );
+                       }
+                       $tableName = $database . '.' . $tableName;
+               }
+
+               return $tableName;
+       }
+
+       /**
+        * Fetch a number of table names into an array
+        * This is handy when you need to construct SQL for joins
+        *
+        * Example:
+        * extract( $dbr->tableNames( 'user', 'watchlist' ) );
+        * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
+        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
+        *
+        * @return array
+        */
+       public function tableNames() {
+               $inArray = func_get_args();
+               $retVal = [];
+
+               foreach ( $inArray as $name ) {
+                       $retVal[$name] = $this->tableName( $name );
+               }
+
+               return $retVal;
+       }
+
+       /**
+        * Fetch a number of table names into an zero-indexed numerical array
+        * This is handy when you need to construct SQL for joins
+        *
+        * Example:
+        * list( $user, $watchlist ) = $dbr->tableNamesN( 'user', 'watchlist' );
+        * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
+        *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
+        *
+        * @return array
+        */
+       public function tableNamesN() {
+               $inArray = func_get_args();
+               $retVal = [];
+
+               foreach ( $inArray as $name ) {
+                       $retVal[] = $this->tableName( $name );
+               }
+
+               return $retVal;
+       }
+
+       /**
+        * Get an aliased table name
+        * e.g. tableName AS newTableName
+        *
+        * @param string $name Table name, see tableName()
+        * @param string|bool $alias Alias (optional)
+        * @return string SQL name for aliased table. Will not alias a table to its own name
+        */
+       public function tableNameWithAlias( $name, $alias = false ) {
+               if ( !$alias || $alias == $name ) {
+                       return $this->tableName( $name );
+               } else {
+                       return $this->tableName( $name ) . ' ' . $this->addIdentifierQuotes( $alias );
+               }
+       }
+
+       /**
+        * Gets an array of aliased table names
+        *
+        * @param array $tables [ [alias] => table ]
+        * @return string[] See tableNameWithAlias()
+        */
+       public function tableNamesWithAlias( $tables ) {
+               $retval = [];
+               foreach ( $tables as $alias => $table ) {
+                       if ( is_numeric( $alias ) ) {
+                               $alias = $table;
+                       }
+                       $retval[] = $this->tableNameWithAlias( $table, $alias );
+               }
+
+               return $retval;
+       }
+
+       /**
+        * Get an aliased field name
+        * e.g. fieldName AS newFieldName
+        *
+        * @param string $name Field name
+        * @param string|bool $alias Alias (optional)
+        * @return string SQL name for aliased field. Will not alias a field to its own name
+        */
+       public function fieldNameWithAlias( $name, $alias = false ) {
+               if ( !$alias || (string)$alias === (string)$name ) {
+                       return $name;
+               } else {
+                       return $name . ' AS ' . $this->addIdentifierQuotes( $alias ); // PostgreSQL needs AS
+               }
+       }
+
+       /**
+        * Gets an array of aliased field names
+        *
+        * @param array $fields [ [alias] => field ]
+        * @return string[] See fieldNameWithAlias()
+        */
+       public function fieldNamesWithAlias( $fields ) {
+               $retval = [];
+               foreach ( $fields as $alias => $field ) {
+                       if ( is_numeric( $alias ) ) {
+                               $alias = $field;
+                       }
+                       $retval[] = $this->fieldNameWithAlias( $field, $alias );
+               }
+
+               return $retval;
+       }
+
+       /**
+        * Get the aliased table name clause for a FROM clause
+        * which might have a JOIN and/or USE INDEX or IGNORE INDEX clause
+        *
+        * @param array $tables ( [alias] => table )
+        * @param array $use_index Same as for select()
+        * @param array $ignore_index Same as for select()
+        * @param array $join_conds Same as for select()
+        * @return string
+        */
+       protected function tableNamesWithIndexClauseOrJOIN(
+               $tables, $use_index = [], $ignore_index = [], $join_conds = []
+       ) {
+               $ret = [];
+               $retJOIN = [];
+               $use_index = (array)$use_index;
+               $ignore_index = (array)$ignore_index;
+               $join_conds = (array)$join_conds;
+
+               foreach ( $tables as $alias => $table ) {
+                       if ( !is_string( $alias ) ) {
+                               // No alias? Set it equal to the table name
+                               $alias = $table;
+                       }
+                       // Is there a JOIN clause for this table?
+                       if ( isset( $join_conds[$alias] ) ) {
+                               list( $joinType, $conds ) = $join_conds[$alias];
+                               $tableClause = $joinType;
+                               $tableClause .= ' ' . $this->tableNameWithAlias( $table, $alias );
+                               if ( isset( $use_index[$alias] ) ) { // has USE INDEX?
+                                       $use = $this->useIndexClause( implode( ',', (array)$use_index[$alias] ) );
+                                       if ( $use != '' ) {
+                                               $tableClause .= ' ' . $use;
+                                       }
+                               }
+                               if ( isset( $ignore_index[$alias] ) ) { // has IGNORE INDEX?
+                                       $ignore = $this->ignoreIndexClause( implode( ',', (array)$ignore_index[$alias] ) );
+                                       if ( $ignore != '' ) {
+                                               $tableClause .= ' ' . $ignore;
+                                       }
+                               }
+                               $on = $this->makeList( (array)$conds, LIST_AND );
+                               if ( $on != '' ) {
+                                       $tableClause .= ' ON (' . $on . ')';
+                               }
+
+                               $retJOIN[] = $tableClause;
+                       } elseif ( isset( $use_index[$alias] ) ) {
+                               // Is there an INDEX clause for this table?
+                               $tableClause = $this->tableNameWithAlias( $table, $alias );
+                               $tableClause .= ' ' . $this->useIndexClause(
+                                               implode( ',', (array)$use_index[$alias] )
+                                       );
+
+                               $ret[] = $tableClause;
+                       } elseif ( isset( $ignore_index[$alias] ) ) {
+                               // Is there an INDEX clause for this table?
+                               $tableClause = $this->tableNameWithAlias( $table, $alias );
+                               $tableClause .= ' ' . $this->ignoreIndexClause(
+                                               implode( ',', (array)$ignore_index[$alias] )
+                                       );
+
+                               $ret[] = $tableClause;
+                       } else {
+                               $tableClause = $this->tableNameWithAlias( $table, $alias );
+
+                               $ret[] = $tableClause;
+                       }
+               }
+
+               // We can't separate explicit JOIN clauses with ',', use ' ' for those
+               $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : "";
+               $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : "";
+
+               // Compile our final table clause
+               return implode( ' ', [ $implicitJoins, $explicitJoins ] );
+       }
+
+       /**
+        * Get the name of an index in a given table.
+        *
+        * @param string $index
+        * @return string
+        */
+       protected function indexName( $index ) {
+               // Backwards-compatibility hack
+               $renamed = [
+                       'ar_usertext_timestamp' => 'usertext_timestamp',
+                       'un_user_id' => 'user_id',
+                       'un_user_ip' => 'user_ip',
+               ];
+
+               if ( isset( $renamed[$index] ) ) {
+                       return $renamed[$index];
+               } else {
+                       return $index;
+               }
+       }
+
+       public function addQuotes( $s ) {
+               if ( $s instanceof Blob ) {
+                       $s = $s->fetch();
+               }
+               if ( $s === null ) {
+                       return 'NULL';
+               } else {
+                       # This will also quote numeric values. This should be harmless,
+                       # and protects against weird problems that occur when they really
+                       # _are_ strings such as article titles and string->number->string
+                       # conversion is not 1:1.
+                       return "'" . $this->strencode( $s ) . "'";
+               }
+       }
+
+       /**
+        * Quotes an identifier using `backticks` or "double quotes" depending on the database type.
+        * MySQL uses `backticks` while basically everything else uses double quotes.
+        * Since MySQL is the odd one out here the double quotes are our generic
+        * and we implement backticks in DatabaseMysql.
+        *
+        * @param string $s
+        * @return string
+        */
+       public function addIdentifierQuotes( $s ) {
+               return '"' . str_replace( '"', '""', $s ) . '"';
+       }
+
+       /**
+        * Returns if the given identifier looks quoted or not according to
+        * the database convention for quoting identifiers .
+        *
+        * @note Do not use this to determine if untrusted input is safe.
+        *   A malicious user can trick this function.
+        * @param string $name
+        * @return bool
+        */
+       public function isQuotedIdentifier( $name ) {
+               return $name[0] == '"' && substr( $name, -1, 1 ) == '"';
+       }
+
+       /**
+        * @param string $s
+        * @return string
+        */
+       protected function escapeLikeInternal( $s ) {
+               return addcslashes( $s, '\%_' );
+       }
+
+       public function buildLike() {
+               $params = func_get_args();
+
+               if ( count( $params ) > 0 && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+
+               $s = '';
+
+               foreach ( $params as $value ) {
+                       if ( $value instanceof LikeMatch ) {
+                               $s .= $value->toString();
+                       } else {
+                               $s .= $this->escapeLikeInternal( $value );
+                       }
+               }
+
+               return " LIKE {$this->addQuotes( $s )} ";
+       }
+
+       public function anyChar() {
+               return new LikeMatch( '_' );
+       }
+
+       public function anyString() {
+               return new LikeMatch( '%' );
+       }
+
+       public function nextSequenceValue( $seqName ) {
+               return null;
+       }
+
+       /**
+        * USE INDEX clause. Unlikely to be useful for anything but MySQL. This
+        * is only needed because a) MySQL must be as efficient as possible due to
+        * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
+        * which index to pick. Anyway, other databases might have different
+        * indexes on a given table. So don't bother overriding this unless you're
+        * MySQL.
+        * @param string $index
+        * @return string
+        */
+       public function useIndexClause( $index ) {
+               return '';
+       }
+
+       /**
+        * IGNORE INDEX clause. Unlikely to be useful for anything but MySQL. This
+        * is only needed because a) MySQL must be as efficient as possible due to
+        * its use on Wikipedia, and b) MySQL 4.0 is kind of dumb sometimes about
+        * which index to pick. Anyway, other databases might have different
+        * indexes on a given table. So don't bother overriding this unless you're
+        * MySQL.
+        * @param string $index
+        * @return string
+        */
+       public function ignoreIndexClause( $index ) {
+               return '';
+       }
+
+       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
+               $quotedTable = $this->tableName( $table );
+
+               if ( count( $rows ) == 0 ) {
+                       return;
+               }
+
+               # Single row case
+               if ( !is_array( reset( $rows ) ) ) {
+                       $rows = [ $rows ];
+               }
+
+               // @FXIME: this is not atomic, but a trx would break affectedRows()
+               foreach ( $rows as $row ) {
+                       # Delete rows which collide
+                       if ( $uniqueIndexes ) {
+                               $sql = "DELETE FROM $quotedTable WHERE ";
+                               $first = true;
+                               foreach ( $uniqueIndexes as $index ) {
+                                       if ( $first ) {
+                                               $first = false;
+                                               $sql .= '( ';
+                                       } else {
+                                               $sql .= ' ) OR ( ';
+                                       }
+                                       if ( is_array( $index ) ) {
+                                               $first2 = true;
+                                               foreach ( $index as $col ) {
+                                                       if ( $first2 ) {
+                                                               $first2 = false;
+                                                       } else {
+                                                               $sql .= ' AND ';
+                                                       }
+                                                       $sql .= $col . '=' . $this->addQuotes( $row[$col] );
+                                               }
+                                       } else {
+                                               $sql .= $index . '=' . $this->addQuotes( $row[$index] );
+                                       }
+                               }
+                               $sql .= ' )';
+                               $this->query( $sql, $fname );
+                       }
+
+                       # Now insert the row
+                       $this->insert( $table, $row, $fname );
+               }
+       }
+
+       /**
+        * REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE
+        * statement.
+        *
+        * @param string $table Table name
+        * @param array|string $rows Row(s) to insert
+        * @param string $fname Caller function name
+        *
+        * @return ResultWrapper
+        */
+       protected function nativeReplace( $table, $rows, $fname ) {
+               $table = $this->tableName( $table );
+
+               # Single row case
+               if ( !is_array( reset( $rows ) ) ) {
+                       $rows = [ $rows ];
+               }
+
+               $sql = "REPLACE INTO $table (" . implode( ',', array_keys( $rows[0] ) ) . ') VALUES ';
+               $first = true;
+
+               foreach ( $rows as $row ) {
+                       if ( $first ) {
+                               $first = false;
+                       } else {
+                               $sql .= ',';
+                       }
+
+                       $sql .= '(' . $this->makeList( $row ) . ')';
+               }
+
+               return $this->query( $sql, $fname );
+       }
+
+       public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
+               $fname = __METHOD__
+       ) {
+               if ( !count( $rows ) ) {
+                       return true; // nothing to do
+               }
+
+               if ( !is_array( reset( $rows ) ) ) {
+                       $rows = [ $rows ];
+               }
+
+               if ( count( $uniqueIndexes ) ) {
+                       $clauses = []; // list WHERE clauses that each identify a single row
+                       foreach ( $rows as $row ) {
+                               foreach ( $uniqueIndexes as $index ) {
+                                       $index = is_array( $index ) ? $index : [ $index ]; // columns
+                                       $rowKey = []; // unique key to this row
+                                       foreach ( $index as $column ) {
+                                               $rowKey[$column] = $row[$column];
+                                       }
+                                       $clauses[] = $this->makeList( $rowKey, LIST_AND );
+                               }
+                       }
+                       $where = [ $this->makeList( $clauses, LIST_OR ) ];
+               } else {
+                       $where = false;
+               }
+
+               $useTrx = !$this->mTrxLevel;
+               if ( $useTrx ) {
+                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
+               }
+               try {
+                       # Update any existing conflicting row(s)
+                       if ( $where !== false ) {
+                               $ok = $this->update( $table, $set, $where, $fname );
+                       } else {
+                               $ok = true;
+                       }
+                       # Now insert any non-conflicting row(s)
+                       $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
+               } catch ( Exception $e ) {
+                       if ( $useTrx ) {
+                               $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       }
+                       throw $e;
+               }
+               if ( $useTrx ) {
+                       $this->commit( $fname, self::FLUSHING_INTERNAL );
+               }
+
+               return $ok;
+       }
+
+       public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
+               $fname = __METHOD__
+       ) {
+               if ( !$conds ) {
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with empty $conds' );
+               }
+
+               $delTable = $this->tableName( $delTable );
+               $joinTable = $this->tableName( $joinTable );
+               $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
+               if ( $conds != '*' ) {
+                       $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
+               }
+               $sql .= ')';
+
+               $this->query( $sql, $fname );
+       }
+
+       /**
+        * Returns the size of a text field, or -1 for "unlimited"
+        *
+        * @param string $table
+        * @param string $field
+        * @return int
+        */
+       public function textFieldSize( $table, $field ) {
+               $table = $this->tableName( $table );
+               $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";";
+               $res = $this->query( $sql, __METHOD__ );
+               $row = $this->fetchObject( $res );
+
+               $m = [];
+
+               if ( preg_match( '/\((.*)\)/', $row->Type, $m ) ) {
+                       $size = $m[1];
+               } else {
+                       $size = -1;
+               }
+
+               return $size;
+       }
+
+       public function delete( $table, $conds, $fname = __METHOD__ ) {
+               if ( !$conds ) {
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with no conditions' );
+               }
+
+               $table = $this->tableName( $table );
+               $sql = "DELETE FROM $table";
+
+               if ( $conds != '*' ) {
+                       if ( is_array( $conds ) ) {
+                               $conds = $this->makeList( $conds, LIST_AND );
+                       }
+                       $sql .= ' WHERE ' . $conds;
+               }
+
+               return $this->query( $sql, $fname );
+       }
+
+       public function insertSelect(
+               $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__, $insertOptions = [], $selectOptions = []
+       ) {
+               if ( $this->cliMode ) {
+                       // For massive migrations with downtime, we don't want to select everything
+                       // into memory and OOM, so do all this native on the server side if possible.
+                       return $this->nativeInsertSelect(
+                               $destTable,
+                               $srcTable,
+                               $varMap,
+                               $conds,
+                               $fname,
+                               $insertOptions,
+                               $selectOptions
+                       );
+               }
+
+               // For web requests, do a locking SELECT and then INSERT. This puts the SELECT burden
+               // on only the master (without needing row-based-replication). It also makes it easy to
+               // know how big the INSERT is going to be.
+               $fields = [];
+               foreach ( $varMap as $dstColumn => $sourceColumnOrSql ) {
+                       $fields[] = $this->fieldNameWithAlias( $sourceColumnOrSql, $dstColumn );
+               }
+               $selectOptions[] = 'FOR UPDATE';
+               $res = $this->select( $srcTable, implode( ',', $fields ), $conds, $fname, $selectOptions );
+               if ( !$res ) {
+                       return false;
+               }
+
+               $rows = [];
+               foreach ( $res as $row ) {
+                       $rows[] = (array)$row;
+               }
+
+               return $this->insert( $destTable, $rows, $fname, $insertOptions );
+       }
+
+       public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__,
+               $insertOptions = [], $selectOptions = []
+       ) {
+               $destTable = $this->tableName( $destTable );
+
+               if ( !is_array( $insertOptions ) ) {
+                       $insertOptions = [ $insertOptions ];
+               }
+
+               $insertOptions = $this->makeInsertOptions( $insertOptions );
+
+               if ( !is_array( $selectOptions ) ) {
+                       $selectOptions = [ $selectOptions ];
+               }
+
+               list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) = $this->makeSelectOptions(
+                       $selectOptions );
+
+               if ( is_array( $srcTable ) ) {
+                       $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) );
+               } else {
+                       $srcTable = $this->tableName( $srcTable );
+               }
+
+               $sql = "INSERT $insertOptions INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
+                       " SELECT $startOpts " . implode( ',', $varMap ) .
+                       " FROM $srcTable $useIndex $ignoreIndex ";
+
+               if ( $conds != '*' ) {
+                       if ( is_array( $conds ) ) {
+                               $conds = $this->makeList( $conds, LIST_AND );
+                       }
+                       $sql .= " WHERE $conds";
+               }
+
+               $sql .= " $tailOpts";
+
+               return $this->query( $sql, $fname );
+       }
+
+       /**
+        * Construct a LIMIT query with optional offset. This is used for query
+        * pages. The SQL should be adjusted so that only the first $limit rows
+        * are returned. If $offset is provided as well, then the first $offset
+        * rows should be discarded, and the next $limit rows should be returned.
+        * If the result of the query is not ordered, then the rows to be returned
+        * are theoretically arbitrary.
+        *
+        * $sql is expected to be a SELECT, if that makes a difference.
+        *
+        * The version provided by default works in MySQL and SQLite. It will very
+        * likely need to be overridden for most other DBMSes.
+        *
+        * @param string $sql SQL query we will append the limit too
+        * @param int $limit The SQL limit
+        * @param int|bool $offset The SQL offset (default false)
+        * @throws DBUnexpectedError
+        * @return string
+        */
+       public function limitResult( $sql, $limit, $offset = false ) {
+               if ( !is_numeric( $limit ) ) {
+                       throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
+               }
+
+               return "$sql LIMIT "
+               . ( ( is_numeric( $offset ) && $offset != 0 ) ? "{$offset}," : "" )
+               . "{$limit} ";
+       }
+
+       public function unionSupportsOrderAndLimit() {
+               return true; // True for almost every DB supported
+       }
+
+       public function unionQueries( $sqls, $all ) {
+               $glue = $all ? ') UNION ALL (' : ') UNION (';
+
+               return '(' . implode( $glue, $sqls ) . ')';
+       }
+
+       public function conditional( $cond, $trueVal, $falseVal ) {
+               if ( is_array( $cond ) ) {
+                       $cond = $this->makeList( $cond, LIST_AND );
+               }
+
+               return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
+       }
+
+       public function strreplace( $orig, $old, $new ) {
+               return "REPLACE({$orig}, {$old}, {$new})";
+       }
+
+       public function getServerUptime() {
+               return 0;
+       }
+
+       public function wasDeadlock() {
+               return false;
+       }
+
+       public function wasLockTimeout() {
+               return false;
+       }
+
+       public function wasErrorReissuable() {
+               return false;
+       }
+
+       public function wasReadOnlyError() {
+               return false;
+       }
+
+       /**
+        * Determines if the given query error was a connection drop
+        * STUB
+        *
+        * @param integer|string $errno
+        * @return bool
+        */
+       public function wasConnectionError( $errno ) {
+               return false;
+       }
+
+       /**
+        * Perform a deadlock-prone transaction.
+        *
+        * This function invokes a callback function to perform a set of write
+        * queries. If a deadlock occurs during the processing, the transaction
+        * will be rolled back and the callback function will be called again.
+        *
+        * Avoid using this method outside of Job or Maintenance classes.
+        *
+        * Usage:
+        *   $dbw->deadlockLoop( callback, ... );
+        *
+        * Extra arguments are passed through to the specified callback function.
+        * This method requires that no transactions are already active to avoid
+        * causing premature commits or exceptions.
+        *
+        * Returns whatever the callback function returned on its successful,
+        * iteration, or false on error, for example if the retry limit was
+        * reached.
+        *
+        * @return mixed
+        * @throws DBUnexpectedError
+        * @throws Exception
+        */
+       public function deadlockLoop() {
+               $args = func_get_args();
+               $function = array_shift( $args );
+               $tries = self::DEADLOCK_TRIES;
+
+               $this->begin( __METHOD__ );
+
+               $retVal = null;
+               /** @var Exception $e */
+               $e = null;
+               do {
+                       try {
+                               $retVal = call_user_func_array( $function, $args );
+                               break;
+                       } catch ( DBQueryError $e ) {
+                               if ( $this->wasDeadlock() ) {
+                                       // Retry after a randomized delay
+                                       usleep( mt_rand( self::DEADLOCK_DELAY_MIN, self::DEADLOCK_DELAY_MAX ) );
+                               } else {
+                                       // Throw the error back up
+                                       throw $e;
+                               }
+                       }
+               } while ( --$tries > 0 );
+
+               if ( $tries <= 0 ) {
+                       // Too many deadlocks; give up
+                       $this->rollback( __METHOD__ );
+                       throw $e;
+               } else {
+                       $this->commit( __METHOD__ );
+
+                       return $retVal;
+               }
+       }
+
+       public function masterPosWait( DBMasterPos $pos, $timeout ) {
+               # Real waits are implemented in the subclass.
+               return 0;
+       }
+
+       public function getSlavePos() {
+               # Stub
+               return false;
+       }
+
+       public function getMasterPos() {
+               # Stub
+               return false;
+       }
+
+       public function serverIsReadOnly() {
+               return false;
+       }
+
+       final public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) {
+               if ( !$this->mTrxLevel ) {
+                       throw new DBUnexpectedError( $this, "No transaction is active." );
+               }
+               $this->mTrxEndCallbacks[] = [ $callback, $fname ];
+       }
+
+       final public function onTransactionIdle( callable $callback, $fname = __METHOD__ ) {
+               $this->mTrxIdleCallbacks[] = [ $callback, $fname ];
+               if ( !$this->mTrxLevel ) {
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
+               }
+       }
+
+       final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
+               if ( $this->mTrxLevel ) {
+                       $this->mTrxPreCommitCallbacks[] = [ $callback, $fname ];
+               } else {
+                       // If no transaction is active, then make one for this callback
+                       $this->startAtomic( __METHOD__ );
+                       try {
+                               call_user_func( $callback );
+                               $this->endAtomic( __METHOD__ );
+                       } catch ( Exception $e ) {
+                               $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
+                               throw $e;
+                       }
+               }
+       }
+
+       final public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->mTrxRecurringCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->mTrxRecurringCallbacks[$name] );
+               }
+       }
+
+       /**
+        * Whether to disable running of post-COMMIT/ROLLBACK callbacks
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param bool $suppress
+        * @since 1.28
+        */
+       final public function setTrxEndCallbackSuppression( $suppress ) {
+               $this->mTrxEndCallbacksSuppressed = $suppress;
+       }
+
+       /**
+        * Actually run and consume any "on transaction idle/resolution" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
+        * @since 1.20
+        * @throws Exception
+        */
+       public function runOnTransactionIdleCallbacks( $trigger ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
+                       return;
+               }
+
+               $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
+               /** @var Exception $e */
+               $e = null; // first exception
+               do { // callbacks may add callbacks :)
+                       $callbacks = array_merge(
+                               $this->mTrxIdleCallbacks,
+                               $this->mTrxEndCallbacks // include "transaction resolution" callbacks
+                       );
+                       $this->mTrxIdleCallbacks = []; // consumed (and recursion guard)
+                       $this->mTrxEndCallbacks = []; // consumed (recursion guard)
+                       foreach ( $callbacks as $callback ) {
+                               try {
+                                       list( $phpCallback ) = $callback;
+                                       $this->clearFlag( DBO_TRX ); // make each query its own transaction
+                                       call_user_func_array( $phpCallback, [ $trigger ] );
+                                       if ( $autoTrx ) {
+                                               $this->setFlag( DBO_TRX ); // restore automatic begin()
+                                       } else {
+                                               $this->clearFlag( DBO_TRX ); // restore auto-commit
+                                       }
+                               } catch ( Exception $ex ) {
+                                       call_user_func( $this->errorLogger, $ex );
+                                       $e = $e ?: $ex;
+                                       // Some callbacks may use startAtomic/endAtomic, so make sure
+                                       // their transactions are ended so other callbacks don't fail
+                                       if ( $this->trxLevel() ) {
+                                               $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
+                                       }
+                               }
+                       }
+               } while ( count( $this->mTrxIdleCallbacks ) );
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any first exception
+               }
+       }
+
+       /**
+        * Actually run and consume any "on transaction pre-commit" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @since 1.22
+        * @throws Exception
+        */
+       public function runOnTransactionPreCommitCallbacks() {
+               $e = null; // first exception
+               do { // callbacks may add callbacks :)
+                       $callbacks = $this->mTrxPreCommitCallbacks;
+                       $this->mTrxPreCommitCallbacks = []; // consumed (and recursion guard)
+                       foreach ( $callbacks as $callback ) {
+                               try {
+                                       list( $phpCallback ) = $callback;
+                                       call_user_func( $phpCallback );
+                               } catch ( Exception $ex ) {
+                                       call_user_func( $this->errorLogger, $ex );
+                                       $e = $e ?: $ex;
+                               }
+                       }
+               } while ( count( $this->mTrxPreCommitCallbacks ) );
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any first exception
+               }
+       }
+
+       /**
+        * Actually run any "transaction listener" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
+        * @throws Exception
+        * @since 1.20
+        */
+       public function runTransactionListenerCallbacks( $trigger ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
+                       return;
+               }
+
+               /** @var Exception $e */
+               $e = null; // first exception
+
+               foreach ( $this->mTrxRecurringCallbacks as $phpCallback ) {
+                       try {
+                               $phpCallback( $trigger, $this );
+                       } catch ( Exception $ex ) {
+                               call_user_func( $this->errorLogger, $ex );
+                               $e = $e ?: $ex;
+                       }
+               }
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any first exception
+               }
+       }
+
+       final public function startAtomic( $fname = __METHOD__ ) {
+               if ( !$this->mTrxLevel ) {
+                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
+                       // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
+                       // in all changes being in one transaction to keep requests transactional.
+                       if ( !$this->getFlag( DBO_TRX ) ) {
+                               $this->mTrxAutomaticAtomic = true;
+                       }
+               }
+
+               $this->mTrxAtomicLevels[] = $fname;
+       }
+
+       final public function endAtomic( $fname = __METHOD__ ) {
+               if ( !$this->mTrxLevel ) {
+                       throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
+               }
+               if ( !$this->mTrxAtomicLevels ||
+                       array_pop( $this->mTrxAtomicLevels ) !== $fname
+               ) {
+                       throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
+               }
+
+               if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
+                       $this->commit( $fname, self::FLUSHING_INTERNAL );
+               }
+       }
+
+       final public function doAtomicSection( $fname, callable $callback ) {
+               $this->startAtomic( $fname );
+               try {
+                       $res = call_user_func_array( $callback, [ $this, $fname ] );
+               } catch ( Exception $e ) {
+                       $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       throw $e;
+               }
+               $this->endAtomic( $fname );
+
+               return $res;
+       }
+
+       final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
+               // Protect against mismatched atomic section, transaction nesting, and snapshot loss
+               if ( $this->mTrxLevel ) {
+                       if ( $this->mTrxAtomicLevels ) {
+                               $levels = implode( ', ', $this->mTrxAtomicLevels );
+                               $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
+                               throw new DBUnexpectedError( $this, $msg );
+                       } elseif ( !$this->mTrxAutomatic ) {
+                               $msg = "$fname: Explicit transaction already active (from {$this->mTrxFname}).";
+                               throw new DBUnexpectedError( $this, $msg );
+                       } else {
+                               // @TODO: make this an exception at some point
+                               $msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
+                               $this->queryLogger->error( $msg );
+                               return; // join the main transaction set
+                       }
+               } elseif ( $this->getFlag( DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
+                       // @TODO: make this an exception at some point
+                       $msg = "$fname: Implicit transaction expected (DBO_TRX set).";
+                       $this->queryLogger->error( $msg );
+                       return; // let any writes be in the main transaction
+               }
+
+               // Avoid fatals if close() was called
+               $this->assertOpen();
+
+               $this->doBegin( $fname );
+               $this->mTrxTimestamp = microtime( true );
+               $this->mTrxFname = $fname;
+               $this->mTrxDoneWrites = false;
+               $this->mTrxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
+               $this->mTrxAutomaticAtomic = false;
+               $this->mTrxAtomicLevels = [];
+               $this->mTrxShortId = sprintf( '%06x', mt_rand( 0, 0xffffff ) );
+               $this->mTrxWriteDuration = 0.0;
+               $this->mTrxWriteQueryCount = 0;
+               $this->mTrxWriteAdjDuration = 0.0;
+               $this->mTrxWriteAdjQueryCount = 0;
+               $this->mTrxWriteCallers = [];
+               // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
+               // Get an estimate of the replica DB lag before then, treating estimate staleness
+               // as lag itself just to be safe
+               $status = $this->getApproximateLagStatus();
+               $this->mTrxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
+       }
+
+       /**
+        * Issues the BEGIN command to the database server.
+        *
+        * @see DatabaseBase::begin()
+        * @param string $fname
+        */
+       protected function doBegin( $fname ) {
+               $this->query( 'BEGIN', $fname );
+               $this->mTrxLevel = 1;
+       }
+
+       final public function commit( $fname = __METHOD__, $flush = '' ) {
+               if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
+                       // There are still atomic sections open. This cannot be ignored
+                       $levels = implode( ', ', $this->mTrxAtomicLevels );
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Got COMMIT while atomic sections $levels are still open."
+                       );
+               }
+
+               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
+                       if ( !$this->mTrxLevel ) {
+                               return; // nothing to do
+                       } elseif ( !$this->mTrxAutomatic ) {
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       "$fname: Flushing an explicit transaction, getting out of sync."
+                               );
+                       }
+               } else {
+                       if ( !$this->mTrxLevel ) {
+                               $this->queryLogger->error( "$fname: No transaction to commit, something got out of sync." );
+                               return; // nothing to do
+                       } elseif ( $this->mTrxAutomatic ) {
+                               // @TODO: make this an exception at some point
+                               $msg = "$fname: Explicit commit of implicit transaction.";
+                               $this->queryLogger->error( $msg );
+                               return; // wait for the main transaction set commit round
+                       }
+               }
+
+               // Avoid fatals if close() was called
+               $this->assertOpen();
+
+               $this->runOnTransactionPreCommitCallbacks();
+               $writeTime = $this->pendingWriteQueryDuration( self::ESTIMATE_DB_APPLY );
+               $this->doCommit( $fname );
+               if ( $this->mTrxDoneWrites ) {
+                       $this->mDoneWrites = microtime( true );
+                       $this->trxProfiler->transactionWritingOut(
+                               $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
+               }
+
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_COMMIT );
+       }
+
+       /**
+        * Issues the COMMIT command to the database server.
+        *
+        * @see DatabaseBase::commit()
+        * @param string $fname
+        */
+       protected function doCommit( $fname ) {
+               if ( $this->mTrxLevel ) {
+                       $this->query( 'COMMIT', $fname );
+                       $this->mTrxLevel = 0;
+               }
+       }
+
+       final public function rollback( $fname = __METHOD__, $flush = '' ) {
+               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
+                       if ( !$this->mTrxLevel ) {
+                               return; // nothing to do
+                       }
+               } else {
+                       if ( !$this->mTrxLevel ) {
+                               $this->queryLogger->error(
+                                       "$fname: No transaction to rollback, something got out of sync." );
+                               return; // nothing to do
+                       } elseif ( $this->getFlag( DBO_TRX ) ) {
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       "$fname: Expected mass rollback of all peer databases (DBO_TRX set)."
+                               );
+                       }
+               }
+
+               // Avoid fatals if close() was called
+               $this->assertOpen();
+
+               $this->doRollback( $fname );
+               $this->mTrxAtomicLevels = [];
+               if ( $this->mTrxDoneWrites ) {
+                       $this->trxProfiler->transactionWritingOut(
+                               $this->mServer, $this->mDBname, $this->mTrxShortId );
+               }
+
+               $this->mTrxIdleCallbacks = []; // clear
+               $this->mTrxPreCommitCallbacks = []; // clear
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
+       }
+
+       /**
+        * Issues the ROLLBACK command to the database server.
+        *
+        * @see DatabaseBase::rollback()
+        * @param string $fname
+        */
+       protected function doRollback( $fname ) {
+               if ( $this->mTrxLevel ) {
+                       # Disconnects cause rollback anyway, so ignore those errors
+                       $ignoreErrors = true;
+                       $this->query( 'ROLLBACK', $fname, $ignoreErrors );
+                       $this->mTrxLevel = 0;
+               }
+       }
+
+       public function flushSnapshot( $fname = __METHOD__ ) {
+               if ( $this->writesOrCallbacksPending() || $this->explicitTrxActive() ) {
+                       // This only flushes transactions to clear snapshots, not to write data
+                       $fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Cannot COMMIT to clear snapshot because writes are pending ($fnames)."
+                       );
+               }
+
+               $this->commit( $fname, self::FLUSHING_INTERNAL );
+       }
+
+       public function explicitTrxActive() {
+               return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
+       }
+
+       /**
+        * Creates a new table with structure copied from existing table
+        * Note that unlike most database abstraction functions, this function does not
+        * automatically append database prefix, because it works at a lower
+        * abstraction level.
+        * The table names passed to this function shall not be quoted (this
+        * function calls addIdentifierQuotes when needed).
+        *
+        * @param string $oldName Name of table whose structure should be copied
+        * @param string $newName Name of table to be created
+        * @param bool $temporary Whether the new table should be temporary
+        * @param string $fname Calling function name
+        * @throws RuntimeException
+        * @return bool True if operation was successful
+        */
+       public function duplicateTableStructure( $oldName, $newName, $temporary = false,
+               $fname = __METHOD__
+       ) {
+               throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' );
+       }
+
+       function listTables( $prefix = null, $fname = __METHOD__ ) {
+               throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' );
+       }
+
+       /**
+        * Reset the views process cache set by listViews()
+        * @since 1.22
+        */
+       final public function clearViewsCache() {
+               $this->allViews = null;
+       }
+
+       /**
+        * Lists all the VIEWs in the database
+        *
+        * For caching purposes the list of all views should be stored in
+        * $this->allViews. The process cache can be cleared with clearViewsCache()
+        *
+        * @param string $prefix Only show VIEWs with this prefix, eg. unit_test_
+        * @param string $fname Name of calling function
+        * @throws RuntimeException
+        * @return array
+        * @since 1.22
+        */
+       public function listViews( $prefix = null, $fname = __METHOD__ ) {
+               throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' );
+       }
+
+       /**
+        * Differentiates between a TABLE and a VIEW
+        *
+        * @param string $name Name of the database-structure to test.
+        * @throws RuntimeException
+        * @return bool
+        * @since 1.22
+        */
+       public function isView( $name ) {
+               throw new RuntimeException( __METHOD__ . ' is not implemented in descendant class' );
+       }
+
+       public function timestamp( $ts = 0 ) {
+               $t = new ConvertableTimestamp( $ts );
+               // Let errors bubble up to avoid putting garbage in the DB
+               return $t->getTimestamp( TS_MW );
+       }
+
+       public function timestampOrNull( $ts = null ) {
+               if ( is_null( $ts ) ) {
+                       return null;
+               } else {
+                       return $this->timestamp( $ts );
+               }
+       }
+
+       /**
+        * Take the result from a query, and wrap it in a ResultWrapper if
+        * necessary. Boolean values are passed through as is, to indicate success
+        * of write queries or failure.
+        *
+        * Once upon a time, DatabaseBase::query() returned a bare MySQL result
+        * resource, and it was necessary to call this function to convert it to
+        * a wrapper. Nowadays, raw database objects are never exposed to external
+        * callers, so this is unnecessary in external code.
+        *
+        * @param bool|ResultWrapper|resource|object $result
+        * @return bool|ResultWrapper
+        */
+       protected function resultObject( $result ) {
+               if ( !$result ) {
+                       return false;
+               } elseif ( $result instanceof ResultWrapper ) {
+                       return $result;
+               } elseif ( $result === true ) {
+                       // Successful write query
+                       return $result;
+               } else {
+                       return new ResultWrapper( $this, $result );
+               }
+       }
+
+       public function ping( &$rtt = null ) {
+               // Avoid hitting the server if it was hit recently
+               if ( $this->isOpen() && ( microtime( true ) - $this->lastPing ) < self::PING_TTL ) {
+                       if ( !func_num_args() || $this->mRTTEstimate > 0 ) {
+                               $rtt = $this->mRTTEstimate;
+                               return true; // don't care about $rtt
+                       }
+               }
+
+               // This will reconnect if possible or return false if not
+               $this->clearFlag( DBO_TRX, self::REMEMBER_PRIOR );
+               $ok = ( $this->query( self::PING_QUERY, __METHOD__, true ) !== false );
+               $this->restoreFlags( self::RESTORE_PRIOR );
+
+               if ( $ok ) {
+                       $rtt = $this->mRTTEstimate;
+               }
+
+               return $ok;
+       }
+
+       /**
+        * @return bool
+        */
+       protected function reconnect() {
+               $this->closeConnection();
+               $this->mOpened = false;
+               $this->mConn = false;
+               try {
+                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
+                       $this->lastPing = microtime( true );
+                       $ok = true;
+               } catch ( DBConnectionError $e ) {
+                       $ok = false;
+               }
+
+               return $ok;
+       }
+
+       public function getSessionLagStatus() {
+               return $this->getTransactionLagStatus() ?: $this->getApproximateLagStatus();
+       }
+
+       /**
+        * Get the replica DB lag when the current transaction started
+        *
+        * This is useful when transactions might use snapshot isolation
+        * (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
+        * is this lag plus transaction duration. If they don't, it is still
+        * safe to be pessimistic. This returns null if there is no transaction.
+        *
+        * @return array|null ('lag': seconds or false on error, 'since': UNIX timestamp of BEGIN)
+        * @since 1.27
+        */
+       public function getTransactionLagStatus() {
+               return $this->mTrxLevel
+                       ? [ 'lag' => $this->mTrxReplicaLag, 'since' => $this->trxTimestamp() ]
+                       : null;
+       }
+
+       /**
+        * Get a replica DB lag estimate for this server
+        *
+        * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of estimate)
+        * @since 1.27
+        */
+       public function getApproximateLagStatus() {
+               return [
+                       'lag'   => $this->getLBInfo( 'replica' ) ? $this->getLag() : 0,
+                       'since' => microtime( true )
+               ];
+       }
+
+       /**
+        * Merge the result of getSessionLagStatus() for several DBs
+        * using the most pessimistic values to estimate the lag of
+        * any data derived from them in combination
+        *
+        * This is information is useful for caching modules
+        *
+        * @see WANObjectCache::set()
+        * @see WANObjectCache::getWithSetCallback()
+        *
+        * @param IDatabase $db1
+        * @param IDatabase ...
+        * @return array Map of values:
+        *   - lag: highest lag of any of the DBs or false on error (e.g. replication stopped)
+        *   - since: oldest UNIX timestamp of any of the DB lag estimates
+        *   - pending: whether any of the DBs have uncommitted changes
+        * @since 1.27
+        */
+       public static function getCacheSetOptions( IDatabase $db1 ) {
+               $res = [ 'lag' => 0, 'since' => INF, 'pending' => false ];
+               foreach ( func_get_args() as $db ) {
+                       /** @var IDatabase $db */
+                       $status = $db->getSessionLagStatus();
+                       if ( $status['lag'] === false ) {
+                               $res['lag'] = false;
+                       } elseif ( $res['lag'] !== false ) {
+                               $res['lag'] = max( $res['lag'], $status['lag'] );
+                       }
+                       $res['since'] = min( $res['since'], $status['since'] );
+                       $res['pending'] = $res['pending'] ?: $db->writesPending();
+               }
+
+               return $res;
+       }
+
+       public function getLag() {
+               return 0;
+       }
+
+       function maxListLen() {
+               return 0;
+       }
+
+       public function encodeBlob( $b ) {
+               return $b;
+       }
+
+       public function decodeBlob( $b ) {
+               if ( $b instanceof Blob ) {
+                       $b = $b->fetch();
+               }
+               return $b;
+       }
+
+       public function setSessionOptions( array $options ) {
+       }
+
+       /**
+        * Read and execute SQL commands from a file.
+        *
+        * Returns true on success, error string or exception on failure (depending
+        * on object's error ignore settings).
+        *
+        * @param string $filename File name to open
+        * @param bool|callable $lineCallback Optional function called before reading each line
+        * @param bool|callable $resultCallback Optional function called for each MySQL result
+        * @param bool|string $fname Calling function name or false if name should be
+        *   generated dynamically using $filename
+        * @param bool|callable $inputCallback Optional function called for each
+        *   complete line sent
+        * @return bool|string
+        * @throws Exception
+        */
+       public function sourceFile(
+               $filename, $lineCallback = false, $resultCallback = false, $fname = false, $inputCallback = false
+       ) {
+               MediaWiki\suppressWarnings();
+               $fp = fopen( $filename, 'r' );
+               MediaWiki\restoreWarnings();
+
+               if ( false === $fp ) {
+                       throw new RuntimeException( "Could not open \"{$filename}\".\n" );
+               }
+
+               if ( !$fname ) {
+                       $fname = __METHOD__ . "( $filename )";
+               }
+
+               try {
+                       $error = $this->sourceStream( $fp, $lineCallback, $resultCallback, $fname, $inputCallback );
+               } catch ( Exception $e ) {
+                       fclose( $fp );
+                       throw $e;
+               }
+
+               fclose( $fp );
+
+               return $error;
+       }
+
+       public function setSchemaVars( $vars ) {
+               $this->mSchemaVars = $vars;
+       }
+
+       /**
+        * Read and execute commands from an open file handle.
+        *
+        * Returns true on success, error string or exception on failure (depending
+        * on object's error ignore settings).
+        *
+        * @param resource $fp File handle
+        * @param bool|callable $lineCallback Optional function called before reading each query
+        * @param bool|callable $resultCallback Optional function called for each MySQL result
+        * @param string $fname Calling function name
+        * @param bool|callable $inputCallback Optional function called for each complete query sent
+        * @return bool|string
+        */
+       public function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
+               $fname = __METHOD__, $inputCallback = false
+       ) {
+               $cmd = '';
+
+               while ( !feof( $fp ) ) {
+                       if ( $lineCallback ) {
+                               call_user_func( $lineCallback );
+                       }
+
+                       $line = trim( fgets( $fp ) );
+
+                       if ( $line == '' ) {
+                               continue;
+                       }
+
+                       if ( '-' == $line[0] && '-' == $line[1] ) {
+                               continue;
+                       }
+
+                       if ( $cmd != '' ) {
+                               $cmd .= ' ';
+                       }
+
+                       $done = $this->streamStatementEnd( $cmd, $line );
+
+                       $cmd .= "$line\n";
+
+                       if ( $done || feof( $fp ) ) {
+                               $cmd = $this->replaceVars( $cmd );
+
+                               if ( ( $inputCallback && call_user_func( $inputCallback, $cmd ) ) || !$inputCallback ) {
+                                       $res = $this->query( $cmd, $fname );
+
+                                       if ( $resultCallback ) {
+                                               call_user_func( $resultCallback, $res, $this );
+                                       }
+
+                                       if ( false === $res ) {
+                                               $err = $this->lastError();
+
+                                               return "Query \"{$cmd}\" failed with error code \"$err\".\n";
+                                       }
+                               }
+                               $cmd = '';
+                       }
+               }
+
+               return true;
+       }
+
+       /**
+        * Called by sourceStream() to check if we've reached a statement end
+        *
+        * @param string $sql SQL assembled so far
+        * @param string $newLine New line about to be added to $sql
+        * @return bool Whether $newLine contains end of the statement
+        */
+       public function streamStatementEnd( &$sql, &$newLine ) {
+               if ( $this->delimiter ) {
+                       $prev = $newLine;
+                       $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
+                       if ( $newLine != $prev ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Database independent variable replacement. Replaces a set of variables
+        * in an SQL statement with their contents as given by $this->getSchemaVars().
+        *
+        * Supports '{$var}' `{$var}` and / *$var* / (without the spaces) style variables.
+        *
+        * - '{$var}' should be used for text and is passed through the database's
+        *   addQuotes method.
+        * - `{$var}` should be used for identifiers (e.g. table and database names).
+        *   It is passed through the database's addIdentifierQuotes method which
+        *   can be overridden if the database uses something other than backticks.
+        * - / *_* / or / *$wgDBprefix* / passes the name that follows through the
+        *   database's tableName method.
+        * - / *i* / passes the name that follows through the database's indexName method.
+        * - In all other cases, / *$var* / is left unencoded. Except for table options,
+        *   its use should be avoided. In 1.24 and older, string encoding was applied.
+        *
+        * @param string $ins SQL statement to replace variables in
+        * @return string The new SQL statement with variables replaced
+        */
+       protected function replaceVars( $ins ) {
+               $vars = $this->getSchemaVars();
+               return preg_replace_callback(
+                       '!
+                               /\* (\$wgDBprefix|[_i]) \*/ (\w*) | # 1-2. tableName, indexName
+                               \'\{\$ (\w+) }\'                  | # 3. addQuotes
+                               `\{\$ (\w+) }`                    | # 4. addIdentifierQuotes
+                               /\*\$ (\w+) \*/                     # 5. leave unencoded
+                       !x',
+                       function ( $m ) use ( $vars ) {
+                               // Note: Because of <https://bugs.php.net/bug.php?id=51881>,
+                               // check for both nonexistent keys *and* the empty string.
+                               if ( isset( $m[1] ) && $m[1] !== '' ) {
+                                       if ( $m[1] === 'i' ) {
+                                               return $this->indexName( $m[2] );
+                                       } else {
+                                               return $this->tableName( $m[2] );
+                                       }
+                               } elseif ( isset( $m[3] ) && $m[3] !== '' && array_key_exists( $m[3], $vars ) ) {
+                                       return $this->addQuotes( $vars[$m[3]] );
+                               } elseif ( isset( $m[4] ) && $m[4] !== '' && array_key_exists( $m[4], $vars ) ) {
+                                       return $this->addIdentifierQuotes( $vars[$m[4]] );
+                               } elseif ( isset( $m[5] ) && $m[5] !== '' && array_key_exists( $m[5], $vars ) ) {
+                                       return $vars[$m[5]];
+                               } else {
+                                       return $m[0];
+                               }
+                       },
+                       $ins
+               );
+       }
+
+       /**
+        * Get schema variables. If none have been set via setSchemaVars(), then
+        * use some defaults from the current object.
+        *
+        * @return array
+        */
+       protected function getSchemaVars() {
+               if ( $this->mSchemaVars ) {
+                       return $this->mSchemaVars;
+               } else {
+                       return $this->getDefaultSchemaVars();
+               }
+       }
+
+       /**
+        * Get schema variables to use if none have been set via setSchemaVars().
+        *
+        * Override this in derived classes to provide variables for tables.sql
+        * and SQL patch files.
+        *
+        * @return array
+        */
+       protected function getDefaultSchemaVars() {
+               return [];
+       }
+
+       public function lockIsFree( $lockName, $method ) {
+               return true;
+       }
+
+       public function lock( $lockName, $method, $timeout = 5 ) {
+               $this->mNamedLocksHeld[$lockName] = 1;
+
+               return true;
+       }
+
+       public function unlock( $lockName, $method ) {
+               unset( $this->mNamedLocksHeld[$lockName] );
+
+               return true;
+       }
+
+       public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
+               if ( $this->writesOrCallbacksPending() ) {
+                       // This only flushes transactions to clear snapshots, not to write data
+                       $fnames = implode( ', ', $this->pendingWriteAndCallbackCallers() );
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Cannot COMMIT to clear snapshot because writes are pending ($fnames)."
+                       );
+               }
+
+               if ( !$this->lock( $lockKey, $fname, $timeout ) ) {
+                       return null;
+               }
+
+               $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
+                       if ( $this->trxLevel() ) {
+                               // There is a good chance an exception was thrown, causing any early return
+                               // from the caller. Let any error handler get a chance to issue rollback().
+                               // If there isn't one, let the error bubble up and trigger server-side rollback.
+                               $this->onTransactionResolution(
+                                       function () use ( $lockKey, $fname ) {
+                                               $this->unlock( $lockKey, $fname );
+                                       },
+                                       $fname
+                               );
+                       } else {
+                               $this->unlock( $lockKey, $fname );
+                       }
+               } );
+
+               $this->commit( $fname, self::FLUSHING_INTERNAL );
+
+               return $unlocker;
+       }
+
+       public function namedLocksEnqueue() {
+               return false;
+       }
+
+       /**
+        * Lock specific tables
+        *
+        * @param array $read Array of tables to lock for read access
+        * @param array $write Array of tables to lock for write access
+        * @param string $method Name of caller
+        * @param bool $lowPriority Whether to indicate writes to be LOW PRIORITY
+        * @return bool
+        */
+       public function lockTables( $read, $write, $method, $lowPriority = true ) {
+               return true;
+       }
+
+       /**
+        * Unlock specific tables
+        *
+        * @param string $method The caller
+        * @return bool
+        */
+       public function unlockTables( $method ) {
+               return true;
+       }
+
+       /**
+        * Delete a table
+        * @param string $tableName
+        * @param string $fName
+        * @return bool|ResultWrapper
+        * @since 1.18
+        */
+       public function dropTable( $tableName, $fName = __METHOD__ ) {
+               if ( !$this->tableExists( $tableName, $fName ) ) {
+                       return false;
+               }
+               $sql = "DROP TABLE " . $this->tableName( $tableName ) . " CASCADE";
+
+               return $this->query( $sql, $fName );
+       }
+
+       public function getInfinity() {
+               return 'infinity';
+       }
+
+       public function encodeExpiry( $expiry ) {
+               return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
+                       ? $this->getInfinity()
+                       : $this->timestamp( $expiry );
+       }
+
+       public function decodeExpiry( $expiry, $format = TS_MW ) {
+               if ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() ) {
+                       return 'infinity';
+               }
+
+               try {
+                       $t = new ConvertableTimestamp( $expiry );
+
+                       return $t->getTimestamp( $format );
+               } catch ( TimestampException $e ) {
+                       return false;
+               }
+       }
+
+       public function setBigSelects( $value = true ) {
+               // no-op
+       }
+
+       public function isReadOnly() {
+               return ( $this->getReadOnlyReason() !== false );
+       }
+
+       /**
+        * @return string|bool Reason this DB is read-only or false if it is not
+        */
+       protected function getReadOnlyReason() {
+               $reason = $this->getLBInfo( 'readOnlyReason' );
+
+               return is_string( $reason ) ? $reason : false;
+       }
+
+       public function setTableAliases( array $aliases ) {
+               $this->tableAliases = $aliases;
+       }
+
+       /**
+        * @since 1.19
+        * @return string
+        */
+       public function __toString() {
+               return (string)$this->mConn;
+       }
+
+       /**
+        * Called by serialize. Throw an exception when DB connection is serialized.
+        * This causes problems on some database engines because the connection is
+        * not restored on unserialize.
+        */
+       public function __sleep() {
+               throw new RuntimeException( 'Database serialization may cause problems, since ' .
+                       'the connection is not restored on wakeup.' );
+       }
+
+       /**
+        * Run a few simple sanity checks
+        */
+       public function __destruct() {
+               if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
+                       trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
+               }
+
+               $danglingWriters = $this->pendingWriteAndCallbackCallers();
+               if ( $danglingWriters ) {
+                       $fnames = implode( ', ', $danglingWriters );
+                       trigger_error( "DB transaction writes or callbacks still pending ($fnames)." );
+               }
+       }
+}
diff --git a/includes/libs/rdbms/database/DatabaseBase.php b/includes/libs/rdbms/database/DatabaseBase.php
new file mode 100644 (file)
index 0000000..4f823b5
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+/**
+ * @defgroup Database Database
+ *
+ * This file deals with database interface functions
+ * and query specifics/optimisations.
+ *
+ * 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 Database
+ */
+
+/**
+ * Database abstraction object
+ * @ingroup Database
+ */
+abstract class DatabaseBase extends Database {
+       /**
+        * Boolean, controls output of large amounts of debug information.
+        * @param bool|null $debug
+        *   - true to enable debugging
+        *   - false to disable debugging
+        *   - omitted or null to do nothing
+        *
+        * @return bool Previous value of the flag
+        * @deprecated since 1.28; use setFlag()
+        */
+       public function debug( $debug = null ) {
+               $res = $this->getFlag( DBO_DEBUG );
+               if ( $debug !== null ) {
+                       $debug ? $this->setFlag( DBO_DEBUG ) : $this->clearFlag( DBO_DEBUG );
+               }
+
+               return $res;
+       }
+
+       /**
+        * @return string Command delimiter used by this database engine
+        */
+       public function getDelimiter() {
+               return $this->delimiter;
+       }
+
+       /**
+        * Returns true if this database supports (and uses) cascading deletes
+        *
+        * @return bool
+        */
+       public function cascadingDeletes() {
+               return false;
+       }
+       /**
+        * Returns true if this database supports (and uses) triggers (e.g. on the page table)
+        *
+        * @return bool
+        */
+       public function cleanupTriggers() {
+               return false;
+       }
+       /**
+        * Returns true if this database is strict about what can be put into an IP field.
+        * Specifically, it uses a NULL value instead of an empty string.
+        *
+        * @return bool
+        */
+       public function strictIPs() {
+               return false;
+       }
+
+       /**
+        * Returns true if this database can do a native search on IP columns
+        * e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
+        *
+        * @return bool
+        */
+       public function searchableIPs() {
+               return false;
+       }
+
+       /**
+        * Returns true if this database uses timestamps rather than integers
+        *
+        * @return bool
+        */
+       public function realTimestamps() {
+               return false;
+       }
+
+       /**
+        * Returns true if this database can use functional indexes
+        *
+        * @return bool
+        */
+       public function functionalIndexes() {
+               return false;
+       }
+
+       /**
+        * Intended to be compatible with the PEAR::DB wrapper functions.
+        * http://pear.php.net/manual/en/package.database.db.intro-execute.php
+        *
+        * ? = scalar value, quoted as necessary
+        * ! = raw SQL bit (a function for instance)
+        * & = filename; reads the file and inserts as a blob
+        *     (we don't use this though...)
+        *
+        * @param string $sql
+        * @param string $func
+        *
+        * @return array
+        */
+       protected function prepare( $sql, $func = __METHOD__ ) {
+               /* MySQL doesn't support prepared statements (yet), so just
+                * pack up the query for reference. We'll manually replace
+                * the bits later.
+                */
+               return [ 'query' => $sql, 'func' => $func ];
+       }
+
+       /**
+        * Free a prepared query, generated by prepare().
+        * @param string $prepared
+        */
+       protected function freePrepared( $prepared ) {
+               /* No-op by default */
+       }
+
+       /**
+        * Execute a prepared query with the various arguments
+        * @param string $prepared The prepared sql
+        * @param mixed $args Either an array here, or put scalars as varargs
+        *
+        * @return ResultWrapper
+        */
+       public function execute( $prepared, $args = null ) {
+               if ( !is_array( $args ) ) {
+                       # Pull the var args
+                       $args = func_get_args();
+                       array_shift( $args );
+               }
+
+               $sql = $this->fillPrepared( $prepared['query'], $args );
+
+               return $this->query( $sql, $prepared['func'] );
+       }
+
+       /**
+        * For faking prepared SQL statements on DBs that don't support it directly.
+        *
+        * @param string $preparedQuery A 'preparable' SQL statement
+        * @param array $args Array of Arguments to fill it with
+        * @return string Executable SQL
+        */
+       public function fillPrepared( $preparedQuery, $args ) {
+               reset( $args );
+               $this->preparedArgs =& $args;
+
+               return preg_replace_callback( '/(\\\\[?!&]|[?!&])/',
+                       [ &$this, 'fillPreparedArg' ], $preparedQuery );
+       }
+
+       /**
+        * preg_callback func for fillPrepared()
+        * The arguments should be in $this->preparedArgs and must not be touched
+        * while we're doing this.
+        *
+        * @param array $matches
+        * @throws DBUnexpectedError
+        * @return string
+        */
+       protected function fillPreparedArg( $matches ) {
+               switch ( $matches[1] ) {
+                       case '\\?':
+                               return '?';
+                       case '\\!':
+                               return '!';
+                       case '\\&':
+                               return '&';
+               }
+
+               list( /* $n */, $arg ) = each( $this->preparedArgs );
+
+               switch ( $matches[1] ) {
+                       case '?':
+                               return $this->addQuotes( $arg );
+                       case '!':
+                               return $arg;
+                       case '&':
+                               # return $this->addQuotes( file_get_contents( $arg ) );
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       '& mode is not implemented. If it\'s really needed, uncomment the line above.'
+                               );
+                       default:
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       'Received invalid match. This should never happen!'
+                               );
+               }
+       }
+
+       /**
+        * Get search engine class. All subclasses of this need to implement this
+        * if they wish to use searching.
+        *
+        * @return string
+        */
+       public function getSearchEngine() {
+               return 'SearchEngineDummy';
+       }
+}
diff --git a/includes/libs/rdbms/database/DatabaseDomain.php b/includes/libs/rdbms/database/DatabaseDomain.php
new file mode 100644 (file)
index 0000000..01b6b21
--- /dev/null
@@ -0,0 +1,203 @@
+<?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 Database
+ */
+
+/**
+ * Class to handle database/prefix specification for IDatabase domains
+ */
+class DatabaseDomain {
+       /** @var string|null */
+       private $database;
+       /** @var string|null */
+       private $schema;
+       /** @var string */
+       private $prefix;
+
+       /** @var string Cache of convertToString() */
+       private $equivalentString;
+
+       /**
+        * @param string|null $database Database name
+        * @param string|null $schema Schema name
+        * @param string $prefix Table prefix
+        */
+       public function __construct( $database, $schema, $prefix ) {
+               if ( $database !== null && ( !is_string( $database ) || !strlen( $database ) ) ) {
+                       throw new InvalidArgumentException( "Database must be null or a non-empty string." );
+               }
+               $this->database = $database;
+               if ( $schema !== null && ( !is_string( $schema ) || !strlen( $schema ) ) ) {
+                       throw new InvalidArgumentException( "Schema must be null or a non-empty string." );
+               }
+               $this->schema = $schema;
+               if ( !is_string( $prefix ) ) {
+                       throw new InvalidArgumentException( "Prefix must be a string." );
+               }
+               $this->prefix = $prefix;
+               $this->equivalentString = $this->convertToString();
+       }
+
+       /**
+        * @param DatabaseDomain|string $domain Result of DatabaseDomain::toString()
+        * @return DatabaseDomain
+        */
+       public static function newFromId( $domain ) {
+               if ( $domain instanceof self ) {
+                       return $domain;
+               }
+
+               $parts = array_map( [ __CLASS__, 'decode' ], explode( '-', $domain ) );
+
+               $schema = null;
+               $prefix = '';
+
+               if ( count( $parts ) == 1 ) {
+                       $database = $parts[0];
+               } elseif ( count( $parts ) == 2 ) {
+                       list( $database, $prefix ) = $parts;
+               } elseif ( count( $parts ) == 3 ) {
+                       list( $database, $schema, $prefix ) = $parts;
+               } else {
+                       throw new InvalidArgumentException( "Domain has too few or too many parts." );
+               }
+
+               if ( $database === '' ) {
+                       $database = null;
+               }
+
+               return new self( $database, $schema, $prefix );
+       }
+
+       /**
+        * @return DatabaseDomain
+        */
+       public static function newUnspecified() {
+               return new self( null, null, '' );
+       }
+
+       /**
+        * @param DatabaseDomain|string $other
+        * @return bool
+        */
+       public function equals( $other ) {
+               if ( $other instanceof DatabaseDomain ) {
+                       return (
+                               $this->database === $other->database &&
+                               $this->schema === $other->schema &&
+                               $this->prefix === $other->prefix
+                       );
+               }
+
+               return ( $this->equivalentString === $other );
+       }
+
+       /**
+        * @return string|null Database name
+        */
+       public function getDatabase() {
+               return $this->database;
+       }
+
+       /**
+        * @return string|null Database schema
+        */
+       public function getSchema() {
+               return $this->schema;
+       }
+
+       /**
+        * @return string Table prefix
+        */
+       public function getTablePrefix() {
+               return $this->prefix;
+       }
+
+       /**
+        * @return string
+        */
+       public function getId() {
+               return $this->equivalentString;
+       }
+
+       /**
+        * @return string
+        */
+       private function convertToString() {
+               $parts = [ $this->database ];
+               if ( $this->schema !== null ) {
+                       $parts[] = $this->schema;
+               }
+               if ( $this->prefix != '' ) {
+                       $parts[] = $this->prefix;
+               }
+
+               return implode( '-', array_map( [ __CLASS__, 'encode' ], $parts ) );
+       }
+
+       private static function encode( $decoded ) {
+               $encoded = '';
+
+               $length = strlen( $decoded );
+               for ( $i = 0; $i < $length; ++$i ) {
+                       $char = $decoded[$i];
+                       if ( $char === '-' ) {
+                               $encoded .= '?h';
+                       } elseif ( $char === '?' ) {
+                               $encoded .= '??';
+                       } else {
+                               $encoded .= $char;
+                       }
+               }
+
+               return $encoded;
+       }
+
+       private static function decode( $encoded ) {
+               $decoded = '';
+
+               $length = strlen( $encoded );
+               for ( $i = 0; $i < $length; ++$i ) {
+                       $char = $encoded[$i];
+                       if ( $char === '?' ) {
+                               $nextChar = isset( $encoded[$i + 1] ) ? $encoded[$i + 1] : null;
+                               if ( $nextChar === 'h' ) {
+                                       $decoded .= '-';
+                                       ++$i;
+                               } elseif ( $nextChar === '?' ) {
+                                       $decoded .= '?';
+                                       ++$i;
+                               } else {
+                                       $decoded .= $char;
+                               }
+                       } else {
+                               $decoded .= $char;
+                       }
+               }
+
+               return $decoded;
+       }
+
+       /**
+        * @return string
+        */
+       function __toString() {
+               return $this->getId();
+       }
+}
diff --git a/includes/libs/rdbms/database/IDatabase.php b/includes/libs/rdbms/database/IDatabase.php
new file mode 100644 (file)
index 0000000..495816f
--- /dev/null
@@ -0,0 +1,1747 @@
+<?php
+
+/**
+ * @defgroup Database Database
+ *
+ * This file deals with database interface functions
+ * and query specifics/optimisations.
+ *
+ * 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 Database
+ */
+
+/**
+ * Basic database interface for live and lazy-loaded DB handles
+ *
+ * @note: IDatabase and DBConnRef should be updated to reflect any changes
+ * @ingroup Database
+ */
+interface IDatabase {
+       /** @var int Callback triggered immediately due to no active transaction */
+       const TRIGGER_IDLE = 1;
+       /** @var int Callback triggered by COMMIT */
+       const TRIGGER_COMMIT = 2;
+       /** @var int Callback triggered by ROLLBACK */
+       const TRIGGER_ROLLBACK = 3;
+
+       /** @var string Transaction is requested by regular caller outside of the DB layer */
+       const TRANSACTION_EXPLICIT = '';
+       /** @var string Transaction is requested internally via DBO_TRX/startAtomic() */
+       const TRANSACTION_INTERNAL = 'implicit';
+
+       /** @var string Transaction operation comes from service managing all DBs */
+       const FLUSHING_ALL_PEERS = 'flush';
+       /** @var string Transaction operation comes from the database class internally */
+       const FLUSHING_INTERNAL = 'flush';
+
+       /** @var string Do not remember the prior flags */
+       const REMEMBER_NOTHING = '';
+       /** @var string Remember the prior flags */
+       const REMEMBER_PRIOR = 'remember';
+       /** @var string Restore to the prior flag state */
+       const RESTORE_PRIOR = 'prior';
+       /** @var string Restore to the initial flag state */
+       const RESTORE_INITIAL = 'initial';
+
+       /** @var string Estimate total time (RTT, scanning, waiting on locks, applying) */
+       const ESTIMATE_TOTAL = 'total';
+       /** @var string Estimate time to apply (scanning, applying) */
+       const ESTIMATE_DB_APPLY = 'apply';
+
+       /**
+        * A string describing the current software version, and possibly
+        * other details in a user-friendly way. Will be listed on Special:Version, etc.
+        * Use getServerVersion() to get machine-friendly information.
+        *
+        * @return string Version information from the database server
+        */
+       public function getServerInfo();
+
+       /**
+        * Turns buffering of SQL result sets on (true) or off (false). Default is
+        * "on".
+        *
+        * Unbuffered queries are very troublesome in MySQL:
+        *
+        *   - If another query is executed while the first query is being read
+        *     out, the first query is killed. This means you can't call normal
+        *     MediaWiki functions while you are reading an unbuffered query result
+        *     from a normal wfGetDB() connection.
+        *
+        *   - Unbuffered queries cause the MySQL server to use large amounts of
+        *     memory and to hold broad locks which block other queries.
+        *
+        * If you want to limit client-side memory, it's almost always better to
+        * split up queries into batches using a LIMIT clause than to switch off
+        * buffering.
+        *
+        * @param null|bool $buffer
+        * @return null|bool The previous value of the flag
+        */
+       public function bufferResults( $buffer = null );
+
+       /**
+        * Gets the current transaction level.
+        *
+        * Historically, transactions were allowed to be "nested". This is no
+        * longer supported, so this function really only returns a boolean.
+        *
+        * @return int The previous value
+        */
+       public function trxLevel();
+
+       /**
+        * Get the UNIX timestamp of the time that the transaction was established
+        *
+        * This can be used to reason about the staleness of SELECT data
+        * in REPEATABLE-READ transaction isolation level.
+        *
+        * @return float|null Returns null if there is not active transaction
+        * @since 1.25
+        */
+       public function trxTimestamp();
+
+       /**
+        * @return bool Whether an explicit transaction or atomic sections are still open
+        * @since 1.28
+        */
+       public function explicitTrxActive();
+
+       /**
+        * Get/set the table prefix.
+        * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
+        * @return string The previous table prefix.
+        */
+       public function tablePrefix( $prefix = null );
+
+       /**
+        * Get/set the db schema.
+        * @param string $schema The database schema to set, or omitted to leave it unchanged.
+        * @return string The previous db schema.
+        */
+       public function dbSchema( $schema = null );
+
+       /**
+        * Get properties passed down from the server info array of the load
+        * balancer.
+        *
+        * @param string $name The entry of the info array to get, or null to get the
+        *   whole array
+        *
+        * @return array|mixed|null
+        */
+       public function getLBInfo( $name = null );
+
+       /**
+        * Set the LB info array, or a member of it. If called with one parameter,
+        * the LB info array is set to that parameter. If it is called with two
+        * parameters, the member with the given name is set to the given value.
+        *
+        * @param string $name
+        * @param array $value
+        */
+       public function setLBInfo( $name, $value = null );
+
+       /**
+        * Set a lazy-connecting DB handle to the master DB (for replication status purposes)
+        *
+        * @param IDatabase $conn
+        * @since 1.27
+        */
+       public function setLazyMasterHandle( IDatabase $conn );
+
+       /**
+        * Returns true if this database does an implicit sort when doing GROUP BY
+        *
+        * @return bool
+        */
+       public function implicitGroupby();
+
+       /**
+        * Returns true if this database does an implicit order by when the column has an index
+        * For example: SELECT page_title FROM page LIMIT 1
+        *
+        * @return bool
+        */
+       public function implicitOrderby();
+
+       /**
+        * Return the last query that went through IDatabase::query()
+        * @return string
+        */
+       public function lastQuery();
+
+       /**
+        * Returns true if the connection may have been used for write queries.
+        * Should return true if unsure.
+        *
+        * @return bool
+        */
+       public function doneWrites();
+
+       /**
+        * Returns the last time the connection may have been used for write queries.
+        * Should return a timestamp if unsure.
+        *
+        * @return int|float UNIX timestamp or false
+        * @since 1.24
+        */
+       public function lastDoneWrites();
+
+       /**
+        * @return bool Whether there is a transaction open with possible write queries
+        * @since 1.27
+        */
+       public function writesPending();
+
+       /**
+        * Returns true if there is a transaction open with possible write
+        * queries or transaction pre-commit/idle callbacks waiting on it to finish.
+        * This does *not* count recurring callbacks, e.g. from setTransactionListener().
+        *
+        * @return bool
+        */
+       public function writesOrCallbacksPending();
+
+       /**
+        * Get the time spend running write queries for this transaction
+        *
+        * High times could be due to scanning, updates, locking, and such
+        *
+        * @param string $type IDatabase::ESTIMATE_* constant [default: ESTIMATE_ALL]
+        * @return float|bool Returns false if not transaction is active
+        * @since 1.26
+        */
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL );
+
+       /**
+        * Get the list of method names that did write queries for this transaction
+        *
+        * @return array
+        * @since 1.27
+        */
+       public function pendingWriteCallers();
+
+       /**
+        * Is a connection to the database open?
+        * @return bool
+        */
+       public function isOpen();
+
+       /**
+        * Set a flag for this connection
+        *
+        * @param int $flag DBO_* constants from Defines.php:
+        *   - DBO_DEBUG: output some debug info (same as debug())
+        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
+        *   - DBO_TRX: automatically start transactions
+        *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
+        *       and removes it in command line mode
+        *   - DBO_PERSISTENT: use persistant database connection
+        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
+        */
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING );
+
+       /**
+        * Clear a flag for this connection
+        *
+        * @param int $flag DBO_* constants from Defines.php:
+        *   - DBO_DEBUG: output some debug info (same as debug())
+        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
+        *   - DBO_TRX: automatically start transactions
+        *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
+        *       and removes it in command line mode
+        *   - DBO_PERSISTENT: use persistant database connection
+        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
+        */
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING );
+
+       /**
+        * Restore the flags to their prior state before the last setFlag/clearFlag call
+        *
+        * @param string $state IDatabase::RESTORE_* constant. [default: RESTORE_PRIOR]
+        * @since 1.28
+        */
+       public function restoreFlags( $state = self::RESTORE_PRIOR );
+
+       /**
+        * Returns a boolean whether the flag $flag is set for this connection
+        *
+        * @param int $flag DBO_* constants from Defines.php:
+        *   - DBO_DEBUG: output some debug info (same as debug())
+        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
+        *   - DBO_TRX: automatically start transactions
+        *   - DBO_PERSISTENT: use persistant database connection
+        * @return bool
+        */
+       public function getFlag( $flag );
+
+       /**
+        * General read-only accessor
+        *
+        * @param string $name
+        * @return string
+        */
+       public function getProperty( $name );
+
+       /**
+        * @return string
+        */
+       public function getDomainID();
+
+       /**
+        * Alias for getDomainID()
+        *
+        * @return string
+        */
+       public function getWikiID();
+
+       /**
+        * Get the type of the DBMS, as it appears in $wgDBtype.
+        *
+        * @return string
+        */
+       public function getType();
+
+       /**
+        * Open a connection to the database. Usually aborts on failure
+        *
+        * @param string $server Database server host
+        * @param string $user Database user name
+        * @param string $password Database user password
+        * @param string $dbName Database name
+        * @return bool
+        * @throws DBConnectionError
+        */
+       public function open( $server, $user, $password, $dbName );
+
+       /**
+        * Fetch the next row from the given result object, in object form.
+        * Fields can be retrieved with $row->fieldname, with fields acting like
+        * member variables.
+        * If no more rows are available, false is returned.
+        *
+        * @param ResultWrapper|stdClass $res Object as returned from IDatabase::query(), etc.
+        * @return stdClass|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       public function fetchObject( $res );
+
+       /**
+        * Fetch the next row from the given result object, in associative array
+        * form. Fields are retrieved with $row['fieldname'].
+        * If no more rows are available, false is returned.
+        *
+        * @param ResultWrapper $res Result object as returned from IDatabase::query(), etc.
+        * @return array|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       public function fetchRow( $res );
+
+       /**
+        * Get the number of rows in a result object
+        *
+        * @param mixed $res A SQL result
+        * @return int
+        */
+       public function numRows( $res );
+
+       /**
+        * Get the number of fields in a result object
+        * @see http://www.php.net/mysql_num_fields
+        *
+        * @param mixed $res A SQL result
+        * @return int
+        */
+       public function numFields( $res );
+
+       /**
+        * Get a field name in a result object
+        * @see http://www.php.net/mysql_field_name
+        *
+        * @param mixed $res A SQL result
+        * @param int $n
+        * @return string
+        */
+       public function fieldName( $res, $n );
+
+       /**
+        * Get the inserted value of an auto-increment row
+        *
+        * The value inserted should be fetched from nextSequenceValue()
+        *
+        * Example:
+        * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
+        * $dbw->insert( 'page', [ 'page_id' => $id ] );
+        * $id = $dbw->insertId();
+        *
+        * @return int
+        */
+       public function insertId();
+
+       /**
+        * Change the position of the cursor in a result object
+        * @see http://www.php.net/mysql_data_seek
+        *
+        * @param mixed $res A SQL result
+        * @param int $row
+        */
+       public function dataSeek( $res, $row );
+
+       /**
+        * Get the last error number
+        * @see http://www.php.net/mysql_errno
+        *
+        * @return int
+        */
+       public function lastErrno();
+
+       /**
+        * Get a description of the last error
+        * @see http://www.php.net/mysql_error
+        *
+        * @return string
+        */
+       public function lastError();
+
+       /**
+        * mysql_fetch_field() wrapper
+        * Returns false if the field doesn't exist
+        *
+        * @param string $table Table name
+        * @param string $field Field name
+        *
+        * @return Field
+        */
+       public function fieldInfo( $table, $field );
+
+       /**
+        * Get the number of rows affected by the last write query
+        * @see http://www.php.net/mysql_affected_rows
+        *
+        * @return int
+        */
+       public function affectedRows();
+
+       /**
+        * Returns a wikitext link to the DB's website, e.g.,
+        *   return "[http://www.mysql.com/ MySQL]";
+        * Should at least contain plain text, if for some reason
+        * your database has no website.
+        *
+        * @return string Wikitext of a link to the server software's web site
+        */
+       public function getSoftwareLink();
+
+       /**
+        * A string describing the current software version, like from
+        * mysql_get_server_info().
+        *
+        * @return string Version information from the database server.
+        */
+       public function getServerVersion();
+
+       /**
+        * Closes a database connection.
+        * if it is open : commits any open transactions
+        *
+        * @throws DBError
+        * @return bool Operation success. true if already closed.
+        */
+       public function close();
+
+       /**
+        * @param string $error Fallback error message, used if none is given by DB
+        * @throws DBConnectionError
+        */
+       public function reportConnectionError( $error = 'Unknown error' );
+
+       /**
+        * Run an SQL query and return the result. Normally throws a DBQueryError
+        * on failure. If errors are ignored, returns false instead.
+        *
+        * In new code, the query wrappers select(), insert(), update(), delete(),
+        * etc. should be used where possible, since they give much better DBMS
+        * independence and automatically quote or validate user input in a variety
+        * of contexts. This function is generally only useful for queries which are
+        * explicitly DBMS-dependent and are unsupported by the query wrappers, such
+        * as CREATE TABLE.
+        *
+        * However, the query wrappers themselves should call this function.
+        *
+        * @param string $sql SQL query
+        * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST
+        *     comment (you can use __METHOD__ or add some extra info)
+        * @param bool $tempIgnore Whether to avoid throwing an exception on errors...
+        *     maybe best to catch the exception instead?
+        * @throws DBError
+        * @return bool|ResultWrapper True for a successful write query, ResultWrapper object
+        *     for a successful read query, or false on failure if $tempIgnore set
+        */
+       public function query( $sql, $fname = __METHOD__, $tempIgnore = false );
+
+       /**
+        * Report a query error. Log the error, and if neither the object ignore
+        * flag nor the $tempIgnore flag is set, throw a DBQueryError.
+        *
+        * @param string $error
+        * @param int $errno
+        * @param string $sql
+        * @param string $fname
+        * @param bool $tempIgnore
+        * @throws DBQueryError
+        */
+       public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false );
+
+       /**
+        * Free a result object returned by query() or select(). It's usually not
+        * necessary to call this, just use unset() or let the variable holding
+        * the result object go out of scope.
+        *
+        * @param mixed $res A SQL result
+        */
+       public function freeResult( $res );
+
+       /**
+        * A SELECT wrapper which returns a single field from a single result row.
+        *
+        * Usually throws a DBQueryError on failure. If errors are explicitly
+        * ignored, returns false on failure.
+        *
+        * If no result rows are returned from the query, false is returned.
+        *
+        * @param string|array $table Table name. See IDatabase::select() for details.
+        * @param string $var The field name to select. This must be a valid SQL
+        *   fragment: do not use unvalidated user input.
+        * @param string|array $cond The condition array. See IDatabase::select() for details.
+        * @param string $fname The function name of the caller.
+        * @param string|array $options The query options. See IDatabase::select() for details.
+        *
+        * @return bool|mixed The value from the field, or false on failure.
+        */
+       public function selectField(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = []
+       );
+
+       /**
+        * A SELECT wrapper which returns a list of single field values from result rows.
+        *
+        * Usually throws a DBQueryError on failure. If errors are explicitly
+        * ignored, returns false on failure.
+        *
+        * If no result rows are returned from the query, false is returned.
+        *
+        * @param string|array $table Table name. See IDatabase::select() for details.
+        * @param string $var The field name to select. This must be a valid SQL
+        *   fragment: do not use unvalidated user input.
+        * @param string|array $cond The condition array. See IDatabase::select() for details.
+        * @param string $fname The function name of the caller.
+        * @param string|array $options The query options. See IDatabase::select() for details.
+        *
+        * @return bool|array The values from the field, or false on failure
+        * @since 1.25
+        */
+       public function selectFieldValues(
+               $table, $var, $cond = '', $fname = __METHOD__, $options = []
+       );
+
+       /**
+        * Execute a SELECT query constructed using the various parameters provided.
+        * See below for full details of the parameters.
+        *
+        * @param string|array $table Table name
+        * @param string|array $vars Field names
+        * @param string|array $conds Conditions
+        * @param string $fname Caller function name
+        * @param array $options Query options
+        * @param array $join_conds Join conditions
+        *
+        *
+        * @param string|array $table
+        *
+        * May be either an array of table names, or a single string holding a table
+        * name. If an array is given, table aliases can be specified, for example:
+        *
+        *    [ 'a' => 'user' ]
+        *
+        * This includes the user table in the query, with the alias "a" available
+        * for use in field names (e.g. a.user_name).
+        *
+        * All of the table names given here are automatically run through
+        * DatabaseBase::tableName(), which causes the table prefix (if any) to be
+        * added, and various other table name mappings to be performed.
+        *
+        * Do not use untrusted user input as a table name. Alias names should
+        * not have characters outside of the Basic multilingual plane.
+        *
+        * @param string|array $vars
+        *
+        * May be either a field name or an array of field names. The field names
+        * can be complete fragments of SQL, for direct inclusion into the SELECT
+        * query. If an array is given, field aliases can be specified, for example:
+        *
+        *   [ 'maxrev' => 'MAX(rev_id)' ]
+        *
+        * This includes an expression with the alias "maxrev" in the query.
+        *
+        * If an expression is given, care must be taken to ensure that it is
+        * DBMS-independent.
+        *
+        * Untrusted user input must not be passed to this parameter.
+        *
+        * @param string|array $conds
+        *
+        * May be either a string containing a single condition, or an array of
+        * conditions. If an array is given, the conditions constructed from each
+        * element are combined with AND.
+        *
+        * Array elements may take one of two forms:
+        *
+        *   - Elements with a numeric key are interpreted as raw SQL fragments.
+        *   - Elements with a string key are interpreted as equality conditions,
+        *     where the key is the field name.
+        *     - If the value of such an array element is a scalar (such as a
+        *       string), it will be treated as data and thus quoted appropriately.
+        *       If it is null, an IS NULL clause will be added.
+        *     - If the value is an array, an IN (...) clause will be constructed
+        *       from its non-null elements, and an IS NULL clause will be added
+        *       if null is present, such that the field may match any of the
+        *       elements in the array. The non-null elements will be quoted.
+        *
+        * Note that expressions are often DBMS-dependent in their syntax.
+        * DBMS-independent wrappers are provided for constructing several types of
+        * expression commonly used in condition queries. See:
+        *    - IDatabase::buildLike()
+        *    - IDatabase::conditional()
+        *
+        * Untrusted user input is safe in the values of string keys, however untrusted
+        * input must not be used in the array key names or in the values of numeric keys.
+        * Escaping of untrusted input used in values of numeric keys should be done via
+        * IDatabase::addQuotes()
+        *
+        * @param string|array $options
+        *
+        * Optional: Array of query options. Boolean options are specified by
+        * including them in the array as a string value with a numeric key, for
+        * example:
+        *
+        *    [ 'FOR UPDATE' ]
+        *
+        * The supported options are:
+        *
+        *   - OFFSET: Skip this many rows at the start of the result set. OFFSET
+        *     with LIMIT can theoretically be used for paging through a result set,
+        *     but this is discouraged in MediaWiki for performance reasons.
+        *
+        *   - LIMIT: Integer: return at most this many rows. The rows are sorted
+        *     and then the first rows are taken until the limit is reached. LIMIT
+        *     is applied to a result set after OFFSET.
+        *
+        *   - FOR UPDATE: Boolean: lock the returned rows so that they can't be
+        *     changed until the next COMMIT.
+        *
+        *   - DISTINCT: Boolean: return only unique result rows.
+        *
+        *   - GROUP BY: May be either an SQL fragment string naming a field or
+        *     expression to group by, or an array of such SQL fragments.
+        *
+        *   - HAVING: May be either an string containing a HAVING clause or an array of
+        *     conditions building the HAVING clause. If an array is given, the conditions
+        *     constructed from each element are combined with AND.
+        *
+        *   - ORDER BY: May be either an SQL fragment giving a field name or
+        *     expression to order by, or an array of such SQL fragments.
+        *
+        *   - USE INDEX: This may be either a string giving the index name to use
+        *     for the query, or an array. If it is an associative array, each key
+        *     gives the table name (or alias), each value gives the index name to
+        *     use for that table. All strings are SQL fragments and so should be
+        *     validated by the caller.
+        *
+        *   - EXPLAIN: In MySQL, this causes an EXPLAIN SELECT query to be run,
+        *     instead of SELECT.
+        *
+        * And also the following boolean MySQL extensions, see the MySQL manual
+        * for documentation:
+        *
+        *    - LOCK IN SHARE MODE
+        *    - STRAIGHT_JOIN
+        *    - HIGH_PRIORITY
+        *    - SQL_BIG_RESULT
+        *    - SQL_BUFFER_RESULT
+        *    - SQL_SMALL_RESULT
+        *    - SQL_CALC_FOUND_ROWS
+        *    - SQL_CACHE
+        *    - SQL_NO_CACHE
+        *
+        *
+        * @param string|array $join_conds
+        *
+        * Optional associative array of table-specific join conditions. In the
+        * most common case, this is unnecessary, since the join condition can be
+        * in $conds. However, it is useful for doing a LEFT JOIN.
+        *
+        * The key of the array contains the table name or alias. The value is an
+        * array with two elements, numbered 0 and 1. The first gives the type of
+        * join, the second is the same as the $conds parameter. Thus it can be
+        * an SQL fragment, or an array where the string keys are equality and the
+        * numeric keys are SQL fragments all AND'd together. For example:
+        *
+        *    [ 'page' => [ 'LEFT JOIN', 'page_latest=rev_id' ] ]
+        *
+        * @return ResultWrapper|bool If the query returned no rows, a ResultWrapper
+        *   with no rows in it will be returned. If there was a query error, a
+        *   DBQueryError exception will be thrown, except if the "ignore errors"
+        *   option was set, in which case false will be returned.
+        */
+       public function select(
+               $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = []
+       );
+
+       /**
+        * The equivalent of IDatabase::select() except that the constructed SQL
+        * is returned, instead of being immediately executed. This can be useful for
+        * doing UNION queries, where the SQL text of each query is needed. In general,
+        * however, callers outside of Database classes should just use select().
+        *
+        * @param string|array $table Table name
+        * @param string|array $vars Field names
+        * @param string|array $conds Conditions
+        * @param string $fname Caller function name
+        * @param string|array $options Query options
+        * @param string|array $join_conds Join conditions
+        *
+        * @return string SQL query string.
+        * @see IDatabase::select()
+        */
+       public function selectSQLText(
+               $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = [], $join_conds = []
+       );
+
+       /**
+        * Single row SELECT wrapper. Equivalent to IDatabase::select(), except
+        * that a single row object is returned. If the query returns no rows,
+        * false is returned.
+        *
+        * @param string|array $table Table name
+        * @param string|array $vars Field names
+        * @param array $conds Conditions
+        * @param string $fname Caller function name
+        * @param string|array $options Query options
+        * @param array|string $join_conds Join conditions
+        *
+        * @return stdClass|bool
+        */
+       public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
+               $options = [], $join_conds = []
+       );
+
+       /**
+        * Estimate the number of rows in dataset
+        *
+        * MySQL allows you to estimate the number of rows that would be returned
+        * by a SELECT query, using EXPLAIN SELECT. The estimate is provided using
+        * index cardinality statistics, and is notoriously inaccurate, especially
+        * when large numbers of rows have recently been added or deleted.
+        *
+        * For DBMSs that don't support fast result size estimation, this function
+        * will actually perform the SELECT COUNT(*).
+        *
+        * Takes the same arguments as IDatabase::select().
+        *
+        * @param string $table Table name
+        * @param string $vars Unused
+        * @param array|string $conds Filters on the table
+        * @param string $fname Function name for profiling
+        * @param array $options Options for select
+        * @return int Row count
+        */
+       public function estimateRowCount(
+               $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = []
+       );
+
+       /**
+        * Get the number of rows in dataset
+        *
+        * This is useful when trying to do COUNT(*) but with a LIMIT for performance.
+        *
+        * Takes the same arguments as IDatabase::select().
+        *
+        * @since 1.27 Added $join_conds parameter
+        *
+        * @param array|string $tables Table names
+        * @param string $vars Unused
+        * @param array|string $conds Filters on the table
+        * @param string $fname Function name for profiling
+        * @param array $options Options for select
+        * @param array $join_conds Join conditions (since 1.27)
+        * @return int Row count
+        */
+       public function selectRowCount(
+               $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = [], $join_conds = []
+       );
+
+       /**
+        * Determines whether a field exists in a table
+        *
+        * @param string $table Table name
+        * @param string $field Filed to check on that table
+        * @param string $fname Calling function name (optional)
+        * @return bool Whether $table has filed $field
+        */
+       public function fieldExists( $table, $field, $fname = __METHOD__ );
+
+       /**
+        * Determines whether an index exists
+        * Usually throws a DBQueryError on failure
+        * If errors are explicitly ignored, returns NULL on failure
+        *
+        * @param string $table
+        * @param string $index
+        * @param string $fname
+        * @return bool|null
+        */
+       public function indexExists( $table, $index, $fname = __METHOD__ );
+
+       /**
+        * Query whether a given table exists
+        *
+        * @param string $table
+        * @param string $fname
+        * @return bool
+        */
+       public function tableExists( $table, $fname = __METHOD__ );
+
+       /**
+        * Determines if a given index is unique
+        *
+        * @param string $table
+        * @param string $index
+        *
+        * @return bool
+        */
+       public function indexUnique( $table, $index );
+
+       /**
+        * INSERT wrapper, inserts an array into a table.
+        *
+        * $a may be either:
+        *
+        *   - A single associative array. The array keys are the field names, and
+        *     the values are the values to insert. The values are treated as data
+        *     and will be quoted appropriately. If NULL is inserted, this will be
+        *     converted to a database NULL.
+        *   - An array with numeric keys, holding a list of associative arrays.
+        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
+        *     each subarray must be identical to each other, and in the same order.
+        *
+        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
+        * returns success.
+        *
+        * $options is an array of options, with boolean options encoded as values
+        * with numeric keys, in the same style as $options in
+        * IDatabase::select(). Supported options are:
+        *
+        *   - IGNORE: Boolean: if present, duplicate key errors are ignored, and
+        *     any rows which cause duplicate key errors are not inserted. It's
+        *     possible to determine how many rows were successfully inserted using
+        *     IDatabase::affectedRows().
+        *
+        * @param string $table Table name. This will be passed through
+        *   DatabaseBase::tableName().
+        * @param array $a Array of rows to insert
+        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+        * @param array $options Array of options
+        *
+        * @return bool
+        */
+       public function insert( $table, $a, $fname = __METHOD__, $options = [] );
+
+       /**
+        * UPDATE wrapper. Takes a condition array and a SET array.
+        *
+        * @param string $table Name of the table to UPDATE. This will be passed through
+        *   DatabaseBase::tableName().
+        * @param array $values An array of values to SET. For each array element,
+        *   the key gives the field name, and the value gives the data to set
+        *   that field to. The data will be quoted by IDatabase::addQuotes().
+        * @param array $conds An array of conditions (WHERE). See
+        *   IDatabase::select() for the details of the format of condition
+        *   arrays. Use '*' to update all rows.
+        * @param string $fname The function name of the caller (from __METHOD__),
+        *   for logging and profiling.
+        * @param array $options An array of UPDATE options, can be:
+        *   - IGNORE: Ignore unique key conflicts
+        *   - LOW_PRIORITY: MySQL-specific, see MySQL manual.
+        * @return bool
+        */
+       public function update( $table, $values, $conds, $fname = __METHOD__, $options = [] );
+
+       /**
+        * Makes an encoded list of strings from an array
+        *
+        * @param array $a Containing the data
+        * @param int $mode Constant
+        *    - LIST_COMMA: Comma separated, no field names
+        *    - LIST_AND:   ANDed WHERE clause (without the WHERE). See the
+        *      documentation for $conds in IDatabase::select().
+        *    - LIST_OR:    ORed WHERE clause (without the WHERE)
+        *    - LIST_SET:   Comma separated with field names, like a SET clause
+        *    - LIST_NAMES: Comma separated field names
+        * @throws DBError
+        * @return string
+        */
+       public function makeList( $a, $mode = LIST_COMMA );
+
+       /**
+        * Build a partial where clause from a 2-d array such as used for LinkBatch.
+        * The keys on each level may be either integers or strings.
+        *
+        * @param array $data Organized as 2-d
+        *    [ baseKeyVal => [ subKeyVal => [ignored], ... ], ... ]
+        * @param string $baseKey Field name to match the base-level keys to (eg 'pl_namespace')
+        * @param string $subKey Field name to match the sub-level keys to (eg 'pl_title')
+        * @return string|bool SQL fragment, or false if no items in array
+        */
+       public function makeWhereFrom2d( $data, $baseKey, $subKey );
+
+       /**
+        * @param string $field
+        * @return string
+        */
+       public function bitNot( $field );
+
+       /**
+        * @param string $fieldLeft
+        * @param string $fieldRight
+        * @return string
+        */
+       public function bitAnd( $fieldLeft, $fieldRight );
+
+       /**
+        * @param string $fieldLeft
+        * @param string $fieldRight
+        * @return string
+        */
+       public function bitOr( $fieldLeft, $fieldRight );
+
+       /**
+        * Build a concatenation list to feed into a SQL query
+        * @param array $stringList List of raw SQL expressions; caller is
+        *   responsible for any quoting
+        * @return string
+        */
+       public function buildConcat( $stringList );
+
+       /**
+        * Build a GROUP_CONCAT or equivalent statement for a query.
+        *
+        * This is useful for combining a field for several rows into a single string.
+        * NULL values will not appear in the output, duplicated values will appear,
+        * and the resulting delimiter-separated values have no defined sort order.
+        * Code using the results may need to use the PHP unique() or sort() methods.
+        *
+        * @param string $delim Glue to bind the results together
+        * @param string|array $table Table name
+        * @param string $field Field name
+        * @param string|array $conds Conditions
+        * @param string|array $join_conds Join conditions
+        * @return string SQL text
+        * @since 1.23
+        */
+       public function buildGroupConcatField(
+               $delim, $table, $field, $conds = '', $join_conds = []
+       );
+
+       /**
+        * Change the current database
+        *
+        * @param string $db
+        * @return bool Success or failure
+        */
+       public function selectDB( $db );
+
+       /**
+        * Get the current DB name
+        * @return string
+        */
+       public function getDBname();
+
+       /**
+        * Get the server hostname or IP address
+        * @return string
+        */
+       public function getServer();
+
+       /**
+        * Adds quotes and backslashes.
+        *
+        * @param string|Blob $s
+        * @return string
+        */
+       public function addQuotes( $s );
+
+       /**
+        * LIKE statement wrapper, receives a variable-length argument list with
+        * parts of pattern to match containing either string literals that will be
+        * escaped or tokens returned by anyChar() or anyString(). Alternatively,
+        * the function could be provided with an array of aforementioned
+        * parameters.
+        *
+        * Example: $dbr->buildLike( 'My_page_title/', $dbr->anyString() ) returns
+        * a LIKE clause that searches for subpages of 'My page title'.
+        * Alternatively:
+        *   $pattern = [ 'My_page_title/', $dbr->anyString() ];
+        *   $query .= $dbr->buildLike( $pattern );
+        *
+        * @since 1.16
+        * @return string Fully built LIKE statement
+        */
+       public function buildLike();
+
+       /**
+        * Returns a token for buildLike() that denotes a '_' to be used in a LIKE query
+        *
+        * @return LikeMatch
+        */
+       public function anyChar();
+
+       /**
+        * Returns a token for buildLike() that denotes a '%' to be used in a LIKE query
+        *
+        * @return LikeMatch
+        */
+       public function anyString();
+
+       /**
+        * Returns an appropriately quoted sequence value for inserting a new row.
+        * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
+        * subclass will return an integer, and save the value for insertId()
+        *
+        * Any implementation of this function should *not* involve reusing
+        * sequence numbers created for rolled-back transactions.
+        * See http://bugs.mysql.com/bug.php?id=30767 for details.
+        * @param string $seqName
+        * @return null|int
+        */
+       public function nextSequenceValue( $seqName );
+
+       /**
+        * REPLACE query wrapper.
+        *
+        * REPLACE is a very handy MySQL extension, which functions like an INSERT
+        * except that when there is a duplicate key error, the old row is deleted
+        * and the new row is inserted in its place.
+        *
+        * We simulate this with standard SQL with a DELETE followed by INSERT. To
+        * perform the delete, we need to know what the unique indexes are so that
+        * we know how to find the conflicting rows.
+        *
+        * It may be more efficient to leave off unique indexes which are unlikely
+        * to collide. However if you do this, you run the risk of encountering
+        * errors which wouldn't have occurred in MySQL.
+        *
+        * @param string $table The table to replace the row(s) in.
+        * @param array $uniqueIndexes Is an array of indexes. Each element may be either
+        *    a field name or an array of field names
+        * @param array $rows Can be either a single row to insert, or multiple rows,
+        *    in the same format as for IDatabase::insert()
+        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+        */
+       public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ );
+
+       /**
+        * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
+        *
+        * This updates any conflicting rows (according to the unique indexes) using
+        * the provided SET clause and inserts any remaining (non-conflicted) rows.
+        *
+        * $rows may be either:
+        *   - A single associative array. The array keys are the field names, and
+        *     the values are the values to insert. The values are treated as data
+        *     and will be quoted appropriately. If NULL is inserted, this will be
+        *     converted to a database NULL.
+        *   - An array with numeric keys, holding a list of associative arrays.
+        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
+        *     each subarray must be identical to each other, and in the same order.
+        *
+        * It may be more efficient to leave off unique indexes which are unlikely
+        * to collide. However if you do this, you run the risk of encountering
+        * errors which wouldn't have occurred in MySQL.
+        *
+        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
+        * returns success.
+        *
+        * @since 1.22
+        *
+        * @param string $table Table name. This will be passed through DatabaseBase::tableName().
+        * @param array $rows A single row or list of rows to insert
+        * @param array $uniqueIndexes List of single field names or field name tuples
+        * @param array $set An array of values to SET. For each array element, the
+        *   key gives the field name, and the value gives the data to set that
+        *   field to. The data will be quoted by IDatabase::addQuotes().
+        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+        * @throws Exception
+        * @return bool
+        */
+       public function upsert(
+               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+       );
+
+       /**
+        * DELETE where the condition is a join.
+        *
+        * MySQL overrides this to use a multi-table DELETE syntax, in other databases
+        * we use sub-selects
+        *
+        * For safety, an empty $conds will not delete everything. If you want to
+        * delete all rows where the join condition matches, set $conds='*'.
+        *
+        * DO NOT put the join condition in $conds.
+        *
+        * @param string $delTable The table to delete from.
+        * @param string $joinTable The other table.
+        * @param string $delVar The variable to join on, in the first table.
+        * @param string $joinVar The variable to join on, in the second table.
+        * @param array $conds Condition array of field names mapped to variables,
+        *   ANDed together in the WHERE clause
+        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+        * @throws DBUnexpectedError
+        */
+       public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
+               $fname = __METHOD__
+       );
+
+       /**
+        * DELETE query wrapper.
+        *
+        * @param array $table Table name
+        * @param string|array $conds Array of conditions. See $conds in IDatabase::select()
+        *   for the format. Use $conds == "*" to delete all rows
+        * @param string $fname Name of the calling function
+        * @throws DBUnexpectedError
+        * @return bool|ResultWrapper
+        */
+       public function delete( $table, $conds, $fname = __METHOD__ );
+
+       /**
+        * INSERT SELECT wrapper. Takes data from a SELECT query and inserts it
+        * into another table.
+        *
+        * @param string $destTable The table name to insert into
+        * @param string|array $srcTable May be either a table name, or an array of table names
+        *    to include in a join.
+        *
+        * @param array $varMap Must be an associative array of the form
+        *    [ 'dest1' => 'source1', ... ]. Source items may be literals
+        *    rather than field names, but strings should be quoted with
+        *    IDatabase::addQuotes()
+        *
+        * @param array $conds Condition array. See $conds in IDatabase::select() for
+        *    the details of the format of condition arrays. May be "*" to copy the
+        *    whole table.
+        *
+        * @param string $fname The function name of the caller, from __METHOD__
+        *
+        * @param array $insertOptions Options for the INSERT part of the query, see
+        *    IDatabase::insert() for details.
+        * @param array $selectOptions Options for the SELECT part of the query, see
+        *    IDatabase::select() for details.
+        *
+        * @return ResultWrapper
+        */
+       public function insertSelect( $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__,
+               $insertOptions = [], $selectOptions = []
+       );
+
+       /**
+        * Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries
+        * within the UNION construct.
+        * @return bool
+        */
+       public function unionSupportsOrderAndLimit();
+
+       /**
+        * Construct a UNION query
+        * This is used for providing overload point for other DB abstractions
+        * not compatible with the MySQL syntax.
+        * @param array $sqls SQL statements to combine
+        * @param bool $all Use UNION ALL
+        * @return string SQL fragment
+        */
+       public function unionQueries( $sqls, $all );
+
+       /**
+        * Returns an SQL expression for a simple conditional. This doesn't need
+        * to be overridden unless CASE isn't supported in your DBMS.
+        *
+        * @param string|array $cond SQL expression which will result in a boolean value
+        * @param string $trueVal SQL expression to return if true
+        * @param string $falseVal SQL expression to return if false
+        * @return string SQL fragment
+        */
+       public function conditional( $cond, $trueVal, $falseVal );
+
+       /**
+        * Returns a comand for str_replace function in SQL query.
+        * Uses REPLACE() in MySQL
+        *
+        * @param string $orig Column to modify
+        * @param string $old Column to seek
+        * @param string $new Column to replace with
+        *
+        * @return string
+        */
+       public function strreplace( $orig, $old, $new );
+
+       /**
+        * Determines how long the server has been up
+        *
+        * @return int
+        */
+       public function getServerUptime();
+
+       /**
+        * Determines if the last failure was due to a deadlock
+        *
+        * @return bool
+        */
+       public function wasDeadlock();
+
+       /**
+        * Determines if the last failure was due to a lock timeout
+        *
+        * @return bool
+        */
+       public function wasLockTimeout();
+
+       /**
+        * Determines if the last query error was due to a dropped connection and should
+        * be dealt with by pinging the connection and reissuing the query.
+        *
+        * @return bool
+        */
+       public function wasErrorReissuable();
+
+       /**
+        * Determines if the last failure was due to the database being read-only.
+        *
+        * @return bool
+        */
+       public function wasReadOnlyError();
+
+       /**
+        * Wait for the replica DB to catch up to a given master position
+        *
+        * @param DBMasterPos $pos
+        * @param int $timeout The maximum number of seconds to wait for synchronisation
+        * @return int|null Zero if the replica DB was past that position already,
+        *   greater than zero if we waited for some period of time, less than
+        *   zero if it timed out, and null on error
+        */
+       public function masterPosWait( DBMasterPos $pos, $timeout );
+
+       /**
+        * Get the replication position of this replica DB
+        *
+        * @return DBMasterPos|bool False if this is not a replica DB.
+        */
+       public function getSlavePos();
+
+       /**
+        * Get the position of this master
+        *
+        * @return DBMasterPos|bool False if this is not a master
+        */
+       public function getMasterPos();
+
+       /**
+        * @return bool Whether the DB is marked as read-only server-side
+        * @since 1.28
+        */
+       public function serverIsReadOnly();
+
+       /**
+        * Run a callback as soon as the current transaction commits or rolls back.
+        * An error is thrown if no transaction is pending. Queries in the function will run in
+        * AUTO-COMMIT mode unless there are begin() calls. Callbacks must commit any transactions
+        * that they begin.
+        *
+        * This is useful for combining cooperative locks and DB transactions.
+        *
+        * The callback takes one argument:
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
+        *
+        * @param callable $callback
+        * @param string $fname Caller name
+        * @return mixed
+        * @since 1.28
+        */
+       public function onTransactionResolution( callable $callback, $fname = __METHOD__ );
+
+       /**
+        * Run a callback as soon as there is no transaction pending.
+        * If there is a transaction and it is rolled back, then the callback is cancelled.
+        * Queries in the function will run in AUTO-COMMIT mode unless there are begin() calls.
+        * Callbacks must commit any transactions that they begin.
+        *
+        * This is useful for updates to different systems or when separate transactions are needed.
+        * For example, one might want to enqueue jobs into a system outside the database, but only
+        * after the database is updated so that the jobs will see the data when they actually run.
+        * It can also be used for updates that easily cause deadlocks if locks are held too long.
+        *
+        * Updates will execute in the order they were enqueued.
+        *
+        * The callback takes one argument:
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
+        *
+        * @param callable $callback
+        * @param string $fname Caller name
+        * @since 1.20
+        */
+       public function onTransactionIdle( callable $callback, $fname = __METHOD__ );
+
+       /**
+        * Run a callback before the current transaction commits or now if there is none.
+        * If there is a transaction and it is rolled back, then the callback is cancelled.
+        * Callbacks must not start nor commit any transactions. If no transaction is active,
+        * then a transaction will wrap the callback.
+        *
+        * This is useful for updates that easily cause deadlocks if locks are held too long
+        * but where atomicity is strongly desired for these updates and some related updates.
+        *
+        * Updates will execute in the order they were enqueued.
+        *
+        * @param callable $callback
+        * @param string $fname Caller name
+        * @since 1.22
+        */
+       public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ );
+
+       /**
+        * Run a callback each time any transaction commits or rolls back
+        *
+        * The callback takes two arguments:
+        *   - IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK
+        *   - This IDatabase object
+        * Callbacks must commit any transactions that they begin.
+        *
+        * Registering a callback here will not affect writesOrCallbacks() pending
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a listener
+        * @return mixed
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null );
+
+       /**
+        * Begin an atomic section of statements
+        *
+        * If a transaction has been started already, just keep track of the given
+        * section name to make sure the transaction is not committed pre-maturely.
+        * This function can be used in layers (with sub-sections), so use a stack
+        * to keep track of the different atomic sections. If there is no transaction,
+        * start one implicitly.
+        *
+        * The goal of this function is to create an atomic section of SQL queries
+        * without having to start a new transaction if it already exists.
+        *
+        * Atomic sections are more strict than transactions. With transactions,
+        * attempting to begin a new transaction when one is already running results
+        * in MediaWiki issuing a brief warning and doing an implicit commit. All
+        * atomic levels *must* be explicitly closed using IDatabase::endAtomic(),
+        * and any database transactions cannot be began or committed until all atomic
+        * levels are closed. There is no such thing as implicitly opening or closing
+        * an atomic section.
+        *
+        * @since 1.23
+        * @param string $fname
+        * @throws DBError
+        */
+       public function startAtomic( $fname = __METHOD__ );
+
+       /**
+        * Ends an atomic section of SQL statements
+        *
+        * Ends the next section of atomic SQL statements and commits the transaction
+        * if necessary.
+        *
+        * @since 1.23
+        * @see IDatabase::startAtomic
+        * @param string $fname
+        * @throws DBError
+        */
+       public function endAtomic( $fname = __METHOD__ );
+
+       /**
+        * Run a callback to do an atomic set of updates for this database
+        *
+        * The $callback takes the following arguments:
+        *   - This database object
+        *   - The value of $fname
+        *
+        * If any exception occurs in the callback, then rollback() will be called and the error will
+        * be re-thrown. It may also be that the rollback itself fails with an exception before then.
+        * In any case, such errors are expected to terminate the request, without any outside caller
+        * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
+        * atomic section and uncommitted updates, which trashes the current request, requiring an
+        * error to be displayed.
+        *
+        * This can be an alternative to explicit startAtomic()/endAtomic() calls.
+        *
+        * @see DatabaseBase::startAtomic
+        * @see DatabaseBase::endAtomic
+        *
+        * @param string $fname Caller name (usually __METHOD__)
+        * @param callable $callback Callback that issues DB updates
+        * @return mixed $res Result of the callback (since 1.28)
+        * @throws DBError
+        * @throws RuntimeException
+        * @throws UnexpectedValueException
+        * @since 1.27
+        */
+       public function doAtomicSection( $fname, callable $callback );
+
+       /**
+        * Begin a transaction. If a transaction is already in progress,
+        * that transaction will be committed before the new transaction is started.
+        *
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        * Nesting of transactions is not supported.
+        *
+        * Note that when the DBO_TRX flag is set (which is usually the case for web
+        * requests, but not for maintenance scripts), any previous database query
+        * will have started a transaction automatically.
+        *
+        * Nesting of transactions is not supported. Attempts to nest transactions
+        * will cause a warning, unless the current transaction was started
+        * automatically because of the DBO_TRX flag.
+        *
+        * @param string $fname Calling function name
+        * @param string $mode A situationally valid IDatabase::TRANSACTION_* constant [optional]
+        * @throws DBError
+        */
+       public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT );
+
+       /**
+        * Commits a transaction previously started using begin().
+        * If no transaction is in progress, a warning is issued.
+        *
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        * Nesting of transactions is not supported.
+        *
+        * @param string $fname
+        * @param string $flush Flush flag, set to situationally valid IDatabase::FLUSHING_*
+        *   constant to disable warnings about explicitly committing implicit transactions,
+        *   or calling commit when no transaction is in progress.
+        *
+        *   This will trigger an exception if there is an ongoing explicit transaction.
+        *
+        *   Only set the flush flag if you are sure that these warnings are not applicable,
+        *   and no explicit transactions are open.
+        *
+        * @throws DBUnexpectedError
+        */
+       public function commit( $fname = __METHOD__, $flush = '' );
+
+       /**
+        * Rollback a transaction previously started using begin().
+        * If no transaction is in progress, a warning is issued.
+        *
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        * Nesting of transactions is not supported. If a serious unexpected error occurs,
+        * throwing an Exception is preferrable, using a pre-installed error handler to trigger
+        * rollback (in any case, failure to issue COMMIT will cause rollback server-side).
+        *
+        * @param string $fname Calling function name
+        * @param string $flush Flush flag, set to a situationally valid IDatabase::FLUSHING_*
+        *   constant to disable warnings about calling rollback when no transaction is in
+        *   progress. This will silently break any ongoing explicit transaction. Only set the
+        *   flush flag if you are sure that it is safe to ignore these warnings in your context.
+        * @throws DBUnexpectedError
+        * @since 1.23 Added $flush parameter
+        */
+       public function rollback( $fname = __METHOD__, $flush = '' );
+
+       /**
+        * Commit any transaction but error out if writes or callbacks are pending
+        *
+        * This is intended for clearing out REPEATABLE-READ snapshots so that callers can
+        * see a new point-in-time of the database. This is useful when one of many transaction
+        * rounds finished and significant time will pass in the script's lifetime. It is also
+        * useful to call on a replica DB after waiting on replication to catch up to the master.
+        *
+        * @param string $fname Calling function name
+        * @throws DBUnexpectedError
+        * @since 1.28
+        */
+       public function flushSnapshot( $fname = __METHOD__ );
+
+       /**
+        * List all tables on the database
+        *
+        * @param string $prefix Only show tables with this prefix, e.g. mw_
+        * @param string $fname Calling function name
+        * @throws DBError
+        * @return array
+        */
+       public function listTables( $prefix = null, $fname = __METHOD__ );
+
+       /**
+        * Convert a timestamp in one of the formats accepted by wfTimestamp()
+        * to the format used for inserting into timestamp fields in this DBMS.
+        *
+        * The result is unquoted, and needs to be passed through addQuotes()
+        * before it can be included in raw SQL.
+        *
+        * @param string|int $ts
+        *
+        * @return string
+        */
+       public function timestamp( $ts = 0 );
+
+       /**
+        * Convert a timestamp in one of the formats accepted by wfTimestamp()
+        * to the format used for inserting into timestamp fields in this DBMS. If
+        * NULL is input, it is passed through, allowing NULL values to be inserted
+        * into timestamp fields.
+        *
+        * The result is unquoted, and needs to be passed through addQuotes()
+        * before it can be included in raw SQL.
+        *
+        * @param string|int $ts
+        *
+        * @return string
+        */
+       public function timestampOrNull( $ts = null );
+
+       /**
+        * Ping the server and try to reconnect if it there is no connection
+        *
+        * @param float|null &$rtt Value to store the estimated RTT [optional]
+        * @return bool Success or failure
+        */
+       public function ping( &$rtt = null );
+
+       /**
+        * Get replica DB lag. Currently supported only by MySQL.
+        *
+        * Note that this function will generate a fatal error on many
+        * installations. Most callers should use LoadBalancer::safeGetLag()
+        * instead.
+        *
+        * @return int|bool Database replication lag in seconds or false on error
+        */
+       public function getLag();
+
+       /**
+        * Get the replica DB lag when the current transaction started
+        * or a general lag estimate if not transaction is active
+        *
+        * This is useful when transactions might use snapshot isolation
+        * (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
+        * is this lag plus transaction duration. If they don't, it is still
+        * safe to be pessimistic. In AUTO-COMMIT mode, this still gives an
+        * indication of the staleness of subsequent reads.
+        *
+        * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of BEGIN)
+        * @since 1.27
+        */
+       public function getSessionLagStatus();
+
+       /**
+        * Return the maximum number of items allowed in a list, or 0 for unlimited.
+        *
+        * @return int
+        */
+       public function maxListLen();
+
+       /**
+        * Some DBMSs have a special format for inserting into blob fields, they
+        * don't allow simple quoted strings to be inserted. To insert into such
+        * a field, pass the data through this function before passing it to
+        * IDatabase::insert().
+        *
+        * @param string $b
+        * @return string
+        */
+       public function encodeBlob( $b );
+
+       /**
+        * Some DBMSs return a special placeholder object representing blob fields
+        * in result objects. Pass the object through this function to return the
+        * original string.
+        *
+        * @param string|Blob $b
+        * @return string
+        */
+       public function decodeBlob( $b );
+
+       /**
+        * Override database's default behavior. $options include:
+        *     'connTimeout' : Set the connection timeout value in seconds.
+        *                     May be useful for very long batch queries such as
+        *                     full-wiki dumps, where a single query reads out over
+        *                     hours or days.
+        *
+        * @param array $options
+        * @return void
+        */
+       public function setSessionOptions( array $options );
+
+       /**
+        * Set variables to be used in sourceFile/sourceStream, in preference to the
+        * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
+        * all. If it's set to false, $GLOBALS will be used.
+        *
+        * @param bool|array $vars Mapping variable name to value.
+        */
+       public function setSchemaVars( $vars );
+
+       /**
+        * Check to see if a named lock is available (non-blocking)
+        *
+        * @param string $lockName Name of lock to poll
+        * @param string $method Name of method calling us
+        * @return bool
+        * @since 1.20
+        */
+       public function lockIsFree( $lockName, $method );
+
+       /**
+        * Acquire a named lock
+        *
+        * Named locks are not related to transactions
+        *
+        * @param string $lockName Name of lock to aquire
+        * @param string $method Name of the calling method
+        * @param int $timeout Acquisition timeout in seconds
+        * @return bool
+        */
+       public function lock( $lockName, $method, $timeout = 5 );
+
+       /**
+        * Release a lock
+        *
+        * Named locks are not related to transactions
+        *
+        * @param string $lockName Name of lock to release
+        * @param string $method Name of the calling method
+        *
+        * @return int Returns 1 if the lock was released, 0 if the lock was not established
+        * by this thread (in which case the lock is not released), and NULL if the named
+        * lock did not exist
+        */
+       public function unlock( $lockName, $method );
+
+       /**
+        * Acquire a named lock, flush any transaction, and return an RAII style unlocker object
+        *
+        * Only call this from outer transcation scope and when only one DB will be affected.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        *
+        * This is suitiable for transactions that need to be serialized using cooperative locks,
+        * where each transaction can see each others' changes. Any transaction is flushed to clear
+        * out stale REPEATABLE-READ snapshot data. Once the returned object falls out of PHP scope,
+        * the lock will be released unless a transaction is active. If one is active, then the lock
+        * will be released when it either commits or rolls back.
+        *
+        * If the lock acquisition failed, then no transaction flush happens, and null is returned.
+        *
+        * @param string $lockKey Name of lock to release
+        * @param string $fname Name of the calling method
+        * @param int $timeout Acquisition timeout in seconds
+        * @return ScopedCallback|null
+        * @throws DBUnexpectedError
+        * @since 1.27
+        */
+       public function getScopedLockAndFlush( $lockKey, $fname, $timeout );
+
+       /**
+        * Check to see if a named lock used by lock() use blocking queues
+        *
+        * @return bool
+        * @since 1.26
+        */
+       public function namedLocksEnqueue();
+
+       /**
+        * Find out when 'infinity' is. Most DBMSes support this. This is a special
+        * keyword for timestamps in PostgreSQL, and works with CHAR(14) as well
+        * because "i" sorts after all numbers.
+        *
+        * @return string
+        */
+       public function getInfinity();
+
+       /**
+        * Encode an expiry time into the DBMS dependent format
+        *
+        * @param string $expiry Timestamp for expiry, or the 'infinity' string
+        * @return string
+        */
+       public function encodeExpiry( $expiry );
+
+       /**
+        * Decode an expiry time into a DBMS independent format
+        *
+        * @param string $expiry DB timestamp field value for expiry
+        * @param int $format TS_* constant, defaults to TS_MW
+        * @return string
+        */
+       public function decodeExpiry( $expiry, $format = TS_MW );
+
+       /**
+        * Allow or deny "big selects" for this session only. This is done by setting
+        * the sql_big_selects session variable.
+        *
+        * This is a MySQL-specific feature.
+        *
+        * @param bool|string $value True for allow, false for deny, or "default" to
+        *   restore the initial value
+        */
+       public function setBigSelects( $value = true );
+
+       /**
+        * @return bool Whether this DB is read-only
+        * @since 1.27
+        */
+       public function isReadOnly();
+
+       /**
+        * Make certain table names use their own database, schema, and table prefix
+        * when passed into SQL queries pre-escaped and without a qualified database name
+        *
+        * For example, "user" can be converted to "myschema.mydbname.user" for convenience.
+        * Appearances like `user`, somedb.user, somedb.someschema.user will used literally.
+        *
+        * Calling this twice will completely clear any old table aliases. Also, note that
+        * callers are responsible for making sure the schemas and databases actually exist.
+        *
+        * @param array[] $aliases Map of (table => (dbname, schema, prefix) map)
+        * @since 1.28
+        */
+       public function setTableAliases( array $aliases );
+}
diff --git a/includes/libs/rdbms/database/position/DBMasterPos.php b/includes/libs/rdbms/database/position/DBMasterPos.php
new file mode 100644 (file)
index 0000000..eda0ff3
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * An object representing a master or replica DB position in a replicated setup.
+ *
+ * The implementation details of this opaque type are up to the database subclass.
+ */
+interface DBMasterPos {
+       /**
+        * @return float UNIX timestamp
+        * @since 1.25
+        */
+       public function asOfTime();
+
+       /**
+        * @param DBMasterPos $pos
+        * @return bool Whether this position is at or higher than $pos
+        * @since 1.27
+        */
+       public function hasReached( DBMasterPos $pos );
+
+       /**
+        * @param DBMasterPos $pos
+        * @return bool Whether this position appears to be for the same channel as another
+        * @since 1.27
+        */
+       public function channelsMatch( DBMasterPos $pos );
+
+       /**
+        * @return string
+        * @since 1.27
+        */
+       public function __toString();
+}
diff --git a/includes/libs/rdbms/database/position/MySQLMasterPos.php b/includes/libs/rdbms/database/position/MySQLMasterPos.php
new file mode 100644 (file)
index 0000000..71fbe7e
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * DBMasterPos class for MySQL/MariaDB
+ *
+ * Note that master positions and sync logic here make some assumptions:
+ *  - Binlog-based usage assumes single-source replication and non-hierarchical replication.
+ *  - GTID-based usage allows getting/syncing with multi-source replication. It is assumed
+ *    that GTID sets are complete (e.g. include all domains on the server).
+ */
+class MySQLMasterPos implements DBMasterPos {
+       /** @var string Binlog file */
+       public $file;
+       /** @var int Binglog file position */
+       public $pos;
+       /** @var string[] GTID list */
+       public $gtids = [];
+       /** @var float UNIX timestamp */
+       public $asOfTime = 0.0;
+
+       /**
+        * @param string $file Binlog file name
+        * @param integer $pos Binlog position
+        * @param string $gtid Comma separated GTID set [optional]
+        */
+       function __construct( $file, $pos, $gtid = '' ) {
+               $this->file = $file;
+               $this->pos = $pos;
+               $this->gtids = array_map( 'trim', explode( ',', $gtid ) );
+               $this->asOfTime = microtime( true );
+       }
+
+       /**
+        * @return string <binlog file>/<position>, e.g db1034-bin.000976/843431247
+        */
+       function __toString() {
+               return "{$this->file}/{$this->pos}";
+       }
+
+       function asOfTime() {
+               return $this->asOfTime;
+       }
+
+       function hasReached( DBMasterPos $pos ) {
+               if ( !( $pos instanceof self ) ) {
+                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
+               }
+
+               // Prefer GTID comparisons, which work with multi-tier replication
+               $thisPosByDomain = $this->getGtidCoordinates();
+               $thatPosByDomain = $pos->getGtidCoordinates();
+               if ( $thisPosByDomain && $thatPosByDomain ) {
+                       $reached = true;
+                       // Check that this has positions GTE all of those in $pos for all domains in $pos
+                       foreach ( $thatPosByDomain as $domain => $thatPos ) {
+                               $thisPos = isset( $thisPosByDomain[$domain] ) ? $thisPosByDomain[$domain] : -1;
+                               $reached = $reached && ( $thatPos <= $thisPos );
+                       }
+
+                       return $reached;
+               }
+
+               // Fallback to the binlog file comparisons
+               $thisBinPos = $this->getBinlogCoordinates();
+               $thatBinPos = $pos->getBinlogCoordinates();
+               if ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] ) {
+                       return ( $thisBinPos['pos'] >= $thatBinPos['pos'] );
+               }
+
+               // Comparing totally different binlogs does not make sense
+               return false;
+       }
+
+       function channelsMatch( DBMasterPos $pos ) {
+               if ( !( $pos instanceof self ) ) {
+                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
+               }
+
+               // Prefer GTID comparisons, which work with multi-tier replication
+               $thisPosDomains = array_keys( $this->getGtidCoordinates() );
+               $thatPosDomains = array_keys( $pos->getGtidCoordinates() );
+               if ( $thisPosDomains && $thatPosDomains ) {
+                       // Check that this has GTIDs for all domains in $pos
+                       return !array_diff( $thatPosDomains, $thisPosDomains );
+               }
+
+               // Fallback to the binlog file comparisons
+               $thisBinPos = $this->getBinlogCoordinates();
+               $thatBinPos = $pos->getBinlogCoordinates();
+
+               return ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] );
+       }
+
+       /**
+        * @note: this returns false for multi-source replication GTID sets
+        * @see https://mariadb.com/kb/en/mariadb/gtid
+        * @see https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html
+        * @return array Map of (domain => integer position) or false
+        */
+       protected function getGtidCoordinates() {
+               $gtidInfos = [];
+               foreach ( $this->gtids as $gtid ) {
+                       $m = [];
+                       // MariaDB style: <domain>-<server id>-<sequence number>
+                       if ( preg_match( '!^(\d+)-\d+-(\d+)$!', $gtid, $m ) ) {
+                               $gtidInfos[(int)$m[1]] = (int)$m[2];
+                               // MySQL style: <UUID domain>:<sequence number>
+                       } elseif ( preg_match( '!^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}):(\d+)$!', $gtid, $m ) ) {
+                               $gtidInfos[$m[1]] = (int)$m[2];
+                       } else {
+                               $gtidInfos = [];
+                               break; // unrecognized GTID
+                       }
+
+               }
+
+               return $gtidInfos;
+       }
+
+       /**
+        * @see http://dev.mysql.com/doc/refman/5.7/en/show-master-status.html
+        * @see http://dev.mysql.com/doc/refman/5.7/en/show-slave-status.html
+        * @return array|bool (binlog, (integer file number, integer position)) or false
+        */
+       protected function getBinlogCoordinates() {
+               $m = [];
+               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
+                       return [ 'binlog' => $m[1], 'pos' => [ (int)$m[2], (int)$m[3] ] ];
+               }
+
+               return false;
+       }
+}
diff --git a/includes/libs/rdbms/database/resultwrapper/FakeResultWrapper.php b/includes/libs/rdbms/database/resultwrapper/FakeResultWrapper.php
new file mode 100644 (file)
index 0000000..774def8
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Overloads the relevant methods of the real ResultsWrapper so it
+ * doesn't go anywhere near an actual database.
+ */
+class FakeResultWrapper extends ResultWrapper {
+       /** @var array */
+       public $result = [];
+
+       /** @var null And it's going to stay that way :D */
+       protected $db = null;
+
+       /** @var int */
+       protected $pos = 0;
+
+       /** @var array|stdClass|bool */
+       protected $currentRow = null;
+
+       /**
+        * @param array $array
+        */
+       function __construct( $array ) {
+               $this->result = $array;
+       }
+
+       /**
+        * @return int
+        */
+       function numRows() {
+               return count( $this->result );
+       }
+
+       /**
+        * @return array|bool
+        */
+       function fetchRow() {
+               if ( $this->pos < count( $this->result ) ) {
+                       $this->currentRow = $this->result[$this->pos];
+               } else {
+                       $this->currentRow = false;
+               }
+               $this->pos++;
+               if ( is_object( $this->currentRow ) ) {
+                       return get_object_vars( $this->currentRow );
+               } else {
+                       return $this->currentRow;
+               }
+       }
+
+       function seek( $row ) {
+               $this->pos = $row;
+       }
+
+       function free() {
+       }
+
+       /**
+        * Callers want to be able to access fields with $this->fieldName
+        * @return bool|stdClass
+        */
+       function fetchObject() {
+               $this->fetchRow();
+               if ( $this->currentRow ) {
+                       return (object)$this->currentRow;
+               } else {
+                       return false;
+               }
+       }
+
+       function rewind() {
+               $this->pos = 0;
+               $this->currentRow = null;
+       }
+
+       /**
+        * @return bool|stdClass
+        */
+       function next() {
+               return $this->fetchObject();
+       }
+}
diff --git a/includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php b/includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php
new file mode 100644 (file)
index 0000000..cccb8f1
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+class MssqlResultWrapper extends ResultWrapper {
+       private $mSeekTo = null;
+
+       /**
+        * @return stdClass|bool
+        */
+       public function fetchObject() {
+               $res = $this->result;
+
+               if ( $this->mSeekTo !== null ) {
+                       $result = sqlsrv_fetch_object( $res, 'stdClass', [],
+                               SQLSRV_SCROLL_ABSOLUTE, $this->mSeekTo );
+                       $this->mSeekTo = null;
+               } else {
+                       $result = sqlsrv_fetch_object( $res );
+               }
+
+               // MediaWiki expects us to return boolean false when there are no more rows instead of null
+               if ( $result === null ) {
+                       return false;
+               }
+
+               return $result;
+       }
+
+       /**
+        * @return array|bool
+        */
+       public function fetchRow() {
+               $res = $this->result;
+
+               if ( $this->mSeekTo !== null ) {
+                       $result = sqlsrv_fetch_array( $res, SQLSRV_FETCH_BOTH,
+                               SQLSRV_SCROLL_ABSOLUTE, $this->mSeekTo );
+                       $this->mSeekTo = null;
+               } else {
+                       $result = sqlsrv_fetch_array( $res );
+               }
+
+               // MediaWiki expects us to return boolean false when there are no more rows instead of null
+               if ( $result === null ) {
+                       return false;
+               }
+
+               return $result;
+       }
+
+       /**
+        * @param int $row
+        * @return bool
+        */
+       public function seek( $row ) {
+               $res = $this->result;
+
+               // check bounds
+               $numRows = $this->db->numRows( $res );
+               $row = intval( $row );
+
+               if ( $numRows === 0 ) {
+                       return false;
+               } elseif ( $row < 0 || $row > $numRows - 1 ) {
+                       return false;
+               }
+
+               // Unlike MySQL, the seek actually happens on the next access
+               $this->mSeekTo = $row;
+               return true;
+       }
+}
diff --git a/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php b/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php
new file mode 100644 (file)
index 0000000..4843d02
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Result wrapper for grabbing data queried by someone else
+ * @ingroup Database
+ */
+class ResultWrapper implements Iterator {
+       /** @var resource */
+       public $result;
+
+       /** @var IDatabase */
+       protected $db;
+
+       /** @var int */
+       protected $pos = 0;
+
+       /** @var object|null */
+       protected $currentRow = null;
+
+       /**
+        * Create a new result object from a result resource and a Database object
+        *
+        * @param IDatabase $database
+        * @param resource|ResultWrapper $result
+        */
+       function __construct( $database, $result ) {
+               $this->db = $database;
+
+               if ( $result instanceof ResultWrapper ) {
+                       $this->result = $result->result;
+               } else {
+                       $this->result = $result;
+               }
+       }
+
+       /**
+        * Get the number of rows in a result object
+        *
+        * @return int
+        */
+       function numRows() {
+               return $this->db->numRows( $this );
+       }
+
+       /**
+        * Fetch the next row from the given result object, in object form. Fields can be retrieved with
+        * $row->fieldname, with fields acting like member variables. If no more rows are available,
+        * false is returned.
+        *
+        * @return stdClass|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       function fetchObject() {
+               return $this->db->fetchObject( $this );
+       }
+
+       /**
+        * Fetch the next row from the given result object, in associative array form. Fields are
+        * retrieved with $row['fieldname']. If no more rows are available, false is returned.
+        *
+        * @return array|bool
+        * @throws DBUnexpectedError Thrown if the database returns an error
+        */
+       function fetchRow() {
+               return $this->db->fetchRow( $this );
+       }
+
+       /**
+        * Free a result object
+        */
+       function free() {
+               $this->db->freeResult( $this );
+               unset( $this->result );
+               unset( $this->db );
+       }
+
+       /**
+        * Change the position of the cursor in a result object.
+        * See mysql_data_seek()
+        *
+        * @param int $row
+        */
+       function seek( $row ) {
+               $this->db->dataSeek( $this, $row );
+       }
+
+       /*
+        * ======= Iterator functions =======
+        * Note that using these in combination with the non-iterator functions
+        * above may cause rows to be skipped or repeated.
+        */
+
+       function rewind() {
+               if ( $this->numRows() ) {
+                       $this->db->dataSeek( $this, 0 );
+               }
+               $this->pos = 0;
+               $this->currentRow = null;
+       }
+
+       /**
+        * @return stdClass|array|bool
+        */
+       function current() {
+               if ( is_null( $this->currentRow ) ) {
+                       $this->next();
+               }
+
+               return $this->currentRow;
+       }
+
+       /**
+        * @return int
+        */
+       function key() {
+               return $this->pos;
+       }
+
+       /**
+        * @return stdClass
+        */
+       function next() {
+               $this->pos++;
+               $this->currentRow = $this->fetchObject();
+
+               return $this->currentRow;
+       }
+
+       /**
+        * @return bool
+        */
+       function valid() {
+               return $this->current() !== false;
+       }
+}
diff --git a/includes/libs/rdbms/defines.php b/includes/libs/rdbms/defines.php
new file mode 100644 (file)
index 0000000..48baa3c
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/**@{
+ * Database related constants
+ */
+define( 'DBO_DEBUG', 1 );
+define( 'DBO_NOBUFFER', 2 );
+define( 'DBO_IGNORE', 4 );
+define( 'DBO_TRX', 8 ); // automatically start transaction on first query
+define( 'DBO_DEFAULT', 16 );
+define( 'DBO_PERSISTENT', 32 );
+define( 'DBO_SYSDBA', 64 ); // for oracle maintenance
+define( 'DBO_DDLMODE', 128 ); // when using schema files: mostly for Oracle
+define( 'DBO_SSL', 256 );
+define( 'DBO_COMPRESS', 512 );
+/**@}*/
+
+/**@{
+ * Valid database indexes
+ * Operation-based indexes
+ */
+define( 'DB_REPLICA', -1 );     # Read from a replica (or only server)
+define( 'DB_MASTER', -2 );    # Write to master (or only server)
+/**@}*/
+
+/**@{
+ * Flags for IDatabase::makeList()
+ * These are also available as Database class constants
+ */
+define( 'LIST_COMMA', 0 );
+define( 'LIST_AND', 1 );
+define( 'LIST_SET', 2 );
+define( 'LIST_NAMES', 3 );
+define( 'LIST_OR', 4 );
+/**@}*/
diff --git a/includes/libs/rdbms/encasing/Blob.php b/includes/libs/rdbms/encasing/Blob.php
new file mode 100644 (file)
index 0000000..bd90330
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Utility class
+ * @ingroup Database
+ *
+ * This allows us to distinguish a blob from a normal string and an array of strings
+ */
+class Blob {
+       /** @var string */
+       protected $mData;
+
+       function __construct( $data ) {
+               $this->mData = $data;
+       }
+
+       function fetch() {
+               return $this->mData;
+       }
+}
diff --git a/includes/libs/rdbms/encasing/LikeMatch.php b/includes/libs/rdbms/encasing/LikeMatch.php
new file mode 100644 (file)
index 0000000..5dee884
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Used by DatabaseBase::buildLike() to represent characters that have special
+ * meaning in SQL LIKE clauses and thus need no escaping. Don't instantiate it
+ * manually, use DatabaseBase::anyChar() and anyString() instead.
+ */
+class LikeMatch {
+       /** @var string */
+       private $str;
+
+       /**
+        * Store a string into a LikeMatch marker object.
+        *
+        * @param string $s
+        */
+       public function __construct( $s ) {
+               $this->str = $s;
+       }
+
+       /**
+        * Return the original stored string.
+        *
+        * @return string
+        */
+       public function toString() {
+               return $this->str;
+       }
+}
diff --git a/includes/libs/rdbms/encasing/MssqlBlob.php b/includes/libs/rdbms/encasing/MssqlBlob.php
new file mode 100644 (file)
index 0000000..35be65c
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+class MssqlBlob extends Blob {
+       public function __construct( $data ) {
+               if ( $data instanceof MssqlBlob ) {
+                       return $data;
+               } elseif ( $data instanceof Blob ) {
+                       $this->mData = $data->fetch();
+               } elseif ( is_array( $data ) && is_object( $data ) ) {
+                       $this->mData = serialize( $data );
+               } else {
+                       $this->mData = $data;
+               }
+       }
+
+       /**
+        * Returns an unquoted hex representation of a binary string
+        * for insertion into varbinary-type fields
+        * @return string
+        */
+       public function fetch() {
+               if ( $this->mData === null ) {
+                       return 'null';
+               }
+
+               $ret = '0x';
+               $dataLength = strlen( $this->mData );
+               for ( $i = 0; $i < $dataLength; $i++ ) {
+                       $ret .= bin2hex( pack( 'C', ord( $this->mData[$i] ) ) );
+               }
+
+               return $ret;
+       }
+}
diff --git a/includes/libs/rdbms/encasing/PostgresBlob.php b/includes/libs/rdbms/encasing/PostgresBlob.php
new file mode 100644 (file)
index 0000000..cc52336
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+class PostgresBlob extends Blob {
+
+}
diff --git a/includes/libs/rdbms/exception/DBError.php b/includes/libs/rdbms/exception/DBError.php
new file mode 100644 (file)
index 0000000..38887cf
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+/**
+ * This file contains database error 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.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+
+/**
+ * Database error base class
+ * @ingroup Database
+ */
+class DBError extends Exception {
+       /** @var IDatabase|null */
+       public $db;
+
+       /**
+        * Construct a database error
+        * @param IDatabase $db Object which threw the error
+        * @param string $error A simple error message to be used for debugging
+        */
+       function __construct( IDatabase $db = null, $error ) {
+               $this->db = $db;
+               parent::__construct( $error );
+       }
+}
+
+/**
+ * Base class for the more common types of database errors. These are known to occur
+ * frequently, so we try to give friendly error messages for them.
+ *
+ * @ingroup Database
+ * @since 1.23
+ */
+class DBExpectedError extends DBError implements MessageSpecifier {
+       /** @var string[] Message parameters */
+       protected $params;
+
+       function __construct( IDatabase $db = null, $error, array $params = [] ) {
+               parent::__construct( $db, $error );
+               $this->params = $params;
+       }
+
+       public function getKey() {
+               return 'databaseerror-text';
+       }
+
+       public function getParams() {
+               return $this->params;
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBConnectionError extends DBExpectedError {
+       /**
+        * @param IDatabase $db Object throwing the error
+        * @param string $error Error text
+        */
+       function __construct( IDatabase $db = null, $error = 'unknown error' ) {
+               $msg = 'Cannot access the database';
+               if ( trim( $error ) != '' ) {
+                       $msg .= ": $error";
+               }
+
+               parent::__construct( $db, $msg );
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBQueryError extends DBExpectedError {
+       /** @var string */
+       public $error;
+       /** @var integer */
+       public $errno;
+       /** @var string */
+       public $sql;
+       /** @var string */
+       public $fname;
+
+       /**
+        * @param IDatabase $db
+        * @param string $error
+        * @param int|string $errno
+        * @param string $sql
+        * @param string $fname
+        */
+       function __construct( IDatabase $db, $error, $errno, $sql, $fname ) {
+               if ( $db instanceof DatabaseBase && $db->wasConnectionError( $errno ) ) {
+                       $message = "A connection error occured. \n" .
+                               "Query: $sql\n" .
+                               "Function: $fname\n" .
+                               "Error: $errno $error\n";
+               } else {
+                       $message = "A database error has occurred. Did you forget to run " .
+                               "maintenance/update.php after upgrading?  See: " .
+                               "https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
+                               "Query: $sql\n" .
+                               "Function: $fname\n" .
+                               "Error: $errno $error\n";
+               }
+               parent::__construct( $db, $message );
+
+               $this->error = $error;
+               $this->errno = $errno;
+               $this->sql = $sql;
+               $this->fname = $fname;
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBReadOnlyError extends DBExpectedError {
+}
+
+/**
+ * @ingroup Database
+ */
+class DBTransactionError extends DBExpectedError {
+}
+
+/**
+ * @ingroup Database
+ */
+class DBTransactionSizeError extends DBTransactionError {
+       function getKey() {
+               return 'transaction-duration-limit-exceeded';
+       }
+}
+
+/**
+ * Exception class for replica DB wait timeouts
+ * @ingroup Database
+ */
+class DBReplicationWaitError extends DBExpectedError {
+}
+
+/**
+ * @ingroup Database
+ */
+class DBUnexpectedError extends DBError {
+}
+
+/**
+ * Exception class for attempted DB access
+ * @ingroup Database
+ */
+class DBAccessError extends DBUnexpectedError {
+       public function __construct() {
+               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
+                       "This is not allowed, because database access has been disabled." );
+       }
+}
+
diff --git a/includes/libs/rdbms/field/Field.php b/includes/libs/rdbms/field/Field.php
new file mode 100644 (file)
index 0000000..ed102f4
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Base for all database-specific classes representing information about database fields
+ * @ingroup Database
+ */
+interface Field {
+       /**
+        * Field name
+        * @return string
+        */
+       function name();
+
+       /**
+        * Name of table this field belongs to
+        * @return string
+        */
+       function tableName();
+
+       /**
+        * Database type
+        * @return string
+        */
+       function type();
+
+       /**
+        * Whether this field can store NULL values
+        * @return bool
+        */
+       function isNullable();
+}
diff --git a/includes/libs/rdbms/field/MssqlField.php b/includes/libs/rdbms/field/MssqlField.php
new file mode 100644 (file)
index 0000000..80e1924
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+class MssqlField implements Field {
+       private $name, $tableName, $default, $max_length, $nullable, $type;
+
+       function __construct( $info ) {
+               $this->name = $info['COLUMN_NAME'];
+               $this->tableName = $info['TABLE_NAME'];
+               $this->default = $info['COLUMN_DEFAULT'];
+               $this->max_length = $info['CHARACTER_MAXIMUM_LENGTH'];
+               $this->nullable = !( strtolower( $info['IS_NULLABLE'] ) == 'no' );
+               $this->type = $info['DATA_TYPE'];
+       }
+
+       function name() {
+               return $this->name;
+       }
+
+       function tableName() {
+               return $this->tableName;
+       }
+
+       function defaultValue() {
+               return $this->default;
+       }
+
+       function maxLength() {
+               return $this->max_length;
+       }
+
+       function isNullable() {
+               return $this->nullable;
+       }
+
+       function type() {
+               return $this->type;
+       }
+}
+
diff --git a/includes/libs/rdbms/field/MySQLField.php b/includes/libs/rdbms/field/MySQLField.php
new file mode 100644 (file)
index 0000000..8cf964c
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+class MySQLField implements Field {
+       private $name, $tablename, $default, $max_length, $nullable,
+               $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary,
+               $is_numeric, $is_blob, $is_unsigned, $is_zerofill;
+
+       function __construct( $info ) {
+               $this->name = $info->name;
+               $this->tablename = $info->table;
+               $this->default = $info->def;
+               $this->max_length = $info->max_length;
+               $this->nullable = !$info->not_null;
+               $this->is_pk = $info->primary_key;
+               $this->is_unique = $info->unique_key;
+               $this->is_multiple = $info->multiple_key;
+               $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
+               $this->type = $info->type;
+               $this->binary = isset( $info->binary ) ? $info->binary : false;
+               $this->is_numeric = isset( $info->numeric ) ? $info->numeric : false;
+               $this->is_blob = isset( $info->blob ) ? $info->blob : false;
+               $this->is_unsigned = isset( $info->unsigned ) ? $info->unsigned : false;
+               $this->is_zerofill = isset( $info->zerofill ) ? $info->zerofill : false;
+       }
+
+       /**
+        * @return string
+        */
+       function name() {
+               return $this->name;
+       }
+
+       /**
+        * @return string
+        */
+       function tableName() {
+               return $this->tablename;
+       }
+
+       /**
+        * @return string
+        */
+       function type() {
+               return $this->type;
+       }
+
+       /**
+        * @return bool
+        */
+       function isNullable() {
+               return $this->nullable;
+       }
+
+       function defaultValue() {
+               return $this->default;
+       }
+
+       /**
+        * @return bool
+        */
+       function isKey() {
+               return $this->is_key;
+       }
+
+       /**
+        * @return bool
+        */
+       function isMultipleKey() {
+               return $this->is_multiple;
+       }
+
+       /**
+        * @return bool
+        */
+       function isBinary() {
+               return $this->binary;
+       }
+
+       /**
+        * @return bool
+        */
+       function isNumeric() {
+               return $this->is_numeric;
+       }
+
+       /**
+        * @return bool
+        */
+       function isBlob() {
+               return $this->is_blob;
+       }
+
+       /**
+        * @return bool
+        */
+       function isUnsigned() {
+               return $this->is_unsigned;
+       }
+
+       /**
+        * @return bool
+        */
+       function isZerofill() {
+               return $this->is_zerofill;
+       }
+}
+
diff --git a/includes/libs/rdbms/field/ORAField.php b/includes/libs/rdbms/field/ORAField.php
new file mode 100644 (file)
index 0000000..e48310d
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+class ORAField implements Field {
+       private $name, $tablename, $default, $max_length, $nullable,
+               $is_pk, $is_unique, $is_multiple, $is_key, $type;
+
+       function __construct( $info ) {
+               $this->name = $info['column_name'];
+               $this->tablename = $info['table_name'];
+               $this->default = $info['data_default'];
+               $this->max_length = $info['data_length'];
+               $this->nullable = $info['not_null'];
+               $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0;
+               $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0;
+               $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0;
+               $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
+               $this->type = $info['data_type'];
+       }
+
+       function name() {
+               return $this->name;
+       }
+
+       function tableName() {
+               return $this->tablename;
+       }
+
+       function defaultValue() {
+               return $this->default;
+       }
+
+       function maxLength() {
+               return $this->max_length;
+       }
+
+       function isNullable() {
+               return $this->nullable;
+       }
+
+       function isKey() {
+               return $this->is_key;
+       }
+
+       function isMultipleKey() {
+               return $this->is_multiple;
+       }
+
+       function type() {
+               return $this->type;
+       }
+}
diff --git a/includes/libs/rdbms/field/SQLiteField.php b/includes/libs/rdbms/field/SQLiteField.php
new file mode 100644 (file)
index 0000000..0a2389b
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+class SQLiteField implements Field {
+       private $info, $tableName;
+
+       function __construct( $info, $tableName ) {
+               $this->info = $info;
+               $this->tableName = $tableName;
+       }
+
+       function name() {
+               return $this->info->name;
+       }
+
+       function tableName() {
+               return $this->tableName;
+       }
+
+       function defaultValue() {
+               if ( is_string( $this->info->dflt_value ) ) {
+                       // Typically quoted
+                       if ( preg_match( '/^\'(.*)\'$', $this->info->dflt_value ) ) {
+                               return str_replace( "''", "'", $this->info->dflt_value );
+                       }
+               }
+
+               return $this->info->dflt_value;
+       }
+
+       /**
+        * @return bool
+        */
+       function isNullable() {
+               return !$this->info->notnull;
+       }
+
+       function type() {
+               return $this->info->type;
+       }
+}
diff --git a/includes/libs/rdbms/lbfactory/LBFactory.php b/includes/libs/rdbms/lbfactory/LBFactory.php
new file mode 100644 (file)
index 0000000..3ab7362
--- /dev/null
@@ -0,0 +1,669 @@
+<?php
+/**
+ * Generator and manager of database load balancing objects
+ *
+ * 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 Database
+ */
+
+use Psr\Log\LoggerInterface;
+
+/**
+ * An interface for generating database load balancers
+ * @ingroup Database
+ */
+abstract class LBFactory {
+       /** @var ChronologyProtector */
+       protected $chronProt;
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
+       /** @var LoggerInterface */
+       protected $replLogger;
+       /** @var LoggerInterface */
+       protected $connLogger;
+       /** @var LoggerInterface */
+       protected $queryLogger;
+       /** @var LoggerInterface */
+       protected $perfLogger;
+       /** @var callable Error logger */
+       protected $errorLogger;
+       /** @var BagOStuff */
+       protected $srvCache;
+       /** @var BagOStuff */
+       protected $memCache;
+       /** @var WANObjectCache */
+       protected $wanCache;
+
+       /** @var DatabaseDomain Local domain */
+       protected $localDomain;
+       /** @var string Local hostname of the app server */
+       protected $hostname;
+       /** @var mixed */
+       protected $ticket;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       protected $trxRoundId = false;
+       /** @var string|bool Reason all LBs are read-only or false if not */
+       protected $readOnlyReason = false;
+       /** @var callable[] */
+       protected $replicationWaitCallbacks = [];
+
+       /** @var bool Whether this PHP instance is for a CLI script */
+       protected $cliMode;
+       /** @var string Agent name for query profiling */
+       protected $agent;
+
+       const SHUTDOWN_NO_CHRONPROT = 0; // don't save DB positions at all
+       const SHUTDOWN_CHRONPROT_ASYNC = 1; // save DB positions, but don't wait on remote DCs
+       const SHUTDOWN_CHRONPROT_SYNC = 2; // save DB positions, waiting on all DCs
+
+       private static $loggerFields =
+               [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ];
+
+       /**
+        * @TODO: document base params here
+        * @param array $conf
+        */
+       public function __construct( array $conf ) {
+               $this->localDomain = isset( $conf['localDomain'] )
+                       ? DatabaseDomain::newFromId( $conf['localDomain'] )
+                       : DatabaseDomain::newUnspecified();
+
+               if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
+                       $this->readOnlyReason = $conf['readOnlyReason'];
+               }
+
+               $this->srvCache = isset( $conf['srvCache'] ) ? $conf['srvCache'] : new EmptyBagOStuff();
+               $this->memCache = isset( $conf['memCache'] ) ? $conf['memCache'] : new EmptyBagOStuff();
+               $this->wanCache = isset( $conf['wanCache'] )
+                       ? $conf['wanCache']
+                       : WANObjectCache::newEmpty();
+
+               foreach ( self::$loggerFields as $key ) {
+                       $this->$key = isset( $conf[$key] ) ? $conf[$key] : new \Psr\Log\NullLogger();
+               }
+               $this->errorLogger = isset( $conf['errorLogger'] )
+                       ? $conf['errorLogger']
+                       : function ( Exception $e ) {
+                               trigger_error( E_WARNING, get_class( $e ) . ': ' . $e->getMessage() );
+                       };
+               $this->hostname = isset( $conf['hostname'] )
+                       ? $conf['hostname']
+                       : gethostname();
+
+               $this->chronProt = isset( $conf['chronProt'] )
+                       ? $conf['chronProt']
+                       : $this->newChronologyProtector();
+               $this->trxProfiler = isset( $conf['trxProfiler'] )
+                       ? $conf['trxProfiler']
+                       : new TransactionProfiler();
+
+               $this->ticket = mt_rand();
+               $this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : PHP_SAPI === 'cli';
+               $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
+       }
+
+       /**
+        * Disables all load balancers. All connections are closed, and any attempt to
+        * open a new connection will result in a DBAccessError.
+        * @see LoadBalancer::disable()
+        */
+       public function destroy() {
+               $this->shutdown( self::SHUTDOWN_NO_CHRONPROT );
+               $this->forEachLBCallMethod( 'disable' );
+       }
+
+       /**
+        * Create a new load balancer object. The resulting object will be untracked,
+        * not chronology-protected, and the caller is responsible for cleaning it up.
+        *
+        * @param bool|string $domain Wiki ID, or false for the current wiki
+        * @return ILoadBalancer
+        */
+       abstract public function newMainLB( $domain = false );
+
+       /**
+        * Get a cached (tracked) load balancer object.
+        *
+        * @param bool|string $domain Wiki ID, or false for the current wiki
+        * @return ILoadBalancer
+        */
+       abstract public function getMainLB( $domain = false );
+
+       /**
+        * Create a new load balancer for external storage. The resulting object will be
+        * untracked, not chronology-protected, and the caller is responsible for
+        * cleaning it up.
+        *
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $domain Wiki ID, or false for the current wiki
+        * @return ILoadBalancer
+        */
+       abstract protected function newExternalLB( $cluster, $domain = false );
+
+       /**
+        * Get a cached (tracked) load balancer for external storage
+        *
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $domain Wiki ID, or false for the current wiki
+        * @return ILoadBalancer
+        */
+       abstract public function getExternalLB( $cluster, $domain = false );
+
+       /**
+        * Execute a function for each tracked load balancer
+        * The callback is called with the load balancer as the first parameter,
+        * and $params passed as the subsequent parameters.
+        *
+        * @param callable $callback
+        * @param array $params
+        */
+       abstract public function forEachLB( $callback, array $params = [] );
+
+       /**
+        * Prepare all tracked load balancers for shutdown
+        * @param integer $mode One of the class SHUTDOWN_* constants
+        * @param callable|null $workCallback Work to mask ChronologyProtector writes
+        */
+       public function shutdown(
+               $mode = self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback = null
+       ) {
+               if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
+                       $this->shutdownChronologyProtector( $this->chronProt, $workCallback, 'sync' );
+               } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
+                       $this->shutdownChronologyProtector( $this->chronProt, null, 'async' );
+               }
+
+               $this->commitMasterChanges( __METHOD__ ); // sanity
+       }
+
+       /**
+        * Call a method of each tracked load balancer
+        *
+        * @param string $methodName
+        * @param array $args
+        */
+       protected function forEachLBCallMethod( $methodName, array $args = [] ) {
+               $this->forEachLB(
+                       function ( ILoadBalancer $loadBalancer, $methodName, array $args ) {
+                               call_user_func_array( [ $loadBalancer, $methodName ], $args );
+                       },
+                       [ $methodName, $args ]
+               );
+       }
+
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        * @since 1.28
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+       }
+
+       /**
+        * Commit on all connections. Done for two reasons:
+        * 1. To commit changes to the masters.
+        * 2. To release the snapshot on all connections, master and replica DB.
+        * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
+        */
+       public function commitAll( $fname = __METHOD__, array $options = [] ) {
+               $this->commitMasterChanges( $fname, $options );
+               $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
+       }
+
+       /**
+        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+        *
+        * The DBO_TRX setting will be reverted to the default in each of these methods:
+        *   - commitMasterChanges()
+        *   - rollbackMasterChanges()
+        *   - commitAll()
+        *
+        * This allows for custom transaction rounds from any outer transaction scope.
+        *
+        * @param string $fname
+        * @throws DBTransactionError
+        * @since 1.28
+        */
+       public function beginMasterChanges( $fname = __METHOD__ ) {
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+               // Set DBO_TRX flags on all appropriate DBs
+               $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
+       }
+
+       /**
+        * Commit changes on all master connections
+        * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
+        * @throws Exception
+        */
+       public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
+               if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: transaction round '{$this->trxRoundId}' still running."
+                       );
+               }
+               // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
+               $this->forEachLBCallMethod( 'finalizeMasterChanges' );
+               $this->trxRoundId = false;
+               // Perform pre-commit checks, aborting on failure
+               $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
+               // Log the DBs and methods involved in multi-DB transactions
+               $this->logIfMultiDbTransaction();
+               // Actually perform the commit on all master DB connections and revert DBO_TRX
+               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+               // Run all post-commit callbacks
+               /** @var Exception $e */
+               $e = null; // first callback exception
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
+                       $ex = $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_COMMIT );
+                       $e = $e ?: $ex;
+               } );
+               // Commit any dangling DBO_TRX transactions from callbacks on one DB to another DB
+               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+               // Throw any last post-commit callback error
+               if ( $e instanceof Exception ) {
+                       throw $e;
+               }
+       }
+
+       /**
+        * Rollback changes on all master connections
+        * @param string $fname Caller name
+        * @since 1.23
+        */
+       public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               $this->trxRoundId = false;
+               $this->forEachLBCallMethod( 'suppressTransactionEndCallbacks' );
+               $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
+               // Run all post-rollback callbacks
+               $this->forEachLB( function ( ILoadBalancer $lb ) {
+                       $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_ROLLBACK );
+               } );
+       }
+
+       /**
+        * Log query info if multi DB transactions are going to be committed now
+        */
+       private function logIfMultiDbTransaction() {
+               $callersByDB = [];
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$callersByDB ) {
+                       $masterName = $lb->getServerName( $lb->getWriterIndex() );
+                       $callers = $lb->pendingMasterChangeCallers();
+                       if ( $callers ) {
+                               $callersByDB[$masterName] = $callers;
+                       }
+               } );
+
+               if ( count( $callersByDB ) >= 2 ) {
+                       $dbs = implode( ', ', array_keys( $callersByDB ) );
+                       $msg = "Multi-DB transaction [{$dbs}]:\n";
+                       foreach ( $callersByDB as $db => $callers ) {
+                               $msg .= "$db: " . implode( '; ', $callers ) . "\n";
+                       }
+                       $this->queryLogger->info( $msg );
+               }
+       }
+
+       /**
+        * Determine if any master connection has pending changes
+        * @return bool
+        * @since 1.23
+        */
+       public function hasMasterChanges() {
+               $ret = false;
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
+                       $ret = $ret || $lb->hasMasterChanges();
+               } );
+
+               return $ret;
+       }
+
+       /**
+        * Detemine if any lagged replica DB connection was used
+        * @return bool
+        * @since 1.28
+        */
+       public function laggedReplicaUsed() {
+               $ret = false;
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
+                       $ret = $ret || $lb->laggedReplicaUsed();
+               } );
+
+               return $ret;
+       }
+
+       /**
+        * Determine if any master connection has pending/written changes from this request
+        * @param float $age How many seconds ago is "recent" [defaults to LB lag wait timeout]
+        * @return bool
+        * @since 1.27
+        */
+       public function hasOrMadeRecentMasterChanges( $age = null ) {
+               $ret = false;
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( $age, &$ret ) {
+                       $ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age );
+               } );
+               return $ret;
+       }
+
+       /**
+        * Waits for the replica DBs to catch up to the current master position
+        *
+        * Use this when updating very large numbers of rows, as in maintenance scripts,
+        * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
+        *
+        * By default this waits on all DB clusters actually used in this request.
+        * This makes sense when lag being waiting on is caused by the code that does this check.
+        * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
+        * that were not changed since the last wait check. To forcefully wait on a specific cluster
+        * for a given wiki, use the 'wiki' parameter. To forcefully wait on an "external" cluster,
+        * use the "cluster" parameter.
+        *
+        * Never call this function after a large DB write that is *still* in a transaction.
+        * It only makes sense to call this after the possible lag inducing changes were committed.
+        *
+        * @param array $opts Optional fields that include:
+        *   - wiki : wait on the load balancer DBs that handles the given wiki
+        *   - cluster : wait on the given external load balancer DBs
+        *   - timeout : Max wait time. Default: ~60 seconds
+        *   - ifWritesSince: Only wait if writes were done since this UNIX timestamp
+        * @throws DBReplicationWaitError If a timeout or error occured waiting on a DB cluster
+        * @since 1.27
+        */
+       public function waitForReplication( array $opts = [] ) {
+               $opts += [
+                       'wiki' => false,
+                       'cluster' => false,
+                       'timeout' => 60,
+                       'ifWritesSince' => null
+               ];
+
+               // Figure out which clusters need to be checked
+               /** @var ILoadBalancer[] $lbs */
+               $lbs = [];
+               if ( $opts['cluster'] !== false ) {
+                       $lbs[] = $this->getExternalLB( $opts['cluster'] );
+               } elseif ( $opts['wiki'] !== false ) {
+                       $lbs[] = $this->getMainLB( $opts['wiki'] );
+               } else {
+                       $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
+                               $lbs[] = $lb;
+                       } );
+                       if ( !$lbs ) {
+                               return; // nothing actually used
+                       }
+               }
+
+               // Get all the master positions of applicable DBs right now.
+               // This can be faster since waiting on one cluster reduces the
+               // time needed to wait on the next clusters.
+               $masterPositions = array_fill( 0, count( $lbs ), false );
+               foreach ( $lbs as $i => $lb ) {
+                       if ( $lb->getServerCount() <= 1 ) {
+                               // Bug 27975 - Don't try to wait for replica DBs if there are none
+                               // Prevents permission error when getting master position
+                               continue;
+                       } elseif ( $opts['ifWritesSince']
+                               && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
+                       ) {
+                               continue; // no writes since the last wait
+                       }
+                       $masterPositions[$i] = $lb->getMasterPos();
+               }
+
+               // Run any listener callbacks *after* getting the DB positions. The more
+               // time spent in the callbacks, the less time is spent in waitForAll().
+               foreach ( $this->replicationWaitCallbacks as $callback ) {
+                       $callback();
+               }
+
+               $failed = [];
+               foreach ( $lbs as $i => $lb ) {
+                       if ( $masterPositions[$i] ) {
+                               // The DBMS may not support getMasterPos()
+                               if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
+                                       $failed[] = $lb->getServerName( $lb->getWriterIndex() );
+                               }
+                       }
+               }
+
+               if ( $failed ) {
+                       throw new DBReplicationWaitError(
+                               "Could not wait for replica DBs to catch up to " .
+                               implode( ', ', $failed )
+                       );
+               }
+       }
+
+       /**
+        * Add a callback to be run in every call to waitForReplication() before waiting
+        *
+        * Callbacks must clear any transactions that they start
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a callback
+        * @since 1.28
+        */
+       public function setWaitForReplicationListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->replicationWaitCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->replicationWaitCallbacks[$name] );
+               }
+       }
+
+       /**
+        * Get a token asserting that no transaction writes are active
+        *
+        * @param string $fname Caller name (e.g. __METHOD__)
+        * @return mixed A value to pass to commitAndWaitForReplication()
+        * @since 1.28
+        */
+       public function getEmptyTransactionTicket( $fname ) {
+               if ( $this->hasMasterChanges() ) {
+                       $this->queryLogger->error( __METHOD__ . ": $fname does not have outer scope." );
+                       return null;
+               }
+
+               return $this->ticket;
+       }
+
+       /**
+        * Convenience method for safely running commitMasterChanges()/waitForReplication()
+        *
+        * This will commit and wait unless $ticket indicates it is unsafe to do so
+        *
+        * @param string $fname Caller name (e.g. __METHOD__)
+        * @param mixed $ticket Result of getEmptyTransactionTicket()
+        * @param array $opts Options to waitForReplication()
+        * @throws DBReplicationWaitError
+        * @since 1.28
+        */
+       public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
+               if ( $ticket !== $this->ticket ) {
+                       $this->perfLogger->error( __METHOD__ . ": $fname does not have outer scope." );
+                       return;
+               }
+
+               // The transaction owner and any caller with the empty transaction ticket can commit
+               // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
+               if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
+                       $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
+                       $fnameEffective = $this->trxRoundId;
+               } else {
+                       $fnameEffective = $fname;
+               }
+
+               $this->commitMasterChanges( $fnameEffective );
+               $this->waitForReplication( $opts );
+               // If a nested caller committed on behalf of $fname, start another empty $fname
+               // transaction, leaving the caller with the same empty transaction state as before.
+               if ( $fnameEffective !== $fname ) {
+                       $this->beginMasterChanges( $fnameEffective );
+               }
+       }
+
+       /**
+        * @param string $dbName DB master name (e.g. "db1052")
+        * @return float|bool UNIX timestamp when client last touched the DB or false if not recent
+        * @since 1.28
+        */
+       public function getChronologyProtectorTouched( $dbName ) {
+               return $this->chronProt->getTouched( $dbName );
+       }
+
+       /**
+        * Disable the ChronologyProtector for all load balancers
+        *
+        * This can be called at the start of special API entry points
+        *
+        * @since 1.27
+        */
+       public function disableChronologyProtection() {
+               $this->chronProt->setEnabled( false );
+       }
+
+       /**
+        * @return ChronologyProtector
+        */
+       protected function newChronologyProtector() {
+               $chronProt = new ChronologyProtector(
+                       $this->memCache,
+                       [
+                               'ip' => isset( $_SERVER[ 'REMOTE_ADDR' ] ) ? $_SERVER[ 'REMOTE_ADDR' ] : '',
+                               'agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : ''
+                       ],
+                       isset( $_GET['cpPosTime'] ) ? $_GET['cpPosTime'] : null
+               );
+               $chronProt->setLogger( $this->replLogger );
+               if ( $this->cliMode ) {
+                       $chronProt->setEnabled( false );
+               }
+
+               return $chronProt;
+       }
+
+       /**
+        * Get and record all of the staged DB positions into persistent memory storage
+        *
+        * @param ChronologyProtector $cp
+        * @param callable|null $workCallback Work to do instead of waiting on syncing positions
+        * @param string $mode One of (sync, async); whether to wait on remote datacenters
+        */
+       protected function shutdownChronologyProtector(
+               ChronologyProtector $cp, $workCallback, $mode
+       ) {
+               // Record all the master positions needed
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( $cp ) {
+                       $cp->shutdownLB( $lb );
+               } );
+               // Write them to the persistent stash. Try to do something useful by running $work
+               // while ChronologyProtector waits for the stash write to replicate to all DCs.
+               $unsavedPositions = $cp->shutdown( $workCallback, $mode );
+               if ( $unsavedPositions && $workCallback ) {
+                       // Invoke callback in case it did not cache the result yet
+                       $workCallback(); // work now to block for less time in waitForAll()
+               }
+               // If the positions failed to write to the stash, at least wait on local datacenter
+               // replica DBs to catch up before responding. Even if there are several DCs, this increases
+               // the chance that the user will see their own changes immediately afterwards. As long
+               // as the sticky DC cookie applies (same domain), this is not even an issue.
+               $this->forEachLB( function ( ILoadBalancer $lb ) use ( $unsavedPositions ) {
+                       $masterName = $lb->getServerName( $lb->getWriterIndex() );
+                       if ( isset( $unsavedPositions[$masterName] ) ) {
+                               $lb->waitForAll( $unsavedPositions[$masterName] );
+                       }
+               } );
+       }
+
+       /**
+        * Base parameters to LoadBalancer::__construct()
+        * @return array
+        */
+       final protected function baseLoadBalancerParams() {
+               return [
+                       'localDomain' => $this->localDomain,
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'srvCache' => $this->srvCache,
+                       'wanCache' => $this->wanCache,
+                       'trxProfiler' => $this->trxProfiler,
+                       'queryLogger' => $this->queryLogger,
+                       'connLogger' => $this->connLogger,
+                       'replLogger' => $this->replLogger,
+                       'errorLogger' => $this->errorLogger,
+                       'hostname' => $this->hostname,
+                       'cliMode' => $this->cliMode,
+                       'agent' => $this->agent
+               ];
+       }
+
+       /**
+        * @param ILoadBalancer $lb
+        */
+       protected function initLoadBalancer( ILoadBalancer $lb ) {
+               if ( $this->trxRoundId !== false ) {
+                       $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
+               }
+       }
+
+       /**
+        * Set a new table prefix for the existing local domain ID for testing
+        *
+        * @param string $prefix
+        * @since 1.28
+        */
+       public function setDomainPrefix( $prefix ) {
+               $this->localDomain = new DatabaseDomain(
+                       $this->localDomain->getDatabase(),
+                       null,
+                       $prefix
+               );
+
+               $this->forEachLB( function( LoadBalancer $lb ) use ( $prefix ) {
+                       $lb->setDomainPrefix( $prefix );
+               } );
+       }
+
+       /**
+        * Close all open database connections on all open load balancers.
+        * @since 1.28
+        */
+       public function closeAll() {
+               $this->forEachLBCallMethod( 'closeAll', [] );
+       }
+
+       /**
+        * @param string $agent Agent name for query profiling
+        * @since 1.28
+        */
+       public function setAgentName( $agent ) {
+               $this->agent = $agent;
+       }
+}
diff --git a/includes/libs/rdbms/loadbalancer/ILoadBalancer.php b/includes/libs/rdbms/loadbalancer/ILoadBalancer.php
new file mode 100644 (file)
index 0000000..0f6bea3
--- /dev/null
@@ -0,0 +1,474 @@
+<?php
+/**
+ * Database load balancing interface
+ *
+ * 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 Database
+ * @author Aaron Schulz
+ */
+
+/**
+ * Interface for database load balancing object that manages IDatabase handles
+ *
+ * @since 1.28
+ * @ingroup Database
+ */
+interface ILoadBalancer {
+       /**
+        * @param array $params Array with keys:
+        *  - servers : Required. Array of server info structures.
+        *  - loadMonitor : Name of a class used to fetch server lag and load.
+        *  - readOnlyReason : Reason the master DB is read-only if so [optional]
+        *  - waitTimeout : Maximum time to wait for replicas for consistency [optional]
+        *  - srvCache : BagOStuff object for server cache [optional]
+        *  - memCache : BagOStuff object for cluster memory cache [optional]
+        *  - wanCache : WANObjectCache object [optional]
+        *  - hostname : the name of the current server [optional]
+        * @throws InvalidArgumentException
+        */
+       public function __construct( array $params );
+
+       /**
+        * Get the index of the reader connection, which may be a replica DB
+        * This takes into account load ratios and lag times. It should
+        * always return a consistent index during a given invocation
+        *
+        * Side effect: opens connections to databases
+        * @param string|bool $group Query group, or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @throws DBError
+        * @return bool|int|string
+        */
+       public function getReaderIndex( $group = false, $wiki = false );
+
+       /**
+        * Set the master wait position
+        * If a DB_REPLICA connection has been opened already, waits
+        * Otherwise sets a variable telling it to wait if such a connection is opened
+        * @param DBMasterPos $pos
+        */
+       public function waitFor( $pos );
+
+       /**
+        * Set the master wait position and wait for a "generic" replica DB to catch up to it
+        *
+        * This can be used a faster proxy for waitForAll()
+        *
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        */
+       public function waitForOne( $pos, $timeout = null );
+
+       /**
+        * Set the master wait position and wait for ALL replica DBs to catch up to it
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        */
+       public function waitForAll( $pos, $timeout = null );
+
+       /**
+        * Get any open connection to a given server index, local or foreign
+        * Returns false if there is no connection open
+        *
+        * @param int $i Server index
+        * @return IDatabase|bool False on failure
+        */
+       public function getAnyOpenConnection( $i );
+
+       /**
+        * Get a connection by index
+        * This is the main entry point for this class.
+        *
+        * @param int $i Server index
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        *
+        * @throws DBError
+        * @return IDatabase
+        */
+       public function getConnection( $i, $groups = [], $wiki = false );
+
+       /**
+        * Mark a foreign connection as being available for reuse under a different
+        * DB name or prefix. This mechanism is reference-counted, and must be called
+        * the same number of times as getConnection() to work.
+        *
+        * @param IDatabase $conn
+        * @throws InvalidArgumentException
+        */
+       public function reuseConnection( $conn );
+
+       /**
+        * Get a database connection handle reference
+        *
+        * The handle's methods wrap simply wrap those of a IDatabase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return DBConnRef
+        */
+       public function getConnectionRef( $db, $groups = [], $wiki = false );
+
+       /**
+        * Get a database connection handle reference without connecting yet
+        *
+        * The handle's methods wrap simply wrap those of a IDatabase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return DBConnRef
+        */
+       public function getLazyConnectionRef( $db, $groups = [], $wiki = false );
+
+       /**
+        * Open a connection to the server given by the specified index
+        * Index must be an actual index into the array.
+        * If the server is already open, returns it.
+        *
+        * On error, returns false, and the connection which caused the
+        * error will be available via $this->mErrorConnection.
+        *
+        * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
+        *
+        * @param int $i Server index
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return IDatabase|bool Returns false on errors
+        */
+       public function openConnection( $i, $wiki = false );
+
+       /**
+        * @return int
+        */
+       public function getWriterIndex();
+
+       /**
+        * Returns true if the specified index is a valid server index
+        *
+        * @param string $i
+        * @return bool
+        */
+       public function haveIndex( $i );
+
+       /**
+        * Returns true if the specified index is valid and has non-zero load
+        *
+        * @param string $i
+        * @return bool
+        */
+       public function isNonZeroLoad( $i );
+
+       /**
+        * Get the number of defined servers (not the number of open connections)
+        *
+        * @return int
+        */
+       public function getServerCount();
+
+       /**
+        * Get the host name or IP address of the server with the specified index
+        * Prefer a readable name if available.
+        * @param string $i
+        * @return string
+        */
+       public function getServerName( $i );
+
+       /**
+        * Return the server info structure for a given index, or false if the index is invalid.
+        * @param int $i
+        * @return array|bool
+        */
+       public function getServerInfo( $i );
+
+       /**
+        * Sets the server info structure for the given index. Entry at index $i
+        * is created if it doesn't exist
+        * @param int $i
+        * @param array $serverInfo
+        */
+       public function setServerInfo( $i, array $serverInfo );
+
+       /**
+        * Get the current master position for chronology control purposes
+        * @return DBMasterPos|bool Returns false if not applicable
+        */
+       public function getMasterPos();
+
+       /**
+        * Disable this load balancer. All connections are closed, and any attempt to
+        * open a new connection will result in a DBAccessError.
+        */
+       public function disable();
+
+       /**
+        * Close all open connections
+        */
+       public function closeAll();
+
+       /**
+        * Close a connection
+        *
+        * Using this function makes sure the LoadBalancer knows the connection is closed.
+        * If you use $conn->close() directly, the load balancer won't update its state.
+        *
+        * @param IDatabase $conn
+        */
+       public function closeConnection( IDatabase $conn );
+
+       /**
+        * Commit transactions on all open connections
+        * @param string $fname Caller name
+        * @throws DBExpectedError
+        */
+       public function commitAll( $fname = __METHOD__ );
+
+       /**
+        * Perform all pre-commit callbacks that remain part of the atomic transactions
+        * and disable any post-commit callbacks until runMasterPostTrxCallbacks()
+        *
+        * Use this only for mutli-database commits
+        */
+       public function finalizeMasterChanges();
+
+       /**
+        * Perform all pre-commit checks for things like replication safety
+        *
+        * Use this only for mutli-database commits
+        *
+        * @param array $options Includes:
+        *   - maxWriteDuration : max write query duration time in seconds
+        * @throws DBTransactionError
+        */
+       public function approveMasterChanges( array $options );
+
+       /**
+        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+        *
+        * The DBO_TRX setting will be reverted to the default in each of these methods:
+        *   - commitMasterChanges()
+        *   - rollbackMasterChanges()
+        *   - commitAll()
+        * This allows for custom transaction rounds from any outer transaction scope.
+        *
+        * @param string $fname
+        * @throws DBExpectedError
+        */
+       public function beginMasterChanges( $fname = __METHOD__ );
+
+       /**
+        * Issue COMMIT on all master connections where writes where done
+        * @param string $fname Caller name
+        * @throws DBExpectedError
+        */
+       public function commitMasterChanges( $fname = __METHOD__ );
+
+       /**
+        * Issue all pending post-COMMIT/ROLLBACK callbacks
+        *
+        * Use this only for mutli-database commits
+        *
+        * @param integer $type IDatabase::TRIGGER_* constant
+        * @return Exception|null The first exception or null if there were none
+        */
+       public function runMasterPostTrxCallbacks( $type );
+
+       /**
+        * Issue ROLLBACK only on master, only if queries were done on connection
+        * @param string $fname Caller name
+        * @throws DBExpectedError
+        */
+       public function rollbackMasterChanges( $fname = __METHOD__ );
+
+       /**
+        * Suppress all pending post-COMMIT/ROLLBACK callbacks
+        *
+        * Use this only for mutli-database commits
+        *
+        * @return Exception|null The first exception or null if there were none
+        */
+       public function suppressTransactionEndCallbacks();
+
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ );
+
+       /**
+        * @return bool Whether a master connection is already open
+        */
+       public function hasMasterConnection();
+
+       /**
+        * Determine if there are pending changes in a transaction by this thread
+        * @return bool
+        */
+       public function hasMasterChanges();
+
+       /**
+        * Get the timestamp of the latest write query done by this thread
+        * @return float|bool UNIX timestamp or false
+        */
+       public function lastMasterChangeTimestamp();
+
+       /**
+        * Check if this load balancer object had any recent or still
+        * pending writes issued against it by this PHP thread
+        *
+        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
+        * @return bool
+        */
+       public function hasOrMadeRecentMasterChanges( $age = null );
+
+       /**
+        * Get the list of callers that have pending master changes
+        *
+        * @return string[] List of method names
+        */
+       public function pendingMasterChangeCallers();
+
+       /**
+        * @note This method will trigger a DB connection if not yet done
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return bool Whether the generic connection for reads is highly "lagged"
+        */
+       public function getLaggedReplicaMode( $wiki = false );
+
+       /**
+        * @note This method will never cause a new DB connection
+        * @return bool Whether any generic connection used for reads was highly "lagged"
+        */
+       public function laggedReplicaUsed();
+
+       /**
+        * @note This method may trigger a DB connection if not yet done
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @param IDatabase|null DB master connection; used to avoid loops [optional]
+        * @return string|bool Reason the master is read-only or false if it is not
+        */
+       public function getReadOnlyReason( $wiki = false, IDatabase $conn = null );
+
+       /**
+        * Disables/enables lag checks
+        * @param null|bool $mode
+        * @return bool
+        */
+       public function allowLagged( $mode = null );
+
+       /**
+        * @return bool
+        */
+       public function pingAll();
+
+       /**
+        * Call a function with each open connection object
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachOpenConnection( $callback, array $params = [] );
+
+       /**
+        * Call a function with each open connection object to a master
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachOpenMasterConnection( $callback, array $params = [] );
+
+       /**
+        * Call a function with each open replica DB connection object
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachOpenReplicaConnection( $callback, array $params = [] );
+
+       /**
+        * Get the hostname and lag time of the most-lagged replica DB
+        *
+        * This is useful for maintenance scripts that need to throttle their updates.
+        * May attempt to open connections to replica DBs on the default DB. If there is
+        * no lag, the maximum lag will be reported as -1.
+        *
+        * @param bool|string $wiki Wiki ID, or false for the default database
+        * @return array ( host, max lag, index of max lagged host )
+        */
+       public function getMaxLag( $wiki = false );
+
+       /**
+        * Get an estimate of replication lag (in seconds) for each server
+        *
+        * Results are cached for a short time in memcached/process cache
+        *
+        * Values may be "false" if replication is too broken to estimate
+        *
+        * @param string|bool $wiki
+        * @return int[] Map of (server index => float|int|bool)
+        */
+       public function getLagTimes( $wiki = false );
+
+       /**
+        * Get the lag in seconds for a given connection, or zero if this load
+        * balancer does not have replication enabled.
+        *
+        * This should be used in preference to Database::getLag() in cases where
+        * replication may not be in use, since there is no way to determine if
+        * replication is in use at the connection level without running
+        * potentially restricted queries such as SHOW SLAVE STATUS. Using this
+        * function instead of Database::getLag() avoids a fatal error in this
+        * case on many installations.
+        *
+        * @param IDatabase $conn
+        * @return int|bool Returns false on error
+        */
+       public function safeGetLag( IDatabase $conn );
+
+       /**
+        * Wait for a replica DB to reach a specified master position
+        *
+        * This will connect to the master to get an accurate position if $pos is not given
+        *
+        * @param IDatabase $conn Replica DB
+        * @param DBMasterPos|bool $pos Master position; default: current position
+        * @param integer|null $timeout Timeout in seconds [optional]
+        * @return bool Success
+        */
+       public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = null );
+
+       /**
+        * Clear the cache for slag lag delay times
+        *
+        * This is only used for testing
+        */
+       public function clearLagTimeCache();
+
+       /**
+        * Set a callback via IDatabase::setTransactionListener() on
+        * all current and future master connections of this load balancer
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback
+        */
+       public function setTransactionListener( $name, callable $callback = null );
+}
diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php
new file mode 100644 (file)
index 0000000..3c5d9b1
--- /dev/null
@@ -0,0 +1,1641 @@
+<?php
+/**
+ * Database load balancing manager
+ *
+ * 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 Database
+ */
+use Psr\Log\LoggerInterface;
+
+/**
+ * Database load balancing, tracking, and transaction management object
+ *
+ * @ingroup Database
+ */
+class LoadBalancer implements ILoadBalancer {
+       /** @var array[] Map of (server index => server config array) */
+       private $mServers;
+       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => IDatabase array) */
+       private $mConns;
+       /** @var array Map of (server index => weight) */
+       private $mLoads;
+       /** @var array[] Map of (group => server index => weight) */
+       private $mGroupLoads;
+       /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
+       private $mAllowLagged;
+       /** @var integer Seconds to spend waiting on replica DB lag to resolve */
+       private $mWaitTimeout;
+       /** @var string The LoadMonitor subclass name */
+       private $mLoadMonitorClass;
+       /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
+       private $tableAliases = [];
+
+       /** @var ILoadMonitor */
+       private $mLoadMonitor;
+       /** @var BagOStuff */
+       private $srvCache;
+       /** @var BagOStuff */
+       private $memCache;
+       /** @var WANObjectCache */
+       private $wanCache;
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
+       /** @var LoggerInterface */
+       protected $replLogger;
+       /** @var LoggerInterface */
+       protected $connLogger;
+       /** @var LoggerInterface */
+       protected $queryLogger;
+       /** @var LoggerInterface */
+       protected $perfLogger;
+
+       /** @var bool|IDatabase Database connection that caused a problem */
+       private $mErrorConnection;
+       /** @var integer The generic (not query grouped) replica DB index (of $mServers) */
+       private $mReadIndex;
+       /** @var bool|DBMasterPos False if not set */
+       private $mWaitForPos;
+       /** @var bool Whether the generic reader fell back to a lagged replica DB */
+       private $laggedReplicaMode = false;
+       /** @var bool Whether the generic reader fell back to a lagged replica DB */
+       private $allReplicasDownMode = false;
+       /** @var string The last DB selection or connection error */
+       private $mLastError = 'Unknown error';
+       /** @var string|bool Reason the LB is read-only or false if not */
+       private $readOnlyReason = false;
+       /** @var integer Total connections opened */
+       private $connsOpened = 0;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       private $trxRoundId = false;
+       /** @var array[] Map of (name => callable) */
+       private $trxRecurringCallbacks = [];
+       /** @var DatabaseDomain Local Domain ID and default for selectDB() calls */
+       private $localDomain;
+       /** @var string Alternate ID string for the domain instead of DatabaseDomain::getId() */
+       private $localDomainIdAlias;
+       /** @var string Current server name */
+       private $host;
+       /** @var bool Whether this PHP instance is for a CLI script */
+       protected $cliMode;
+       /** @var string Agent name for query profiling */
+       protected $agent;
+
+       /** @var callable Exception logger */
+       private $errorLogger;
+
+       /** @var boolean */
+       private $disabled = false;
+
+       /** @var integer Warn when this many connection are held */
+       const CONN_HELD_WARN_THRESHOLD = 10;
+       /** @var integer Default 'max lag' when unspecified */
+       const MAX_LAG_DEFAULT = 10;
+       /** @var integer Max time to wait for a replica DB to catch up (e.g. ChronologyProtector) */
+       const POS_WAIT_TIMEOUT = 10;
+       /** @var integer Seconds to cache master server read-only status */
+       const TTL_CACHE_READONLY = 5;
+
+       public function __construct( array $params ) {
+               if ( !isset( $params['servers'] ) ) {
+                       throw new InvalidArgumentException( __CLASS__ . ': missing servers parameter' );
+               }
+               $this->mServers = $params['servers'];
+
+               $this->localDomain = isset( $params['localDomain'] )
+                       ? DatabaseDomain::newFromId( $params['localDomain'] )
+                       : DatabaseDomain::newUnspecified();
+               // In case a caller assumes that the domain ID is simply <db>-<prefix>, which is almost
+               // always true, gracefully handle the case when they fail to account for escaping.
+               if ( $this->localDomain->getTablePrefix() != '' ) {
+                       $this->localDomainIdAlias =
+                               $this->localDomain->getDatabase() . '-' . $this->localDomain->getTablePrefix();
+               } else {
+                       $this->localDomainIdAlias = $this->localDomain->getDatabase();
+               }
+
+               $this->mWaitTimeout = isset( $params['waitTimeout'] )
+                       ? $params['waitTimeout']
+                       : self::POS_WAIT_TIMEOUT;
+
+               $this->mReadIndex = -1;
+               $this->mConns = [
+                       'local' => [],
+                       'foreignUsed' => [],
+                       'foreignFree' => [] ];
+               $this->mLoads = [];
+               $this->mWaitForPos = false;
+               $this->mErrorConnection = false;
+               $this->mAllowLagged = false;
+
+               if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
+                       $this->readOnlyReason = $params['readOnlyReason'];
+               }
+
+               if ( isset( $params['loadMonitor'] ) ) {
+                       $this->mLoadMonitorClass = $params['loadMonitor'];
+               } else {
+                       $master = reset( $params['servers'] );
+                       if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
+                               $this->mLoadMonitorClass = 'LoadMonitorMySQL';
+                       } else {
+                               $this->mLoadMonitorClass = 'LoadMonitorNull';
+                       }
+               }
+
+               foreach ( $params['servers'] as $i => $server ) {
+                       $this->mLoads[$i] = $server['load'];
+                       if ( isset( $server['groupLoads'] ) ) {
+                               foreach ( $server['groupLoads'] as $group => $ratio ) {
+                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
+                                               $this->mGroupLoads[$group] = [];
+                                       }
+                                       $this->mGroupLoads[$group][$i] = $ratio;
+                               }
+                       }
+               }
+
+               if ( isset( $params['srvCache'] ) ) {
+                       $this->srvCache = $params['srvCache'];
+               } else {
+                       $this->srvCache = new EmptyBagOStuff();
+               }
+               if ( isset( $params['memCache'] ) ) {
+                       $this->memCache = $params['memCache'];
+               } else {
+                       $this->memCache = new EmptyBagOStuff();
+               }
+               if ( isset( $params['wanCache'] ) ) {
+                       $this->wanCache = $params['wanCache'];
+               } else {
+                       $this->wanCache = WANObjectCache::newEmpty();
+               }
+               if ( isset( $params['trxProfiler'] ) ) {
+                       $this->trxProfiler = $params['trxProfiler'];
+               } else {
+                       $this->trxProfiler = new TransactionProfiler();
+               }
+
+               $this->errorLogger = isset( $params['errorLogger'] )
+                       ? $params['errorLogger']
+                       : function ( Exception $e ) {
+                               trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_WARNING );
+                       };
+
+               foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) {
+                       $this->$key = isset( $params[$key] ) ? $params[$key] : new \Psr\Log\NullLogger();
+               }
+
+               $this->host = isset( $params['hostname'] )
+                       ? $params['hostname']
+                       : ( gethostname() ?: 'unknown' );
+               $this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : PHP_SAPI === 'cli';
+               $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
+       }
+
+       /**
+        * Get a LoadMonitor instance
+        *
+        * @return ILoadMonitor
+        */
+       private function getLoadMonitor() {
+               if ( !isset( $this->mLoadMonitor ) ) {
+                       $class = $this->mLoadMonitorClass;
+                       $this->mLoadMonitor = new $class( $this, $this->srvCache, $this->memCache );
+                       $this->mLoadMonitor->setLogger( $this->replLogger );
+               }
+
+               return $this->mLoadMonitor;
+       }
+
+       /**
+        * @param array $loads
+        * @param bool|string $domain Domain to get non-lagged for
+        * @param int $maxLag Restrict the maximum allowed lag to this many seconds
+        * @return bool|int|string
+        */
+       private function getRandomNonLagged( array $loads, $domain = false, $maxLag = INF ) {
+               $lags = $this->getLagTimes( $domain );
+
+               # Unset excessively lagged servers
+               foreach ( $lags as $i => $lag ) {
+                       if ( $i != 0 ) {
+                               # How much lag this server nominally is allowed to have
+                               $maxServerLag = isset( $this->mServers[$i]['max lag'] )
+                                       ? $this->mServers[$i]['max lag']
+                                       : self::MAX_LAG_DEFAULT; // default
+                               # Constrain that futher by $maxLag argument
+                               $maxServerLag = min( $maxServerLag, $maxLag );
+
+                               $host = $this->getServerName( $i );
+                               if ( $lag === false && !is_infinite( $maxServerLag ) ) {
+                                       $this->replLogger->error( "Server $host (#$i) is not replicating?" );
+                                       unset( $loads[$i] );
+                               } elseif ( $lag > $maxServerLag ) {
+                                       $this->replLogger->warning( "Server $host (#$i) has >= $lag seconds of lag" );
+                                       unset( $loads[$i] );
+                               }
+                       }
+               }
+
+               # Find out if all the replica DBs with non-zero load are lagged
+               $sum = 0;
+               foreach ( $loads as $load ) {
+                       $sum += $load;
+               }
+               if ( $sum == 0 ) {
+                       # No appropriate DB servers except maybe the master and some replica DBs with zero load
+                       # Do NOT use the master
+                       # Instead, this function will return false, triggering read-only mode,
+                       # and a lagged replica DB will be used instead.
+                       return false;
+               }
+
+               if ( count( $loads ) == 0 ) {
+                       return false;
+               }
+
+               # Return a random representative of the remainder
+               return ArrayUtils::pickRandom( $loads );
+       }
+
+       public function getReaderIndex( $group = false, $domain = false ) {
+               if ( count( $this->mServers ) == 1 ) {
+                       # Skip the load balancing if there's only one server
+                       return $this->getWriterIndex();
+               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
+                       # Shortcut if generic reader exists already
+                       return $this->mReadIndex;
+               }
+
+               # Find the relevant load array
+               if ( $group !== false ) {
+                       if ( isset( $this->mGroupLoads[$group] ) ) {
+                               $nonErrorLoads = $this->mGroupLoads[$group];
+                       } else {
+                               # No loads for this group, return false and the caller can use some other group
+                               $this->connLogger->info( __METHOD__ . ": no loads for group $group" );
+
+                               return false;
+                       }
+               } else {
+                       $nonErrorLoads = $this->mLoads;
+               }
+
+               if ( !count( $nonErrorLoads ) ) {
+                       throw new InvalidArgumentException( "Empty server array given to LoadBalancer" );
+               }
+
+               # Scale the configured load ratios according to the dynamic load if supported
+               $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $domain );
+
+               $laggedReplicaMode = false;
+
+               # No server found yet
+               $i = false;
+               # First try quickly looking through the available servers for a server that
+               # meets our criteria
+               $currentLoads = $nonErrorLoads;
+               while ( count( $currentLoads ) ) {
+                       if ( $this->mAllowLagged || $laggedReplicaMode ) {
+                               $i = ArrayUtils::pickRandom( $currentLoads );
+                       } else {
+                               $i = false;
+                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
+                                       # ChronologyProtecter causes mWaitForPos to be set via sessions.
+                                       # This triggers doWait() after connect, so it's especially good to
+                                       # avoid lagged servers so as to avoid just blocking in that method.
+                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
+                                       # Aim for <= 1 second of waiting (being too picky can backfire)
+                                       $i = $this->getRandomNonLagged( $currentLoads, $domain, $ago + 1 );
+                               }
+                               if ( $i === false ) {
+                                       # Any server with less lag than it's 'max lag' param is preferable
+                                       $i = $this->getRandomNonLagged( $currentLoads, $domain );
+                               }
+                               if ( $i === false && count( $currentLoads ) != 0 ) {
+                                       # All replica DBs lagged. Switch to read-only mode
+                                       $this->replLogger->error( "All replica DBs lagged. Switch to read-only mode" );
+                                       $i = ArrayUtils::pickRandom( $currentLoads );
+                                       $laggedReplicaMode = true;
+                               }
+                       }
+
+                       if ( $i === false ) {
+                               # pickRandom() returned false
+                               # This is permanent and means the configuration or the load monitor
+                               # wants us to return false.
+                               $this->connLogger->debug( __METHOD__ . ": pickRandom() returned false" );
+
+                               return false;
+                       }
+
+                       $serverName = $this->getServerName( $i );
+                       $this->connLogger->debug( __METHOD__ . ": Using reader #$i: $serverName..." );
+
+                       $conn = $this->openConnection( $i, $domain );
+                       if ( !$conn ) {
+                               $this->connLogger->warning( __METHOD__ . ": Failed connecting to $i/$domain" );
+                               unset( $nonErrorLoads[$i] );
+                               unset( $currentLoads[$i] );
+                               $i = false;
+                               continue;
+                       }
+
+                       // Decrement reference counter, we are finished with this connection.
+                       // It will be incremented for the caller later.
+                       if ( $domain !== false ) {
+                               $this->reuseConnection( $conn );
+                       }
+
+                       # Return this server
+                       break;
+               }
+
+               # If all servers were down, quit now
+               if ( !count( $nonErrorLoads ) ) {
+                       $this->connLogger->error( "All servers down" );
+               }
+
+               if ( $i !== false ) {
+                       # Replica DB connection successful.
+                       # Wait for the session master pos for a short time.
+                       if ( $this->mWaitForPos && $i > 0 ) {
+                               $this->doWait( $i );
+                       }
+                       if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
+                               $this->mReadIndex = $i;
+                               # Record if the generic reader index is in "lagged replica DB" mode
+                               if ( $laggedReplicaMode ) {
+                                       $this->laggedReplicaMode = true;
+                               }
+                       }
+                       $serverName = $this->getServerName( $i );
+                       $this->connLogger->debug(
+                               __METHOD__ . ": using server $serverName for group '$group'" );
+               }
+
+               return $i;
+       }
+
+       public function waitFor( $pos ) {
+               $this->mWaitForPos = $pos;
+               $i = $this->mReadIndex;
+
+               if ( $i > 0 ) {
+                       if ( !$this->doWait( $i ) ) {
+                               $this->laggedReplicaMode = true;
+                       }
+               }
+       }
+
+       /**
+        * Set the master wait position and wait for a "generic" replica DB to catch up to it
+        *
+        * This can be used a faster proxy for waitForAll()
+        *
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        * @since 1.26
+        */
+       public function waitForOne( $pos, $timeout = null ) {
+               $this->mWaitForPos = $pos;
+
+               $i = $this->mReadIndex;
+               if ( $i <= 0 ) {
+                       // Pick a generic replica DB if there isn't one yet
+                       $readLoads = $this->mLoads;
+                       unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
+                       $readLoads = array_filter( $readLoads ); // with non-zero load
+                       $i = ArrayUtils::pickRandom( $readLoads );
+               }
+
+               if ( $i > 0 ) {
+                       $ok = $this->doWait( $i, true, $timeout );
+               } else {
+                       $ok = true; // no applicable loads
+               }
+
+               return $ok;
+       }
+
+       public function waitForAll( $pos, $timeout = null ) {
+               $this->mWaitForPos = $pos;
+               $serverCount = count( $this->mServers );
+
+               $ok = true;
+               for ( $i = 1; $i < $serverCount; $i++ ) {
+                       if ( $this->mLoads[$i] > 0 ) {
+                               $ok = $this->doWait( $i, true, $timeout ) && $ok;
+                       }
+               }
+
+               return $ok;
+       }
+
+       public function getAnyOpenConnection( $i ) {
+               foreach ( $this->mConns as $connsByServer ) {
+                       if ( !empty( $connsByServer[$i] ) ) {
+                               return reset( $connsByServer[$i] );
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Wait for a given replica DB to catch up to the master pos stored in $this
+        * @param int $index Server index
+        * @param bool $open Check the server even if a new connection has to be made
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool
+        */
+       protected function doWait( $index, $open = false, $timeout = null ) {
+               $close = false; // close the connection afterwards
+
+               // Check if we already know that the DB has reached this point
+               $server = $this->getServerName( $index );
+               $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server );
+               /** @var DBMasterPos $knownReachedPos */
+               $knownReachedPos = $this->srvCache->get( $key );
+               if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
+                       $this->replLogger->debug( __METHOD__ .
+                               ": replica DB $server known to be caught up (pos >= $knownReachedPos)." );
+                       return true;
+               }
+
+               // Find a connection to wait on, creating one if needed and allowed
+               $conn = $this->getAnyOpenConnection( $index );
+               if ( !$conn ) {
+                       if ( !$open ) {
+                               $this->replLogger->debug( __METHOD__ . ": no connection open for $server" );
+
+                               return false;
+                       } else {
+                               $conn = $this->openConnection( $index, '' );
+                               if ( !$conn ) {
+                                       $this->replLogger->warning( __METHOD__ . ": failed to connect to $server" );
+
+                                       return false;
+                               }
+                               // Avoid connection spam in waitForAll() when connections
+                               // are made just for the sake of doing this lag check.
+                               $close = true;
+                       }
+               }
+
+               $this->replLogger->info( __METHOD__ . ": Waiting for replica DB $server to catch up..." );
+               $timeout = $timeout ?: $this->mWaitTimeout;
+               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
+
+               if ( $result == -1 || is_null( $result ) ) {
+                       // Timed out waiting for replica DB, use master instead
+                       $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
+                       $this->replLogger->warning( "$msg" );
+                       $ok = false;
+               } else {
+                       $this->replLogger->info( __METHOD__ . ": Done" );
+                       $ok = true;
+                       // Remember that the DB reached this point
+                       $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
+               }
+
+               if ( $close ) {
+                       $this->closeConnection( $conn );
+               }
+
+               return $ok;
+       }
+
+       public function getConnection( $i, $groups = [], $domain = false ) {
+               if ( $i === null || $i === false ) {
+                       throw new InvalidArgumentException( 'Attempt to call ' . __METHOD__ .
+                               ' with invalid server index' );
+               }
+
+               if ( $this->localDomain->equals( $domain ) || $domain === $this->localDomainIdAlias ) {
+                       $domain = false; // local connection requested
+               }
+
+               $groups = ( $groups === false || $groups === [] )
+                       ? [ false ] // check one "group": the generic pool
+                       : (array)$groups;
+
+               $masterOnly = ( $i == DB_MASTER || $i == $this->getWriterIndex() );
+               $oldConnsOpened = $this->connsOpened; // connections open now
+
+               if ( $i == DB_MASTER ) {
+                       $i = $this->getWriterIndex();
+               } else {
+                       # Try to find an available server in any the query groups (in order)
+                       foreach ( $groups as $group ) {
+                               $groupIndex = $this->getReaderIndex( $group, $domain );
+                               if ( $groupIndex !== false ) {
+                                       $i = $groupIndex;
+                                       break;
+                               }
+                       }
+               }
+
+               # Operation-based index
+               if ( $i == DB_REPLICA ) {
+                       $this->mLastError = 'Unknown error'; // reset error string
+                       # Try the general server pool if $groups are unavailable.
+                       $i = in_array( false, $groups, true )
+                               ? false // don't bother with this if that is what was tried above
+                               : $this->getReaderIndex( false, $domain );
+                       # Couldn't find a working server in getReaderIndex()?
+                       if ( $i === false ) {
+                               $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
+
+                               return $this->reportConnectionError();
+                       }
+               }
+
+               # Now we have an explicit index into the servers array
+               $conn = $this->openConnection( $i, $domain );
+               if ( !$conn ) {
+                       return $this->reportConnectionError();
+               }
+
+               # Profile any new connections that happen
+               if ( $this->connsOpened > $oldConnsOpened ) {
+                       $host = $conn->getServer();
+                       $dbname = $conn->getDBname();
+                       $this->trxProfiler->recordConnection( $host, $dbname, $masterOnly );
+               }
+
+               if ( $masterOnly ) {
+                       # Make master-requested DB handles inherit any read-only mode setting
+                       $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $domain, $conn ) );
+               }
+
+               return $conn;
+       }
+
+       public function reuseConnection( $conn ) {
+               $serverIndex = $conn->getLBInfo( 'serverIndex' );
+               $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
+               if ( $serverIndex === null || $refCount === null ) {
+                       /**
+                        * This can happen in code like:
+                        *   foreach ( $dbs as $db ) {
+                        *     $conn = $lb->getConnection( DB_REPLICA, [], $db );
+                        *     ...
+                        *     $lb->reuseConnection( $conn );
+                        *   }
+                        * When a connection to the local DB is opened in this way, reuseConnection()
+                        * should be ignored
+                        */
+                       return;
+               }
+
+               $dbName = $conn->getDBname();
+               $prefix = $conn->tablePrefix();
+               if ( strval( $prefix ) !== '' ) {
+                       $domain = "$dbName-$prefix";
+               } else {
+                       $domain = $dbName;
+               }
+               if ( $this->mConns['foreignUsed'][$serverIndex][$domain] !== $conn ) {
+                       throw new InvalidArgumentException( __METHOD__ . ": connection not found, has " .
+                               "the connection been freed already?" );
+               }
+               $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
+               if ( $refCount <= 0 ) {
+                       $this->mConns['foreignFree'][$serverIndex][$domain] = $conn;
+                       unset( $this->mConns['foreignUsed'][$serverIndex][$domain] );
+                       $this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
+               } else {
+                       $this->connLogger->debug( __METHOD__ .
+                               ": reference count for $serverIndex/$domain reduced to $refCount" );
+               }
+       }
+
+       /**
+        * Get a database connection handle reference
+        *
+        * The handle's methods wrap simply wrap those of a IDatabase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $domain Domain ID, or false for the current domain
+        * @return DBConnRef
+        * @since 1.22
+        */
+       public function getConnectionRef( $db, $groups = [], $domain = false ) {
+               $domain = ( $domain !== false ) ? $domain : $this->localDomain;
+
+               return new DBConnRef( $this, $this->getConnection( $db, $groups, $domain ) );
+       }
+
+       /**
+        * Get a database connection handle reference without connecting yet
+        *
+        * The handle's methods wrap simply wrap those of a IDatabase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $domain Domain ID, or false for the current domain
+        * @return DBConnRef
+        * @since 1.22
+        */
+       public function getLazyConnectionRef( $db, $groups = [], $domain = false ) {
+               $domain = ( $domain !== false ) ? $domain : $this->localDomain;
+
+               return new DBConnRef( $this, [ $db, $groups, $domain ] );
+       }
+
+       public function openConnection( $i, $domain = false ) {
+               if ( $this->localDomain->equals( $domain ) || $domain === $this->localDomainIdAlias ) {
+                       $domain = false; // local connection requested
+               }
+
+               if ( $domain !== false ) {
+                       $conn = $this->openForeignConnection( $i, $domain );
+               } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
+                       $conn = $this->mConns['local'][$i][0];
+               } else {
+                       $server = $this->mServers[$i];
+                       $server['serverIndex'] = $i;
+                       $conn = $this->reallyOpenConnection( $server, false );
+                       $serverName = $this->getServerName( $i );
+                       if ( $conn->isOpen() ) {
+                               $this->connLogger->debug( "Connected to database $i at '$serverName'." );
+                               $this->mConns['local'][$i][0] = $conn;
+                       } else {
+                               $this->connLogger->warning( "Failed to connect to database $i at '$serverName'." );
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       }
+               }
+
+               if ( $conn && !$conn->isOpen() ) {
+                       // Connection was made but later unrecoverably lost for some reason.
+                       // Do not return a handle that will just throw exceptions on use,
+                       // but let the calling code (e.g. getReaderIndex) try another server.
+                       // See DatabaseMyslBase::ping() for how this can happen.
+                       $this->mErrorConnection = $conn;
+                       $conn = false;
+               }
+
+               return $conn;
+       }
+
+       /**
+        * Open a connection to a foreign DB, or return one if it is already open.
+        *
+        * Increments a reference count on the returned connection which locks the
+        * connection to the requested domain. This reference count can be
+        * decremented by calling reuseConnection().
+        *
+        * If a connection is open to the appropriate server already, but with the wrong
+        * database, it will be switched to the right database and returned, as long as
+        * it has been freed first with reuseConnection().
+        *
+        * On error, returns false, and the connection which caused the
+        * error will be available via $this->mErrorConnection.
+        *
+        * @note If disable() was called on this LoadBalancer, this method will throw a DBAccessError.
+        *
+        * @param int $i Server index
+        * @param string $domain Domain ID to open
+        * @return IDatabase
+        */
+       private function openForeignConnection( $i, $domain ) {
+               $domainInstance = DatabaseDomain::newFromId( $domain );
+               $dbName = $domainInstance->getDatabase();
+               $prefix = $domainInstance->getTablePrefix();
+
+               if ( isset( $this->mConns['foreignUsed'][$i][$domain] ) ) {
+                       // Reuse an already-used connection
+                       $conn = $this->mConns['foreignUsed'][$i][$domain];
+                       $this->connLogger->debug( __METHOD__ . ": reusing connection $i/$domain" );
+               } elseif ( isset( $this->mConns['foreignFree'][$i][$domain] ) ) {
+                       // Reuse a free connection for the same domain
+                       $conn = $this->mConns['foreignFree'][$i][$domain];
+                       unset( $this->mConns['foreignFree'][$i][$domain] );
+                       $this->mConns['foreignUsed'][$i][$domain] = $conn;
+                       $this->connLogger->debug( __METHOD__ . ": reusing free connection $i/$domain" );
+               } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
+                       // Reuse a connection from another domain
+                       $conn = reset( $this->mConns['foreignFree'][$i] );
+                       $oldDomain = key( $this->mConns['foreignFree'][$i] );
+
+                       // The empty string as a DB name means "don't care".
+                       // DatabaseMysqlBase::open() already handle this on connection.
+                       if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
+                               $this->mLastError = "Error selecting database $dbName on server " .
+                                       $conn->getServer() . " from client host {$this->host}";
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       } else {
+                               $conn->tablePrefix( $prefix );
+                               unset( $this->mConns['foreignFree'][$i][$oldDomain] );
+                               $this->mConns['foreignUsed'][$i][$domain] = $conn;
+                               $this->connLogger->debug( __METHOD__ .
+                                       ": reusing free connection from $oldDomain for $domain" );
+                       }
+               } else {
+                       // Open a new connection
+                       $server = $this->mServers[$i];
+                       $server['serverIndex'] = $i;
+                       $server['foreignPoolRefCount'] = 0;
+                       $server['foreign'] = true;
+                       $conn = $this->reallyOpenConnection( $server, $dbName );
+                       if ( !$conn->isOpen() ) {
+                               $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       } else {
+                               $conn->tablePrefix( $prefix );
+                               $this->mConns['foreignUsed'][$i][$domain] = $conn;
+                               $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
+                       }
+               }
+
+               // Increment reference count
+               if ( $conn ) {
+                       $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
+                       $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
+               }
+
+               return $conn;
+       }
+
+       /**
+        * Test if the specified index represents an open connection
+        *
+        * @param int $index Server index
+        * @access private
+        * @return bool
+        */
+       private function isOpen( $index ) {
+               if ( !is_integer( $index ) ) {
+                       return false;
+               }
+
+               return (bool)$this->getAnyOpenConnection( $index );
+       }
+
+       /**
+        * Really opens a connection. Uncached.
+        * Returns a Database object whether or not the connection was successful.
+        * @access private
+        *
+        * @param array $server
+        * @param bool $dbNameOverride
+        * @return IDatabase
+        * @throws DBAccessError
+        * @throws InvalidArgumentException
+        */
+       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
+               if ( $this->disabled ) {
+                       throw new DBAccessError();
+               }
+
+               if ( !is_array( $server ) ) {
+                       throw new InvalidArgumentException(
+                               'You must update your load-balancing configuration. ' .
+                               'See DefaultSettings.php entry for $wgDBservers.' );
+               }
+
+               if ( $dbNameOverride !== false ) {
+                       $server['dbname'] = $dbNameOverride;
+               }
+
+               // Let the handle know what the cluster master is (e.g. "db1052")
+               $masterName = $this->getServerName( $this->getWriterIndex() );
+               $server['clusterMasterHost'] = $masterName;
+
+               // Log when many connection are made on requests
+               if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
+                       $this->perfLogger->warning( __METHOD__ . ": " .
+                               "{$this->connsOpened}+ connections made (master=$masterName)" );
+               }
+
+               $server['srvCache'] = $this->srvCache;
+               // Set loggers
+               $server['connLogger'] = $this->connLogger;
+               $server['queryLogger'] = $this->queryLogger;
+               $server['trxProfiler'] = $this->trxProfiler;
+               $server['cliMode'] = $this->cliMode;
+               $server['errorLogger'] = $this->errorLogger;
+               $server['agent'] = $this->agent;
+
+               // Create a live connection object
+               try {
+                       $db = DatabaseBase::factory( $server['type'], $server );
+               } catch ( DBConnectionError $e ) {
+                       // FIXME: This is probably the ugliest thing I have ever done to
+                       // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
+                       $db = $e->db;
+               }
+
+               $db->setLBInfo( $server );
+               $db->setLazyMasterHandle(
+                       $this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() )
+               );
+               $db->setTableAliases( $this->tableAliases );
+
+               if ( $server['serverIndex'] === $this->getWriterIndex() ) {
+                       if ( $this->trxRoundId !== false ) {
+                               $this->applyTransactionRoundFlags( $db );
+                       }
+                       foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
+                               $db->setTransactionListener( $name, $callback );
+                       }
+               }
+
+               return $db;
+       }
+
+       /**
+        * @throws DBConnectionError
+        * @return bool
+        */
+       private function reportConnectionError() {
+               $conn = $this->mErrorConnection; // The connection which caused the error
+               $context = [
+                       'method' => __METHOD__,
+                       'last_error' => $this->mLastError,
+               ];
+
+               if ( !is_object( $conn ) ) {
+                       // No last connection, probably due to all servers being too busy
+                       $this->connLogger->error(
+                               "LB failure with no last connection. Connection error: {last_error}",
+                               $context
+                       );
+
+                       // If all servers were busy, mLastError will contain something sensible
+                       throw new DBConnectionError( null, $this->mLastError );
+               } else {
+                       $context['db_server'] = $conn->getProperty( 'mServer' );
+                       $this->connLogger->warning(
+                               "Connection error: {last_error} ({db_server})",
+                               $context
+                       );
+
+                       // throws DBConnectionError
+                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" );
+               }
+
+               return false; /* not reached */
+       }
+
+       public function getWriterIndex() {
+               return 0;
+       }
+
+       public function haveIndex( $i ) {
+               return array_key_exists( $i, $this->mServers );
+       }
+
+       public function isNonZeroLoad( $i ) {
+               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
+       }
+
+       public function getServerCount() {
+               return count( $this->mServers );
+       }
+
+       public function getServerName( $i ) {
+               if ( isset( $this->mServers[$i]['hostName'] ) ) {
+                       $name = $this->mServers[$i]['hostName'];
+               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
+                       $name = $this->mServers[$i]['host'];
+               } else {
+                       $name = '';
+               }
+
+               return ( $name != '' ) ? $name : 'localhost';
+       }
+
+       public function getServerInfo( $i ) {
+               if ( isset( $this->mServers[$i] ) ) {
+                       return $this->mServers[$i];
+               } else {
+                       return false;
+               }
+       }
+
+       public function setServerInfo( $i, array $serverInfo ) {
+               $this->mServers[$i] = $serverInfo;
+       }
+
+       public function getMasterPos() {
+               # If this entire request was served from a replica DB without opening a connection to the
+               # master (however unlikely that may be), then we can fetch the position from the replica DB.
+               $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
+               if ( !$masterConn ) {
+                       $serverCount = count( $this->mServers );
+                       for ( $i = 1; $i < $serverCount; $i++ ) {
+                               $conn = $this->getAnyOpenConnection( $i );
+                               if ( $conn ) {
+                                       return $conn->getSlavePos();
+                               }
+                       }
+               } else {
+                       return $masterConn->getMasterPos();
+               }
+
+               return false;
+       }
+
+       /**
+        * Disable this load balancer. All connections are closed, and any attempt to
+        * open a new connection will result in a DBAccessError.
+        *
+        * @since 1.27
+        */
+       public function disable() {
+               $this->closeAll();
+               $this->disabled = true;
+       }
+
+       public function closeAll() {
+               $this->forEachOpenConnection( function ( IDatabase $conn ) {
+                       $conn->close();
+               } );
+
+               $this->mConns = [
+                       'local' => [],
+                       'foreignFree' => [],
+                       'foreignUsed' => [],
+               ];
+               $this->connsOpened = 0;
+       }
+
+       public function closeConnection( IDatabase $conn ) {
+               $serverIndex = $conn->getLBInfo( 'serverIndex' ); // second index level of mConns
+               foreach ( $this->mConns as $type => $connsByServer ) {
+                       if ( !isset( $connsByServer[$serverIndex] ) ) {
+                               continue;
+                       }
+
+                       foreach ( $connsByServer[$serverIndex] as $i => $trackedConn ) {
+                               if ( $conn === $trackedConn ) {
+                                       unset( $this->mConns[$type][$serverIndex][$i] );
+                                       --$this->connsOpened;
+                                       break 2;
+                               }
+                       }
+               }
+
+               $conn->close();
+       }
+
+       public function commitAll( $fname = __METHOD__ ) {
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenConnection(
+                       function ( IDatabase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                               } catch ( DBError $e ) {
+                                       call_user_func( $this->errorLogger, $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore && $conn->getLBInfo( 'master' ) ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
+                       }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
+       }
+
+       /**
+        * Perform all pre-commit callbacks that remain part of the atomic transactions
+        * and disable any post-commit callbacks until runMasterPostTrxCallbacks()
+        * @since 1.28
+        */
+       public function finalizeMasterChanges() {
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+                       // Any error should cause all DB transactions to be rolled back together
+                       $conn->setTrxEndCallbackSuppression( false );
+                       $conn->runOnTransactionPreCommitCallbacks();
+                       // Defer post-commit callbacks until COMMIT finishes for all DBs
+                       $conn->setTrxEndCallbackSuppression( true );
+               } );
+       }
+
+       /**
+        * Perform all pre-commit checks for things like replication safety
+        * @param array $options Includes:
+        *   - maxWriteDuration : max write query duration time in seconds
+        * @throws DBTransactionError
+        * @since 1.28
+        */
+       public function approveMasterChanges( array $options ) {
+               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
+               $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( $limit ) {
+                       // If atomic sections or explicit transactions are still open, some caller must have
+                       // caught an exception but failed to properly rollback any changes. Detect that and
+                       // throw and error (causing rollback).
+                       if ( $conn->explicitTrxActive() ) {
+                               throw new DBTransactionError(
+                                       $conn,
+                                       "Explicit transaction still active. A caller may have caught an error."
+                               );
+                       }
+                       // Assert that the time to replicate the transaction will be sane.
+                       // If this fails, then all DB transactions will be rollback back together.
+                       $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );
+                       if ( $limit > 0 && $time > $limit ) {
+                               throw new DBTransactionSizeError(
+                                       $conn,
+                                       "Transaction spent $time second(s) in writes, exceeding the $limit limit.",
+                                       [ $time, $limit ]
+                               );
+                       }
+                       // If a connection sits idle while slow queries execute on another, that connection
+                       // may end up dropped before the commit round is reached. Ping servers to detect this.
+                       if ( $conn->writesOrCallbacksPending() && !$conn->ping() ) {
+                               throw new DBTransactionError(
+                                       $conn,
+                                       "A connection to the {$conn->getDBname()} database was lost before commit."
+                               );
+                       }
+               } );
+       }
+
+       /**
+        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+        *
+        * The DBO_TRX setting will be reverted to the default in each of these methods:
+        *   - commitMasterChanges()
+        *   - rollbackMasterChanges()
+        *   - commitAll()
+        * This allows for custom transaction rounds from any outer transaction scope.
+        *
+        * @param string $fname
+        * @throws DBExpectedError
+        * @since 1.28
+        */
+       public function beginMasterChanges( $fname = __METHOD__ ) {
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: Transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+
+               $failures = [];
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, &$failures ) {
+                               $conn->setTrxEndCallbackSuppression( true );
+                               try {
+                                       $conn->flushSnapshot( $fname );
+                               } catch ( DBError $e ) {
+                                       call_user_func( $this->errorLogger, $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               $conn->setTrxEndCallbackSuppression( false );
+                               $this->applyTransactionRoundFlags( $conn );
+                       }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Flush failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
+       }
+
+       public function commitMasterChanges( $fname = __METHOD__ ) {
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( IDatabase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       if ( $conn->writesOrCallbacksPending() ) {
+                                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                                       } elseif ( $restore ) {
+                                               $conn->flushSnapshot( $fname );
+                                       }
+                               } catch ( DBError $e ) {
+                                       call_user_func( $this->errorLogger, $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
+                       }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
+       }
+
+       /**
+        * Issue all pending post-COMMIT/ROLLBACK callbacks
+        * @param integer $type IDatabase::TRIGGER_* constant
+        * @return Exception|null The first exception or null if there were none
+        * @since 1.28
+        */
+       public function runMasterPostTrxCallbacks( $type ) {
+               $e = null; // first exception
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $type, &$e ) {
+                       $conn->setTrxEndCallbackSuppression( false );
+                       if ( $conn->writesOrCallbacksPending() ) {
+                               // This happens if onTransactionIdle() callbacks leave callbacks on *another* DB
+                               // (which finished its callbacks already). Warn and recover in this case. Let the
+                               // callbacks run in the final commitMasterChanges() in LBFactory::shutdown().
+                               $this->queryLogger->error( __METHOD__ . ": found writes/callbacks pending." );
+                               return;
+                       } elseif ( $conn->trxLevel() ) {
+                               // This happens for single-DB setups where DB_REPLICA uses the master DB,
+                               // thus leaving an implicit read-only transaction open at this point. It
+                               // also happens if onTransactionIdle() callbacks leave implicit transactions
+                               // open on *other* DBs (which is slightly improper). Let these COMMIT on the
+                               // next call to commitMasterChanges(), possibly in LBFactory::shutdown().
+                               return;
+                       }
+                       try {
+                               $conn->runOnTransactionIdleCallbacks( $type );
+                       } catch ( Exception $ex ) {
+                               $e = $e ?: $ex;
+                       }
+                       try {
+                               $conn->runTransactionListenerCallbacks( $type );
+                       } catch ( Exception $ex ) {
+                               $e = $e ?: $ex;
+                       }
+               } );
+
+               return $e;
+       }
+
+       /**
+        * Issue ROLLBACK only on master, only if queries were done on connection
+        * @param string $fname Caller name
+        * @throws DBExpectedError
+        * @since 1.23
+        */
+       public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( IDatabase $conn ) use ( $fname, $restore ) {
+                               if ( $conn->writesOrCallbacksPending() ) {
+                                       $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
+                       }
+               );
+       }
+
+       /**
+        * Suppress all pending post-COMMIT/ROLLBACK callbacks
+        * @return Exception|null The first exception or null if there were none
+        * @since 1.28
+        */
+       public function suppressTransactionEndCallbacks() {
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+                       $conn->setTrxEndCallbackSuppression( true );
+               } );
+       }
+
+       /**
+        * @param IDatabase $conn
+        */
+       private function applyTransactionRoundFlags( IDatabase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
+                       // Force DBO_TRX even in CLI mode since a commit round is expected soon.
+                       $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
+                       // If config has explicitly requested DBO_TRX be either on or off by not
+                       // setting DBO_DEFAULT, then respect that. Forcing no transactions is useful
+                       // for things like blob stores (ExternalStore) which want auto-commit mode.
+               }
+       }
+
+       /**
+        * @param IDatabase $conn
+        */
+       private function undoTransactionRoundFlags( IDatabase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       $conn->restoreFlags( $conn::RESTORE_PRIOR );
+               }
+       }
+
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        * @since 1.28
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+               $this->forEachOpenReplicaConnection( function ( IDatabase $conn ) {
+                       $conn->flushSnapshot( __METHOD__ );
+               } );
+       }
+
+       /**
+        * @return bool Whether a master connection is already open
+        * @since 1.24
+        */
+       public function hasMasterConnection() {
+               return $this->isOpen( $this->getWriterIndex() );
+       }
+
+       /**
+        * Determine if there are pending changes in a transaction by this thread
+        * @since 1.23
+        * @return bool
+        */
+       public function hasMasterChanges() {
+               $pending = 0;
+               $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$pending ) {
+                       $pending |= $conn->writesOrCallbacksPending();
+               } );
+
+               return (bool)$pending;
+       }
+
+       /**
+        * Get the timestamp of the latest write query done by this thread
+        * @since 1.25
+        * @return float|bool UNIX timestamp or false
+        */
+       public function lastMasterChangeTimestamp() {
+               $lastTime = false;
+               $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$lastTime ) {
+                       $lastTime = max( $lastTime, $conn->lastDoneWrites() );
+               } );
+
+               return $lastTime;
+       }
+
+       /**
+        * Check if this load balancer object had any recent or still
+        * pending writes issued against it by this PHP thread
+        *
+        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
+        * @return bool
+        * @since 1.25
+        */
+       public function hasOrMadeRecentMasterChanges( $age = null ) {
+               $age = ( $age === null ) ? $this->mWaitTimeout : $age;
+
+               return ( $this->hasMasterChanges()
+                       || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
+       }
+
+       /**
+        * Get the list of callers that have pending master changes
+        *
+        * @return string[] List of method names
+        * @since 1.27
+        */
+       public function pendingMasterChangeCallers() {
+               $fnames = [];
+               $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$fnames ) {
+                       $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
+               } );
+
+               return $fnames;
+       }
+
+       public function getLaggedReplicaMode( $domain = false ) {
+               // No-op if there is only one DB (also avoids recursion)
+               if ( !$this->laggedReplicaMode && $this->getServerCount() > 1 ) {
+                       try {
+                               // See if laggedReplicaMode gets set
+                               $conn = $this->getConnection( DB_REPLICA, false, $domain );
+                               $this->reuseConnection( $conn );
+                       } catch ( DBConnectionError $e ) {
+                               // Avoid expensive re-connect attempts and failures
+                               $this->allReplicasDownMode = true;
+                               $this->laggedReplicaMode = true;
+                       }
+               }
+
+               return $this->laggedReplicaMode;
+       }
+
+       /**
+        * @param bool $domain
+        * @return bool
+        * @deprecated 1.28; use getLaggedReplicaMode()
+        */
+       public function getLaggedSlaveMode( $domain = false ) {
+               return $this->getLaggedReplicaMode( $domain );
+       }
+
+       /**
+        * @note This method will never cause a new DB connection
+        * @return bool Whether any generic connection used for reads was highly "lagged"
+        * @since 1.28
+        */
+       public function laggedReplicaUsed() {
+               return $this->laggedReplicaMode;
+       }
+
+       /**
+        * @return bool
+        * @since 1.27
+        * @deprecated Since 1.28; use laggedReplicaUsed()
+        */
+       public function laggedSlaveUsed() {
+               return $this->laggedReplicaUsed();
+       }
+
+       /**
+        * @note This method may trigger a DB connection if not yet done
+        * @param string|bool $domain Domain ID, or false for the current domain
+        * @param IDatabase|null DB master connection; used to avoid loops [optional]
+        * @return string|bool Reason the master is read-only or false if it is not
+        * @since 1.27
+        */
+       public function getReadOnlyReason( $domain = false, IDatabase $conn = null ) {
+               if ( $this->readOnlyReason !== false ) {
+                       return $this->readOnlyReason;
+               } elseif ( $this->getLaggedReplicaMode( $domain ) ) {
+                       if ( $this->allReplicasDownMode ) {
+                               return 'The database has been automatically locked ' .
+                                       'until the replica database servers become available';
+                       } else {
+                               return 'The database has been automatically locked ' .
+                                       'while the replica database servers catch up to the master.';
+                       }
+               } elseif ( $this->masterRunningReadOnly( $domain, $conn ) ) {
+                       return 'The database master is running in read-only mode.';
+               }
+
+               return false;
+       }
+
+       /**
+        * @param string $domain Domain ID, or false for the current domain
+        * @param IDatabase|null DB master connectionl used to avoid loops [optional]
+        * @return bool
+        */
+       private function masterRunningReadOnly( $domain, IDatabase $conn = null ) {
+               $cache = $this->wanCache;
+               $masterServer = $this->getServerName( $this->getWriterIndex() );
+
+               return (bool)$cache->getWithSetCallback(
+                       $cache->makeGlobalKey( __CLASS__, 'server-read-only', $masterServer ),
+                       self::TTL_CACHE_READONLY,
+                       function () use ( $domain, $conn ) {
+                               $this->trxProfiler->setSilenced( true );
+                               try {
+                                       $dbw = $conn ?: $this->getConnection( DB_MASTER, [], $domain );
+                                       $readOnly = (int)$dbw->serverIsReadOnly();
+                               } catch ( DBError $e ) {
+                                       $readOnly = 0;
+                               }
+                               $this->trxProfiler->setSilenced( false );
+                               return $readOnly;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG, 'busyValue' => 0 ]
+               );
+       }
+
+       public function allowLagged( $mode = null ) {
+               if ( $mode === null ) {
+                       return $this->mAllowLagged;
+               }
+               $this->mAllowLagged = $mode;
+
+               return $this->mAllowLagged;
+       }
+
+       public function pingAll() {
+               $success = true;
+               $this->forEachOpenConnection( function ( IDatabase $conn ) use ( &$success ) {
+                       if ( !$conn->ping() ) {
+                               $success = false;
+                       }
+               } );
+
+               return $success;
+       }
+
+       public function forEachOpenConnection( $callback, array $params = [] ) {
+               foreach ( $this->mConns as $connsByServer ) {
+                       foreach ( $connsByServer as $serverConns ) {
+                               foreach ( $serverConns as $conn ) {
+                                       $mergedParams = array_merge( [ $conn ], $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Call a function with each open connection object to a master
+        * @param callable $callback
+        * @param array $params
+        * @since 1.28
+        */
+       public function forEachOpenMasterConnection( $callback, array $params = [] ) {
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $connsByServer ) {
+                       if ( isset( $connsByServer[$masterIndex] ) ) {
+                               /** @var IDatabase $conn */
+                               foreach ( $connsByServer[$masterIndex] as $conn ) {
+                                       $mergedParams = array_merge( [ $conn ], $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Call a function with each open replica DB connection object
+        * @param callable $callback
+        * @param array $params
+        * @since 1.28
+        */
+       public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
+               foreach ( $this->mConns as $connsByServer ) {
+                       foreach ( $connsByServer as $i => $serverConns ) {
+                               if ( $i === $this->getWriterIndex() ) {
+                                       continue; // skip master
+                               }
+                               foreach ( $serverConns as $conn ) {
+                                       $mergedParams = array_merge( [ $conn ], $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       public function getMaxLag( $domain = false ) {
+               $maxLag = -1;
+               $host = '';
+               $maxIndex = 0;
+
+               if ( $this->getServerCount() <= 1 ) {
+                       return [ $host, $maxLag, $maxIndex ]; // no replication = no lag
+               }
+
+               $lagTimes = $this->getLagTimes( $domain );
+               foreach ( $lagTimes as $i => $lag ) {
+                       if ( $this->mLoads[$i] > 0 && $lag > $maxLag ) {
+                               $maxLag = $lag;
+                               $host = $this->mServers[$i]['host'];
+                               $maxIndex = $i;
+                       }
+               }
+
+               return [ $host, $maxLag, $maxIndex ];
+       }
+
+       public function getLagTimes( $domain = false ) {
+               if ( $this->getServerCount() <= 1 ) {
+                       return [ 0 => 0 ]; // no replication = no lag
+               }
+
+               # Send the request to the load monitor
+               return $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $domain );
+       }
+
+       public function safeGetLag( IDatabase $conn ) {
+               if ( $this->getServerCount() == 1 ) {
+                       return 0;
+               } else {
+                       return $conn->getLag();
+               }
+       }
+
+       /**
+        * Wait for a replica DB to reach a specified master position
+        *
+        * This will connect to the master to get an accurate position if $pos is not given
+        *
+        * @param IDatabase $conn Replica DB
+        * @param DBMasterPos|bool $pos Master position; default: current position
+        * @param integer $timeout Timeout in seconds
+        * @return bool Success
+        * @since 1.27
+        */
+       public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
+               if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'replica' ) ) {
+                       return true; // server is not a replica DB
+               }
+
+               $pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
+               if ( !( $pos instanceof DBMasterPos ) ) {
+                       return false; // something is misconfigured
+               }
+
+               $result = $conn->masterPosWait( $pos, $timeout );
+               if ( $result == -1 || is_null( $result ) ) {
+                       $msg = __METHOD__ . ": Timed out waiting on {$conn->getServer()} pos {$pos}";
+                       $this->replLogger->warning( "$msg" );
+                       $ok = false;
+               } else {
+                       $this->replLogger->info( __METHOD__ . ": Done" );
+                       $ok = true;
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Clear the cache for slag lag delay times
+        *
+        * This is only used for testing
+        * @since 1.26
+        */
+       public function clearLagTimeCache() {
+               $this->getLoadMonitor()->clearCaches();
+       }
+
+       /**
+        * Set a callback via IDatabase::setTransactionListener() on
+        * all current and future master connections of this load balancer
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->trxRecurringCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->trxRecurringCallbacks[$name] );
+               }
+               $this->forEachOpenMasterConnection(
+                       function ( IDatabase $conn ) use ( $name, $callback ) {
+                               $conn->setTransactionListener( $name, $callback );
+                       }
+               );
+       }
+
+       /**
+        * Make certain table names use their own database, schema, and table prefix
+        * when passed into SQL queries pre-escaped and without a qualified database name
+        *
+        * For example, "user" can be converted to "myschema.mydbname.user" for convenience.
+        * Appearances like `user`, somedb.user, somedb.someschema.user will used literally.
+        *
+        * Calling this twice will completely clear any old table aliases. Also, note that
+        * callers are responsible for making sure the schemas and databases actually exist.
+        *
+        * @param array[] $aliases Map of (table => (dbname, schema, prefix) map)
+        * @since 1.28
+        */
+       public function setTableAliases( array $aliases ) {
+               $this->tableAliases = $aliases;
+       }
+
+       /**
+        * Set a new table prefix for the existing local domain ID for testing
+        *
+        * @param string $prefix
+        * @since 1.28
+        */
+       public function setDomainPrefix( $prefix ) {
+               $this->localDomain = new DatabaseDomain(
+                       $this->localDomain->getDatabase(),
+                       null,
+                       $prefix
+               );
+
+               $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) {
+                       $db->tablePrefix( $prefix );
+               } );
+       }
+}
diff --git a/includes/libs/rdbms/loadmonitor/ILoadMonitor.php b/includes/libs/rdbms/loadmonitor/ILoadMonitor.php
new file mode 100644 (file)
index 0000000..e355c03
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Database load monitoring interface
+ *
+ * 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 Database
+ */
+use Psr\Log\LoggerAwareInterface;
+
+/**
+ * An interface for database load monitoring
+ *
+ * @ingroup Database
+ */
+interface ILoadMonitor extends LoggerAwareInterface {
+       /**
+        * Construct a new LoadMonitor with a given LoadBalancer parent
+        *
+        * @param ILoadBalancer $lb LoadBalancer this instance serves
+        * @param BagOStuff $sCache Local server memory cache
+        * @param BagOStuff $cCache Local cluster memory cache
+        */
+       public function __construct( ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache );
+
+       /**
+        * Perform pre-connection load ratio adjustment.
+        * @param int[] &$loads
+        * @param string|bool $group The selected query group. Default: false
+        * @param string|bool $domain Default: false
+        */
+       public function scaleLoads( &$loads, $group = false, $domain = false );
+
+       /**
+        * Get an estimate of replication lag (in seconds) for each server
+        *
+        * Values may be "false" if replication is too broken to estimate
+        *
+        * @param integer[] $serverIndexes
+        * @param string $domain
+        *
+        * @return array Map of (server index => float|int|bool)
+        */
+       public function getLagTimes( $serverIndexes, $domain );
+
+       /**
+        * Clear any process and persistent cache of lag times
+        * @since 1.27
+        */
+       public function clearCaches();
+}
diff --git a/includes/libs/rdbms/loadmonitor/LoadMonitor.php b/includes/libs/rdbms/loadmonitor/LoadMonitor.php
new file mode 100644 (file)
index 0000000..1da8f4e
--- /dev/null
@@ -0,0 +1,157 @@
+<?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 Database
+ */
+
+use Psr\Log\LoggerInterface;
+
+/**
+ * Basic DB load monitor with no external dependencies
+ * Uses memcached to cache the replication lag for a short time
+ *
+ * @ingroup Database
+ */
+class LoadMonitor implements ILoadMonitor {
+       /** @var ILoadBalancer */
+       protected $parent;
+       /** @var BagOStuff */
+       protected $srvCache;
+       /** @var BagOStuff */
+       protected $mainCache;
+       /** @var LoggerInterface */
+       protected $replLogger;
+
+       public function __construct( ILoadBalancer $lb, BagOStuff $srvCache, BagOStuff $cache ) {
+               $this->parent = $lb;
+               $this->srvCache = $srvCache;
+               $this->mainCache = $cache;
+               $this->replLogger = new \Psr\Log\NullLogger();
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->replLogger = $logger;
+       }
+
+       public function scaleLoads( &$loads, $group = false, $domain = false ) {
+       }
+
+       public function getLagTimes( $serverIndexes, $domain ) {
+               if ( count( $serverIndexes ) == 1 && reset( $serverIndexes ) == 0 ) {
+                       # Single server only, just return zero without caching
+                       return [ 0 => 0 ];
+               }
+
+               $key = $this->getLagTimeCacheKey();
+               # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec)
+               $ttl = mt_rand( 4e6, 5e6 ) / 1e6;
+               # Keep keys around longer as fallbacks
+               $staleTTL = 60;
+
+               # (a) Check the local APC cache
+               $value = $this->srvCache->get( $key );
+               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
+                       $this->replLogger->debug( __METHOD__ . ": got lag times ($key) from local cache" );
+                       return $value['lagTimes']; // cache hit
+               }
+               $staleValue = $value ?: false;
+
+               # (b) Check the shared cache and backfill APC
+               $value = $this->mainCache->get( $key );
+               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
+                       $this->srvCache->set( $key, $value, $staleTTL );
+                       $this->replLogger->debug( __METHOD__ . ": got lag times ($key) from main cache" );
+
+                       return $value['lagTimes']; // cache hit
+               }
+               $staleValue = $value ?: $staleValue;
+
+               # (c) Cache key missing or expired; regenerate and backfill
+               if ( $this->mainCache->lock( $key, 0, 10 ) ) {
+                       # Let this process alone update the cache value
+                       $cache = $this->mainCache;
+                       /** @noinspection PhpUnusedLocalVariableInspection */
+                       $unlocker = new ScopedCallback( function () use ( $cache, $key ) {
+                               $cache->unlock( $key );
+                       } );
+               } elseif ( $staleValue ) {
+                       # Could not acquire lock but an old cache exists, so use it
+                       return $staleValue['lagTimes'];
+               }
+
+               $lagTimes = [];
+               foreach ( $serverIndexes as $i ) {
+                       if ( $i == $this->parent->getWriterIndex() ) {
+                               $lagTimes[$i] = 0; // master always has no lag
+                               continue;
+                       }
+
+                       $conn = $this->parent->getAnyOpenConnection( $i );
+                       if ( $conn ) {
+                               $close = false; // already open
+                       } else {
+                               $conn = $this->parent->openConnection( $i, $domain );
+                               $close = true; // new connection
+                       }
+
+                       if ( !$conn ) {
+                               $lagTimes[$i] = false;
+                               $host = $this->parent->getServerName( $i );
+                               $this->replLogger->error( __METHOD__ . ": host $host (#$i) is unreachable" );
+                               continue;
+                       }
+
+                       $lagTimes[$i] = $conn->getLag();
+                       if ( $lagTimes[$i] === false ) {
+                               $host = $this->parent->getServerName( $i );
+                               $this->replLogger->error( __METHOD__ . ": host $host (#$i) is not replicating?" );
+                       }
+
+                       if ( $close ) {
+                               # Close the connection to avoid sleeper connections piling up.
+                               # Note that the caller will pick one of these DBs and reconnect,
+                               # which is slightly inefficient, but this only matters for the lag
+                               # time cache miss cache, which is far less common that cache hits.
+                               $this->parent->closeConnection( $conn );
+                       }
+               }
+
+               # Add a timestamp key so we know when it was cached
+               $value = [ 'lagTimes' => $lagTimes, 'timestamp' => microtime( true ) ];
+               $this->mainCache->set( $key, $value, $staleTTL );
+               $this->srvCache->set( $key, $value, $staleTTL );
+               $this->replLogger->info( __METHOD__ . ": re-calculated lag times ($key)" );
+
+               return $value['lagTimes'];
+       }
+
+       public function clearCaches() {
+               $key = $this->getLagTimeCacheKey();
+               $this->srvCache->delete( $key );
+               $this->mainCache->delete( $key );
+       }
+
+       private function getLagTimeCacheKey() {
+               $writerIndex = $this->parent->getWriterIndex();
+               // Lag is per-server, not per-DB, so key on the master DB name
+               return $this->srvCache->makeGlobalKey(
+                       'lag-times',
+                       $this->parent->getServerName( $writerIndex )
+               );
+       }
+}
diff --git a/includes/libs/rdbms/loadmonitor/LoadMonitorMySQL.php b/includes/libs/rdbms/loadmonitor/LoadMonitorMySQL.php
new file mode 100644 (file)
index 0000000..7286417
--- /dev/null
@@ -0,0 +1,33 @@
+<?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 Database
+ */
+
+/**
+ * Basic MySQL load monitor with no external dependencies
+ * Uses memcached to cache the replication lag for a short time
+ *
+ * @ingroup Database
+ */
+class LoadMonitorMySQL extends LoadMonitor {
+       public function scaleLoads( &$loads, $group = false, $domain = false ) {
+               // @TODO: maybe use Threads_running/Threads_created ratio to guess load
+               // and Queries/Uptime to guess if a server is warming up the buffer pool
+       }
+}
diff --git a/includes/libs/rdbms/loadmonitor/LoadMonitorNull.php b/includes/libs/rdbms/loadmonitor/LoadMonitorNull.php
new file mode 100644 (file)
index 0000000..8062001
--- /dev/null
@@ -0,0 +1,42 @@
+<?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 Database
+ */
+use Psr\Log\LoggerInterface;
+
+class LoadMonitorNull implements ILoadMonitor {
+       public function __construct( ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache ) {
+
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+       }
+
+       public function scaleLoads( &$loads, $group = false, $domain = false ) {
+
+       }
+
+       public function getLagTimes( $serverIndexes, $domain ) {
+               return array_fill_keys( $serverIndexes, 0 );
+       }
+
+       public function clearCaches() {
+
+       }
+}
diff --git a/includes/libs/time/ConvertableTimestamp.php b/includes/libs/time/ConvertableTimestamp.php
new file mode 100644 (file)
index 0000000..af7eca6
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Creation, parsing, and conversion of timestamps.
+ *
+ * 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
+ * @since 1.20
+ * @author Tyler Romeo, 2012
+ */
+
+/**
+ * Library for creating, parsing, and converting timestamps. Based on the JS
+ * library that does the same thing.
+ *
+ * @since 1.28
+ */
+class ConvertableTimestamp {
+       /**
+        * Standard gmdate() formats for the different timestamp types.
+        */
+       private static $formats = [
+               TS_UNIX => 'U',
+               TS_MW => 'YmdHis',
+               TS_DB => 'Y-m-d H:i:s',
+               TS_ISO_8601 => 'Y-m-d\TH:i:s\Z',
+               TS_ISO_8601_BASIC => 'Ymd\THis\Z',
+               TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness
+               TS_RFC2822 => 'D, d M Y H:i:s',
+               TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500
+               TS_POSTGRES => 'Y-m-d H:i:s',
+       ];
+
+       /**
+        * The actual timestamp being wrapped (DateTime object).
+        * @var DateTime
+        */
+       public $timestamp;
+
+       /**
+        * Make a new timestamp and set it to the specified time,
+        * or the current time if unspecified.
+        *
+        * @param bool|string|int|float|DateTime $timestamp Timestamp to set, or false for current time
+        */
+       public function __construct( $timestamp = false ) {
+               if ( $timestamp instanceof DateTime ) {
+                       $this->timestamp = $timestamp;
+               } else {
+                       $this->setTimestamp( $timestamp );
+               }
+       }
+
+       /**
+        * Set the timestamp to the specified time, or the current time if unspecified.
+        *
+        * Parse the given timestamp into either a DateTime object or a Unix timestamp,
+        * and then store it.
+        *
+        * @param string|bool $ts Timestamp to store, or false for now
+        * @throws TimestampException
+        */
+       public function setTimestamp( $ts = false ) {
+               $m = [];
+               $da = [];
+               $strtime = '';
+
+               // We want to catch 0, '', null... but not date strings starting with a letter.
+               if ( !$ts || $ts === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ) {
+                       $uts = time();
+                       $strtime = "@$uts";
+               } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
+                       # TS_DB
+               } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
+                       # TS_EXIF
+               } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) {
+                       # TS_MW
+               } elseif ( preg_match( '/^(-?\d{1,13})(\.\d+)?$/D', $ts, $m ) ) {
+                       # TS_UNIX
+                       $strtime = "@{$m[1]}"; // http://php.net/manual/en/datetime.formats.compound.php
+               } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) {
+                       # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
+                       $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
+                               str_replace( '+00:00', 'UTC', $ts ) );
+               } elseif ( preg_match(
+                       '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z?$/',
+                       $ts,
+                       $da
+               ) ) {
+                       # TS_ISO_8601
+               } elseif ( preg_match(
+                       '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z?$/',
+                       $ts,
+                       $da
+               ) ) {
+                       # TS_ISO_8601_BASIC
+               } elseif ( preg_match(
+                       '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/',
+                       $ts,
+                       $da
+               ) ) {
+                       # TS_POSTGRES
+               } elseif ( preg_match(
+                       '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/',
+                       $ts,
+                       $da
+               ) ) {
+                       # TS_POSTGRES
+               } elseif ( preg_match(
+               # Day of week
+                       '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' .
+                       # dd Mon yyyy
+                       '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' .
+                       # hh:mm:ss
+                       '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S',
+                       $ts
+               ) ) {
+                       # TS_RFC2822, accepting a trailing comment.
+                       # See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171
+                       # The regex is a superset of rfc2822 for readability
+                       $strtime = strtok( $ts, ';' );
+               } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) {
+                       # TS_RFC850
+                       $strtime = $ts;
+               } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) {
+                       # asctime
+                       $strtime = $ts;
+               } else {
+                       throw new TimestampException( __METHOD__ . ": Invalid timestamp - $ts" );
+               }
+
+               if ( !$strtime ) {
+                       $da = array_map( 'intval', $da );
+                       $da[0] = "%04d-%02d-%02dT%02d:%02d:%02d.00+00:00";
+                       $strtime = call_user_func_array( "sprintf", $da );
+               }
+
+               try {
+                       $final = new DateTime( $strtime, new DateTimeZone( 'GMT' ) );
+               } catch ( Exception $e ) {
+                       throw new TimestampException( __METHOD__ . ': Invalid timestamp format.', $e->getCode(), $e );
+               }
+
+               if ( $final === false ) {
+                       throw new TimestampException( __METHOD__ . ': Invalid timestamp format.' );
+               }
+
+               $this->timestamp = $final;
+       }
+
+       /**
+        * Get the timestamp represented by this object in a certain form.
+        *
+        * Convert the internal timestamp to the specified format and then
+        * return it.
+        *
+        * @param int $style Constant Output format for timestamp
+        * @throws TimestampException
+        * @return string The formatted timestamp
+        */
+       public function getTimestamp( $style = TS_UNIX ) {
+               if ( !isset( self::$formats[$style] ) ) {
+                       throw new TimestampException( __METHOD__ . ': Illegal timestamp output type.' );
+               }
+
+               $output = $this->timestamp->format( self::$formats[$style] );
+
+               if ( ( $style == TS_RFC2822 ) || ( $style == TS_POSTGRES ) ) {
+                       $output .= ' GMT';
+               }
+
+               if ( $style == TS_MW && strlen( $output ) !== 14 ) {
+                       throw new TimestampException( __METHOD__ . ': The timestamp cannot be represented in ' .
+                               'the specified format' );
+               }
+
+               return $output;
+       }
+
+       /**
+        * @return string
+        */
+       public function __toString() {
+               return $this->getTimestamp();
+       }
+
+       /**
+        * Calculate the difference between two ConvertableTimestamp objects.
+        *
+        * @param ConvertableTimestamp $relativeTo Base time to calculate difference from
+        * @return DateInterval|bool The DateInterval object representing the
+        *   difference between the two dates or false on failure
+        */
+       public function diff( ConvertableTimestamp $relativeTo ) {
+               return $this->timestamp->diff( $relativeTo->timestamp );
+       }
+
+       /**
+        * Set the timezone of this timestamp to the specified timezone.
+        *
+        * @param string $timezone Timezone to set
+        * @throws TimestampException
+        */
+       public function setTimezone( $timezone ) {
+               try {
+                       $this->timestamp->setTimezone( new DateTimeZone( $timezone ) );
+               } catch ( Exception $e ) {
+                       throw new TimestampException( __METHOD__ . ': Invalid timezone.', $e->getCode(), $e );
+               }
+       }
+
+       /**
+        * Get the timezone of this timestamp.
+        *
+        * @return DateTimeZone The timezone
+        */
+       public function getTimezone() {
+               return $this->timestamp->getTimezone();
+       }
+
+       /**
+        * Format the timestamp in a given format.
+        *
+        * @param string $format Pattern to format in
+        * @return string The formatted timestamp
+        */
+       public function format( $format ) {
+               return $this->timestamp->format( $format );
+       }
+}
diff --git a/includes/libs/time/TimestampException.php b/includes/libs/time/TimestampException.php
new file mode 100644 (file)
index 0000000..36ffdee
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+/**
+ * @since 1.20
+ */
+class TimestampException extends Exception {
+}
diff --git a/includes/libs/time/defines.php b/includes/libs/time/defines.php
new file mode 100644 (file)
index 0000000..ff4dde8
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC
+ */
+define( 'TS_UNIX', 0 );
+
+/**
+ * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
+ */
+define( 'TS_MW', 1 );
+
+/**
+ * MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
+ */
+define( 'TS_DB', 2 );
+
+/**
+ * RFC 2822 format, for E-mail and HTTP headers
+ */
+define( 'TS_RFC2822', 3 );
+
+/**
+ * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z
+ *
+ * This is used by Special:Export
+ */
+define( 'TS_ISO_8601', 4 );
+
+/**
+ * An Exif timestamp (YYYY:MM:DD HH:MM:SS)
+ *
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
+ *       DateTime tag and page 36 for the DateTimeOriginal and
+ *       DateTimeDigitized tags.
+ */
+define( 'TS_EXIF', 5 );
+
+/**
+ * Oracle format time.
+ */
+define( 'TS_ORACLE', 6 );
+
+/**
+ * Postgres format time.
+ */
+define( 'TS_POSTGRES', 7 );
+
+/**
+ * ISO 8601 basic format with no timezone: 19860209T200000Z.  This is used by ResourceLoader
+ */
+define( 'TS_ISO_8601_BASIC', 9 );
index 20d0217..7746d99 100644 (file)
@@ -705,39 +705,41 @@ class ManualLogEntry extends LogEntryBase {
         *
         * @param int $newId Id of the log entry.
         * @param string $to One of: rcandudp (default), rc, udp
-        * @return RecentChange|null
         */
        public function publish( $newId, $to = 'rcandudp' ) {
-               $log = new LogPage( $this->getType() );
-               if ( $log->isRestricted() ) {
-                       return null;
-               }
-
-               $rc = $this->getRecentChange( $newId );
-
-               if ( $to === 'rc' || $to === 'rcandudp' ) {
-                       $rc->save( 'pleasedontudp' );
-               }
-
-               if ( $to === 'udp' || $to === 'rcandudp' ) {
-                       $rc->notifyRCFeeds();
-               }
-
-               // Log the autopatrol if the log entry is patrollable
-               if ( $this->getIsPatrollable() &&
-                       $rc->getAttribute( 'rc_patrolled' ) === 1 ) {
-                       PatrolLog::record( $rc, true, $this->getPerformer() );
-               }
-
-               // Add change tags to the log entry and (if applicable) the associated revision
-               $tags = $this->getTags();
-               if ( !is_null( $tags ) ) {
-                       $rcId = $rc->getAttribute( 'rc_id' );
-                       $revId = $this->getAssociatedRevId(); // Use null if $revId is 0
-                       ChangeTags::addTags( $tags, $rcId, $revId > 0 ? $revId : null, $newId );
-               }
-
-               return $rc;
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $newId, $to ) {
+                               $log = new LogPage( $this->getType() );
+                               if ( !$log->isRestricted() ) {
+                                       $rc = $this->getRecentChange( $newId );
+
+                                       if ( $to === 'rc' || $to === 'rcandudp' ) {
+                                               $rc->save( 'pleasedontudp' );
+                                       }
+
+                                       if ( $to === 'udp' || $to === 'rcandudp' ) {
+                                               $rc->notifyRCFeeds();
+                                       }
+
+                                       // Log the autopatrol if the log entry is patrollable
+                                       if ( $this->getIsPatrollable() &&
+                                               $rc->getAttribute( 'rc_patrolled' ) === 1
+                                       ) {
+                                               PatrolLog::record( $rc, true, $this->getPerformer() );
+                                       }
+
+                                       // Add change tags to the log entry and (if applicable) the associated revision
+                                       $tags = $this->getTags();
+                                       if ( !is_null( $tags ) ) {
+                                               $rcId = $rc->getAttribute( 'rc_id' );
+                                               $revId = $this->getAssociatedRevId(); // Use null if $revId is 0
+                                               ChangeTags::addTags( $tags, $rcId, $revId > 0 ? $revId : null, $newId );
+                                       }
+                               }
+                       },
+                       DeferredUpdates::POSTSEND,
+                       wfGetDB( DB_MASTER )
+               );
        }
 
        public function getType() {
index 3d04641..68163c1 100644 (file)
@@ -78,7 +78,7 @@ class LogPager extends ReverseChronologicalPager {
                $this->getDateCond( $year, $month );
                $this->mTagFilter = $tagFilter;
 
-               $this->mDb = wfGetDB( DB_SLAVE, 'logpager' );
+               $this->mDb = wfGetDB( DB_REPLICA, 'logpager' );
        }
 
        public function getDefaultQuery() {
@@ -171,6 +171,9 @@ class LogPager extends ReverseChronologicalPager {
                if ( is_null( $usertitle ) ) {
                        return;
                }
+               // Normalize username first so that non-existent users used
+               // in maintenance scripts work
+               $name = $usertitle->getText();
                /* Fetch userid at first, if known, provides awesome query plan afterwards */
                $userid = User::idFromName( $name );
                if ( !$userid ) {
@@ -187,7 +190,7 @@ class LogPager extends ReverseChronologicalPager {
                                ' != ' . LogPage::SUPPRESSED_USER;
                }
 
-               $this->performer = $usertitle->getText();
+               $this->performer = $name;
        }
 
        /**
diff --git a/includes/objectcache/MemcachedPeclBagOStuff.php b/includes/objectcache/MemcachedPeclBagOStuff.php
deleted file mode 100644 (file)
index bb760bd..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-/**
- * Object caching using memcached.
- *
- * 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 Cache
- */
-
-/**
- * A wrapper class for the PECL memcached client
- *
- * @ingroup Cache
- */
-class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
-
-       /**
-        * Constructor
-        *
-        * Available parameters are:
-        *   - servers:             The list of IP:port combinations holding the memcached servers.
-        *   - persistent:          Whether to use a persistent connection
-        *   - compress_threshold:  The minimum size an object must be before it is compressed
-        *   - timeout:             The read timeout in microseconds
-        *   - connect_timeout:     The connect timeout in seconds
-        *   - retry_timeout:       Time in seconds to wait before retrying a failed connect attempt
-        *   - server_failure_limit:  Limit for server connect failures before it is removed
-        *   - serializer:          May be either "php" or "igbinary". Igbinary produces more compact
-        *                          values, but serialization is much slower unless the php.ini option
-        *                          igbinary.compact_strings is off.
-        * @param array $params
-        * @throws InvalidArgumentException
-        */
-       function __construct( $params ) {
-               parent::__construct( $params );
-               $params = $this->applyDefaultParams( $params );
-
-               if ( $params['persistent'] ) {
-                       // The pool ID must be unique to the server/option combination.
-                       // The Memcached object is essentially shared for each pool ID.
-                       // We can only reuse a pool ID if we keep the config consistent.
-                       $this->client = new Memcached( md5( serialize( $params ) ) );
-                       if ( count( $this->client->getServerList() ) ) {
-                               $this->logger->debug( __METHOD__ . ": persistent Memcached object already loaded." );
-                               return; // already initialized; don't add duplicate servers
-                       }
-               } else {
-                       $this->client = new Memcached;
-               }
-
-               if ( !isset( $params['serializer'] ) ) {
-                       $params['serializer'] = 'php';
-               }
-
-               if ( isset( $params['retry_timeout'] ) ) {
-                       $this->client->setOption( Memcached::OPT_RETRY_TIMEOUT, $params['retry_timeout'] );
-               }
-
-               if ( isset( $params['server_failure_limit'] ) ) {
-                       $this->client->setOption( Memcached::OPT_SERVER_FAILURE_LIMIT, $params['server_failure_limit'] );
-               }
-
-               // The compression threshold is an undocumented php.ini option for some
-               // reason. There's probably not much harm in setting it globally, for
-               // compatibility with the settings for the PHP client.
-               ini_set( 'memcached.compression_threshold', $params['compress_threshold'] );
-
-               // Set timeouts
-               $this->client->setOption( Memcached::OPT_CONNECT_TIMEOUT, $params['connect_timeout'] * 1000 );
-               $this->client->setOption( Memcached::OPT_SEND_TIMEOUT, $params['timeout'] );
-               $this->client->setOption( Memcached::OPT_RECV_TIMEOUT, $params['timeout'] );
-               $this->client->setOption( Memcached::OPT_POLL_TIMEOUT, $params['timeout'] / 1000 );
-
-               // Set libketama mode since it's recommended by the documentation and
-               // is as good as any. There's no way to configure libmemcached to use
-               // hashes identical to the ones currently in use by the PHP client, and
-               // even implementing one of the libmemcached hashes in pure PHP for
-               // forwards compatibility would require MemcachedClient::get_sock() to be
-               // rewritten.
-               $this->client->setOption( Memcached::OPT_LIBKETAMA_COMPATIBLE, true );
-
-               // Set the serializer
-               switch ( $params['serializer'] ) {
-                       case 'php':
-                               $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP );
-                               break;
-                       case 'igbinary':
-                               if ( !Memcached::HAVE_IGBINARY ) {
-                                       throw new InvalidArgumentException(
-                                               __CLASS__ . ': the igbinary extension is not available ' .
-                                               'but igbinary serialization was requested.'
-                                       );
-                               }
-                               $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY );
-                               break;
-                       default:
-                               throw new InvalidArgumentException(
-                                       __CLASS__ . ': invalid value for serializer parameter'
-                               );
-               }
-               $servers = [];
-               foreach ( $params['servers'] as $host ) {
-                       $servers[] = IP::splitHostAndPort( $host ); // (ip, port)
-               }
-               $this->client->addServers( $servers );
-       }
-
-       protected function getWithToken( $key, &$casToken, $flags = 0 ) {
-               $this->debugLog( "get($key)" );
-               $result = $this->client->get( $this->validateKeyEncoding( $key ), null, $casToken );
-               $result = $this->checkResult( $key, $result );
-               return $result;
-       }
-
-       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
-               $this->debugLog( "set($key)" );
-               return $this->checkResult( $key, parent::set( $key, $value, $exptime ) );
-       }
-
-       protected function cas( $casToken, $key, $value, $exptime = 0 ) {
-               $this->debugLog( "cas($key)" );
-               return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) );
-       }
-
-       public function delete( $key ) {
-               $this->debugLog( "delete($key)" );
-               $result = parent::delete( $key );
-               if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) {
-                       // "Not found" is counted as success in our interface
-                       return true;
-               } else {
-                       return $this->checkResult( $key, $result );
-               }
-       }
-
-       public function add( $key, $value, $exptime = 0 ) {
-               $this->debugLog( "add($key)" );
-               return $this->checkResult( $key, parent::add( $key, $value, $exptime ) );
-       }
-
-       public function incr( $key, $value = 1 ) {
-               $this->debugLog( "incr($key)" );
-               $result = $this->client->increment( $key, $value );
-               return $this->checkResult( $key, $result );
-       }
-
-       public function decr( $key, $value = 1 ) {
-               $this->debugLog( "decr($key)" );
-               $result = $this->client->decrement( $key, $value );
-               return $this->checkResult( $key, $result );
-       }
-
-       /**
-        * Check the return value from a client method call and take any necessary
-        * action. Returns the value that the wrapper function should return. At
-        * present, the return value is always the same as the return value from
-        * the client, but some day we might find a case where it should be
-        * different.
-        *
-        * @param string $key The key used by the caller, or false if there wasn't one.
-        * @param mixed $result The return value
-        * @return mixed
-        */
-       protected function checkResult( $key, $result ) {
-               if ( $result !== false ) {
-                       return $result;
-               }
-               switch ( $this->client->getResultCode() ) {
-                       case Memcached::RES_SUCCESS:
-                               break;
-                       case Memcached::RES_DATA_EXISTS:
-                       case Memcached::RES_NOTSTORED:
-                       case Memcached::RES_NOTFOUND:
-                               $this->debugLog( "result: " . $this->client->getResultMessage() );
-                               break;
-                       default:
-                               $msg = $this->client->getResultMessage();
-                               $logCtx = [];
-                               if ( $key !== false ) {
-                                       $server = $this->client->getServerByKey( $key );
-                                       $logCtx['memcached-server'] = "{$server['host']}:{$server['port']}";
-                                       $logCtx['memcached-key'] = $key;
-                                       $msg = "Memcached error for key \"{memcached-key}\" on server \"{memcached-server}\": $msg";
-                               } else {
-                                       $msg = "Memcached error: $msg";
-                               }
-                               $this->logger->error( $msg, $logCtx );
-                               $this->setLastError( BagOStuff::ERR_UNEXPECTED );
-               }
-               return $result;
-       }
-
-       public function getMulti( array $keys, $flags = 0 ) {
-               $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
-               foreach ( $keys as $key ) {
-                       $this->validateKeyEncoding( $key );
-               }
-               $result = $this->client->getMulti( $keys ) ?: [];
-               return $this->checkResult( false, $result );
-       }
-
-       /**
-        * @param array $data
-        * @param int $exptime
-        * @return bool
-        */
-       public function setMulti( array $data, $exptime = 0 ) {
-               $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' );
-               foreach ( array_keys( $data ) as $key ) {
-                       $this->validateKeyEncoding( $key );
-               }
-               $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
-               return $this->checkResult( false, $result );
-       }
-
-       public function changeTTL( $key, $expiry = 0 ) {
-               $this->debugLog( "touch($key)" );
-               $result = $this->client->touch( $key, $expiry );
-               return $this->checkResult( $key, $result );
-       }
-}
index bcdf62f..1e0013f 100644 (file)
@@ -39,7 +39,7 @@ use MediaWiki\Services\ServiceDisabledException;
  *        stored anywhere else (e.g. a "hoard" of objects).
  *
  * The former should always use strongly consistent stores, so callers don't
- * have to deal with stale reads. The later may be eventually consistent, but
+ * have to deal with stale reads. The latter may be eventually consistent, but
  * callers can use BagOStuff:READ_LATEST to see the latest available data.
  *
  * Primary entry points:
@@ -65,7 +65,7 @@ use MediaWiki\Services\ServiceDisabledException;
  *   Purpose: Ephemeral global storage.
  *   Stored centrally within the primary data-center.
  *   Changes are applied there first and replicated to other DCs (best-effort).
- *   To retrieve the latest value (e.g. not from a slave), use BagOStuff::READ_LATEST.
+ *   To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST.
  *   This store may be subject to LRU style evictions.
  *
  * - ObjectCache::getInstance( $cacheType )
index c3e0c96..64cd686 100644 (file)
@@ -83,6 +83,8 @@ class RedisBagOStuff extends BagOStuff {
                } else {
                        $this->automaticFailover = true;
                }
+
+               $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_NONE;
        }
 
        protected function doGet( $key, $flags = 0 ) {
@@ -363,7 +365,7 @@ class RedisBagOStuff extends BagOStuff {
                                try {
                                        if ( $this->getMasterLinkStatus( $conn ) === 'down' ) {
                                                // If the master cannot be reached, fail-over to the next server.
-                                               // If masters are in data-center A, and slaves in data-center B,
+                                               // If masters are in data-center A, and replica DBs in data-center B,
                                                // this helps avoid the case were fail-over happens in A but not
                                                // to the corresponding server in B (e.g. read/write mismatch).
                                                continue;
@@ -384,10 +386,10 @@ class RedisBagOStuff extends BagOStuff {
        }
 
        /**
-        * Check the master link status of a Redis server that is configured as a slave.
+        * Check the master link status of a Redis server that is configured as a replica DB.
         * @param RedisConnRef $conn
         * @return string|null Master link status (either 'up' or 'down'), or null
-        *  if the server is not a slave.
+        *  if the server is not a replica DB.
         */
        protected function getMasterLinkStatus( RedisConnRef $conn ) {
                $info = $conn->info();
index 7a89991..d06213f 100644 (file)
@@ -44,7 +44,7 @@ class SqlBagOStuff extends BagOStuff {
        /** @var string */
        protected $tableName = 'objectcache';
        /** @var bool */
-       protected $slaveOnly = false;
+       protected $replicaOnly = false;
        /** @var int */
        protected $syncTimeout = 3;
 
@@ -85,11 +85,11 @@ class SqlBagOStuff extends BagOStuff {
         *                  required to hold the largest shard index. Data will be
         *                  distributed across all tables by key hash. This is for
         *                  MySQL bugs 61735 and 61736.
-        *   - slaveOnly:   Whether to only use slave DBs and avoid triggering
+        *   - slaveOnly:   Whether to only use replica DBs and avoid triggering
         *                  garbage collection logic of expired items. This only
         *                  makes sense if the primary DB is used and only if get()
         *                  calls will be used. This is used by ReplicatedBagOStuff.
-        *   - syncTimeout: Max seconds to wait for slaves to catch up for WRITE_SYNC.
+        *   - syncTimeout: Max seconds to wait for replica DBs to catch up for WRITE_SYNC.
         *
         * @param array $params
         */
@@ -97,6 +97,7 @@ class SqlBagOStuff extends BagOStuff {
                parent::__construct( $params );
 
                $this->attrMap[self::ATTR_EMULATION] = self::QOS_EMULATION_SQL;
+               $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_NONE;
 
                if ( isset( $params['servers'] ) ) {
                        $this->serverInfos = [];
@@ -119,6 +120,7 @@ class SqlBagOStuff extends BagOStuff {
                        // Default to using the main wiki's database servers
                        $this->serverInfos = false;
                        $this->numServers = 1;
+                       $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_BE;
                }
                if ( isset( $params['purgePeriod'] ) ) {
                        $this->purgePeriod = intval( $params['purgePeriod'] );
@@ -132,7 +134,7 @@ class SqlBagOStuff extends BagOStuff {
                if ( isset( $params['syncTimeout'] ) ) {
                        $this->syncTimeout = $params['syncTimeout'];
                }
-               $this->slaveOnly = !empty( $params['slaveOnly'] );
+               $this->replicaOnly = !empty( $params['slaveOnly'] );
        }
 
        protected function getSeparateMainLB() {
@@ -183,7 +185,7 @@ class SqlBagOStuff extends BagOStuff {
                                $db = DatabaseBase::factory( $type, $info );
                                $db->clearFlag( DBO_TRX );
                        } else {
-                               $index = $this->slaveOnly ? DB_SLAVE : DB_MASTER;
+                               $index = $this->replicaOnly ? DB_REPLICA : DB_MASTER;
                                if ( $this->getSeparateMainLB() ) {
                                        $db = $this->getSeparateMainLB()->getConnection( $index );
                                        $db->clearFlag( DBO_TRX ); // auto-commit mode
@@ -377,7 +379,7 @@ class SqlBagOStuff extends BagOStuff {
        public function set( $key, $value, $exptime = 0, $flags = 0 ) {
                $ok = $this->setMulti( [ $key => $value ], $exptime );
                if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
-                       $ok = $ok && $this->waitForSlaves();
+                       $ok = $this->waitForReplication() && $ok;
                }
 
                return $ok;
@@ -489,7 +491,7 @@ class SqlBagOStuff extends BagOStuff {
        public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
                $ok = $this->mergeViaCas( $key, $callback, $exptime, $attempts );
                if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
-                       $ok = $ok && $this->waitForSlaves();
+                       $ok = $this->waitForReplication() && $ok;
                }
 
                return $ok;
@@ -539,7 +541,7 @@ class SqlBagOStuff extends BagOStuff {
        }
 
        protected function garbageCollect() {
-               if ( !$this->purgePeriod || $this->slaveOnly ) {
+               if ( !$this->purgePeriod || $this->replicaOnly ) {
                        // Disabled
                        return;
                }
@@ -797,22 +799,30 @@ class SqlBagOStuff extends BagOStuff {
                return !$this->serverInfos;
        }
 
-       protected function waitForSlaves() {
-               if ( $this->usesMainDB() ) {
-                       $lb = $this->getSeparateMainLB()
-                               ?: MediaWikiServices::getInstance()->getDBLoadBalancer();
-                       // Main LB is used; wait for any slaves to catch up
-                       try {
-                               $pos = $lb->getMasterPos();
-                               if ( $pos ) {
-                                       return $lb->waitForAll( $pos, 3 );
-                               }
-                       } catch ( DBReplicationWaitError $e ) {
-                               return false;
-                       }
+       protected function waitForReplication() {
+               if ( !$this->usesMainDB() ) {
+                       // Custom DB server list; probably doesn't use replication
+                       return true;
                }
 
-               // Custom DB server list; probably doesn't use replication
-               return true;
+               $lb = $this->getSeparateMainLB()
+                       ?: MediaWikiServices::getInstance()->getDBLoadBalancer();
+
+               if ( $lb->getServerCount() <= 1 ) {
+                       return true; // no replica DBs
+               }
+
+               // Main LB is used; wait for any replica DBs to catch up
+               $masterPos = $lb->getMasterPos();
+
+               $loop = new WaitConditionLoop(
+                       function () use ( $lb, $masterPos ) {
+                               return $lb->waitForAll( $masterPos, 1 );
+                       },
+                       $this->syncTimeout,
+                       $this->busyCallbacks
+               );
+
+               return ( $loop->invoke() === $loop::CONDITION_REACHED );
        }
 }
index b3a97f7..db6df86 100644 (file)
@@ -99,9 +99,7 @@ class Article implements Page {
         */
        public static function newFromID( $id ) {
                $t = Title::newFromID( $id );
-               # @todo FIXME: Doesn't inherit right
-               return $t == null ? null : new self( $t );
-               # return $t == null ? null : new static( $t ); // PHP 5.3
+               return $t == null ? null : new static( $t );
        }
 
        /**
@@ -149,6 +147,15 @@ class Article implements Page {
                return $article;
        }
 
+       /**
+        * Get the page this view was redirected from
+        * @return Title|null
+        * @since 1.28
+        */
+       public function getRedirectedFrom() {
+               return $this->mRedirectedFrom;
+       }
+
        /**
         * Tell the page view functions that this view was redirected
         * from another page on the wiki.
@@ -467,7 +474,7 @@ class Article implements Page {
         * page of the given title.
         */
        public function view() {
-               global $wgUseFileCache, $wgDebugToolbar, $wgMaxRedirects;
+               global $wgUseFileCache, $wgDebugToolbar;
 
                # Get variables from query string
                # As side effect this will load the revision and update the title
@@ -520,29 +527,6 @@ class Article implements Page {
 
                # Try client and file cache
                if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
-                       # Use the greatest of the page's timestamp or the timestamp of any
-                       # redirect in the chain (bug 67849)
-                       $timestamp = $this->mPage->getTouched();
-                       if ( isset( $this->mRedirectedFrom ) ) {
-                               $timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
-
-                               # If there can be more than one redirect in the chain, we have
-                               # to go through the whole chain too in case an intermediate
-                               # redirect was changed.
-                               if ( $wgMaxRedirects > 1 ) {
-                                       $titles = Revision::newFromTitle( $this->mRedirectedFrom )
-                                               ->getContent( Revision::FOR_THIS_USER, $user )
-                                               ->getRedirectChain();
-                                       $thisTitle = $this->getTitle();
-                                       foreach ( $titles as $title ) {
-                                               if ( Title::compare( $title, $thisTitle ) === 0 ) {
-                                                       break;
-                                               }
-                                               $timestamp = max( $timestamp, $title->getTouched() );
-                                       }
-                               }
-                       }
-
                        # Try to stream the output from file cache
                        if ( $wgUseFileCache && $this->tryFileCache() ) {
                                wfDebug( __METHOD__ . ": done file cache\n" );
@@ -1082,7 +1066,7 @@ class Article implements Page {
                        return false;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $oldestRevisionTimestamp = $dbr->selectField(
                        'revision',
                        'MIN( rev_timestamp )',
@@ -1165,7 +1149,7 @@ class Article implements Page {
 
                if ( !$rc ) {
                        // Don't cache: This can be hit if the page gets accessed very fast after
-                       // its creation / latest upload or in case we have high slave lag. In case
+                       // its creation / latest upload or in case we have high replica DB lag. In case
                        // the revision is too old, we will already return above.
                        return false;
                }
@@ -1719,7 +1703,7 @@ class Article implements Page {
                        // This, as a side-effect, also makes sure that the following query isn't being run for
                        // pages with a larger history, unless the user has the 'bigdelete' right
                        // (and is about to delete this page).
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $revisions = $edits = (int)$dbr->selectField(
                                'revision',
                                'COUNT(rev_page)',
@@ -2154,8 +2138,16 @@ class Article implements Page {
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::doPurge
         */
-       public function doPurge() {
-               return $this->mPage->doPurge();
+       public function doPurge( $flags = WikiPage::PURGE_ALL ) {
+               return $this->mPage->doPurge( $flags );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getLastPurgeTimestamp
+        */
+       public function getLastPurgeTimestamp() {
+               return $this->mPage->getLastPurgeTimestamp();
        }
 
        /**
@@ -2341,9 +2333,10 @@ class Article implements Page {
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getText
+        * @deprecated since 1.21 use WikiPage::getContent() instead
         */
        public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
-               ContentHandler::deprecated( __METHOD__, '1.21' );
+               wfDeprecated( __METHOD__, '1.21' );
                return $this->mPage->getText( $audience, $user );
        }
 
index d493002..865471c 100644 (file)
@@ -43,18 +43,6 @@ class CategoryPage extends Article {
                return new WikiCategoryPage( $title );
        }
 
-       /**
-        * Constructor from a page id
-        * @param int $id Article ID to load
-        * @return CategoryPage|null
-        */
-       public static function newFromID( $id ) {
-               $t = Title::newFromID( $id );
-               # @todo FIXME: Doesn't inherit right
-               return $t == null ? null : new self( $t );
-               # return $t == null ? null : new static( $t ); // PHP 5.3
-       }
-
        function view() {
                $request = $this->getContext()->getRequest();
                $diff = $request->getVal( 'diff' );
index 607357f..bb8ed24 100644 (file)
@@ -180,7 +180,6 @@ class ImageHistoryList extends ContextSource {
                                        [
                                                'action' => 'revert',
                                                'oldimage' => $img,
-                                               'wpEditToken' => $user->getEditToken( $img )
                                        ]
                                );
                        }
index 1396685..af77868 100644 (file)
@@ -52,18 +52,6 @@ class ImagePage extends Article {
                return new WikiFilePage( $title );
        }
 
-       /**
-        * Constructor from a page id
-        * @param int $id Article ID to load
-        * @return ImagePage|null
-        */
-       public static function newFromID( $id ) {
-               $t = Title::newFromID( $id );
-               # @todo FIXME: Doesn't inherit right
-               return $t == null ? null : new self( $t );
-               # return $t == null ? null : new static( $t ); // PHP 5.3
-       }
-
        /**
         * @param File $file
         * @return void
@@ -810,7 +798,7 @@ EOT
         * @return ResultWrapper
         */
        protected function queryImageLinks( $target, $limit ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return $dbr->select(
                        [ 'imagelinks', 'page' ],
index 0dc28bd..c478550 100644 (file)
@@ -162,12 +162,9 @@ class WikiFilePage extends WikiPage {
                return $this->mDupes;
        }
 
-       /**
-        * Override handling of action=purge
-        * @return bool
-        */
-       public function doPurge() {
+       public function doPurge( $flags = self::PURGE_ALL ) {
                $this->loadFile();
+
                if ( $this->mFile->exists() ) {
                        wfDebug( 'ImagePage::doPurge purging ' . $this->mFile->getName() . "\n" );
                        DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->mTitle, 'imagelinks' ) );
@@ -183,7 +180,8 @@ class WikiFilePage extends WikiPage {
                        // Purge redirect cache
                        $this->mRepo->invalidateImageRedirect( $this->mTitle );
                }
-               return parent::doPurge();
+
+               return parent::doPurge( $flags );
        }
 
        /**
index 4066501..faac26d 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use \MediaWiki\Logger\LoggerFactory;
+use \MediaWiki\MediaWikiServices;
 
 /**
  * Class representing a MediaWiki article and history.
@@ -82,6 +83,11 @@ class WikiPage implements Page, IDBAccessObject {
         */
        protected $mLinksUpdated = '19700101000000';
 
+       const PURGE_CDN_CACHE = 1; // purge CDN cache for page variant URLs
+       const PURGE_CLUSTER_PCACHE = 2; // purge parser cache in the local datacenter
+       const PURGE_GLOBAL_PCACHE = 4; // set page_touched to clear parser cache in all datacenters
+       const PURGE_ALL = 7;
+
        /**
         * Constructor and clear the article
         * @param Title $title Reference to a Title object.
@@ -134,7 +140,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param int $id Article ID to load
         * @param string|int $from One of the following values:
-        *        - "fromdb" or WikiPage::READ_NORMAL to select from a slave database
+        *        - "fromdb" or WikiPage::READ_NORMAL to select from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database
         *
         * @return WikiPage|null
@@ -146,7 +152,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $from = self::convertSelectType( $from );
-               $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA );
                $row = $db->selectRow(
                        'page', self::selectFields(), [ 'page_id' => $id ], __METHOD__ );
                if ( !$row ) {
@@ -161,7 +167,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.20
         * @param object $row Database row containing at least fields returned by selectFields().
         * @param string|int $from Source of $data:
-        *        - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
+        *        - "fromdb" or WikiPage::READ_NORMAL: from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
         *        - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
         * @return WikiPage
@@ -346,7 +352,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param object|string|int $from One of the following:
         *   - A DB query result object.
-        *   - "fromdb" or WikiPage::READ_NORMAL to get from a slave DB.
+        *   - "fromdb" or WikiPage::READ_NORMAL to get from a replica DB.
         *   - "fromdbmaster" or WikiPage::READ_LATEST to get from the master DB.
         *   - "forupdate"  or WikiPage::READ_LOCKING to get from the master DB
         *     using SELECT FOR UPDATE.
@@ -365,7 +371,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
 
                        if ( !$data
-                               && $index == DB_SLAVE
+                               && $index == DB_REPLICA
                                && wfGetLB()->getServerCount() > 1
                                && wfGetLB()->hasOrMadeRecentMasterChanges()
                        ) {
@@ -374,7 +380,7 @@ class WikiPage implements Page, IDBAccessObject {
                                $data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
                        }
                } else {
-                       // No idea from where the caller got this data, assume slave database.
+                       // No idea from where the caller got this data, assume replica DB.
                        $data = $from;
                        $from = self::READ_NORMAL;
                }
@@ -388,7 +394,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.20
         * @param object|bool $data DB row containing fields returned by selectFields() or false
         * @param string|int $from One of the following:
-        *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
+        *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
         *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from
         *          the master DB using SELECT FOR UPDATE
@@ -459,7 +465,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @return bool
         */
        public function hasViewableContent() {
-               return $this->exists() || $this->mTitle->isAlwaysKnown();
+               return $this->mTitle->isKnown();
        }
 
        /**
@@ -487,15 +493,23 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function getContentModel() {
                if ( $this->exists() ) {
-                       // look at the revision's actual content model
-                       $rev = $this->getRevision();
-
-                       if ( $rev !== null ) {
-                               return $rev->getContentModel();
-                       } else {
-                               $title = $this->mTitle->getPrefixedDBkey();
-                               wfWarn( "Page $title exists but has no (visible) revisions!" );
-                       }
+                       $cache = ObjectCache::getMainWANInstance();
+
+                       return $cache->getWithSetCallback(
+                               $cache->makeKey( 'page', 'content-model', $this->getLatest() ),
+                               $cache::TTL_MONTH,
+                               function () {
+                                       $rev = $this->getRevision();
+                                       if ( $rev ) {
+                                               // Look at the revision's actual content model
+                                               return $rev->getContentModel();
+                                       } else {
+                                               $title = $this->mTitle->getPrefixedDBkey();
+                                               wfWarn( "Page $title exists but has no (visible) revisions!" );
+                                               return $this->mTitle->getContentModel();
+                                       }
+                               }
+                       );
                }
 
                // use the default model for this page
@@ -552,9 +566,9 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function getOldestRevision() {
 
-               // Try using the slave database first, then try the master
+               // Try using the replica DB first, then try the master
                $continue = 2;
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
                $revSelectFields = Revision::selectFields();
 
                $row = null;
@@ -607,15 +621,18 @@ class WikiPage implements Page, IDBAccessObject {
                        // happened after the first S1 SELECT.
                        // http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read
                        $flags = Revision::READ_LOCKING;
+                       $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
                } elseif ( $this->mDataLoadedFrom == self::READ_LATEST ) {
                        // Bug T93976: if page_latest was loaded from the master, fetch the
-                       // revision from there as well, as it may not exist yet on a slave DB.
+                       // revision from there as well, as it may not exist yet on a replica DB.
                        // Also, this keeps the queries in the same REPEATABLE-READ snapshot.
                        $flags = Revision::READ_LATEST;
+                       $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
                } else {
-                       $flags = 0;
+                       $dbr = wfGetDB( DB_REPLICA );
+                       $revision = Revision::newKnownCurrent( $dbr, $this->getId(), $latest );
                }
-               $revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
+
                if ( $revision ) { // sanity
                        $this->setLastEdit( $revision );
                }
@@ -831,7 +848,7 @@ class WikiPage implements Page, IDBAccessObject {
                                // links.
                                $hasLinks = (bool)count( $editInfo->output->getLinks() );
                        } else {
-                               $hasLinks = (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
+                               $hasLinks = (bool)wfGetDB( DB_REPLICA )->selectField( 'pagelinks', 1,
                                        [ 'pl_from' => $this->getId() ], __METHOD__ );
                        }
                }
@@ -856,7 +873,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Query the redirect table
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'redirect',
                        [ 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ],
                        [ 'rd_from' => $this->getId() ],
@@ -989,7 +1006,7 @@ class WikiPage implements Page, IDBAccessObject {
        public function getContributors() {
                // @todo FIXME: This is expensive; cache this info somewhere.
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $dbr->implicitGroupby() ) {
                        $realNameField = 'user_real_name';
@@ -1110,19 +1127,38 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Perform the actions of a page purging
+        * @param integer $flags Bitfield of WikiPage::PURGE_* constants
         * @return bool
         */
-       public function doPurge() {
+       public function doPurge( $flags = self::PURGE_ALL ) {
                if ( !Hooks::run( 'ArticlePurge', [ &$this ] ) ) {
                        return false;
                }
 
-               $this->mTitle->invalidateCache();
-               // Send purge after above page_touched update was committed
-               DeferredUpdates::addUpdate(
-                       new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
-                       DeferredUpdates::PRESEND
-               );
+               if ( ( $flags & self::PURGE_GLOBAL_PCACHE ) == self::PURGE_GLOBAL_PCACHE ) {
+                       // Set page_touched in the database to invalidate all DC caches
+                       $this->mTitle->invalidateCache();
+               } elseif ( ( $flags & self::PURGE_CLUSTER_PCACHE ) == self::PURGE_CLUSTER_PCACHE ) {
+                       // Delete the parser options key in the local cluster to invalidate the DC cache
+                       ParserCache::singleton()->deleteOptionsKey( $this );
+                       // Avoid sending HTTP 304s in ViewAction to the client who just issued the purge
+                       $cache = ObjectCache::getLocalClusterInstance();
+                       $cache->set(
+                               $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ),
+                               wfTimestamp( TS_MW ),
+                               $cache::TTL_HOUR
+                       );
+               }
+
+               if ( ( $flags & self::PURGE_CDN_CACHE ) == self::PURGE_CDN_CACHE ) {
+                       // Clear any HTML file cache
+                       HTMLFileCache::clearFileCache( $this->getTitle() );
+                       // Send purge after any page_touched above update was committed
+                       DeferredUpdates::addUpdate(
+                               new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
+                               DeferredUpdates::PRESEND
+                       );
+               }
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
                        // @todo move this logic to MessageCache
@@ -1146,6 +1182,18 @@ class WikiPage implements Page, IDBAccessObject {
                return true;
        }
 
+       /**
+        * Get the last time a user explicitly purged the page via action=purge
+        *
+        * @return string|bool TS_MW timestamp or false
+        * @since 1.28
+        */
+       public function getLastPurgeTimestamp() {
+               $cache = ObjectCache::getLocalClusterInstance();
+
+               return $cache->get( $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ) );
+       }
+
        /**
         * Insert a new empty page record for this article.
         * This *must* be followed up by creating a revision
@@ -1386,7 +1434,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $baseRevId = null;
                if ( $edittime && $sectionId !== 'new' ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
                        // Try the master if this thread may have just added it.
                        // This could be abstracted into a Revision method, but we don't want
@@ -1595,10 +1643,15 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function doEditContent(
                Content $content, $summary, $flags = 0, $baseRevId = false,
-               User $user = null, $serialFormat = null, $tags = null
+               User $user = null, $serialFormat = null, $tags = []
        ) {
                global $wgUser, $wgUseAutomaticEditSummaries;
 
+               // Old default parameter for $tags was null
+               if ( $tags === null ) {
+                       $tags = [];
+               }
+
                // Low-level sanity check
                if ( $this->mTitle->getText() === '' ) {
                        throw new MWException( 'Something is trying to edit an article with an empty title' );
@@ -1638,6 +1691,10 @@ class WikiPage implements Page, IDBAccessObject {
                $old_revision = $this->getRevision(); // current revision
                $old_content = $this->getContent( Revision::RAW ); // current revision's content
 
+               if ( $old_content && $old_content->getModel() !== $content->getModel() ) {
+                       $tags[] = 'mw-contentmodelchange';
+               }
+
                // Provide autosummaries if one is not provided and autosummaries are enabled
                if ( $wgUseAutomaticEditSummaries && ( $flags & EDIT_AUTOSUMMARY ) && $summary == '' ) {
                        $handler = $content->getContentHandler();
@@ -2110,7 +2167,7 @@ class WikiPage implements Page, IDBAccessObject {
                                // We get here if vary-revision is set. This means that this page references
                                // itself (such as via self-transclusion). In this case, we need to make sure
                                // that any such self-references refer to the newly-saved revision, and not
-                               // to the previous one, which could otherwise happen due to slave lag.
+                               // to the previous one, which could otherwise happen due to replica DB lag.
                                $oldCallback = $edit->popts->getCurrentRevisionCallback();
                                $edit->popts->setCurrentRevisionCallback(
                                        function ( Title $title, $parser = false ) use ( $revision, &$oldCallback ) {
@@ -2713,14 +2770,14 @@ class WikiPage implements Page, IDBAccessObject {
                $protectDescription = '';
 
                foreach ( array_filter( $limit ) as $action => $restrictions ) {
-                       # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
+                       # $action is one of $wgRestrictionTypes = [ 'create', 'edit', 'move', 'upload' ].
                        # All possible message keys are listed here for easier grepping:
                        # * restriction-create
                        # * restriction-edit
                        # * restriction-move
                        # * restriction-upload
                        $actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
-                       # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
+                       # $restrictions is one of $wgRestrictionLevels = [ '', 'autoconfirmed', 'sysop' ],
                        # with '' filtered out. All possible message keys are listed below:
                        # * protect-level-autoconfirmed
                        # * protect-level-sysop
@@ -2883,7 +2940,14 @@ class WikiPage implements Page, IDBAccessObject {
                // unless they actually try to catch exceptions (which is rare).
 
                // we need to remember the old content so we can use it to generate all deletion updates.
-               $content = $this->getContent( Revision::RAW );
+               try {
+                       $content = $this->getContent( Revision::RAW );
+               } catch ( Exception $ex ) {
+                       wfLogWarning( __METHOD__ . ': failed to load content during deletion! '
+                               . $ex->getMessage() );
+
+                       $content = null;
+               }
 
                // Bitfields to further suppress the content
                if ( $suppress ) {
@@ -2893,8 +2957,9 @@ class WikiPage implements Page, IDBAccessObject {
                        $bitfield |= Revision::DELETED_COMMENT;
                        $bitfield |= Revision::DELETED_USER;
                        $bitfield |= Revision::DELETED_RESTRICTED;
+                       $deletionFields = [ $dbw->addQuotes( $bitfield ) . ' AS deleted' ];
                } else {
-                       $bitfield = 'rev_deleted';
+                       $deletionFields = [ 'rev_deleted AS deleted' ];
                }
 
                // For now, shunt the revision data into the archive table.
@@ -2905,9 +2970,10 @@ class WikiPage implements Page, IDBAccessObject {
                // the rev_deleted field, which is reserved for this purpose.
 
                // Get all of the page revisions
+               $fields = array_diff( Revision::selectFields(), [ 'rev_deleted' ] );
                $res = $dbw->select(
                        'revision',
-                       Revision::selectFields(),
+                       array_merge( $fields, $deletionFields ),
                        [ 'rev_page' => $id ],
                        __METHOD__,
                        'FOR UPDATE'
@@ -2930,7 +2996,7 @@ class WikiPage implements Page, IDBAccessObject {
                                'ar_flags'      => '',
                                'ar_len'        => $row->rev_len,
                                'ar_page_id'    => $id,
-                               'ar_deleted'    => $bitfield,
+                               'ar_deleted'    => $row->deleted,
                                'ar_sha1'       => $row->rev_sha1,
                        ];
                        if ( $wgContentHandlerUseDB ) {
@@ -2965,10 +3031,13 @@ class WikiPage implements Page, IDBAccessObject {
                $logEntry->setComment( $reason );
                $logid = $logEntry->insert();
 
-               $dbw->onTransactionPreCommitOrIdle( function () use ( $dbw, $logEntry, $logid ) {
-                       // Bug 56776: avoid deadlocks (especially from FileDeleteForm)
-                       $logEntry->publish( $logid );
-               } );
+               $dbw->onTransactionPreCommitOrIdle(
+                       function () use ( $dbw, $logEntry, $logid ) {
+                               // Bug 56776: avoid deadlocks (especially from FileDeleteForm)
+                               $logEntry->publish( $logid );
+                       },
+                       __METHOD__
+               );
 
                $dbw->endAtomic( __METHOD__ );
 
@@ -3024,8 +3093,16 @@ class WikiPage implements Page, IDBAccessObject {
         *   may already return null when the page proper was deleted.
         */
        public function doDeleteUpdates( $id, Content $content = null ) {
+               try {
+                       $countable = $this->isCountable();
+               } catch ( Exception $ex ) {
+                       // fallback for deleting broken pages for which we cannot load the content for
+                       // some reason. Note that doDeleteArticleReal() already logged this problem.
+                       $countable = false;
+               }
+
                // Update site status
-               DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) );
+               DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$countable, -1 ) );
 
                // Delete pagelinks, update secondary indexes, etc
                $updates = $this->getDeletionUpdates( $content );
@@ -3217,9 +3294,12 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags |= EDIT_FORCE_BOT;
                }
 
+               $targetContent = $target->getContent();
+               $changingContentModel = $targetContent->getModel() !== $current->getContentModel();
+
                // Actually store the edit
                $status = $this->doEditContent(
-                       $target->getContent(),
+                       $targetContent,
                        $summary,
                        $flags,
                        $target->getId(),
@@ -3269,6 +3349,22 @@ class WikiPage implements Page, IDBAccessObject {
                        ] ];
                }
 
+               if ( $changingContentModel ) {
+                       // If the content model changed during the rollback,
+                       // make sure it gets logged to Special:Log/contentmodel
+                       $log = new ManualLogEntry( 'contentmodel', 'change' );
+                       $log->setPerformer( $guser );
+                       $log->setTarget( $this->mTitle );
+                       $log->setComment( $summary );
+                       $log->setParameters( [
+                               '4::oldmodel' => $current->getContentModel(),
+                               '5::newmodel' => $targetContent->getModel(),
+                       ] );
+
+                       $logId = $log->insert( $dbw );
+                       $log->publish( $logId );
+               }
+
                $revId = $statusRev->getId();
 
                Hooks::run( 'ArticleRollbackComplete', [ $this, $guser, $target, $current ] );
@@ -3304,9 +3400,11 @@ class WikiPage implements Page, IDBAccessObject {
                $title->purgeSquid();
                $title->deleteTitleProtection();
 
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
                if ( $title->getNamespace() == NS_CATEGORY ) {
                        // Load the Category object, which will schedule a job to create
-                       // the category table row if necessary. Checking a slave is ok
+                       // the category table row if necessary. Checking a replica DB is ok
                        // here, in the worst case it'll run an unnecessary recount job on
                        // a category that probably doesn't have many members.
                        Category::newFromTitle( $title )->getID();
@@ -3329,6 +3427,8 @@ class WikiPage implements Page, IDBAccessObject {
                $title->touchLinks();
                $title->purgeSquid();
 
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
                // File cache
                HTMLFileCache::clearFileCache( $title );
                InfoAction::invalidateCache( $title );
@@ -3372,6 +3472,8 @@ class WikiPage implements Page, IDBAccessObject {
                // Invalidate the caches of all pages which redirect here
                DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'redirect' ) );
 
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
                // Purge CDN for this page only
                $title->purgeSquid();
                // Clear file cache for this page only
@@ -3397,7 +3499,7 @@ class WikiPage implements Page, IDBAccessObject {
                        return TitleArray::newFromResult( new FakeResultWrapper( [] ) );
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'categorylinks',
                        [ 'cl_to AS page_title, ' . NS_CATEGORY . ' AS page_namespace' ],
                        // Have to do that since DatabaseBase::fieldNamesWithAlias treats numeric indexes
@@ -3422,7 +3524,7 @@ class WikiPage implements Page, IDBAccessObject {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( [ 'categorylinks', 'page_props', 'page' ],
                        [ 'cl_to' ],
                        [ 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
@@ -3573,7 +3675,8 @@ class WikiPage implements Page, IDBAccessObject {
                                                $cat->refreshCounts();
                                        }
                                }
-                       }
+                       },
+                       __METHOD__
                );
        }
 
@@ -3641,7 +3744,14 @@ class WikiPage implements Page, IDBAccessObject {
                if ( !$content ) {
                        // load content object, which may be used to determine the necessary updates.
                        // XXX: the content may not be needed to determine the updates.
-                       $content = $this->getContent( Revision::RAW );
+                       try {
+                               $content = $this->getContent( Revision::RAW );
+                       } catch ( Exception $ex ) {
+                               // If we can't load the content, something is wrong. Perhaps that's why
+                               // the user is trying to delete the page, so let's not fail in that case.
+                               // Note that doDeleteArticleReal() will already have logged an issue with
+                               // loading the content.
+                       }
                }
 
                if ( !$content ) {
@@ -3653,4 +3763,15 @@ class WikiPage implements Page, IDBAccessObject {
                Hooks::run( 'WikiPageDeletionUpdates', [ $this, $content, &$updates ] );
                return $updates;
        }
+
+       /**
+        * Whether this content displayed on this page
+        * comes from the local database
+        *
+        * @since 1.28
+        * @return bool
+        */
+       public function isLocal() {
+               return true;
+       }
 }
index a96ca87..395cee5 100644 (file)
@@ -145,8 +145,8 @@ abstract class IndexPager extends ContextSource implements Pager {
                }
 
                $this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
-               # Let the subclass set the DB here; otherwise use a slave DB for the current wiki
-               $this->mDb = $this->mDb ?: wfGetDB( DB_SLAVE );
+               # Let the subclass set the DB here; otherwise use a replica DB for the current wiki
+               $this->mDb = $this->mDb ?: wfGetDB( DB_REPLICA );
 
                $index = $this->getIndexField(); // column to sort on
                $extraSort = $this->getExtraSortFields(); // extra columns to sort on for query planning
index 31c9c6d..4895b4f 100644 (file)
@@ -29,6 +29,7 @@ abstract class ReverseChronologicalPager extends IndexPager {
        public $mDefaultDirection = IndexPager::DIR_DESCENDING;
        public $mYear;
        public $mMonth;
+       public $mDay;
 
        function getNavigationBar() {
                if ( !$this->isNavigationBarShown() ) {
@@ -60,23 +61,34 @@ abstract class ReverseChronologicalPager extends IndexPager {
                return $this->mNavigationBar;
        }
 
-       function getDateCond( $year, $month ) {
+       /**
+        * Set and return the mOffset timestamp such that we can get all revisions with
+        * a timestamp up to the specified parameters.
+        * @param int $year Year up to which we want revisions
+        * @param int $month Month up to which we want revisions
+        * @param int $day [optional] Day up to which we want revisions. Default is end of month.
+        * @return string|null Timestamp or null if year and month are false/invalid
+        */
+       function getDateCond( $year, $month, $day = -1 ) {
                $year = intval( $year );
                $month = intval( $month );
+               $day = intval( $day );
 
-               // Basic validity checks
+               // Basic validity checks for year and month
                $this->mYear = $year > 0 ? $year : false;
                $this->mMonth = ( $month > 0 && $month < 13 ) ? $month : false;
 
-               // Given an optional year and month, we need to generate a timestamp
-               // to use as "WHERE rev_timestamp <= result"
-               // Examples: year = 2006 equals < 20070101 (+000000)
-               // year=2005, month=1    equals < 20050201
-               // year=2005, month=12   equals < 20060101
+               // If year and month are false, don't update the mOffset
                if ( !$this->mYear && !$this->mMonth ) {
                        return;
                }
 
+               // Given an optional year, month, and day, we need to generate a timestamp
+               // to use as "WHERE rev_timestamp <= result"
+               // Examples: year = 2006      equals < 20070101 (+000000)
+               // year=2005, month=1         equals < 20050201
+               // year=2005, month=12        equals < 20060101
+               // year=2005, month=12, day=5 equals < 20051206
                if ( $this->mYear ) {
                        $year = $this->mYear;
                } else {
@@ -90,15 +102,36 @@ abstract class ReverseChronologicalPager extends IndexPager {
                }
 
                if ( $this->mMonth ) {
-                       $month = $this->mMonth + 1;
-                       // For December, we want January 1 of the next year
+                       $month = $this->mMonth;
+
+                       // Day validity check after we have month and year checked
+                       $this->mDay = checkdate( $month, $day, $year ) ? $day : false;
+
+                       if ( $this->mDay ) {
+                               // If we have a day, we want up to the day immediately afterward
+                               $day = $this->mDay + 1;
+
+                               // Did we overflow the current month?
+                               if ( !checkdate( $month, $day, $year ) ) {
+                                       $day = 1;
+                                       $month++;
+                               }
+                       } else {
+                               // If no day, assume beginning of next month
+                               $day = 1;
+                               $month++;
+                       }
+
+                       // Did we overflow the current year?
                        if ( $month > 12 ) {
                                $month = 1;
                                $year++;
                        }
+
                } else {
                        // No month implies we want up to the end of the year in question
                        $month = 1;
+                       $day = 1;
                        $year++;
                }
 
@@ -107,7 +140,7 @@ abstract class ReverseChronologicalPager extends IndexPager {
                        $year = 2032;
                }
 
-               $ymd = (int)sprintf( "%04d%02d01", $year, $month );
+               $ymd = (int)sprintf( "%04d%02d%02d", $year, $month, $day );
 
                if ( $ymd > 20320101 ) {
                        $ymd = 20320101;
@@ -118,5 +151,6 @@ abstract class ReverseChronologicalPager extends IndexPager {
                $timestamp->setTimezone( $this->getConfig()->get( 'Localtimezone' ) );
 
                $this->mOffset = $this->mDb->timestamp( $timestamp->getTimestamp() );
+               return $this->mOffset;
        }
 }
index b34ac1f..b32f43b 100644 (file)
@@ -289,7 +289,7 @@ class LinkHolderArray {
                $output = $this->parent->getOutput();
                $linkRenderer = $this->parent->getLinkRenderer();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                # Sort by namespace
                ksort( $this->internals );
@@ -534,7 +534,7 @@ class LinkHolderArray {
 
                if ( !$linkBatch->isEmpty() ) {
                        // construct query
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $fields = array_merge(
                                LinkCache::getSelectFields(),
                                [ 'page_namespace', 'page_title' ]
index b116bd4..7c18798 100644 (file)
@@ -395,7 +395,8 @@ class Parser {
         * @param int $revid Number to pass in {{REVISIONID}}
         * @return ParserOutput A ParserOutput
         */
-       public function parse( $text, Title $title, ParserOptions $options,
+       public function parse(
+               $text, Title $title, ParserOptions $options,
                $linestart = true, $clearState = true, $revid = null
        ) {
                /**
@@ -462,6 +463,10 @@ class Parser {
                        }
                }
 
+               # Done parsing! Compute runtime adaptive expiry if set
+               $this->mOutput->finalizeAdaptiveCacheExpiry();
+
+               # Warn if too many heavyweight parser functions were used
                if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
                        $this->limitationWarn( 'expensive-parserfunction',
                                $this->mExpensiveFunctionCount,
@@ -906,11 +911,11 @@ class Parser {
         * the form:
         *
         * @code
-        *   'UNIQ-xxxxx' => array(
+        *   'UNIQ-xxxxx' => [
         *     'element',
         *     'tag content',
-        *     array( 'param' => 'x' ),
-        *     '<element param="x">tag content</element>' ) )
+        *     [ 'param' => 'x' ],
+        *     '<element param="x">tag content</element>' ]
         * @endcode
         *
         * @param array $elements List of element names. Comments are always extracted.
@@ -1434,11 +1439,17 @@ class Parser {
                } elseif ( isset( $m[5] ) && $m[5] !== '' ) {
                        # RFC or PMID
                        if ( substr( $m[0], 0, 3 ) === 'RFC' ) {
+                               if ( !$this->mOptions->getMagicRFCLinks() ) {
+                                       return $m[0];
+                               }
                                $keyword = 'RFC';
                                $urlmsg = 'rfcurl';
                                $cssClass = 'mw-magiclink-rfc';
                                $id = $m[5];
                        } elseif ( substr( $m[0], 0, 4 ) === 'PMID' ) {
+                               if ( !$this->mOptions->getMagicPMIDLinks() ) {
+                                       return $m[0];
+                               }
                                $keyword = 'PMID';
                                $urlmsg = 'pubmedurl';
                                $cssClass = 'mw-magiclink-pmid';
@@ -1449,7 +1460,9 @@ class Parser {
                        }
                        $url = wfMessage( $urlmsg, $id )->inContentLanguage()->text();
                        return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $cssClass, [], $this->mTitle );
-               } elseif ( isset( $m[6] ) && $m[6] !== '' ) {
+               } elseif ( isset( $m[6] ) && $m[6] !== ''
+                       && $this->mOptions->getMagicISBNLinks()
+               ) {
                        # ISBN
                        $isbn = $m[6];
                        $space = self::SPACE_NOT_NL; #  non-newline space
@@ -1770,7 +1783,7 @@ class Parser {
         * Replace external links (REL)
         *
         * Note: this is all very hackish and the order of execution matters a lot.
-        * Make sure to run tests/parserTests.php if you change this code.
+        * Make sure to run tests/parser/parserTests.php if you change this code.
         *
         * @private
         *
@@ -3144,14 +3157,17 @@ class Parser {
                                                $context->setUser( User::newFromName( '127.0.0.1', false ) );
                                        }
                                        $context->setLanguage( $this->mOptions->getUserLangObj() );
-                                       $ret = SpecialPageFactory::capturePath( $title, $context, $this->getLinkRenderer() );
+                                       $ret = SpecialPageFactory::capturePath(
+                                               $title, $context, $this->getLinkRenderer() );
                                        if ( $ret ) {
                                                $text = $context->getOutput()->getHTML();
                                                $this->mOutput->addOutputPageMetadata( $context->getOutput() );
                                                $found = true;
                                                $isHTML = true;
                                                if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
-                                                       $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+                                                       $this->mOutput->updateRuntimeAdaptiveExpiry(
+                                                               $specialPage->maxIncludeCacheTime()
+                                                       );
                                                }
                                        }
                                } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
@@ -3450,10 +3466,18 @@ class Parser {
         * @since 1.24
         * @param Title $title
         * @param Parser|bool $parser
-        * @return Revision
+        * @return Revision|bool False if missing
         */
-       public static function statelessFetchRevision( $title, $parser = false ) {
-               return Revision::newFromTitle( $title );
+       public static function statelessFetchRevision( Title $title, $parser = false ) {
+               $pageId = $title->getArticleID();
+               $revId = $title->getLatestRevID();
+
+               $rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $pageId, $revId );
+               if ( $rev ) {
+                       $rev->setTitle( $title );
+               }
+
+               return $rev;
        }
 
        /**
@@ -3668,7 +3692,7 @@ class Parser {
         */
        public function fetchScaryTemplateMaybeFromCache( $url ) {
                global $wgTranscludeCacheExpiry;
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $tsCond = $dbr->timestamp( time() - $wgTranscludeCacheExpiry );
                $obj = $dbr->selectRow( 'transcache', [ 'tc_time', 'tc_contents' ],
                                [ 'tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ] );
index c6265a7..9e96540 100644 (file)
@@ -72,12 +72,19 @@ class ParserCache {
        }
 
        /**
-        * @param WikiPage $article
+        * @param WikiPage $page
         * @return mixed|string
         */
-       protected function getOptionsKey( $article ) {
-               $pageid = $article->getId();
-               return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" );
+       protected function getOptionsKey( $page ) {
+               return wfMemcKey( 'pcache', 'idoptions', $page->getId() );
+       }
+
+       /**
+        * @param WikiPage $page
+        * @since 1.28
+        */
+       public function deleteOptionsKey( $page ) {
+               $this->mMemc->delete( $this->getOptionsKey( $page ) );
        }
 
        /**
index 891c4dd..25c2aa4 100644 (file)
@@ -215,6 +215,21 @@ class ParserOptions {
         */
        private $mExtraKey = '';
 
+       /**
+        * Are magic ISBN links enabled?
+        */
+       private $mMagicISBNLinks = true;
+
+       /**
+        * Are magic PMID links enabled?
+        */
+       private $mMagicPMIDLinks = true;
+
+       /**
+        * Are magic RFC links enabled?
+        */
+       private $mMagicRFCLinks = true;
+
        /**
         * Function to be called when an option is accessed.
         */
@@ -419,6 +434,28 @@ class ParserOptions {
                return $this->getUserLangObj()->getCode();
        }
 
+       /**
+        * @since 1.28
+        * @return bool
+        */
+       public function getMagicISBNLinks() {
+               return $this->mMagicISBNLinks;
+       }
+
+       /**
+        * @since 1.28
+        * @return bool
+        */
+       public function getMagicPMIDLinks() {
+               return $this->mMagicPMIDLinks;
+       }
+       /**
+        * @since 1.28
+        * @return bool
+        */
+       public function getMagicRFCLinks() {
+               return $this->mMagicRFCLinks;
+       }
        public function setInterwikiMagic( $x ) {
                return wfSetVar( $this->mInterwikiMagic, $x );
        }
@@ -667,7 +704,8 @@ class ParserOptions {
                        $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
                        $wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
                        $wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
-                       $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion;
+                       $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion,
+                       $wgEnableMagicLinks;
 
                // *UPDATE* ParserOptions::matches() if any of this changes as needed
                $this->mInterwikiMagic = $wgInterwikiMagic;
@@ -685,6 +723,9 @@ class ParserOptions {
                $this->mExternalLinkTarget = $wgExternalLinkTarget;
                $this->mDisableContentConversion = $wgDisableLangConversion;
                $this->mDisableTitleConversion = $wgDisableLangConversion || $wgDisableTitleConversion;
+               $this->mMagicISBNLinks = $wgEnableMagicLinks['ISBN'];
+               $this->mMagicPMIDLinks = $wgEnableMagicLinks['PMID'];
+               $this->mMagicRFCLinks = $wgEnableMagicLinks['RFC'];
 
                $this->mUser = $user;
                $this->mNumberHeadings = $user->getOption( 'numberheadings' );
index f052812..9dfa97c 100644 (file)
@@ -209,9 +209,21 @@ class ParserOutput extends CacheTime {
        /** @var integer|null Assumed rev ID for {{REVISIONID}} if no revision is set */
        private $mSpeculativeRevId;
 
+       /** @var integer Upper bound of expiry based on parse duration */
+       private $mMaxAdaptiveExpiry = INF;
+
        const EDITSECTION_REGEX =
                '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
 
+       // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
+       // Current values imply that m=3933.333333 and b=-333.333333
+       // See https://www.nngroup.com/articles/website-response-times/
+       const PARSE_FAST_SEC = .100; // perceived "fast" page parse
+       const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
+       const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
+       const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
+       const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing)
+
        public function __construct( $text = '', $languageLinks = [], $categoryLinks = [],
                $unused = false, $titletext = ''
        ) {
@@ -1037,9 +1049,41 @@ class ParserOutput extends CacheTime {
        }
 
        /**
-        * Save space for serialization by removing useless values
-        * @return array
+        * Lower the runtime adaptive TTL to at most this value
+        *
+        * @param integer $ttl
+        * @since 1.28
+        */
+       public function updateRuntimeAdaptiveExpiry( $ttl ) {
+               $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
+               $this->updateCacheExpiry( $ttl );
+       }
+
+       /**
+        * Call this when parsing is done to lower the TTL based on low parse times
+        *
+        * @since 1.28
         */
+       public function finalizeAdaptiveCacheExpiry() {
+               if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
+                       return; // not set
+               }
+
+               $runtime = $this->getTimeSinceStart( 'wall' );
+               if ( is_float( $runtime ) ) {
+                       $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
+                               / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
+                       // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
+                       $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
+
+                       $adaptiveTTL = min(
+                               max( $slope * $runtime + $point, self::MIN_AR_TTL ),
+                               $this->mMaxAdaptiveExpiry
+                       );
+                       $this->updateCacheExpiry( $adaptiveTTL );
+               }
+       }
+
        public function __sleep() {
                return array_diff(
                        array_keys( get_object_vars( $this ) ),
index f2c59d2..5e8db07 100644 (file)
@@ -246,7 +246,7 @@ LUA;
                        } elseif ( $slot === 'QUEUE_WAIT' ) {
                                // This process is now registered as waiting
                                $keys = ( $doWakeup == self::AWAKE_ALL )
-                                       // Wait for an open slot or wake-up signal (preferring the later)
+                                       // Wait for an open slot or wake-up signal (preferring the latter)
                                        ? [ $this->getWakeupListKey(), $this->getSlotListKey() ]
                                        // Just wait for an actual pool slot
                                        : [ $this->getSlotListKey() ];
@@ -292,7 +292,7 @@ LUA;
                local rMaxWorkers,rMaxQueue,rTimeout,rExpiry,rSess,rTime = unpack(ARGV)
                -- Initialize if the "next release" time sorted-set is empty. The slot key
                -- itself is empty if all slots are busy or when nothing is initialized.
-               -- If the list is empty but the set is not, then it is the later case.
+               -- If the list is empty but the set is not, then it is the latter case.
                -- For sanity, if the list exists but not the set, then reset everything.
                if redis.call('exists',kSlotsNextRelease) == 0 then
                        redis.call('del',kSlots)
diff --git a/includes/profiler/TransactionProfiler.php b/includes/profiler/TransactionProfiler.php
deleted file mode 100644 (file)
index bf26573..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-<?php
-/**
- * Transaction profiling for contention
- *
- * 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
- * @author Aaron Schulz
- */
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\LoggerAwareInterface;
-use Psr\Log\NullLogger;
-
-/**
- * Helper class that detects high-contention DB queries via profiling calls
- *
- * This class is meant to work with a DatabaseBase object, which manages queries
- *
- * @since 1.24
- */
-class TransactionProfiler implements LoggerAwareInterface {
-       /** @var float Seconds */
-       protected $dbLockThreshold = 3.0;
-       /** @var float Seconds */
-       protected $eventThreshold = .25;
-       /** @var bool */
-       protected $silenced = false;
-
-       /** @var array transaction ID => (write start time, list of DBs involved) */
-       protected $dbTrxHoldingLocks = [];
-       /** @var array transaction ID => list of (query name, start time, end time) */
-       protected $dbTrxMethodTimes = [];
-
-       /** @var array */
-       protected $hits = [
-               'writes'      => 0,
-               'queries'     => 0,
-               'conns'       => 0,
-               'masterConns' => 0
-       ];
-       /** @var array */
-       protected $expect = [
-               'writes'         => INF,
-               'queries'        => INF,
-               'conns'          => INF,
-               'masterConns'    => INF,
-               'maxAffected'    => INF,
-               'readQueryTime'  => INF,
-               'writeQueryTime' => INF
-       ];
-       /** @var array */
-       protected $expectBy = [];
-
-       /**
-        * @var LoggerInterface
-        */
-       private $logger;
-
-       public function __construct() {
-               $this->setLogger( new NullLogger() );
-       }
-
-       public function setLogger( LoggerInterface $logger ) {
-               $this->logger = $logger;
-       }
-
-       /**
-        * @param bool $value
-        * @since 1.28
-        */
-       public function setSilenced( $value ) {
-               $this->silenced = $value;
-       }
-
-       /**
-        * Set performance expectations
-        *
-        * With conflicting expectations, the most narrow ones will be used
-        *
-        * @param string $event (writes,queries,conns,mConns)
-        * @param integer $value Maximum count of the event
-        * @param string $fname Caller
-        * @since 1.25
-        */
-       public function setExpectation( $event, $value, $fname ) {
-               $this->expect[$event] = isset( $this->expect[$event] )
-                       ? min( $this->expect[$event], $value )
-                       : $value;
-               if ( $this->expect[$event] == $value ) {
-                       $this->expectBy[$event] = $fname;
-               }
-       }
-
-       /**
-        * Set multiple performance expectations
-        *
-        * With conflicting expectations, the most narrow ones will be used
-        *
-        * @param array $expects Map of (event => limit)
-        * @param $fname
-        * @since 1.26
-        */
-       public function setExpectations( array $expects, $fname ) {
-               foreach ( $expects as $event => $value ) {
-                       $this->setExpectation( $event, $value, $fname );
-               }
-       }
-
-       /**
-        * Reset performance expectations and hit counters
-        *
-        * @since 1.25
-        */
-       public function resetExpectations() {
-               foreach ( $this->hits as &$val ) {
-                       $val = 0;
-               }
-               unset( $val );
-               foreach ( $this->expect as &$val ) {
-                       $val = INF;
-               }
-               unset( $val );
-               $this->expectBy = [];
-       }
-
-       /**
-        * Mark a DB as having been connected to with a new handle
-        *
-        * Note that there can be multiple connections to a single DB.
-        *
-        * @param string $server DB server
-        * @param string $db DB name
-        * @param bool $isMaster
-        */
-       public function recordConnection( $server, $db, $isMaster ) {
-               // Report when too many connections happen...
-               if ( $this->hits['conns']++ == $this->expect['conns'] ) {
-                       $this->reportExpectationViolated( 'conns', "[connect to $server ($db)]" );
-               }
-               if ( $isMaster && $this->hits['masterConns']++ == $this->expect['masterConns'] ) {
-                       $this->reportExpectationViolated( 'masterConns', "[connect to $server ($db)]" );
-               }
-       }
-
-       /**
-        * Mark a DB as in a transaction with one or more writes pending
-        *
-        * Note that there can be multiple connections to a single DB.
-        *
-        * @param string $server DB server
-        * @param string $db DB name
-        * @param string $id ID string of transaction
-        */
-       public function transactionWritingIn( $server, $db, $id ) {
-               $name = "{$server} ({$db}) (TRX#$id)";
-               if ( isset( $this->dbTrxHoldingLocks[$name] ) ) {
-                       $this->logger->info( "Nested transaction for '$name' - out of sync." );
-               }
-               $this->dbTrxHoldingLocks[$name] = [
-                       'start' => microtime( true ),
-                       'conns' => [], // all connections involved
-               ];
-               $this->dbTrxMethodTimes[$name] = [];
-
-               foreach ( $this->dbTrxHoldingLocks as $name => &$info ) {
-                       // Track all DBs in transactions for this transaction
-                       $info['conns'][$name] = 1;
-               }
-       }
-
-       /**
-        * Register the name and time of a method for slow DB trx detection
-        *
-        * This assumes that all queries are synchronous (non-overlapping)
-        *
-        * @param string $query Function name or generalized SQL
-        * @param float $sTime Starting UNIX wall time
-        * @param bool $isWrite Whether this is a write query
-        * @param integer $n Number of affected rows
-        */
-       public function recordQueryCompletion( $query, $sTime, $isWrite = false, $n = 0 ) {
-               $eTime = microtime( true );
-               $elapsed = ( $eTime - $sTime );
-
-               if ( $isWrite && $n > $this->expect['maxAffected'] ) {
-                       $this->logger->info( "Query affected $n row(s):\n" . $query . "\n" .
-                               wfBacktrace( true ) );
-               }
-
-               // Report when too many writes/queries happen...
-               if ( $this->hits['queries']++ == $this->expect['queries'] ) {
-                       $this->reportExpectationViolated( 'queries', $query );
-               }
-               if ( $isWrite && $this->hits['writes']++ == $this->expect['writes'] ) {
-                       $this->reportExpectationViolated( 'writes', $query );
-               }
-               // Report slow queries...
-               if ( !$isWrite && $elapsed > $this->expect['readQueryTime'] ) {
-                       $this->reportExpectationViolated( 'readQueryTime', $query, $elapsed );
-               }
-               if ( $isWrite && $elapsed > $this->expect['writeQueryTime'] ) {
-                       $this->reportExpectationViolated( 'writeQueryTime', $query, $elapsed );
-               }
-
-               if ( !$this->dbTrxHoldingLocks ) {
-                       // Short-circuit
-                       return;
-               } elseif ( !$isWrite && $elapsed < $this->eventThreshold ) {
-                       // Not an important query nor slow enough
-                       return;
-               }
-
-               foreach ( $this->dbTrxHoldingLocks as $name => $info ) {
-                       $lastQuery = end( $this->dbTrxMethodTimes[$name] );
-                       if ( $lastQuery ) {
-                               // Additional query in the trx...
-                               $lastEnd = $lastQuery[2];
-                               if ( $sTime >= $lastEnd ) { // sanity check
-                                       if ( ( $sTime - $lastEnd ) > $this->eventThreshold ) {
-                                               // Add an entry representing the time spent doing non-queries
-                                               $this->dbTrxMethodTimes[$name][] = [ '...delay...', $lastEnd, $sTime ];
-                                       }
-                                       $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
-                               }
-                       } else {
-                               // First query in the trx...
-                               if ( $sTime >= $info['start'] ) { // sanity check
-                                       $this->dbTrxMethodTimes[$name][] = [ $query, $sTime, $eTime ];
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Mark a DB as no longer in a transaction
-        *
-        * This will check if locks are possibly held for longer than
-        * needed and log any affected transactions to a special DB log.
-        * Note that there can be multiple connections to a single DB.
-        *
-        * @param string $server DB server
-        * @param string $db DB name
-        * @param string $id ID string of transaction
-        * @param float $writeTime Time spent in write queries
-        */
-       public function transactionWritingOut( $server, $db, $id, $writeTime = 0.0 ) {
-               $name = "{$server} ({$db}) (TRX#$id)";
-               if ( !isset( $this->dbTrxMethodTimes[$name] ) ) {
-                       $this->logger->info( "Detected no transaction for '$name' - out of sync." );
-                       return;
-               }
-
-               $slow = false;
-
-               // Warn if too much time was spend writing...
-               if ( $writeTime > $this->expect['writeQueryTime'] ) {
-                       $this->reportExpectationViolated(
-                               'writeQueryTime',
-                               "[transaction $id writes to {$server} ({$db})]",
-                               $writeTime
-                       );
-                       $slow = true;
-               }
-               // Fill in the last non-query period...
-               $lastQuery = end( $this->dbTrxMethodTimes[$name] );
-               if ( $lastQuery ) {
-                       $now = microtime( true );
-                       $lastEnd = $lastQuery[2];
-                       if ( ( $now - $lastEnd ) > $this->eventThreshold ) {
-                               $this->dbTrxMethodTimes[$name][] = [ '...delay...', $lastEnd, $now ];
-                       }
-               }
-               // Check for any slow queries or non-query periods...
-               foreach ( $this->dbTrxMethodTimes[$name] as $info ) {
-                       $elapsed = ( $info[2] - $info[1] );
-                       if ( $elapsed >= $this->dbLockThreshold ) {
-                               $slow = true;
-                               break;
-                       }
-               }
-               if ( $slow ) {
-                       $dbs = implode( ', ', array_keys( $this->dbTrxHoldingLocks[$name]['conns'] ) );
-                       $msg = "Sub-optimal transaction on DB(s) [{$dbs}]:\n";
-                       foreach ( $this->dbTrxMethodTimes[$name] as $i => $info ) {
-                               list( $query, $sTime, $end ) = $info;
-                               $msg .= sprintf( "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), $query );
-                       }
-                       $this->logger->info( $msg );
-               }
-               unset( $this->dbTrxHoldingLocks[$name] );
-               unset( $this->dbTrxMethodTimes[$name] );
-       }
-
-       /**
-        * @param string $expect
-        * @param string $query
-        * @param string|float|int $actual [optional]
-        */
-       protected function reportExpectationViolated( $expect, $query, $actual = null ) {
-               if ( $this->silenced ) {
-                       return;
-               }
-
-               $n = $this->expect[$expect];
-               $by = $this->expectBy[$expect];
-               $actual = ( $actual !== null ) ? " (actual: $actual)" : "";
-
-               $this->logger->info(
-                       "Expectation ($expect <= $n) by $by not met$actual:\n$query\n" .
-                       wfBacktrace( true )
-               );
-       }
-}
index 9ce2c5a..34a0a99 100644 (file)
@@ -109,7 +109,7 @@ class ResourceLoader implements LoggerAwareInterface {
                        // Or else Database*::select() will explode, plus it's cheaper!
                        return;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $skin = $context->getSkin();
                $lang = $context->getLanguage();
 
@@ -140,6 +140,9 @@ class ResourceLoader implements LoggerAwareInterface {
                        }
                }
 
+               // Batched version of ResourceLoaderWikiModule::getTitleInfo
+               ResourceLoaderWikiModule::preloadTitleInfo( $context, $dbr, $moduleNames );
+
                // Prime in-object cache for message blobs for modules with messages
                $modules = [];
                foreach ( $moduleNames as $name ) {
@@ -610,17 +613,49 @@ class ResourceLoader implements LoggerAwareInterface {
         *
         * @since 1.26
         * @param ResourceLoaderContext $context
-        * @param array $modules List of ResourceLoaderModule objects
+        * @param string[] $modules List of known module names
         * @return string Hash
         */
-       public function getCombinedVersion( ResourceLoaderContext $context, array $modules ) {
-               if ( !$modules ) {
+       public function getCombinedVersion( ResourceLoaderContext $context, array $moduleNames ) {
+               if ( !$moduleNames ) {
                        return '';
                }
                $hashes = array_map( function ( $module ) use ( $context ) {
                        return $this->getModule( $module )->getVersionHash( $context );
-               }, $modules );
-               return self::makeHash( implode( $hashes ) );
+               }, $moduleNames );
+               return self::makeHash( implode( '', $hashes ) );
+       }
+
+       /**
+        * Get the expected value of the 'version' query parameter.
+        *
+        * This is used by respond() to set a short Cache-Control header for requests with
+        * information newer than the current server has. This avoids pollution of edge caches.
+        * Typically during deployment. (T117587)
+        *
+        * This MUST match return value of `mw.loader#getCombinedVersion()` client-side.
+        *
+        * @since 1.28
+        * @param ResourceLoaderContext $context
+        * @param string[] $modules List of module names
+        * @return string Hash
+        */
+       public function makeVersionQuery( ResourceLoaderContext $context ) {
+               // As of MediaWiki 1.28, the server and client use the same algorithm for combining
+               // version hashes. There is no technical reason for this to be same, and for years the
+               // implementations differed. If getCombinedVersion in PHP (used for StartupModule and
+               // E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
+               // query parameter), then this method must continue to match the JS one.
+               $moduleNames = [];
+               foreach ( $context->getModules() as $name ) {
+                       if ( !$this->getModule( $name ) ) {
+                               // If a versioned request contains a missing module, the version is a mismatch
+                               // as the client considered a module (and version) we don't have.
+                               return '';
+                       }
+                       $moduleNames[] = $name;
+               }
+               return $this->getCombinedVersion( $context, $moduleNames );
        }
 
        /**
@@ -759,10 +794,14 @@ class ResourceLoader implements LoggerAwareInterface {
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
                $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
-               // If a version wasn't specified we need a shorter expiry time for updates
-               // to propagate to clients quickly
-               // If there were errors, we also need a shorter expiry time so we can recover quickly
-               if ( is_null( $context->getVersion() ) || $errors ) {
+               // Use a short cache expiry so that updates propagate to clients quickly, if:
+               // - No version specified (shared resources, e.g. stylesheets)
+               // - There were errors (recover quickly)
+               // - Version mismatch (T117587, T47877)
+               if ( is_null( $context->getVersion() )
+                       || $errors
+                       || $context->getVersion() !== $this->makeVersionQuery( $context )
+               ) {
                        $maxage = $rlMaxage['unversioned']['client'];
                        $smaxage = $rlMaxage['unversioned']['server'];
                // If a version was specified we can use a longer expiry time since changing
@@ -857,7 +896,7 @@ class ResourceLoader implements LoggerAwareInterface {
                $good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
                if ( !$good ) {
                        try { // RL always hits the DB on file cache miss...
-                               wfGetDB( DB_SLAVE );
+                               wfGetDB( DB_REPLICA );
                        } catch ( DBConnectionError $e ) { // ...check if we need to fallback to cache
                                $good = $fileCache->isCacheGood(); // cache existence check
                        }
@@ -992,9 +1031,23 @@ MESSAGE;
                                                $strContent = isset( $styles['css'] ) ? implode( '', $styles['css'] ) : '';
                                                break;
                                        default:
+                                               $scripts = isset( $content['scripts'] ) ? $content['scripts'] : '';
+                                               if ( is_string( $scripts ) ) {
+                                                       if ( $name === 'site' || $name === 'user' ) {
+                                                               // Legacy scripts that run in the global scope without a closure.
+                                                               // mw.loader.implement will use globalEval if scripts is a string.
+                                                               // Minify manually here, because general response minification is
+                                                               // not effective due it being a string literal, not a function.
+                                                               if ( !ResourceLoader::inDebugMode() ) {
+                                                                       $scripts = self::filter( 'minify-js', $scripts ); // T107377
+                                                               }
+                                                       } else {
+                                                               $scripts = new XmlJsCode( $scripts );
+                                                       }
+                                               }
                                                $strContent = self::makeLoaderImplementScript(
                                                        $name,
-                                                       isset( $content['scripts'] ) ? $content['scripts'] : '',
+                                                       $scripts,
                                                        isset( $content['styles'] ) ? $content['styles'] : [],
                                                        isset( $content['messagesBlob'] ) ? new XmlJsCode( $content['messagesBlob'] ) : [],
                                                        isset( $content['templates'] ) ? $content['templates'] : []
@@ -1073,7 +1126,8 @@ MESSAGE;
         * Return JS code that calls mw.loader.implement with given module properties.
         *
         * @param string $name Module name
-        * @param mixed $scripts List of URLs to JavaScript files or String of JavaScript code
+        * @param XmlJsCode|array|string $scripts Code as XmlJsCode (to be wrapped in a closure),
+        *  list of URLs to JavaScript files, or a string of JavaScript for `$.globalEval`.
         * @param mixed $styles Array of CSS strings keyed by media type, or an array of lists of URLs
         *   to CSS files keyed by media type
         * @param mixed $messages List of messages associated with this module. May either be an
@@ -1084,22 +1138,13 @@ MESSAGE;
         * @throws MWException
         * @return string
         */
-       public static function makeLoaderImplementScript(
+       protected static function makeLoaderImplementScript(
                $name, $scripts, $styles, $messages, $templates
        ) {
-               if ( is_string( $scripts ) ) {
-                       // Site and user module are a legacy scripts that run in the global scope (no closure).
-                       // Transportation as string instructs mw.loader.implement to use globalEval.
-                       if ( $name === 'site' || $name === 'user' ) {
-                               // Minify manually because the general makeModuleResponse() minification won't be
-                               // effective here due to the script being a string instead of a function. (T107377)
-                               if ( !ResourceLoader::inDebugMode() ) {
-                                       $scripts = self::filter( 'minify-js', $scripts );
-                               }
-                       } else {
-                               $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts}\n}" );
-                       }
-               } elseif ( !is_array( $scripts ) ) {
+
+               if ( $scripts instanceof XmlJsCode ) {
+                       $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
+               } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) {
                        throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
                }
                // mw.loader.implement requires 'styles', 'messages' and 'templates' to be objects (not
@@ -1234,9 +1279,9 @@ MESSAGE;
         * Values considered empty:
         *
         * - null
-        * - array()
+        * - []
         * - new XmlJsCode( '{}' )
-        * - new stdClass() // (object) array()
+        * - new stdClass() // (object) []
         *
         * @param Array $array
         */
index dc70af4..5729218 100644 (file)
@@ -429,6 +429,7 @@ class ResourceLoaderClientHtml {
                foreach ( $sortedModules as $source => $groups ) {
                        foreach ( $groups as $group => $grpModules ) {
                                $context = self::makeContext( $mainContext, $group, $only, $extraQuery );
+                               $context->setModules( array_keys( $grpModules ) );
 
                                if ( $group === 'private' ) {
                                        // Decide whether to use style or script element
@@ -456,11 +457,10 @@ class ResourceLoaderClientHtml {
                                // This should NOT be done for the site group (bug 27564) because anons get that too
                                // and we shouldn't be putting timestamps in CDN-cached HTML
                                if ( $group === 'user' ) {
-                                       $version = $rl->getCombinedVersion( $context, array_keys( $grpModules ) );
-                                       $context->setVersion( $version );
+                                       // Must setModules() before makeVersionQuery()
+                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
                                }
 
-                               $context->setModules( array_keys( $grpModules ) );
                                $url = $rl->createLoaderURL( $source, $context, $extraQuery );
 
                                // Decide whether to use 'style' or 'script' element
index 79b71df..4a2f759 100644 (file)
@@ -175,8 +175,7 @@ class ResourceLoaderContext {
                        // Stricter version of RequestContext::sanitizeLangCode()
                        if ( !Language::isValidBuiltInCode( $lang ) ) {
                                wfDebug( "Invalid user language code\n" );
-                               global $wgLanguageCode;
-                               $lang = $wgLanguageCode;
+                               $lang = $this->getResourceLoader()->getConfig()->get( 'LanguageCode' );
                        }
                        $this->language = $lang;
                }
@@ -220,7 +219,11 @@ class ResourceLoaderContext {
         */
        public function msg() {
                return call_user_func_array( 'wfMessage', func_get_args() )
-                       ->inLanguage( $this->getLanguage() );
+                       ->inLanguage( $this->getLanguage() )
+                       // Use a dummy title because there is no real title
+                       // for this endpoint, and the cache won't vary on it
+                       // anyways.
+                       ->title( Title::newFromText( 'Dwimmerlaik' ) );
        }
 
        /**
index 574e535..2dcc841 100644 (file)
@@ -177,26 +177,26 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *         // Scripts to always include
         *         'scripts' => [file path string or array of file path strings],
         *         // Scripts to include in specific language contexts
-        *         'languageScripts' => array(
+        *         'languageScripts' => [
         *             [language code] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Scripts to include in specific skin contexts
-        *         'skinScripts' => array(
+        *         'skinScripts' => [
         *             [skin name] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Scripts to include in debug contexts
         *         'debugScripts' => [file path string or array of file path strings],
         *         // Modules which must be loaded before this module
         *         'dependencies' => [module name string or array of module name strings],
-        *         'templates' => array(
+        *         'templates' => [
         *             [template alias with file.ext] => [file path to a template file],
-        *         ),
+        *         ],
         *         // Styles to always load
         *         'styles' => [file path string or array of file path strings],
         *         // Styles to include in specific skin contexts
-        *         'skinStyles' => array(
+        *         'skinStyles' => [
         *             [skin name] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Messages to always load
         *         'messages' => [array of message key strings],
         *         // Group which this module should be loaded together with
index 43327c9..6a8957e 100644 (file)
@@ -59,7 +59,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         * Below is a description for the $options array:
         * @par Construction options:
         * @code
-        *     array(
+        *     [
         *         // Base path to prepend to all local paths in $options. Defaults to $IP
         *         'localBasePath' => [base path],
         *         // Path to JSON file that contains any of the settings below
@@ -72,33 +72,33 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         *         'selectorWithoutVariant' => [CSS selector template, variables: {prefix} {name}],
         *         'selectorWithVariant' => [CSS selector template, variables: {prefix} {name} {variant}],
         *         // List of variants that may be used for the image files
-        *         'variants' => array(
-        *             [theme name] => array(
-        *                 [variant name] => array(
+        *         'variants' => [
+        *             [theme name] => [
+        *                 [variant name] => [
         *                     'color' => [color string, e.g. '#ffff00'],
         *                     'global' => [boolean, if true, this variant is available
         *                                  for all images of this type],
-        *                 ),
+        *                 ],
         *                 ...
-        *             ),
+        *             ],
         *             ...
-        *         ),
+        *         ],
         *         // List of image files and their options
-        *         'images' => array(
-        *             [theme name] => array(
-        *                 [icon name] => array(
+        *         'images' => [
+        *             [theme name] => [
+        *                 [icon name] => [
         *                     'file' => [file path string or array whose values are file path strings
         *                                    and whose keys are 'default', 'ltr', 'rtl', a single
         *                                    language code like 'en', or a list of language codes like
         *                                    'en,de,ar'],
         *                     'variants' => [array of variant name strings, variants
         *                                    available for this image],
-        *                 ),
+        *                 ],
         *                 ...
-        *             ),
+        *             ],
         *             ...
-        *         ),
-        *     )
+        *         ],
+        *     ]
         * @endcode
         * @throws InvalidArgumentException
         */
index 48e7937..3e94460 100644 (file)
@@ -264,8 +264,8 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         *
         * @param ResourceLoaderContext $context
         * @return array List of CSS strings or array of CSS strings keyed by media type.
-        *  like array( 'screen' => '.foo { width: 0 }' );
-        *  or array( 'screen' => array( '.foo { width: 0 }' ) );
+        *  like [ 'screen' => '.foo { width: 0 }' ];
+        *  or [ 'screen' => [ '.foo { width: 0 }' ] ];
         */
        public function getStyles( ResourceLoaderContext $context ) {
                // Stub, override expected
@@ -279,7 +279,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         * load the files directly. See also getScriptURLsForDebug()
         *
         * @param ResourceLoaderContext $context
-        * @return array Array( mediaType => array( URL1, URL2, ... ), ... )
+        * @return array [ mediaType => [ URL1, URL2, ... ], ... ]
         */
        public function getStyleURLsForDebug( ResourceLoaderContext $context ) {
                $resourceLoader = $context->getResourceLoader();
@@ -417,7 +417,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
 
                // Try in-object cache first
                if ( !isset( $this->fileDeps[$vary] ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $deps = $dbr->selectField( 'module_deps',
                                'md_deps',
                                [
@@ -486,9 +486,14 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                                        ]
                                );
 
-                               $dbw->onTransactionResolution( function () use ( &$scopeLock ) {
-                                       ScopedCallback::consume( $scopeLock ); // release after commit
-                               } );
+                               if ( $dbw->trxLevel() ) {
+                                       $dbw->onTransactionResolution(
+                                               function () use ( &$scopeLock ) {
+                                                       ScopedCallback::consume( $scopeLock ); // release after commit
+                                               },
+                                               __METHOD__
+                                       );
+                               }
                        }
                } catch ( Exception $e ) {
                        wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
@@ -637,7 +642,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                // Styles
                if ( $context->shouldIncludeStyles() ) {
                        $styles = [];
-                       // Don't create empty stylesheets like array( '' => '' ) for modules
+                       // Don't create empty stylesheets like [ '' => '' ] for modules
                        // that don't *have* any stylesheets (bug 38024).
                        $stylePairs = $this->getStyles( $context );
                        if ( count( $stylePairs ) ) {
@@ -787,10 +792,10 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         *
         * @code
         *     $summary = parent::getDefinitionSummary( $context );
-        *     $summary[] = array(
+        *     $summary[] = [
         *         'foo' => 123,
         *         'bar' => 'quux',
-        *     );
+        *     ];
         *     return $summary;
         * @endcode
         *
index eb9788c..8970620 100644 (file)
@@ -311,19 +311,14 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
         */
        public static function getStartupModulesUrl( ResourceLoaderContext $context ) {
                $rl = $context->getResourceLoader();
-               $moduleNames = self::getStartupModules();
 
-               $query = [
-                       'modules' => ResourceLoader::makePackedModulesString( $moduleNames ),
-                       'only' => 'scripts',
-                       'lang' => $context->getLanguage(),
-                       'skin' => $context->getSkin(),
-                       'debug' => $context->getDebug() ? 'true' : 'false',
-                       'version' => $rl->getCombinedVersion( $context, $moduleNames ),
-               ];
-               // Ensure uniform query order
-               ksort( $query );
-               return wfAppendQuery( wfScript( 'load' ), $query );
+               $derivative = new DerivativeResourceLoaderContext( $context );
+               $derivative->setModules( self::getStartupModules() );
+               $derivative->setOnly( 'scripts' );
+               // Must setModules() before makeVersionQuery()
+               $derivative->setVersion( $rl->makeVersionQuery( $derivative ) );
+
+               return $rl->createLoaderURL( 'local', $derivative );
        }
 
        /**
index 82051b1..4fdd86e 100644 (file)
@@ -130,7 +130,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
        /**
         * Get the Database object used in getTitleInfo().
         *
-        * Defaults to the local slave DB. Subclasses may want to override this to return a foreign
+        * Defaults to the local replica DB. Subclasses may want to override this to return a foreign
         * database object, or null if getTitleInfo() shouldn't access the database.
         *
         * NOTE: This ONLY works for getTitleInfo() and isKnownEmpty(), NOT FOR ANYTHING ELSE.
@@ -139,7 +139,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
         * @return IDatabase|null
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
@@ -246,7 +246,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                $summary = parent::getDefinitionSummary( $context );
                $summary[] = [
                        'pages' => $this->getPages( $context ),
-                       // Includes SHA1 of content
+                       // Includes meta data of current revisions
                        'titleInfo' => $this->getTitleInfo( $context ),
                ];
                return $summary;
@@ -262,7 +262,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                // For user modules, don't needlessly load if there are no non-empty pages
                if ( $this->getGroup() === 'user' ) {
                        foreach ( $revisions as $revision ) {
-                               if ( $revision['rev_len'] > 0 ) {
+                               if ( $revision['page_len'] > 0 ) {
                                        // At least one non-empty page, module should be loaded
                                        return false;
                                }
@@ -276,10 +276,14 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                return count( $revisions ) === 0;
        }
 
+       private function setTitleInfo( $key, array $titleInfo ) {
+               $this->titleInfo[$key] = $titleInfo;
+       }
+
        /**
         * Get the information about the wiki pages for a given context.
         * @param ResourceLoaderContext $context
-        * @return array Keyed by page name. Contains arrays with 'rev_len' and 'rev_sha1' keys
+        * @return array Keyed by page name
         */
        protected function getTitleInfo( ResourceLoaderContext $context ) {
                $dbr = $this->getDB();
@@ -288,37 +292,84 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                        return [];
                }
 
-               $pages = $this->getPages( $context );
-               $key = implode( '|', array_keys( $pages ) );
+               $pageNames = array_keys( $this->getPages( $context ) );
+               sort( $pageNames );
+               $key = implode( '|', $pageNames );
                if ( !isset( $this->titleInfo[$key] ) ) {
-                       $this->titleInfo[$key] = [];
-                       $batch = new LinkBatch;
-                       foreach ( $pages as $titleText => $options ) {
-                               $batch->addObj( Title::newFromText( $titleText ) );
+                       $this->titleInfo[$key] = static::fetchTitleInfo( $dbr, $pageNames, __METHOD__ );
+               }
+               return $this->titleInfo[$key];
+       }
+
+       protected static function fetchTitleInfo( IDatabase $db, array $pages, $fname = __METHOD__ ) {
+               $titleInfo = [];
+               $batch = new LinkBatch;
+               foreach ( $pages as $titleText ) {
+                       $batch->addObj( Title::newFromText( $titleText ) );
+               }
+               if ( !$batch->isEmpty() ) {
+                       $res = $db->select( 'page',
+                               // Include page_touched to allow purging if cache is poisoned (T117587, T113916)
+                               [ 'page_namespace', 'page_title', 'page_touched', 'page_len', 'page_latest' ],
+                               $batch->constructSet( 'page', $db ),
+                               $fname
+                       );
+                       foreach ( $res as $row ) {
+                               // Avoid including ids or timestamps of revision/page tables so
+                               // that versions are not wasted
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $titleInfo[$title->getPrefixedText()] = [
+                                       'page_len' => $row->page_len,
+                                       'page_latest' => $row->page_latest,
+                                       'page_touched' => $row->page_touched,
+                               ];
                        }
+               }
+               return $titleInfo;
+       }
 
-                       if ( !$batch->isEmpty() ) {
-                               $res = $dbr->select( [ 'page', 'revision' ],
-                                       // Include page_touched to allow purging if cache is poisoned (T117587, T113916)
-                                       [ 'page_namespace', 'page_title', 'page_touched', 'rev_len', 'rev_sha1' ],
-                                       $batch->constructSet( 'page', $dbr ),
-                                       __METHOD__,
-                                       [],
-                                       [ 'revision' => [ 'INNER JOIN', [ 'page_latest=rev_id' ] ] ]
-                               );
-                               foreach ( $res as $row ) {
-                                       // Avoid including ids or timestamps of revision/page tables so
-                                       // that versions are not wasted
-                                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-                                       $this->titleInfo[$key][$title->getPrefixedText()] = [
-                                               'rev_len' => $row->rev_len,
-                                               'rev_sha1' => $row->rev_sha1,
-                                               'page_touched' => $row->page_touched,
-                                       ];
+       /**
+        * @since 1.28
+        * @param ResourceLoaderContext $context
+        * @param IDatabase $db
+        * @param string[] $modules
+        */
+       public static function preloadTitleInfo(
+               ResourceLoaderContext $context, IDatabase $db, array $moduleNames
+       ) {
+               $rl = $context->getResourceLoader();
+               // getDB() can be overridden to point to a foreign database.
+               // For now, only preload local. In the future, we could preload by wikiID.
+               $allPages = [];
+               $wikiModules = [];
+               foreach ( $moduleNames as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( $module instanceof self ) {
+                               $mDB = $module->getDB();
+                               // Subclasses may disable getDB and implement getTitleInfo differently
+                               if ( $mDB && $mDB->getWikiID() === $db->getWikiID() ) {
+                                       $wikiModules[] = $module;
+                                       $allPages += $module->getPages( $context );
                                }
                        }
                }
-               return $this->titleInfo[$key];
+               $allInfo = static::fetchTitleInfo( $db, array_keys( $allPages ), __METHOD__ );
+               foreach ( $wikiModules as $module ) {
+                       $pages = $module->getPages( $context );
+                       // Before we intersect, map the names to canonical form (T145673).
+                       $intersect = [];
+                       foreach ( $pages as $page => $unused ) {
+                               $title = Title::newFromText( $page )->getPrefixedText();
+                               $intersect[$title] = 1;
+                       }
+                       $info = array_intersect_key( $allInfo, $intersect );
+
+                       $pageNames = array_keys( $pages );
+                       sort( $pageNames );
+                       $key = implode( '|', $pageNames );
+                       $module->setTitleInfo( $key, $info );
+               }
+               return $allInfo;
        }
 
        /**
index 48604e1..674846d 100644 (file)
@@ -120,10 +120,13 @@ abstract class RevDelList extends RevisionListBase {
                }
 
                $dbw->startAtomic( __METHOD__ );
-               $dbw->onTransactionResolution( function () {
-                       // Release locks on commit or error
-                       $this->releaseItemLocks();
-               } );
+               $dbw->onTransactionResolution(
+                       function () {
+                               // Release locks on commit or error
+                               $this->releaseItemLocks();
+                       },
+                       __METHOD__
+               );
 
                $missing = array_flip( $this->ids );
                $this->clearFileOps();
index 07fe4a1..ff1d2ed 100644 (file)
@@ -40,7 +40,7 @@ class RevDelLogList extends RevDelList {
        }
 
        public static function suggestTarget( $target, array $ids ) {
-               $result = wfGetDB( DB_SLAVE )->select( 'logging',
+               $result = wfGetDB( DB_REPLICA )->select( 'logging',
                        'log_type',
                        [ 'log_id' => $ids ],
                        __METHOD__,
index 3486645..f0b1907 100644 (file)
@@ -60,13 +60,16 @@ class RevDelRevisionList extends RevDelList {
        public function doQuery( $db ) {
                $ids = array_map( 'intval', $this->ids );
                $queryInfo = [
-                       'tables' => [ 'revision', 'user' ],
+                       'tables' => [ 'revision', 'page', 'user' ],
                        'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ),
                        'conds' => [
                                'rev_page' => $this->title->getArticleID(),
                                'rev_id' => $ids,
                        ],
-                       'options' => [ 'ORDER BY' => 'rev_id DESC' ],
+                       'options' => [
+                               'ORDER BY' => 'rev_id DESC',
+                               'USE INDEX' => [ 'revision' => 'PRIMARY' ] // workaround for MySQL bug (T104313)
+                       ],
                        'join_conds' => [
                                'page' => Revision::pageJoinCond(),
                                'user' => Revision::userJoinCond(),
index eea0c28..b834c15 100644 (file)
@@ -213,7 +213,7 @@ class RevisionDeleter {
         * @return bool|mixed
         */
        public static function checkRevisionExistence( $title, $revid ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $exists = $dbr->selectField( 'revision', '1',
                                [ 'rev_id' => $revid ], __METHOD__ );
 
diff --git a/includes/search/AugmentPageProps.php b/includes/search/AugmentPageProps.php
new file mode 100644 (file)
index 0000000..29bd463
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Augment search result set with values of certain page props.
+ */
+class AugmentPageProps implements ResultSetAugmentor {
+       /**
+        * @var array List of properties.
+        */
+       private $propnames;
+
+       public function __construct( $propnames ) {
+               $this->propnames = $propnames;
+       }
+
+       public function augmentAll( SearchResultSet $resultSet ) {
+               $titles = $resultSet->extractTitles();
+               return PageProps::getInstance()->getProperties( $titles, $this->propnames );
+       }
+}
diff --git a/includes/search/PerRowAugmentor.php b/includes/search/PerRowAugmentor.php
new file mode 100644 (file)
index 0000000..8eb8b17
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Perform augmentation of each row and return composite result,
+ * indexed by ID.
+ */
+class PerRowAugmentor implements ResultSetAugmentor {
+
+       /**
+        * @var ResultAugmentor
+        */
+       private $rowAugmentor;
+
+       /**
+        * PerRowAugmentor constructor.
+        * @param ResultAugmentor $augmentor Per-result augmentor to use.
+        */
+       public function __construct( ResultAugmentor $augmentor ) {
+               $this->rowAugmentor = $augmentor;
+       }
+
+       /**
+        * Produce data to augment search result set.
+        * @param SearchResultSet $resultSet
+        * @return array Data for all results
+        */
+       public function augmentAll( SearchResultSet $resultSet ) {
+               $data = [];
+               foreach ( $resultSet->extractResults() as $result ) {
+                       $id = $result->getTitle()->getArticleID();
+                       if ( !$id ) {
+                               continue;
+                       }
+                       $data[$id] = $this->rowAugmentor->augment( $result );
+               }
+               return $data;
+       }
+}
diff --git a/includes/search/ResultAugmentor.php b/includes/search/ResultAugmentor.php
new file mode 100644 (file)
index 0000000..350b780
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Augment search results.
+ *
+ */
+interface ResultAugmentor {
+       /**
+        * Produce data to augment search result set.
+        * @param SearchResult $result
+        * @return mixed Data for this result
+        */
+       public function augment( SearchResult $result );
+}
diff --git a/includes/search/ResultSetAugmentor.php b/includes/search/ResultSetAugmentor.php
new file mode 100644 (file)
index 0000000..94710a8
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Augment search results.
+ *
+ */
+interface ResultSetAugmentor {
+       /**
+        * Produce data to augment search result set.
+        * @param SearchResultSet $resultSet
+        * @return array Data for all results
+        */
+       public function augmentAll( SearchResultSet $resultSet );
+}
index 5b18b7c..38c60d0 100644 (file)
@@ -40,7 +40,7 @@ class SearchDatabase extends SearchEngine {
                if ( $db ) {
                        $this->db = $db;
                } else {
-                       $this->db = wfGetDB( DB_SLAVE );
+                       $this->db = wfGetDB( DB_REPLICA );
                }
        }
 
index c2ccca0..1eba141 100644 (file)
@@ -695,6 +695,37 @@ abstract class SearchEngine {
                Hooks::run( 'SearchIndexFields', [ &$fields, $this ] );
                return $fields;
        }
+
+       /**
+        * Augment search results with extra data.
+        *
+        * @param SearchResultSet $resultSet
+        */
+       public function augmentSearchResults( SearchResultSet $resultSet ) {
+               $setAugmentors = [];
+               $rowAugmentors = [];
+               Hooks::run( "SearchResultsAugment", [ &$setAugmentors, &$rowAugmentors ] );
+
+               if ( !$setAugmentors && !$rowAugmentors ) {
+                       // We're done here
+                       return;
+               }
+
+               // Convert row augmentors to set augmentor
+               foreach ( $rowAugmentors as $name => $row ) {
+                       if ( isset( $setAugmentors[$name] ) ) {
+                               throw new InvalidArgumentException( "Both row and set augmentors are defined for $name" );
+                       }
+                       $setAugmentors[$name] = new PerRowAugmentor( $row );
+               }
+
+               foreach ( $setAugmentors as $name => $augmentor ) {
+                       $data = $augmentor->augmentAll( $resultSet );
+                       if ( $data ) {
+                               $resultSet->setAugmentedData( $name, $data );
+                       }
+               }
+       }
 }
 
 /**
index 67f500c..a767bc3 100644 (file)
@@ -5,7 +5,6 @@
  * Allows to create engine of the specific type.
  */
 class SearchEngineFactory {
-
        /**
         * Configuration for SearchEngine classes.
         * @var SearchEngineConfig
@@ -32,11 +31,33 @@ class SearchEngineFactory {
                } elseif ( $configType !== null ) {
                        $class = $configType;
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $class = $dbr->getSearchEngine();
+                       $dbr = wfGetDB( DB_REPLICA );
+                       $class = self::getSearchEngineClass( $dbr );
                }
 
                $search = new $class( $dbr );
                return $search;
        }
+
+       /**
+        * @param IDatabase $db
+        * @return string SearchEngine subclass name
+        * @since 1.28
+        */
+       public static function getSearchEngineClass( IDatabase $db ) {
+               switch ( $db->getType() ) {
+                       case 'sqlite':
+                               return 'SearchSqlite';
+                       case 'mysql':
+                               return 'SearchMySQL';
+                       case 'postgres':
+                               return 'SearchPostgres';
+                       case 'mssql':
+                               return 'SearchMssql';
+                       case 'oracle':
+                               return 'SearchOracle';
+                       default:
+                               return 'SearchEngineDummy';
+               }
+       }
 }
index 2bd1955..dd41a6e 100644 (file)
@@ -34,10 +34,11 @@ class SearchHighlighter {
        }
 
        /**
-        * Default implementation of wikitext highlighting
+        * Wikitext highlighting when $wgAdvancedSearchHighlighting = true
         *
         * @param string $text
-        * @param array $terms Terms to highlight (unescaped)
+        * @param array $terms Terms to highlight (not html escaped but
+        *   regex escaped via SearchDatabase::regexTerm())
         * @param int $contextlines
         * @param int $contextchars
         * @return string
@@ -145,7 +146,6 @@ class SearchHighlighter {
                }
                $anyterm = implode( '|', $terms );
                $phrase = implode( "$wgSearchHighlightBoundaries+", $terms );
-
                // @todo FIXME: A hack to scale contextchars, a correct solution
                // would be to have contextchars actually be char and not byte
                // length, and do proper utf-8 substrings and lengths everywhere,
@@ -485,8 +485,10 @@ class SearchHighlighter {
         * Simple & fast snippet extraction, but gives completely unrelevant
         * snippets
         *
+        * Used when $wgAdvancedSearchHighlighting is false.
+        *
         * @param string $text
-        * @param array $terms
+        * @param array $terms Escaped for regex by SearchDatabase::regexTerm()
         * @param int $contextlines
         * @param int $contextchars
         * @return string
index 7499853..6806ee5 100644 (file)
@@ -35,6 +35,7 @@ interface SearchIndexField {
         * Do not index this field, just store it.
         */
        const FLAG_NO_INDEX = 8;
+
        /**
         * Get mapping for specific search engine
         * @param SearchEngine $engine
index 7e82378..36cbbaa 100644 (file)
@@ -437,7 +437,7 @@ class SearchMySQL extends SearchDatabase {
                if ( is_null( self::$mMinSearchLength ) ) {
                        $sql = "SHOW GLOBAL VARIABLES LIKE 'ft\\_min\\_word\\_len'";
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->query( $sql, __METHOD__ );
                        $row = $result->fetchObject();
                        $result->free();
index 6d66707..3141797 100644 (file)
@@ -21,7 +21,7 @@ class SearchNearMatchResultSet extends SearchResultSet {
                        return false;
                }
                $this->fetched = true;
-               return SearchResult::newFromTitle( $this->result );
+               return SearchResult::newFromTitle( $this->result, $this );
        }
 
        public function rewind() {
index 21effbb..50db84b 100644 (file)
@@ -56,15 +56,25 @@ class SearchResult {
         */
        protected $searchEngine;
 
+       /**
+        * A set of extension data.
+        * @var array[]
+        */
+       protected $extensionData;
+
        /**
         * Return a new SearchResult and initializes it with a title.
         *
-        * @param Title $title
+        * @param Title           $title
+        * @param SearchResultSet $parentSet
         * @return SearchResult
         */
-       public static function newFromTitle( $title ) {
+       public static function newFromTitle( $title, SearchResultSet $parentSet = null ) {
                $result = new static();
                $result->initFromTitle( $title );
+               if ( $parentSet ) {
+                       $parentSet->augmentResult( $result );
+               }
                return $result;
        }
 
@@ -250,4 +260,24 @@ class SearchResult {
        function isFileMatch() {
                return false;
        }
+
+       /**
+        * Get the extension data as:
+        * augmentor name => data
+        * @return array[]
+        */
+       public function getExtensionData() {
+               return $this->extensionData;
+       }
+
+       /**
+        * Set extension data for this result.
+        * The data is:
+        * augmentor name => data
+        * @param array[] $extensionData
+        */
+       public function setExtensionData( array $extensionData ) {
+               $this->extensionData = $extensionData;
+       }
+
 }
index 69795e7..978db27 100644 (file)
@@ -42,6 +42,29 @@ class SearchResultSet {
 
        protected $containedSyntax = false;
 
+       /**
+        * Cache of titles.
+        * Lists titles of the result set, in the same order as results.
+        * @var Title[]
+        */
+       private $titles;
+
+       /**
+        * Cache of results - serialization of the result iterator
+        * as an array.
+        * @var SearchResult[]
+        */
+       private $results;
+
+       /**
+        * Set of result's extra data, indexed per result id
+        * and then per data item name.
+        * The structure is:
+        * PAGE_ID => [ augmentor name => data, ... ]
+        * @var array[]
+        */
+       protected $extraData = [];
+
        public function __construct( $containedSyntax = false ) {
                $this->containedSyntax = $containedSyntax;
        }
@@ -147,15 +170,15 @@ class SearchResultSet {
        /**
         * Fetches next search result, or false.
         * STUB
-        *
-        * @return SearchResult
+        * FIXME: refactor as iterator, so we could use nicer interfaces.
+        * @return SearchResult|false
         */
        function next() {
                return false;
        }
 
        /**
-        * Rewind result set back to begining
+        * Rewind result set back to beginning
         */
        function rewind() {
        }
@@ -176,4 +199,69 @@ class SearchResultSet {
        public function searchContainedSyntax() {
                return $this->containedSyntax;
        }
+
+       /**
+        * Extract all the results in the result set as array.
+        * @return SearchResult[]
+        */
+       public function extractResults() {
+               if ( is_null( $this->results ) ) {
+                       $this->results = [];
+                       if ( $this->numRows() == 0 ) {
+                               // Don't bother if we've got empty result
+                               return $this->results;
+                       }
+                       $this->rewind();
+                       while ( ( $result = $this->next() ) != false ) {
+                               $this->results[] = $result;
+                       }
+                       $this->rewind();
+               }
+               return $this->results;
+       }
+
+       /**
+        * Extract all the titles in the result set.
+        * @return Title[]
+        */
+       public function extractTitles() {
+               if ( is_null( $this->titles ) ) {
+                       if ( $this->numRows() == 0 ) {
+                               // Don't bother if we've got empty result
+                               $this->titles = [];
+                       } else {
+                               $this->titles = array_map(
+                                       function ( SearchResult $result ) {
+                                               return $result->getTitle();
+                                       },
+                                       $this->extractResults() );
+                       }
+               }
+               return $this->titles;
+       }
+
+       /**
+        * Sets augmented data for result set.
+        * @param string $name Extra data item name
+        * @param array[] $data Extra data as PAGEID => data
+        */
+       public function setAugmentedData( $name, $data ) {
+               foreach ( $data as $id => $resultData ) {
+                       $this->extraData[$id][$name] = $resultData;
+               }
+       }
+
+       /**
+        * Returns extra data for specific result and store it in SearchResult object.
+        * @param SearchResult $result
+        * @return array|null List of data as name => value or null if none present.
+        */
+       public function augmentResult( SearchResult $result ) {
+               $id = $result->getTitle()->getArticleID();
+               if ( !$id || !isset( $this->extraData[$id] ) ) {
+                       return null;
+               }
+               $result->setExtensionData( $this->extraData[$id] );
+               return $this->extraData[$id];
+       }
 }
index 6b60899..c3985d1 100644 (file)
@@ -37,7 +37,7 @@ class SqlSearchResultSet extends SearchResultSet {
                }
 
                return SearchResult::newFromTitle(
-                       Title::makeTitle( $row->page_namespace, $row->page_title )
+                       Title::makeTitle( $row->page_namespace, $row->page_title ), $this
                );
        }
 
index 8fa212e..31761c3 100644 (file)
@@ -129,6 +129,11 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
 
        /**
         * Make this session not be persisted across requests
+        *
+        * This will remove persistence information (e.g. delete cookies)
+        * from the associated WebRequest(s), and delete session data in the
+        * backend. The session data will still be available via get() until
+        * the end of the request.
         */
        public function unpersist() {
                $this->backend->unpersist();
@@ -603,6 +608,9 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
 
        /**
         * Save the session
+        *
+        * This will update the backend data and might re-persist the session
+        * if needed.
         */
        public function save() {
                $this->backend->save();
index 264e1ae..263cb11 100644 (file)
@@ -599,7 +599,8 @@ final class SessionBackend {
        }
 
        /**
-        * Save and persist session data, unless delayed
+        * Save the session, unless delayed
+        * @see SessionBackend::save()
         */
        private function autosave() {
                if ( $this->delaySave <= 0 ) {
@@ -608,7 +609,12 @@ final class SessionBackend {
        }
 
        /**
-        * Save and persist session data
+        * Save the session
+        *
+        * Update both the backend data and the associated WebRequest(s) to
+        * reflect the state of the the SessionBackend. This might include
+        * persisting or unpersisting the session.
+        *
         * @param bool $closing Whether the session is being closed
         */
        public function save( $closing = false ) {
@@ -716,6 +722,8 @@ final class SessionBackend {
                        }
                }
 
+               $flags = $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY;
+               $flags |= CachedBagOStuff::WRITE_SYNC; // write to all datacenters
                $this->store->set(
                        wfMemcKey( 'MWSession', (string)$this->id ),
                        [
@@ -723,7 +731,7 @@ final class SessionBackend {
                                'metadata' => $metadata,
                        ],
                        $metadata['expires'],
-                       $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY
+                       $flags
                );
 
                $this->metaDirty = false;
index c235861..287da9d 100644 (file)
@@ -73,7 +73,8 @@ class SessionInfo {
         *    Defaults to true.
         *  - forceHTTPS: (bool) Whether to force HTTPS for this session
         *  - metadata: (array) Provider metadata, to be returned by
-        *    Session::getProviderMetadata().
+        *    Session::getProviderMetadata(). See SessionProvider::mergeMetadata()
+        *    and SessionProvider::refreshSessionInfo().
         *  - idIsSafe: (bool) Set true if the 'id' did not come from the user.
         *    Generally you'll use this from SessionProvider::newEmptySession(),
         *    and not from any other method.
@@ -200,7 +201,8 @@ class SessionInfo {
         * The normal behavior is to discard the SessionInfo if validation against
         * the data stored in the session store fails. If this returns true,
         * SessionManager will instead delete the session store data so this
-        * SessionInfo may still be used.
+        * SessionInfo may still be used. This is important for providers which use
+        * deterministic IDs and so cannot just generate a random new one.
         *
         * @return bool
         */
index 8ccb6d1..87fdcd3 100644 (file)
@@ -35,8 +35,15 @@ use WebRequest;
 /**
  * This serves as the entry point to the MediaWiki session handling system.
  *
+ * Most methods here are for internal use by session handling code. Other callers
+ * should only use getGlobalSession and the methods of SessionManagerInterface;
+ * the rest of the functionality is exposed via MediaWiki\Session\Session methods.
+ *
+ * To provide custom session handling, implement a MediaWiki\Session\SessionProvider.
+ *
  * @ingroup Session
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 final class SessionManager implements SessionManagerInterface {
        /** @var SessionManager|null */
@@ -819,9 +826,9 @@ final class SessionManager implements SessionManagerInterface {
        }
 
        /**
-        * Create a session corresponding to the passed SessionInfo
+        * Create a Session corresponding to the passed SessionInfo
         * @private For use by a SessionProvider that needs to specially create its
-        *  own session.
+        *  own Session. Most session providers won't need this.
         * @param SessionInfo $info
         * @param WebRequest $request
         * @return Session
@@ -941,6 +948,7 @@ final class SessionManager implements SessionManagerInterface {
 
        /**
         * Reset the internal caching for unit testing
+        * @protected Unit tests only
         */
        public static function resetCache() {
                if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
index d4e52c7..3ab0f43 100644 (file)
@@ -36,7 +36,8 @@ use WebRequest;
  */
 interface SessionManagerInterface extends LoggerAwareInterface {
        /**
-        * Fetch the session for a request
+        * Fetch the session for a request (or a new empty session if none is
+        * attached to it)
         *
         * @note You probably want to use $request->getSession() instead. It's more
         *  efficient and doesn't break FauxRequests or sessions that were changed
@@ -52,6 +53,7 @@ interface SessionManagerInterface extends LoggerAwareInterface {
 
        /**
         * Fetch a session by ID
+        *
         * @param string $id
         * @param bool $create If no session exists for $id, try to create a new one.
         *  May still return null if a session for $id exists but cannot be loaded.
@@ -62,7 +64,7 @@ interface SessionManagerInterface extends LoggerAwareInterface {
        public function getSessionById( $id, $create = false, WebRequest $request = null );
 
        /**
-        * Fetch a new, empty session
+        * Create a new, empty session
         *
         * The first provider configured that is able to provide an empty session
         * will be used.
index 4d57ad9..61c7500 100644 (file)
@@ -66,13 +66,14 @@ use WebRequest;
  *    would make sense.
  *
  * Note that many methods that are technically "cannot persist ID" could be
- * turned into "can persist ID but not changing User" using a session cookie,
+ * turned into "can persist ID but not change User" using a session cookie,
  * as implemented by ImmutableSessionProviderWithCookie. If doing so, different
  * session cookie names should be used for different providers to avoid
  * collisions.
  *
  * @ingroup Session
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 abstract class SessionProvider implements SessionProviderInterface, LoggerAwareInterface {
 
@@ -180,14 +181,23 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
        /**
         * Merge saved session provider metadata
         *
+        * This method will be used to compare the metadata returned by
+        * provideSessionInfo() with the saved metadata (which has been returned by
+        * provideSessionInfo() the last time the session was saved), and merge the two
+        * into the new saved metadata, or abort if the current request is not a valid
+        * continuation of the session.
+        *
         * The default implementation checks that anything in both arrays is
         * identical, then returns $providedMetadata.
         *
         * @protected For use by \MediaWiki\Session\SessionManager only
         * @param array $savedMetadata Saved provider metadata
-        * @param array $providedMetadata Provided provider metadata
+        * @param array $providedMetadata Provided provider metadata (from the SessionInfo)
         * @return array Resulting metadata
-        * @throws MetadataMergeException If the metadata cannot be merged
+        * @throws MetadataMergeException If the metadata cannot be merged.
+        *  Such exceptions will be handled by SessionManager and are a safe way of rejecting
+        *  a suspicious or incompatible session. The provider is expected to write an
+        *  appropriate message to its logger.
         */
        public function mergeMetadata( array $savedMetadata, array $providedMetadata ) {
                foreach ( $providedMetadata as $k => $v ) {
@@ -211,7 +221,7 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
         * expected to write an appropriate message to its logger.
         *
         * @protected For use by \MediaWiki\Session\SessionManager only
-        * @param SessionInfo $info
+        * @param SessionInfo $info Any changes by mergeMetadata() will already be reflected here.
         * @param WebRequest $request
         * @param array|null &$metadata Provider metadata, may be altered.
         * @return bool Return false to reject the SessionInfo after all.
@@ -420,6 +430,11 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
 
        /**
         * Fetch the rights allowed the user when the specified session is active.
+        *
+        * This is mainly meant for allowing the user to restrict access to the account
+        * by certain methods; you probably want to use this with MWGrants. The returned
+        * rights will be intersected with the user's actual rights.
+        *
         * @param SessionBackend $backend
         * @return null|string[] Allowed user rights, or null to allow all.
         */
index 9b4a73c..523e0cc 100644 (file)
@@ -73,7 +73,7 @@ class Token {
 
        /**
         * Get the string representation of the token at a timestamp
-        * @param int timestamp
+        * @param int $timestamp
         * @return string
         */
        protected function toStringAtTimestamp( $timestamp ) {
index 974789f..432d5ce 100644 (file)
@@ -73,7 +73,7 @@ class DBSiteStore implements SiteStore {
        protected function loadSites() {
                $this->sites = new SiteList();
 
-               $dbr = $this->dbLoadBalancer->getConnection( DB_SLAVE );
+               $dbr = $this->dbLoadBalancer->getConnection( DB_REPLICA );
 
                $res = $dbr->select(
                        'sites',
index 71ca57b..2d37a0f 100644 (file)
@@ -320,10 +320,10 @@ abstract class BaseTemplate extends QuickTemplate {
         *
         * If a "data" key is present, it must be an array, where the keys represent
         * the data-xxx properties with their provided values. For example,
-        *  $item['data'] = array(
+        *  $item['data'] = [
         *       'foo' => 1,
         *       'bar' => 'baz',
-        *  );
+        *  ];
         * will render as element properties:
         *  data-foo='1' data-bar='baz'
         *
@@ -333,7 +333,7 @@ abstract class BaseTemplate extends QuickTemplate {
         *   a link in. This should be an array of arrays containing a 'tag' and
         *   optionally an 'attributes' key. If you only have one element you don't
         *   need to wrap it in another array. eg: To use <a><span>...</span></a>
-        *   in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
+        *   in all links use [ 'text-wrapper' => [ 'tag' => 'span' ] ]
         *   for your options.
         *   - 'link-class' key can be used to specify additional classes to apply
         *   to all links.
index d473251..b60aa10 100644 (file)
@@ -846,7 +846,7 @@ abstract class Skin extends ContextSource {
                        $s = '';
                }
 
-               if ( wfGetLB()->getLaggedSlaveMode() ) {
+               if ( wfGetLB()->getLaggedReplicaMode() ) {
                        $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
                }
 
@@ -1169,7 +1169,7 @@ abstract class Skin extends ContextSource {
         *
         * BaseTemplate::getSidebar can be used to simplify the format and id generation in new skins.
         *
-        * The format of the returned array is array( heading => content, ... ), where:
+        * The format of the returned array is [ heading => content, ... ], where:
         * - heading is the heading of a navigation portlet. It is either:
         *   - magic string to be handled by the skins ('SEARCH' / 'LANGUAGES' / 'TOOLBOX' / ...)
         *   - a message name (e.g. 'navigation'), the message should be HTML-escaped by the skin
index b4be461..2351ab8 100644 (file)
@@ -17,6 +17,8 @@
  *
  * @file
  */
+
+use MediaWiki\Auth\AuthManager;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -244,7 +246,7 @@ class SkinTemplate extends Skin {
                $out = $this->getOutput();
 
                $this->initPage( $out );
-               $tpl = $this->prepareQuickTemplate( $out );
+               $tpl = $this->prepareQuickTemplate();
                // execute template
                $res = $tpl->execute();
 
@@ -574,6 +576,7 @@ class SkinTemplate extends Skin {
                $title = $this->getTitle();
                $request = $this->getRequest();
                $pageurl = $title->getLocalURL();
+               $authManager = AuthManager::singleton();
 
                /* set up the default links for the personal toolbar */
                $personal_urls = [];
@@ -652,17 +655,25 @@ class SkinTemplate extends Skin {
                                'href' => $href,
                                'active' => $active
                        ];
-                       $personal_urls['logout'] = [
-                               'text' => $this->msg( 'pt-userlogout' )->text(),
-                               'href' => self::makeSpecialUrl( 'Userlogout',
-                                       // userlogout link must always contain an & character, otherwise we might not be able
-                                       // to detect a buggy precaching proxy (bug 17790)
-                                       $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto
-                               ),
-                               'active' => false
-                       ];
+
+                       // if we can't set the user, we can't unset it either
+                       if ( $request->getSession()->canSetUser() ) {
+                               $personal_urls['logout'] = [
+                                       'text' => $this->msg( 'pt-userlogout' )->text(),
+                                       'href' => self::makeSpecialUrl( 'Userlogout',
+                                               // userlogout link must always contain an & character, otherwise we might not be able
+                                               // to detect a buggy precaching proxy (bug 17790)
+                                               $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto ),
+                                       'active' => false
+                               ];
+                       }
                } else {
                        $useCombinedLoginLink = $this->useCombinedLoginLink();
+                       if ( !$authManager->canCreateAccounts() || !$authManager->canAuthenticateNow() ) {
+                               // don't show combined login/signup link if one of those is actually not available
+                               $useCombinedLoginLink = false;
+                       }
+
                        $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink
                                ? 'nav-login-createaccount'
                                : 'pt-login';
@@ -699,11 +710,17 @@ class SkinTemplate extends Skin {
                                ];
                        }
 
-                       if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) {
+                       if (
+                               $authManager->canCreateAccounts()
+                               && $this->getUser()->isAllowed( 'createaccount' )
+                               && !$useCombinedLoginLink
+                       ) {
                                $personal_urls['createaccount'] = $createaccount_url;
                        }
 
-                       $personal_urls['login'] = $login_url;
+                       if ( $authManager->canAuthenticateNow() ) {
+                               $personal_urls['login'] = $login_url;
+                       }
                }
 
                Hooks::run( 'PersonalUrls', [ &$personal_urls, &$title, $this ] );
@@ -892,11 +909,8 @@ class SkinTemplate extends Skin {
                        $content_navigation['namespaces'][$talkId]['context'] = 'talk';
 
                        if ( $userCanRead ) {
-                               $isForeignFile = $title->inNamespace( NS_FILE ) && $this->canUseWikiPage() &&
-                                       $this->getWikiPage() instanceof WikiFilePage && !$this->getWikiPage()->isLocal();
-
-                               // Adds view view link
-                               if ( $title->exists() || $isForeignFile ) {
+                               // Adds "view" view link
+                               if ( $title->isKnown() ) {
                                        $content_navigation['views']['view'] = $this->tabAction(
                                                $isTalk ? $talkPage : $subjectPage,
                                                [ "$skname-view-view", 'view' ],
@@ -906,7 +920,11 @@ class SkinTemplate extends Skin {
                                        $content_navigation['views']['view']['redundant'] = true;
                                }
 
+                               $isForeignFile = $title->inNamespace( NS_FILE ) && $this->canUseWikiPage() &&
+                                       $this->getWikiPage() instanceof WikiFilePage && !$this->getWikiPage()->isLocal();
+
                                // If it is a non-local file, show a link to the file in its own repository
+                               // @todo abstract this for remote content that isn't a file
                                if ( $isForeignFile ) {
                                        $file = $this->getWikiPage()->getFile();
                                        $content_navigation['views']['view-foreign'] = [
@@ -964,7 +982,7 @@ class SkinTemplate extends Skin {
                                                        'href' => $title->getLocalURL( 'action=edit&section=new' )
                                                ];
                                        }
-                               // Checks if the page has some kind of viewable content
+                               // Checks if the page has some kind of viewable source content
                                } elseif ( $title->hasSourceText() ) {
                                        // Adds view source view link
                                        $content_navigation['views']['viewsource'] = [
index 60f1dd8..01782f3 100644 (file)
@@ -340,7 +340,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
         * @return IDatabase
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
index c3d43df..bf83e7b 100644 (file)
@@ -223,11 +223,16 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                $this->setHeaders();
                $this->checkPermissions();
 
-               // Make sure it's possible to log in
-               if ( !$this->isSignup() && !$session->canSetUser() ) {
-                       throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
+               // Make sure the system configuration allows log in / sign up
+               if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
+                       if ( !$session->canSetUser() ) {
+                               throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
                                        $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
                                ] );
+                       }
+                       throw new ErrorPageError( 'cannotlogin-title', 'cannotlogin-text' );
+               } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
+                       throw new ErrorPageError( 'cannotcreateaccount-title', 'cannotcreateaccount-text' );
                }
 
                /*
@@ -354,7 +359,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
                                        : AuthManager::ACTION_LOGIN_CONTINUE;
                                $this->authRequests = $response->neededRequests;
-                               $this->mainLoginForm( $response->neededRequests, $response->message, 'warning' );
+                               $this->mainLoginForm( $response->neededRequests, $response->message, $response->messageType );
                                break;
                        default:
                                throw new LogicException( 'invalid AuthenticationResponse' );
@@ -494,7 +499,21 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
 
                $form = $this->getAuthForm( $requests, $this->authAction, $msg, $msgtype );
                $form->prepareForm();
-               $formHtml = $form->getHTML( $msg ? Status::newFatal( $msg ) : false );
+
+               $submitStatus = Status::newGood();
+               if ( $msg && $msgtype === 'warning' ) {
+                       $submitStatus->warning( $msg );
+               } elseif ( $msg && $msgtype === 'error' ) {
+                       $submitStatus->fatal( $msg );
+               }
+
+               // warning header for non-standard workflows (e.g. security reauthentication)
+               if ( !$this->isSignup() && $this->getUser()->isLoggedIn() ) {
+                       $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
+                       $submitStatus->warning( $reauthMessage, $this->getUser()->getName() );
+               }
+
+               $formHtml = $form->getHTML( $submitStatus );
 
                $out->addHTML( $this->getPageHtml( $formHtml ) );
        }
@@ -616,13 +635,6 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                        $form->setId( 'userlogin2' );
                }
 
-               // warning header for non-standard workflows (e.g. security reauthentication)
-               if ( !$this->isSignup() && $this->getUser()->isLoggedIn() ) {
-                       $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
-                       $form->addHeaderText( Html::rawElement( 'div', [ 'class' => 'warningbox' ],
-                               $this->msg( $reauthMessage )->params( $this->getUser()->getName() )->parse() ) );
-               }
-
                $form->suppressDefaultSubmit();
 
                $this->authForm = $form;
index 1beac43..afbc581 100644 (file)
@@ -376,7 +376,7 @@ abstract class QueryPage extends SpecialPage {
         * @return IDatabase
         */
        function getRecacheDB() {
-               return wfGetDB( DB_SLAVE, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
+               return wfGetDB( DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
        }
 
        /**
@@ -453,7 +453,7 @@ abstract class QueryPage extends SpecialPage {
         * @since 1.18
         */
        public function fetchFromCache( $limit, $offset = false ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $options = [];
                if ( $limit !== false ) {
                        $options['LIMIT'] = intval( $limit );
@@ -477,7 +477,7 @@ abstract class QueryPage extends SpecialPage {
 
        public function getCachedTimestamp() {
                if ( is_null( $this->cachedTimestamp ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $fname = get_class( $this ) . '::getCachedTimestamp';
                        $this->cachedTimestamp = $dbr->selectField( 'querycache_info', 'qci_timestamp',
                                [ 'qci_type' => $this->getName() ], $fname );
index c697ca7..2da441b 100644 (file)
@@ -62,7 +62,7 @@ class SpecialActiveUsers extends SpecialPage {
 
                // Mention the level of cache staleness...
                $cacheText = '';
-               $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
+               $dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
                $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
                if ( $rcMax ) {
                        $cTime = $dbr->selectField( 'querycache_info',
index b55f0b4..4a2a619 100644 (file)
@@ -181,7 +181,7 @@ class SpecialAllPages extends IncludableSpecialPage {
                        list( $namespace, $fromKey, $from ) = $fromList;
                        list( , $toKey, $to ) = $toList;
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $filterConds = [ 'page_namespace' => $namespace ];
                        if ( $hideredirects ) {
                                $filterConds['page_is_redirect'] = 0;
index 7c7f017..c4fb316 100644 (file)
@@ -136,8 +136,7 @@ class SpecialBlockList extends SpecialPage {
                                case Block::TYPE_IP:
                                case Block::TYPE_RANGE:
                                        list( $start, $end ) = IP::parseRange( $target );
-                                       $dbr = wfGetDB( DB_SLAVE );
-                                       $conds[] = $dbr->makeList(
+                                       $conds[] = wfGetDB( DB_REPLICA )->makeList(
                                                [
                                                        'ipb_address' => $target,
                                                        Block::getRangeCond( $start, $end )
index 7e330aa..9975e41 100644 (file)
@@ -163,7 +163,7 @@ class SpecialBotPasswords extends FormSpecialPage {
 
                } else {
                        $linkRenderer = $this->getLinkRenderer();
-                       $dbr = BotPassword::getDB( DB_SLAVE );
+                       $dbr = BotPassword::getDB( DB_REPLICA );
                        $res = $dbr->select(
                                'bot_passwords',
                                [ 'bp_app_id' ],
@@ -290,9 +290,7 @@ class SpecialBotPasswords extends FormSpecialPage {
                ] );
 
                if ( $this->operation === 'insert' || !empty( $data['resetPassword'] ) ) {
-                       $this->password = PasswordFactory::generateRandomPasswordString(
-                               max( 32, $this->getConfig()->get( 'MinimalPasswordLength' ) )
-                       );
+                       $this->password = BotPassword::generatePassword( $this->getConfig() );
                        $passwordFactory = new PasswordFactory();
                        $passwordFactory->init( RequestContext::getMain()->getConfig() );
                        $password = $passwordFactory->newFromPlaintext( $this->password );
@@ -335,7 +333,9 @@ class SpecialBotPasswords extends FormSpecialPage {
                        $out->addWikiMsg(
                                'botpasswords-newpassword',
                                htmlspecialchars( $username . $sep . $this->par ),
-                               htmlspecialchars( $this->password )
+                               htmlspecialchars( $this->password ),
+                               htmlspecialchars( $username ),
+                               htmlspecialchars( $this->par . $sep . $this->password )
                        );
                        $this->password = null;
                }
index 4c3fbe5..b9b2051 100644 (file)
@@ -49,7 +49,7 @@ class BrokenRedirectsPage extends QueryPage {
        }
 
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return [
                        'tables' => [
index ccbb275..dd7f0ed 100644 (file)
@@ -191,6 +191,12 @@ class SpecialChangeContentModel extends FormSpecialPage {
                        // Page doesn't exist, create an empty content object
                        $newContent = ContentHandler::getForModelID( $data['model'] )->makeEmptyContent();
                }
+
+               // All other checks have passed, let's check rate limits
+               if ( $user->pingLimiter( 'editcontentmodel' ) ) {
+                       throw new ThrottledError();
+               }
+
                $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW;
                $flags |= EDIT_INTERNAL;
                if ( $user->isAllowed( 'bot' ) ) {
@@ -215,6 +221,22 @@ class SpecialChangeContentModel extends FormSpecialPage {
                # Truncate for whole multibyte characters.
                $reason = $wgContLang->truncate( $reason, 255 );
 
+               // Run edit filters
+               $derivativeContext = new DerivativeContext( $this->getContext() );
+               $derivativeContext->setTitle( $this->title );
+               $derivativeContext->setWikiPage( $page );
+               $status = new Status();
+               if ( !Hooks::run( 'EditFilterMergedContent',
+                               [ $derivativeContext, $newContent, $status, $reason,
+                               $user, false ] )
+               ) {
+                       if ( $status->isGood() ) {
+                               // TODO: extensions should really specify an error message
+                               $status->fatal( 'hookaborted' );
+                       }
+                       return $status;
+               }
+
                $status = $page->doEditContent(
                        $newContent,
                        $reason,
index 6aeb2c3..0858b18 100644 (file)
@@ -203,7 +203,7 @@ class SpecialContributions extends IncludableSpecialPage {
                        if ( !$pager->getNumRows() ) {
                                $out->addWikiMsg( 'nocontribs', $target );
                        } else {
-                               # Show a message about slave lag, if applicable
+                               # Show a message about replica DB lag, if applicable
                                $lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
                                if ( $lag > 0 ) {
                                        $out->showLagWarning( $lag );
@@ -496,12 +496,12 @@ class SpecialContributions extends IncludableSpecialPage {
 
                if ( $tagFilter ) {
                        $filterSelection = Html::rawElement(
-                               'td',
+                               'div',
                                [],
                                implode( '&#160;', $tagFilter )
                        );
                } else {
-                       $filterSelection = Html::rawElement( 'td', [ 'colspan' => 2 ], '' );
+                       $filterSelection = Html::rawElement( 'div', [], '' );
                }
 
                $this->getOutput()->addModules( 'mediawiki.userSuggest' );
@@ -542,13 +542,13 @@ class SpecialContributions extends IncludableSpecialPage {
                );
 
                $targetSelection = Html::rawElement(
-                       'td',
-                       [ 'colspan' => 2 ],
-                       $labelNewbies . '<br />' . $labelUsername . ' ' . $input . ' '
+                       'div',
+                       [],
+                       $labelNewbies . '<br>' . $labelUsername . ' ' . $input . ' '
                );
 
                $namespaceSelection = Xml::tags(
-                       'td',
+                       'div',
                        [],
                        Xml::label(
                                $this->msg( 'namespace' )->text(),
@@ -647,12 +647,12 @@ class SpecialContributions extends IncludableSpecialPage {
                );
 
                $extraOptions = Html::rawElement(
-                       'td',
-                       [ 'colspan' => 2 ],
+                       'div',
+                       [],
                        implode( '', $filters )
                );
 
-               $dateSelectionAndSubmit = Xml::tags( 'td', [ 'colspan' => 2 ],
+               $dateSelectionAndSubmit = Xml::tags( 'div', [],
                        Xml::dateMenu(
                                $this->opts['year'] === '' ? MWTimestamp::getInstance()->format( 'Y' ) : $this->opts['year'],
                                $this->opts['month']
@@ -663,13 +663,14 @@ class SpecialContributions extends IncludableSpecialPage {
                                )
                );
 
-               $form .= Xml::fieldset( $this->msg( 'sp-contributions-search' )->text() );
-               $form .= Html::rawElement( 'table', [ 'class' => 'mw-contributions-table' ], "\n" .
-                       Html::rawElement( 'tr', [], $targetSelection ) . "\n" .
-                       Html::rawElement( 'tr', [], $namespaceSelection ) . "\n" .
-                       Html::rawElement( 'tr', [], $filterSelection ) . "\n" .
-                       Html::rawElement( 'tr', [], $extraOptions ) . "\n" .
-                       Html::rawElement( 'tr', [], $dateSelectionAndSubmit ) . "\n"
+               $form .= Xml::fieldset(
+                       $this->msg( 'sp-contributions-search' )->text(),
+                       $targetSelection .
+                       $namespaceSelection .
+                       $filterSelection .
+                       $extraOptions .
+                       $dateSelectionAndSubmit,
+                       [ 'class' => 'mw-contributions-table' ]
                );
 
                $explain = $this->msg( 'sp-contributions-explain' );
@@ -677,7 +678,7 @@ class SpecialContributions extends IncludableSpecialPage {
                        $form .= "<p id='mw-sp-contributions-explain'>{$explain->parse()}</p>";
                }
 
-               $form .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' );
+               $form .= Xml::closeElement( 'form' );
 
                return $form;
        }
index 8e168b2..ad12046 100644 (file)
@@ -98,7 +98,7 @@ class DeletedContributionsPage extends SpecialPage {
                        return;
                }
 
-               # Show a message about slave lag, if applicable
+               # Show a message about replica DB lag, if applicable
                $lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
                if ( $lag > 0 ) {
                        $out->showLagWarning( $lag );
@@ -135,7 +135,7 @@ class DeletedContributionsPage extends SpecialPage {
                if ( $userObj->isAnon() ) {
                        $user = htmlspecialchars( $userObj->getName() );
                } else {
-                       $user = $linkRenderer->makeKnownLink( $userObj->getUserPage(), $userObj->getName() );
+                       $user = $linkRenderer->makeLink( $userObj->getUserPage(), $userObj->getName() );
                }
                $links = '';
                $nt = $userObj->getUserPage();
index 7b00064..0cec9d0 100644 (file)
@@ -50,7 +50,7 @@ class DoubleRedirectsPage extends QueryPage {
 
        function reallyGetQueryInfo( $namespace = null, $title = null ) {
                $limitToTitle = !( $namespace === null && $title === null );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $retval = [
                        'tables' => [
                                'ra' => 'redirect',
@@ -121,7 +121,7 @@ class DoubleRedirectsPage extends QueryPage {
                // get a little more detail about each individual entry quickly
                // using the filter of reallyGetQueryInfo.
                if ( $result && !isset( $result->nsb ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $qi = $this->reallyGetQueryInfo(
                                $result->namespace,
                                $result->title
index fb1943f..06be7bc 100644 (file)
@@ -388,13 +388,29 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                        // unless they are emailing themselves, in which case one
                        // copy of the message is sufficient.
                        if ( $data['CCMe'] && $to != $from ) {
-                               $cc_subject = $context->msg( 'emailccsubject' )->rawParams(
+                               $ccTo = $from;
+                               $ccFrom = $from;
+                               $ccSubject = $context->msg( 'emailccsubject' )->rawParams(
                                        $target->getName(), $subject )->text();
-
-                               // target and sender are equal, because this is the CC for the sender
-                               Hooks::run( 'EmailUserCC', [ &$from, &$from, &$cc_subject, &$text ] );
-
-                               $ccStatus = UserMailer::send( $from, $from, $cc_subject, $text );
+                               $ccText = $text;
+
+                               Hooks::run( 'EmailUserCC', [ &$ccTo, &$ccFrom, &$ccSubject, &$ccText ] );
+
+                               if ( $config->get( 'UserEmailUseReplyTo' ) ) {
+                                       $mailFrom = new MailAddress(
+                                               $config->get( 'PasswordSender' ),
+                                               wfMessage( 'emailsender' )->inContentLanguage()->text()
+                                       );
+                                       $replyTo = $ccFrom;
+                               } else {
+                                       $mailFrom = $ccFrom;
+                                       $replyTo = null;
+                               }
+
+                               $ccStatus = UserMailer::send(
+                                       $ccTo, $mailFrom, $ccSubject, $ccText, [
+                                               'replyTo' => $replyTo,
+                               ] );
                                $status->merge( $ccStatus );
                        }
 
index fe97739..bf535a6 100644 (file)
@@ -370,12 +370,12 @@ class SpecialExport extends SpecialPage {
                /* Ok, let's get to it... */
                if ( $history == WikiExporter::CURRENT ) {
                        $lb = false;
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                        $buffer = WikiExporter::BUFFER;
                } else {
                        // Use an unbuffered query; histories may be very long!
                        $lb = wfGetLBFactory()->newMainLB();
-                       $db = $lb->getConnection( DB_SLAVE );
+                       $db = $lb->getConnection( DB_REPLICA );
                        $buffer = WikiExporter::STREAM;
 
                        // This might take a while... :D
@@ -426,7 +426,7 @@ class SpecialExport extends SpecialPage {
 
                $name = $title->getDBkey();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        [ 'page', 'categorylinks' ],
                        [ 'page_namespace', 'page_title' ],
@@ -459,7 +459,7 @@ class SpecialExport extends SpecialPage {
 
                $maxPages = $this->getConfig()->get( 'ExportPagelistLimit' );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'page',
                        [ 'page_namespace', 'page_title' ],
@@ -556,7 +556,7 @@ class SpecialExport extends SpecialPage {
         * @return array
         */
        private function getLinks( $inputPages, $pageSet, $table, $fields, $join ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                foreach ( $inputPages as $page ) {
                        $title = Title::newFromText( $page );
index d4886f0..307d6c3 100644 (file)
@@ -153,7 +153,7 @@ class LinkSearchPage extends QueryPage {
         */
        static function mungeQuery( $query, $prot ) {
                $field = 'el_index';
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $query === '*' && $prot !== '' ) {
                        // Allow queries like 'ftp://*' to find all ftp links
@@ -185,7 +185,7 @@ class LinkSearchPage extends QueryPage {
        }
 
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                // strip everything past first wildcard, so that
                // index-based-only lookup would be done
                list( $this->mungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
index e51e8b5..ec87716 100644 (file)
@@ -62,7 +62,7 @@ class MediaStatisticsPage extends QueryPage {
         * Special:BrokenRedirects also rely on this.
         */
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $fakeTitle = $dbr->buildConcat( [
                        'img_media_type',
                        $dbr->addQuotes( ';' ),
index 9cc6745..3a12cf3 100644 (file)
@@ -223,7 +223,7 @@ class MovePageForm extends UnlistedSpecialPage {
                        ( $oldTalk->exists()
                                || ( $oldTitleTalkSubpages && $canMoveSubpage ) );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $this->getConfig()->get( 'FixDoubleRedirects' ) ) {
                        $hasRedirects = $dbr->selectField( 'redirect', '1',
                                [
@@ -804,6 +804,10 @@ class MovePageForm extends UnlistedSpecialPage {
                $out->addWikiMsg( 'movesubpagetext', $this->getLanguage()->formatNum( $count ) );
                $out->addHTML( "<ul>\n" );
 
+               $linkBatch = new LinkBatch( $subpages );
+               $linkBatch->setCaller( __METHOD__ );
+               $linkBatch->execute();
+
                $linkRenderer = $this->getLinkRenderer();
                foreach ( $subpages as $subpage ) {
                        $link = $linkRenderer->makeLink( $subpage );
index 5322a04..61b6a8c 100644 (file)
@@ -188,6 +188,9 @@ class SpecialPageLanguage extends FormSpecialPage {
                $logid = $entry->insert();
                $entry->publish( $logid );
 
+               // Force re-render so that language-based content (parser functions etc.) gets updated
+               $title->invalidateCache();
+
                return true;
        }
 
index 327ddda..706a1d7 100644 (file)
@@ -174,7 +174,7 @@ class SpecialPagesWithProp extends QueryPage {
                        $opts['OFFSET'] = $offset;
                }
 
-               $res = wfGetDB( DB_SLAVE )->select(
+               $res = wfGetDB( DB_REPLICA )->select(
                        'page_props',
                        'pp_propname',
                        '',
index f00477f..3697e5d 100644 (file)
@@ -53,10 +53,10 @@ class SpecialPreferences extends SpecialPage {
                $out->addModules( 'mediawiki.special.preferences' );
                $out->addModuleStyles( 'mediawiki.special.preferences.styles' );
 
-               $request = $this->getRequest();
-               if ( $request->getSessionData( 'specialPreferencesSaveSuccess' ) ) {
+               $session = $this->getRequest()->getSession();
+               if ( $session->get( 'specialPreferencesSaveSuccess' ) ) {
                        // Remove session data for the success message
-                       $request->setSessionData( 'specialPreferencesSaveSuccess', null );
+                       $session->remove( 'specialPreferencesSaveSuccess' );
                        $out->addModuleStyles( 'mediawiki.notification.convertmessagebox.styles' );
 
                        $out->addHtml(
@@ -146,7 +146,7 @@ class SpecialPreferences extends SpecialPage {
                $user->saveSettings();
 
                // Set session data for the success message
-               $this->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
+               $this->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
 
                $url = $this->getPageTitle()->getFullURL();
                $this->getOutput()->redirect( $url );
index 87a5b27..5e3e430 100644 (file)
@@ -181,7 +181,7 @@ class SpecialPrefixindex extends SpecialAllPages {
 
                        # ## @todo FIXME: Should complain if $fromNs != $namespace
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $conds = [
                                'page_namespace' => $namespace,
index 342509c..5bdae15 100644 (file)
@@ -558,7 +558,7 @@ class ProtectedPagesPager extends TablePager {
                        'join_conds' => [
                                'log_search' => [
                                        'LEFT JOIN', [
-                                               'ls_field' => 'pr_id', 'ls_value = pr_id'
+                                               'ls_field' => 'pr_id', 'ls_value = ' . $this->mDb->buildStringCast( 'pr_id' )
                                        ]
                                ],
                                'logging' => [
index a5e538f..fc924a4 100644 (file)
@@ -223,7 +223,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
                        ]
                ];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $minClTime = $this->getTimestampOffset( $rand );
                if ( $minClTime ) {
                        $qi['conds'][] = 'cl_timestamp ' . $op . ' ' .
@@ -264,7 +264,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
         * @throws MWException If category has no entries.
         */
        protected function getMinAndMaxForCat( Title $category ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->selectRow(
                        'categorylinks',
                        [
@@ -294,7 +294,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
         * @return array Info for the title selected.
         */
        private function selectRandomPageFromDB( $rand, $offset, $up, $fname = __METHOD__ ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $query = $this->getQueryInfo( $rand, $offset, $up );
                $res = $dbr->select(
index d7835d1..e3b567d 100644 (file)
@@ -159,7 +159,7 @@ class RandomPage extends SpecialPage {
        }
 
        private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $query = $this->getQueryInfo( $randstr );
                $res = $dbr->select(
index 31a290d..0df8423 100644 (file)
@@ -28,7 +28,7 @@ class SpecialRandomrootpage extends RandomPage {
 
        public function __construct() {
                parent::__construct( 'Randomrootpage' );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $this->extra[] = 'page_title NOT ' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
        }
 
index d4fb72c..8aff690 100644 (file)
@@ -281,7 +281,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        protected function getDB() {
-               return wfGetDB( DB_SLAVE, 'recentchanges' );
+               return wfGetDB( DB_REPLICA, 'recentchanges' );
        }
 
        public function outputFeedLinks() {
index 57a3d92..aab0f6d 100644 (file)
@@ -74,7 +74,7 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                 * expects only one result set so we use UNION instead.
                 */
 
-               $dbr = wfGetDB( DB_SLAVE, 'recentchangeslinked' );
+               $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
                $id = $title->getArticleID();
                $ns = $title->getNamespace();
                $dbkey = $title->getDBkey();
index 80dc797..1d1df6a 100644 (file)
@@ -182,7 +182,7 @@ class SpecialRedirect extends FormSpecialPage {
                        'log_user_text',
                ];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Gets the nested SQL statement which
                // returns timestamp of the log with the given log ID
index 26b86f9..6daf19f 100644 (file)
@@ -403,6 +403,7 @@ class SpecialSearch extends SpecialPage {
 
                        // show results
                        if ( $numTextMatches > 0 ) {
+                               $search->augmentSearchResults( $textMatches );
                                $out->addHTML( $this->showMatches( $textMatches ) );
                        }
 
@@ -716,7 +717,7 @@ class SpecialSearch extends SpecialPage {
         *
         * @return string
         */
-       protected function showMatches( &$matches, $interwiki = null ) {
+       protected function showMatches( $matches, $interwiki = null ) {
                global $wgContLang;
 
                $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
@@ -725,7 +726,7 @@ class SpecialSearch extends SpecialPage {
                $pos = $this->offset;
 
                if ( $result && $interwiki ) {
-                       $out .= $this->interwikiHeader( $interwiki, $result );
+                       $out .= $this->interwikiHeader( $interwiki, $matches );
                }
 
                $out .= "<ul class='mw-search-results'>\n";
@@ -750,7 +751,7 @@ class SpecialSearch extends SpecialPage {
         *
         * @return string
         */
-       protected function showHit( $result, $terms, $position ) {
+       protected function showHit( SearchResult $result, $terms, $position ) {
 
                if ( $result->isBrokenTitle() ) {
                        return '';
index 47bed62..898d170 100644 (file)
@@ -34,14 +34,14 @@ class SpecialTags extends SpecialPage {
        protected $explicitlyDefinedTags;
 
        /**
-        * @var array List of extension defined tags
+        * @var array List of software defined tags
         */
-       protected $extensionDefinedTags;
+       protected $softwareDefinedTags;
 
        /**
-        * @var array List of extension activated tags
+        * @var array List of software activated tags
         */
-       protected $extensionActivatedTags;
+       protected $softwareActivatedTags;
 
        function __construct() {
                parent::__construct( 'Tags' );
@@ -110,7 +110,7 @@ class SpecialTags extends SpecialPage {
                        // continuing with this, as the user is just going to end up getting sent
                        // somewhere else. Additionally, if we keep going here, we end up
                        // populating the memcache of tag data (see ChangeTags::listDefinedTags)
-                       // with out-of-date data from the slave, because the slave hasn't caught
+                       // with out-of-date data from the replica DB, because the replica DB hasn't caught
                        // up to the fact that a new tag has been created as part of an implicit,
                        // as yet uncommitted transaction on master.
                        if ( $out->getRedirect() !== '' ) {
@@ -124,11 +124,11 @@ class SpecialTags extends SpecialPage {
                // Used in #doTagRow()
                $this->explicitlyDefinedTags = array_fill_keys(
                        ChangeTags::listExplicitlyDefinedTags(), true );
-               $this->extensionDefinedTags = array_fill_keys(
-                       ChangeTags::listExtensionDefinedTags(), true );
+               $this->softwareDefinedTags = array_fill_keys(
+                       ChangeTags::listSoftwareDefinedTags(), true );
 
                // List all defined tags, even if they were never applied
-               $definedTags = array_keys( $this->explicitlyDefinedTags + $this->extensionDefinedTags );
+               $definedTags = array_keys( $this->explicitlyDefinedTags + $this->softwareDefinedTags );
 
                // Show header only if there exists atleast one tag
                if ( !$tagStats && !$definedTags ) {
@@ -149,8 +149,8 @@ class SpecialTags extends SpecialPage {
                );
 
                // Used in #doTagRow()
-               $this->extensionActivatedTags = array_fill_keys(
-                       ChangeTags::listExtensionActivatedTags(), true );
+               $this->softwareActivatedTags = array_fill_keys(
+                       ChangeTags::listSoftwareActivatedTags(), true );
 
                // Insert tags that have been applied at least once
                foreach ( $tagStats as $tag => $hitcount ) {
@@ -200,9 +200,10 @@ class SpecialTags extends SpecialPage {
                $newRow .= Xml::tags( 'td', null, $desc );
 
                $sourceMsgs = [];
-               $isExtension = isset( $this->extensionDefinedTags[$tag] );
+               $isSoftware = isset( $this->softwareDefinedTags[$tag] );
                $isExplicit = isset( $this->explicitlyDefinedTags[$tag] );
-               if ( $isExtension ) {
+               if ( $isSoftware ) {
+                       // TODO: Rename this message
                        $sourceMsgs[] = $this->msg( 'tags-source-extension' )->escaped();
                }
                if ( $isExplicit ) {
@@ -213,7 +214,7 @@ class SpecialTags extends SpecialPage {
                }
                $newRow .= Xml::tags( 'td', null, implode( Xml::element( 'br' ), $sourceMsgs ) );
 
-               $isActive = $isExplicit || isset( $this->extensionActivatedTags[$tag] );
+               $isActive = $isExplicit || isset( $this->softwareActivatedTags[$tag] );
                $activeMsg = ( $isActive ? 'tags-active-yes' : 'tags-active-no' );
                $newRow .= Xml::tags( 'td', null, $this->msg( $activeMsg )->escaped() );
 
@@ -357,9 +358,9 @@ class SpecialTags extends SpecialPage {
                $preText .= $this->msg( 'tags-delete-explanation-warning', $tag )->parseAsBlock();
 
                // see if the tag is in use
-               $this->extensionActivatedTags = array_fill_keys(
-                       ChangeTags::listExtensionActivatedTags(), true );
-               if ( isset( $this->extensionActivatedTags[$tag] ) ) {
+               $this->softwareActivatedTags = array_fill_keys(
+                       ChangeTags::listSoftwareActivatedTags(), true );
+               if ( isset( $this->softwareActivatedTags[$tag] ) ) {
                        $preText .= $this->msg( 'tags-delete-explanation-active', $tag )->parseAsBlock();
                }
 
index 65f0680..91753a9 100644 (file)
@@ -63,7 +63,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        public static function listAllPages() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return self::listPages( $dbr, '' );
        }
@@ -77,7 +77,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        public static function listPagesByPrefix( $prefix ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $title = Title::newFromText( $prefix );
                if ( $title ) {
@@ -127,7 +127,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        function listRevisions() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $tables = [ 'archive' ];
 
@@ -179,7 +179,7 @@ class PageArchive {
                        return null;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                return $dbr->select(
                        'filearchive',
                        ArchivedFile::selectFields(),
@@ -197,7 +197,7 @@ class PageArchive {
         * @return Revision|null
         */
        function getRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $fields = [
                        'ar_rev_id',
@@ -244,7 +244,7 @@ class PageArchive {
         * @return Revision|null Null when there is no previous revision
         */
        function getPreviousRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Check the previous deleted revision...
                $row = $dbr->selectRow( 'archive',
@@ -300,7 +300,7 @@ class PageArchive {
                }
 
                // New-style: keyed to the text storage backend.
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $text = $dbr->selectRow( 'text',
                        [ 'old_text', 'old_flags' ],
                        [ 'old_id' => $row->ar_text_id ],
@@ -318,7 +318,7 @@ class PageArchive {
         * @return string|null
         */
        function getLastRevisionText() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'archive',
                        [ 'ar_text', 'ar_flags', 'ar_text_id' ],
                        [ 'ar_namespace' => $this->title->getNamespace(),
@@ -339,7 +339,7 @@ class PageArchive {
         * @return bool
         */
        function isDeleted() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
                        [ 'ar_namespace' => $this->title->getNamespace(),
                                'ar_title' => $this->title->getDBkey() ],
@@ -1247,7 +1247,7 @@ class SpecialUndelete extends SpecialPage {
 
                $minor = $rev->isMinor() ? ChangesList::flag( 'minor' ) : '';
 
-               $tags = wfGetDB( DB_SLAVE )->selectField(
+               $tags = wfGetDB( DB_REPLICA )->selectField(
                        'tag_summary',
                        'ts_tags',
                        [ 'ts_rev_id' => $rev->getId() ],
index 6ca1100..4583305 100644 (file)
@@ -394,6 +394,18 @@ class SpecialUpload extends SpecialPage {
                        }
                        if ( $warning == 'exists' ) {
                                $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n";
+                       } elseif ( $warning == 'no-change' ) {
+                               $file = $args;
+                               $filename = $file->getTitle()->getPrefixedText();
+                               $msg = "\t<li>" . wfMessage( 'fileexists-no-change', $filename )->parse() . "</li>\n";
+                       } elseif ( $warning == 'duplicate-version' ) {
+                               $file = $args[0];
+                               $count = count( $args );
+                               $filename = $file->getTitle()->getPrefixedText();
+                               $message = wfMessage( 'fileexists-duplicate-version' )
+                                       ->params( $filename )
+                                       ->numParams( $count );
+                               $msg = "\t<li>" . $message->parse() . "</li>\n";
                        } elseif ( $warning == 'was-deleted' ) {
                                # If the file existed before and was deleted, warn the user of this
                                $ltitle = SpecialPage::getTitleFor( 'Log' );
@@ -763,31 +775,31 @@ class SpecialUpload extends SpecialPage {
 
                $file = $exists['file'];
                $filename = $file->getTitle()->getPrefixedText();
-               $warning = '';
+               $warnMsg = null;
 
                if ( $exists['warning'] == 'exists' ) {
                        // Exact match
-                       $warning = wfMessage( 'fileexists', $filename )->parse();
+                       $warnMsg = wfMessage( 'fileexists', $filename );
                } elseif ( $exists['warning'] == 'page-exists' ) {
                        // Page exists but file does not
-                       $warning = wfMessage( 'filepageexists', $filename )->parse();
+                       $warnMsg = wfMessage( 'filepageexists', $filename );
                } elseif ( $exists['warning'] == 'exists-normalized' ) {
-                       $warning = wfMessage( 'fileexists-extension', $filename,
-                               $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse();
+                       $warnMsg = wfMessage( 'fileexists-extension', $filename,
+                               $exists['normalizedFile']->getTitle()->getPrefixedText() );
                } elseif ( $exists['warning'] == 'thumb' ) {
                        // Swapped argument order compared with other messages for backwards compatibility
-                       $warning = wfMessage( 'fileexists-thumbnail-yes',
-                               $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse();
+                       $warnMsg = wfMessage( 'fileexists-thumbnail-yes',
+                               $exists['thumbFile']->getTitle()->getPrefixedText(), $filename );
                } elseif ( $exists['warning'] == 'thumb-name' ) {
                        // Image w/o '180px-' does not exists, but we do not like these filenames
                        $name = $file->getName();
                        $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
-                       $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse();
+                       $warnMsg = wfMessage( 'file-thumbnail-no', $badPart );
                } elseif ( $exists['warning'] == 'bad-prefix' ) {
-                       $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse();
+                       $warnMsg = wfMessage( 'filename-bad-prefix', $exists['prefix'] );
                }
 
-               return $warning;
+               return $warnMsg ? $warnMsg->title( $file->getTitle() )->parse() : '';
        }
 
        /**
index 1412324..9573f33 100644 (file)
@@ -181,7 +181,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
         * Scale a file (probably with a locally installed imagemagick, or similar)
         * and output it to STDOUT.
         * @param File $file
-        * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+        * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
         * @param int $flags Scaling flags ( see File:: constants )
         * @throws MWException|UploadStashFileNotFoundException
         * @return bool Success
@@ -227,7 +227,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
         * client to cache it forever.
         *
         * @param File $file
-        * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+        * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
         * @param int $flags Scaling flags ( see File:: constants )
         * @throws MWException
         * @return bool Success
index c7c1239..8a06abf 100644 (file)
  * @ingroup SpecialPage
  */
 class UserrightsPage extends SpecialPage {
-       # The target of the local right-adjuster's interest.  Can be gotten from
-       # either a GET parameter or a subpage-style parameter, so have a member
-       # variable for it.
+       /**
+        * The target of the local right-adjuster's interest.  Can be gotten from
+        * either a GET parameter or a subpage-style parameter, so have a member
+        * variable for it.
+        * @var null|string $mTarget
+        */
        protected $mTarget;
        /*
         * @var null|User $mFetchedUser The user object of the target username or null.
@@ -101,6 +104,10 @@ class UserrightsPage extends SpecialPage {
                        $this->mTarget = $request->getVal( 'user' );
                }
 
+               if ( is_string( $this->mTarget ) ) {
+                       $this->mTarget = trim( $this->mTarget );
+               }
+
                $available = $this->changeableGroups();
 
                if ( $this->mTarget === null ) {
index 6f71a51..c8b85ae 100644 (file)
@@ -209,7 +209,7 @@ class SpecialVersion extends SpecialPage {
         * @return string
         */
        public static function softwareInformation() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Put the software in an array of form 'name' => 'version'. All messages should
                // be loaded here, so feel free to use wfMessage in the 'name'. Raw HTML or
index 17d77ba..99f9c7c 100644 (file)
@@ -313,7 +313,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
         * @return IDatabase
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE, 'watchlist' );
+               return wfGetDB( DB_REPLICA, 'watchlist' );
        }
 
        /**
@@ -343,7 +343,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $user = $this->getUser();
                $output = $this->getOutput();
 
-               # Show a message about slave lag, if applicable
+               # Show a message about replica DB lag, if applicable
                $lag = wfGetLB()->safeGetLag( $dbr );
                if ( $lag > 0 ) {
                        $output->showLagWarning( $lag );
index baa55f0..1ead290 100644 (file)
@@ -105,7 +105,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
         */
        function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
                $out = $this->getOutput();
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $hidelinks = $this->opts->getValue( 'hidelinks' );
                $hideredirs = $this->opts->getValue( 'hideredirs' );
index 259d1f9..a1e5156 100644 (file)
@@ -97,7 +97,7 @@ class WithoutInterwikiPage extends PageQueryPage {
                        'join_conds' => [ 'langlinks' => [ 'LEFT JOIN', 'll_from = page_id' ] ]
                ];
                if ( $this->prefix ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() );
                }
 
index 60f642d..5609310 100644 (file)
@@ -184,7 +184,7 @@ class AllMessagesTablePager extends TablePager {
 
        /**
         * Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
-        * Returns array( 'pages' => ..., 'talks' => ... ), where the subarrays have
+        * Returns [ 'pages' => ..., 'talks' => ... ], where the subarrays have
         * an entry for each existing page, with the key being the message name and
         * value arbitrary.
         *
@@ -196,7 +196,7 @@ class AllMessagesTablePager extends TablePager {
        public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) {
                // FIXME: This function should be moved to Language:: or something.
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'page',
                        [ 'page_namespace', 'page_title' ],
                        [ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ],
index f8eba9a..a145e45 100644 (file)
@@ -70,11 +70,11 @@ class ContribsPager extends ReverseChronologicalPager {
                $month = isset( $options['month'] ) ? $options['month'] : false;
                $this->getDateCond( $year, $month );
 
-               // Most of this code will use the 'contributions' group DB, which can map to slaves
+               // Most of this code will use the 'contributions' group DB, which can map to replica DBs
                // with extra user based indexes or partioning by user. The additional metadata
-               // queries should use a regular slave since the lookup pattern is not all by user.
-               $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random slave
-               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+               // queries should use a regular replica DB since the lookup pattern is not all by user.
+               $this->mDbSecondary = wfGetDB( DB_REPLICA ); // any random replica DB
+               $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
        }
 
        function getDefaultQuery() {
index f2421f8..1acbba1 100644 (file)
@@ -43,7 +43,7 @@ class DeletedContribsPager extends IndexPager {
                }
                $this->target = $target;
                $this->namespace = $namespace;
-               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+               $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
        }
 
        function getDefaultQuery() {
index 5e10269..7fc4a95 100644 (file)
@@ -74,7 +74,7 @@ class ImageListPager extends TablePager {
                        $nt = Title::newFromText( $this->mSearch );
 
                        if ( $nt ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
                                $this->mQueryConds[] = 'LOWER(img_name)' .
                                        $dbr->buildLike( $dbr->anyString(),
                                                strtolower( $nt->getDBkey() ), $dbr->anyString() );
@@ -136,7 +136,7 @@ class ImageListPager extends TablePager {
                if ( $this->mSearch !== '' ) {
                        $nt = Title::newFromText( $this->mSearch );
                        if ( $nt ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
                                $conds[] = 'LOWER(' . $prefix . '_name)' .
                                        $dbr->buildLike( $dbr->anyString(),
                                                strtolower( $nt->getDBkey() ), $dbr->anyString() );
@@ -272,7 +272,7 @@ class ImageListPager extends TablePager {
                        }
                        unset( $field );
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        if ( $dbr->implicitGroupby() ) {
                                $options = [ 'GROUP BY' => 'img_name' ];
                        } else {
index 0b9587c..56229b3 100644 (file)
@@ -36,7 +36,7 @@ class MergeHistoryPager extends ReverseChronologicalPager {
                $this->title = $source;
                $this->articleID = $source->getArticleID();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $maxtimestamp = $dbr->selectField(
                        'revision',
                        'MIN(rev_timestamp)',
index d1f9f40..41819fc 100644 (file)
@@ -91,7 +91,7 @@ class NewFilesPager extends ReverseChronologicalPager {
 
                $likeVal = $opts->getValue( 'like' );
                if ( !$this->getConfig()->get( 'MiserMode' ) && $likeVal !== '' ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $likeObj = Title::newFromText( $likeVal );
                        if ( $likeObj instanceof Title ) {
                                $like = $dbr->buildLike(
index 7b058c1..901be38 100644 (file)
@@ -100,7 +100,7 @@ class UsersPager extends AlphabeticPager {
         * @return array
         */
        function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds = [];
 
                // Don't show hidden names
@@ -228,7 +228,7 @@ class UsersPager extends AlphabeticPager {
                }
 
                // Lookup groups for all the users
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $groupRes = $dbr->select(
                        'user_groups',
                        [ 'ug_user', 'ug_group' ],
@@ -241,6 +241,11 @@ class UsersPager extends AlphabeticPager {
                        $cache[intval( $row->ug_user )][] = $row->ug_group;
                        $groups[$row->ug_group] = true;
                }
+
+               // Give extensions a chance to add things like global user group data
+               // into the cache array to ensure proper output later on
+               Hooks::run( 'UsersPagerDoBatchLookups', [ $dbr, $userIds, &$cache, &$groups ] );
+
                $this->userGroupCache = $cache;
 
                // Add page of groups to link batch
index e2f7763..1be5c24 100644 (file)
@@ -679,9 +679,23 @@ abstract class UploadBase {
                        $warnings['empty-file'] = true;
                }
 
+               $hash = $this->getTempFileSha1Base36();
                $exists = self::getExistsWarning( $localFile );
                if ( $exists !== false ) {
                        $warnings['exists'] = $exists;
+
+                       // check if file is an exact duplicate of current file version
+                       if ( $hash === $localFile->getSha1() ) {
+                               $warnings['no-change'] = $localFile;
+                       }
+
+                       // check if file is an exact duplicate of older versions of this file
+                       $history = $localFile->getHistory();
+                       foreach ( $history as $oldFile ) {
+                               if ( $hash === $oldFile->getSha1() ) {
+                                       $warnings['duplicate-version'][] = $oldFile;
+                               }
+                       }
                }
 
                if ( $localFile->wasDeleted() && !$localFile->exists() ) {
@@ -689,7 +703,6 @@ abstract class UploadBase {
                }
 
                // Check dupes against existing files
-               $hash = $this->getTempFileSha1Base36();
                $dupes = RepoGroup::singleton()->findBySha1( $hash );
                $title = $this->getTitle();
                // Remove all matches against self
@@ -1659,7 +1672,7 @@ abstract class UploadBase {
         * @return array Containing the namespace URI and prefix
         */
        private static function splitXmlNamespace( $element ) {
-               // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
+               // 'http://www.w3.org/2000/svg:script' -> [ 'http://www.w3.org/2000/svg', 'script' ]
                $parts = explode( ':', strtolower( $element ) );
                $name = array_pop( $parts );
                $ns = implode( ':', $parts );
index c171ded..000c6a4 100644 (file)
@@ -499,10 +499,10 @@ class UploadStash {
         * Helper function: do the actual database query to fetch file metadata.
         *
         * @param string $key
-        * @param int $readFromDB Constant (default: DB_SLAVE)
+        * @param int $readFromDB Constant (default: DB_REPLICA)
         * @return bool
         */
-       protected function fetchFileMetadata( $key, $readFromDB = DB_SLAVE ) {
+       protected function fetchFileMetadata( $key, $readFromDB = DB_REPLICA ) {
                // populate $fileMetadata[$key]
                $dbr = null;
                if ( $readFromDB === DB_MASTER ) {
index 49a7163..eae57f4 100644 (file)
@@ -67,7 +67,7 @@ class BotPassword implements IDBAccessObject {
 
        /**
         * Get a database connection for the bot passwords database
-        * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_SLAVE.
+        * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_REPLICA.
         * @return DatabaseBase
         */
        public static function getDB( $db ) {
@@ -388,6 +388,46 @@ class BotPassword implements IDBAccessObject {
                return (bool)$dbw->affectedRows();
        }
 
+       /**
+        * Returns a (raw, unhashed) random password string.
+        * @param Config $config
+        * @return string
+        */
+       public static function generatePassword( $config ) {
+               return PasswordFactory::generateRandomPasswordString(
+                       max( 32, $config->get( 'MinimalPasswordLength' ) ) );
+       }
+
+       /**
+        * There are two ways to login with a bot password: "username@appId", "password" and
+        * "username", "appId@password". Transform it so it is always in the first form.
+        * Returns [bot username, bot password, could be normal password?] where the last one is a flag
+        * meaning this could either be a bot password or a normal password, it cannot be decided for
+        * certain (although in such cases it almost always will be a bot password).
+        * If this cannot be a bot password login just return false.
+        * @param string $username
+        * @param string $password
+        * @return array|false
+        */
+       public static function canonicalizeLoginData( $username, $password ) {
+               $sep = BotPassword::getSeparator();
+               // the strlen check helps minimize the password information obtainable from timing
+               if ( strlen( $password ) >= 32 && strpos( $username, $sep ) !== false ) {
+                       // the separator is not valid in new usernames but might appear in legacy ones
+                       if ( preg_match( '/^[0-9a-w]{32,}$/', $password ) ) {
+                               return [ $username, $password, true ];
+                       }
+               } elseif ( strlen( $password ) > 32 && strpos( $password, $sep ) !== false ) {
+                       $segments = explode( $sep, $password );
+                       $password = array_pop( $segments );
+                       $appId = implode( $sep, $segments );
+                       if ( preg_match( '/^[0-9a-w]{32,}$/', $password ) ) {
+                               return [ $username . $sep . $appId, $password, true ];
+                       }
+               }
+               return false;
+       }
+
        /**
         * Try to log the user in
         * @param string $username Combined user name and app ID
index f67a8d8..2ced6e2 100644 (file)
@@ -31,7 +31,7 @@ abstract class CentralIdLookup implements IDBAccessObject {
        const AUDIENCE_PUBLIC = 1;
        const AUDIENCE_RAW = 2;
 
-       /** @var CentralIdLookup[][] */
+       /** @var CentralIdLookup[] */
        private static $instances = [];
 
        /** @var string */
index f7c5408..0a34554 100644 (file)
@@ -60,10 +60,8 @@ class LocalIdLookup extends CentralIdLookup {
                }
 
                $audience = $this->checkAudience( $audience );
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
-               $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
-                       ? [ 'LOCK IN SHARE MODE' ]
-                       : [];
+               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
+               $db = wfGetDB( $index );
 
                $tables = [ 'user' ];
                $fields = [ 'user_id', 'user_name' ];
@@ -93,10 +91,8 @@ class LocalIdLookup extends CentralIdLookup {
                }
 
                $audience = $this->checkAudience( $audience );
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
-               $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
-                       ? [ 'LOCK IN SHARE MODE' ]
-                       : [];
+               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
+               $db = wfGetDB( $index );
 
                $tables = [ 'user' ];
                $fields = [ 'user_id', 'user_name' ];
index bc87cd0..889ec92 100644 (file)
@@ -236,7 +236,7 @@ class PasswordReset {
         * @throws MWException On unexpected database errors
         */
        protected function getUsersByEmail( $email ) {
-               $res = wfGetDB( DB_SLAVE )->select(
+               $res = wfGetDB( DB_REPLICA )->select(
                        'user',
                        User::selectFields(),
                        [ 'user_email' => $email ],
index a9ccc4e..0d06c7b 100644 (file)
@@ -473,8 +473,8 @@ class User implements IDBAccessObject {
                $data = $cache->getWithSetCallback(
                        $this->getCacheKey( $cache ),
                        $cache::TTL_HOUR,
-                       function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
                                wfDebug( "User: cache miss for user {$this->mId}\n" );
 
                                $this->loadFromDatabase( self::READ_NORMAL );
@@ -486,6 +486,8 @@ class User implements IDBAccessObject {
                                        $data[$name] = $this->$name;
                                }
 
+                               $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
+
                                return $data;
 
                        },
@@ -564,7 +566,7 @@ class User implements IDBAccessObject {
        public static function newFromConfirmationCode( $code, $flags = 0 ) {
                $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
 
                $id = $db->selectField(
                        'user',
@@ -895,7 +897,7 @@ class User implements IDBAccessObject {
                        $conds[] = 'ug_user > ' . (int)$after;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $ids = $dbr->selectFieldValues(
                        'user_groups',
                        'ug_user',
@@ -1360,7 +1362,7 @@ class User implements IDBAccessObject {
                if ( is_null( $this->mGroups ) ) {
                        $db = ( $this->queryFlagsUsed & self::READ_LATEST )
                                ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
+                               : wfGetDB( DB_REPLICA );
                        $res = $db->select( 'user_groups',
                                [ 'ug_group' ],
                                [ 'ug_user' => $this->mId ],
@@ -1568,8 +1570,8 @@ class User implements IDBAccessObject {
 
        /**
         * Get blocking information
-        * @param bool $bFromSlave Whether to check the slave database first.
-        *   To improve performance, non-critical checks are done against slaves.
+        * @param bool $bFromSlave Whether to check the replica DB first.
+        *   To improve performance, non-critical checks are done against replica DBs.
         *   Check when actually saving should be done against master.
         */
        private function getBlockedStatus( $bFromSlave = true ) {
@@ -1920,7 +1922,7 @@ class User implements IDBAccessObject {
        /**
         * Check if user is blocked
         *
-        * @param bool $bFromSlave Whether to check the slave database instead of
+        * @param bool $bFromSlave Whether to check the replica DB instead of
         *   the master. Hacked from false due to horrible probs on site.
         * @return bool True if blocked, false otherwise
         */
@@ -1931,7 +1933,7 @@ class User implements IDBAccessObject {
        /**
         * Get the block affecting the user, or null if the user is not blocked
         *
-        * @param bool $bFromSlave Whether to check the slave database instead of the master
+        * @param bool $bFromSlave Whether to check the replica DB instead of the master
         * @return Block|null
         */
        public function getBlock( $bFromSlave = true ) {
@@ -1943,7 +1945,7 @@ class User implements IDBAccessObject {
         * Check if user is blocked from editing a particular article
         *
         * @param Title $title Title to check
-        * @param bool $bFromSlave Whether to check the slave database instead of the master
+        * @param bool $bFromSlave Whether to check the replica DB instead of the master
         * @return bool
         */
        public function isBlockedFrom( $title, $bFromSlave = false ) {
@@ -2188,7 +2190,7 @@ class User implements IDBAccessObject {
                        return [];
                }
                $utp = $this->getTalkPage();
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                // Get the "last viewed rev" timestamp from the oldest message notification
                $timestamp = $dbr->selectField( 'user_newtalk',
                        'MIN(user_last_timestamp)',
@@ -2231,7 +2233,7 @@ class User implements IDBAccessObject {
         * @return bool True if the user has new messages
         */
        protected function checkNewtalk( $field, $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
 
@@ -2356,7 +2358,8 @@ class User implements IDBAccessObject {
                        wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
                                function() use ( $cache, $key ) {
                                        $cache->delete( $key );
-                               }
+                               },
+                               __METHOD__
                        );
                }
        }
@@ -3241,7 +3244,7 @@ class User implements IDBAccessObject {
                if ( is_null( $this->mFormerGroups ) ) {
                        $db = ( $this->queryFlagsUsed & self::READ_LATEST )
                                ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
+                               : wfGetDB( DB_REPLICA );
                        $res = $db->select( 'user_former_groups',
                                [ 'ufg_group' ],
                                [ 'ufg_user' => $this->mId ],
@@ -3266,7 +3269,7 @@ class User implements IDBAccessObject {
 
                if ( $this->mEditCount === null ) {
                        /* Populate the count, if it has not been populated yet */
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        // check if the user_editcount field has been initialized
                        $count = $dbr->selectField(
                                'user', 'user_editcount',
@@ -3593,7 +3596,7 @@ class User implements IDBAccessObject {
 
                // Only update the timestamp if the page is being watched.
                // The query to find out if it is watched is cached both in memcached and per-invocation,
-               // and when it does have to be executed, it can be on a slave
+               // and when it does have to be executed, it can be on a replica DB
                // If this is the user's newtalk page, we always update the timestamp
                $force = '';
                if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
@@ -3611,31 +3614,69 @@ class User implements IDBAccessObject {
         * @note If the user doesn't have 'editmywatchlist', this will do nothing.
         */
        public function clearAllNotifications() {
-               if ( wfReadOnly() ) {
-                       return;
-               }
-
+               global $wgUseEnotif, $wgShowUpdatedMarker;
                // Do nothing if not allowed to edit the watchlist
-               if ( !$this->isAllowed( 'editmywatchlist' ) ) {
+               if ( wfReadOnly() || !$this->isAllowed( 'editmywatchlist' ) ) {
                        return;
                }
 
-               global $wgUseEnotif, $wgShowUpdatedMarker;
                if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
                        $this->setNewtalk( false );
                        return;
                }
+
                $id = $this->getId();
-               if ( $id != 0 ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->update( 'watchlist',
-                               [ /* SET */ 'wl_notificationtimestamp' => null ],
-                               [ /* WHERE */ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
-                               __METHOD__
-                       );
-                       // We also need to clear here the "you have new message" notification for the own user_talk page;
-                       // it's cleared one page view later in WikiPage::doViewUpdates().
+               if ( !$id ) {
+                       return;
                }
+
+               $dbw = wfGetDB( DB_MASTER );
+               $asOfTimes = array_unique( $dbw->selectFieldValues(
+                       'watchlist',
+                       'wl_notificationtimestamp',
+                       [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
+                       __METHOD__,
+                       [ 'ORDER BY' => 'wl_notificationtimestamp DESC', 'LIMIT' => 500 ]
+               ) );
+               if ( !$asOfTimes ) {
+                       return;
+               }
+               // Immediately update the most recent touched rows, which hopefully covers what
+               // the user sees on the watchlist page before pressing "mark all pages visited"....
+               $dbw->update(
+                       'watchlist',
+                       [ 'wl_notificationtimestamp' => null ],
+                       [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimes ],
+                       __METHOD__
+               );
+               // ...and finish the older ones in a post-send update with lag checks...
+               DeferredUpdates::addUpdate( new AutoCommitUpdate(
+                       $dbw,
+                       __METHOD__,
+                       function () use ( $dbw, $id ) {
+                               global $wgUpdateRowsPerQuery;
+
+                               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+                               $asOfTimes = array_unique( $dbw->selectFieldValues(
+                                       'watchlist',
+                                       'wl_notificationtimestamp',
+                                       [ 'wl_user' => $id, 'wl_notificationtimestamp IS NOT NULL' ],
+                                       __METHOD__
+                               ) );
+                               foreach ( array_chunk( $asOfTimes, $wgUpdateRowsPerQuery ) as $asOfTimeBatch ) {
+                                       $dbw->update(
+                                               'watchlist',
+                                               [ 'wl_notificationtimestamp' => null ],
+                                               [ 'wl_user' => $id, 'wl_notificationtimestamp' => $asOfTimeBatch ],
+                                               __METHOD__
+                                       );
+                                       $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+                               }
+                       }
+               ) );
+               // We also need to clear here the "you have new message" notification for the own
+               // user_talk page; it's cleared one page view later in WikiPage::doViewUpdates().
        }
 
        /**
@@ -3816,7 +3857,7 @@ class User implements IDBAccessObject {
 
                // Get a new user_touched that is higher than the old one.
                // This will be used for a CAS check as a last-resort safety
-               // check against race conditions and slave lag.
+               // check against race conditions and replica DB lag.
                $newTouched = $this->newTouchedTimestamp();
 
                $dbw = wfGetDB( DB_MASTER );
@@ -3839,7 +3880,7 @@ class User implements IDBAccessObject {
                        // Maybe the problem was a missed cache update; clear it to be safe
                        $this->clearSharedCache( 'refresh' );
                        // User was changed in the meantime or loaded with stale data
-                       $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'slave';
+                       $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
                        throw new MWException(
                                "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
                                " the version of the user to be saved is older than the current version."
@@ -3868,7 +3909,7 @@ class User implements IDBAccessObject {
 
                $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
 
                $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
                        ? [ 'LOCK IN SHARE MODE' ]
@@ -4508,7 +4549,7 @@ class User implements IDBAccessObject {
                if ( $this->getId() == 0 ) {
                        return false; // anons
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $time = $dbr->selectField( 'revision', 'rev_timestamp',
                        [ 'rev_user' => $this->getId() ],
                        __METHOD__,
@@ -4887,9 +4928,12 @@ class User implements IDBAccessObject {
         * Deferred version of incEditCountImmediate()
         */
        public function incEditCount() {
-               wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() {
-                       $this->incEditCountImmediate();
-               } );
+               wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
+                       function () {
+                               $this->incEditCountImmediate();
+                       },
+                       __METHOD__
+               );
        }
 
        /**
@@ -4913,14 +4957,14 @@ class User implements IDBAccessObject {
                // Lazy initialization check...
                if ( $dbw->affectedRows() == 0 ) {
                        // Now here's a goddamn hack...
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        if ( $dbr !== $dbw ) {
-                               // If we actually have a slave server, the count is
+                               // If we actually have a replica DB server, the count is
                                // at least one behind because the current transaction
                                // has not been committed and replicated.
                                $this->mEditCount = $this->initEditCount( 1 );
                        } else {
-                               // But if DB_SLAVE is selecting the master, then the
+                               // But if DB_REPLICA is selecting the master, then the
                                // count we just read includes the revision that was
                                // just added in the working transaction.
                                $this->mEditCount = $this->initEditCount();
@@ -4928,7 +4972,7 @@ class User implements IDBAccessObject {
                } else {
                        if ( $this->mEditCount === null ) {
                                $this->getEditCount();
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
                                $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
                        } else {
                                $this->mEditCount++;
@@ -4945,9 +4989,9 @@ class User implements IDBAccessObject {
         * @return int Number of edits
         */
        protected function initEditCount( $add = 0 ) {
-               // Pull from a slave to be less cruel to servers
+               // Pull from a replica DB to be less cruel to servers
                // Accuracy isn't the point anyway here
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $count = (int)$dbr->selectField(
                        'revision',
                        'COUNT(rev_user)',
@@ -5105,7 +5149,7 @@ class User implements IDBAccessObject {
                                // Load from database
                                $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
                                        ? wfGetDB( DB_MASTER )
-                                       : wfGetDB( DB_SLAVE );
+                                       : wfGetDB( DB_REPLICA );
 
                                $res = $dbr->select(
                                        'user_properties',
@@ -5171,7 +5215,7 @@ class User implements IDBAccessObject {
                        [ 'up_property', 'up_value' ], [ 'up_user' => $userId ], __METHOD__ );
 
                // Find prior rows that need to be removed or updated. These rows will
-               // all be deleted (the later so that INSERT IGNORE applies the new values).
+               // all be deleted (the latter so that INSERT IGNORE applies the new values).
                $keysDelete = [];
                foreach ( $res as $row ) {
                        if ( !isset( $saveOptions[$row->up_property] )
@@ -5315,7 +5359,7 @@ class User implements IDBAccessObject {
         * Get a new instance of this user that was loaded from the master via a locking read
         *
         * Use this instead of the main context User when updating that user. This avoids races
-        * where that user was loaded from a slave or even the master but without proper locks.
+        * where that user was loaded from a replica DB or even the master but without proper locks.
         *
         * @return User|null Returns null if the user was not found in the DB
         * @since 1.27
index a4d4356..dddc850 100644 (file)
@@ -46,7 +46,7 @@ abstract class UserArray implements Iterator {
                        // Database::select() doesn't like empty arrays
                        return new ArrayIterator( [] );
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'user',
                        User::selectFields(),
@@ -67,7 +67,7 @@ abstract class UserArray implements Iterator {
                        // Database::select() doesn't like empty arrays
                        return new ArrayIterator( [] );
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'user',
                        User::selectFields(),
index 4a27a13..b7d5058 100644 (file)
@@ -39,7 +39,7 @@ class UserNamePrefixSearch {
        public static function search( $audience, $search, $limit, $offset = 0 ) {
                $user = User::newFromName( $search );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $prefix = $user ? $user->getName() : '';
                $tables = [ 'user' ];
                $cond = [ 'user_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ) ];
index 09244a4..69bc503 100644 (file)
@@ -273,15 +273,20 @@ class UserRightsProxy {
         * Replaces User::touchUser()
         */
        function invalidateCache() {
-               $this->db->update( 'user',
+               $this->db->update(
+                       'user',
                        [ 'user_touched' => $this->db->timestamp() ],
                        [ 'user_id' => $this->id ],
-                       __METHOD__ );
+                       __METHOD__
+               );
 
                $wikiId = $this->db->getWikiID();
                $userId = $this->id;
-               $this->db->onTransactionPreCommitOrIdle( function() use ( $wikiId, $userId ) {
-                       User::purge( $wikiId, $userId );
-               } );
+               $this->db->onTransactionPreCommitOrIdle(
+                       function () use ( $wikiId, $userId ) {
+                               User::purge( $wikiId, $userId );
+                       },
+                       __METHOD__
+               );
        }
 }
index 549ad41..1e7eda8 100644 (file)
@@ -4,7 +4,7 @@
  * method of batch updating rows in a database. To use create a class
  * implementing the RowUpdateGenerator interface and configure the
  * BatchRowIterator and BatchRowWriter for access to the correct table.
- * The components will handle reading, writing, and waiting for slaves
+ * The components will handle reading, writing, and waiting for replica DBs
  * while the generator implementation handles generating update arrays
  * for singular rows.
  *
index 6a4792c..342dffd 100644 (file)
@@ -26,10 +26,10 @@ interface RowUpdateGenerator {
         * updated value within the database row.
         *
         * Sample Response:
-        *   return array(
+        *   return [
         *       'some_col' => 'new value',
         *       'other_col' => 99,
-        *   );
+        *   ];
         *
         * @param stdClass $row A row from the database
         * @return array Map of column names to updated value within the
index cb3b4b8..169e0ff 100644 (file)
@@ -1037,6 +1037,7 @@ class Language {
         *    xiy  y (two digit year) in Iranian calendar
         *    xiY  Y (full year) in Iranian calendar
         *    xit  t (days in month) in Iranian calendar
+        *    xiz  z (day of the year) in Iranian calendar
         *
         *    xjj  j (day number) in Hebrew calendar
         *    xjF  F (month name) in Hebrew calendar
@@ -1339,6 +1340,13 @@ class Language {
                                        }
                                        $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
                                        break;
+                               case 'xiz':
+                                       $usedIranianYear = true;
+                                       if ( !$iranian ) {
+                                               $iranian = self::tsToIranian( $ts );
+                                       }
+                                       $num = $iranian[3];
+                                       break;
                                case 'a':
                                        $usedAMPM = true;
                                        $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
@@ -1597,6 +1605,8 @@ class Language {
                        $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
                }
 
+               $jz = $jDayNo;
+
                for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
                        $jDayNo -= self::$IRANIAN_DAYS[$i];
                }
@@ -1604,7 +1614,7 @@ class Language {
                $jm = $i + 1;
                $jd = $jDayNo + 1;
 
-               return [ $jy, $jm, $jd ];
+               return [ $jy, $jm, $jd, $jz ];
        }
 
        /**
index 37a0584..a15d910 100644 (file)
@@ -3175,6 +3175,7 @@ public static $zh2Hant = [
 '上系上' => '上繫上',
 '上课钟' => '上課鐘',
 '上面糊' => '上面糊',
+'下文里' => '下文裡',
 '下于' => '下於',
 '下梁' => '下樑',
 '下注解' => '下注解',
@@ -3231,7 +3232,6 @@ public static $zh2Hant = [
 '丑表功' => '丑表功',
 '丑角' => '丑角',
 '且于' => '且於',
-'世田谷' => '世田谷',
 '世界杯' => '世界盃',
 '世纪里' => '世紀裡',
 '世纪钟' => '世紀鐘',
@@ -3362,6 +3362,7 @@ public static $zh2Hant = [
 '干性' => '乾性',
 '干打雷' => '乾打雷',
 '干折' => '乾折',
+'干拌面' => '乾拌麵',
 '干撂台' => '乾撂台',
 '干撇下' => '乾撇下',
 '干擦' => '乾擦',
@@ -3477,6 +3478,7 @@ public static $zh2Hant = [
 '乱发生' => '亂發生',
 '乱发脾气' => '亂發脾氣',
 '乱发' => '亂髮',
+'乱斗' => '亂鬥',
 '乱哄哄' => '亂鬨鬨',
 '了然后' => '了然後',
 '事有斗巧' => '事有鬥巧',
@@ -4087,8 +4089,6 @@ public static $zh2Hant = [
 '刑余' => '刑餘',
 '划一桨' => '划一槳',
 '划一槳' => '划一槳',
-'划上' => '划上',
-'划下' => '划下',
 '划不來' => '划不來',
 '划不来' => '划不來',
 '划了一会' => '划了一會',
@@ -4204,6 +4204,8 @@ public static $zh2Hant = [
 '北回铁路' => '北迴鐵路',
 '匪干' => '匪幹',
 '匿于' => '匿於',
+'区里有' => '區裡有',
+'区里的' => '區裡的',
 '十个' => '十個',
 '十出家' => '十出家',
 '十出击' => '十出擊',
@@ -4263,6 +4265,7 @@ public static $zh2Hant = [
 '卷须' => '卷鬚',
 '厂部' => '厂部',
 '原子钟' => '原子鐘',
+'原文里' => '原文裡',
 '原钟' => '原鐘',
 '历物之意' => '厤物之意',
 '去山里' => '去山裡',
@@ -4291,6 +4294,7 @@ public static $zh2Hant = [
 '口里' => '口裡',
 '口钟' => '口鐘',
 '古人有云' => '古人有云',
+'古文里' => '古文裡',
 '古书云' => '古書云',
 '古書云' => '古書云',
 '古柯咸' => '古柯鹹',
@@ -4842,6 +4846,7 @@ public static $zh2Hant = [
 '寡欲' => '寡慾',
 '实干' => '實幹',
 '实累累' => '實纍纍',
+'实验里' => '實驗裡',
 '写字台' => '寫字檯',
 '宽于' => '寬於',
 '宽余' => '寬餘',
@@ -5068,6 +5073,7 @@ public static $zh2Hant = [
 '干革命' => '幹革命',
 '干头' => '幹頭',
 '干么' => '幹麼',
+'幽并' => '幽并',
 '几个' => '幾個',
 '几周后' => '幾周後',
 '几天后' => '幾天後',
@@ -5392,6 +5398,7 @@ public static $zh2Hant = [
 '怒气冲天' => '怒氣衝天',
 '怒火冲天' => '怒火衝天',
 '怒发冲冠' => '怒髮衝冠',
+'怜奈' => '怜奈',
 '思如泉涌' => '思如泉湧',
 '怠于' => '怠於',
 '急于' => '急於',
@@ -5597,6 +5604,7 @@ public static $zh2Hant = [
 '拉面色' => '拉面色',
 '拉面部' => '拉面部',
 '拉面' => '拉麵',
+'拌面' => '拌麵',
 '拒人于' => '拒人於',
 '拒于' => '拒於',
 '拓朴' => '拓樸',
@@ -5695,6 +5703,7 @@ public static $zh2Hant = [
 '捶炼' => '捶鍊',
 '扫荡' => '掃蕩',
 '授勋' => '授勳',
+'授时历' => '授時曆',
 '掌柜' => '掌柜',
 '排干' => '排乾',
 '排干部' => '排幹部',
@@ -5910,6 +5919,7 @@ public static $zh2Hant = [
 '斫雕为朴' => '斫雕為樸',
 '新井里美' => '新井里美',
 '新干县' => '新幹縣',
+'新庄子' => '新庄子',
 '新历' => '新曆',
 '新历史' => '新歷史',
 '新扎' => '新紮',
@@ -6074,6 +6084,7 @@ public static $zh2Hant = [
 '村落发' => '村落發',
 '村里有' => '村裡有',
 '村里的' => '村裡的',
+'杜琪峰' => '杜琪峯',
 '杜老志道' => '杜老誌道',
 '杞宋无征' => '杞宋無徵',
 '束发' => '束髮',
@@ -6141,6 +6152,7 @@ public static $zh2Hant = [
 '梁上君子' => '梁上君子',
 '梁启超' => '梁啓超',
 '条干' => '條幹',
+'条文里' => '條文裡',
 '梨干' => '梨乾',
 '梯冲' => '梯衝',
 '械系' => '械繫',
@@ -6232,6 +6244,7 @@ public static $zh2Hant = [
 '欧游' => '歐遊',
 '止于' => '止於',
 '正官庄' => '正官庄',
+'正文里' => '正文裡',
 '正杰' => '正杰',
 '武丑' => '武丑',
 '武后' => '武后',
@@ -6274,8 +6287,6 @@ public static $zh2Hant = [
 '水并流' => '水併流',
 '水来汤里去' => '水來湯裡去',
 '水准' => '水準',
-'水无怜奈' => '水無怜奈',
-'水無怜奈' => '水無怜奈',
 '水表示' => '水表示',
 '水表面' => '水表面',
 '水里' => '水裡',
@@ -6333,7 +6344,6 @@ public static $zh2Hant = [
 '泰山梁木' => '泰山梁木',
 '泱郁' => '泱鬱',
 '泳气钟' => '泳氣鐘',
-'洄游' => '洄遊',
 '洋河大曲' => '洋河大麯',
 '洒家' => '洒家',
 '洒淅' => '洒淅',
@@ -6380,6 +6390,8 @@ public static $zh2Hant = [
 '涂敏恒' => '涂敏恆',
 '涂泽民' => '涂澤民',
 '涂澤民' => '涂澤民',
+'涂尔干' => '涂爾幹',
+'涂爾幹' => '涂爾幹',
 '涂紹煃' => '涂紹煃',
 '涂绍煃' => '涂紹煃',
 '涂羽卿' => '涂羽卿',
@@ -6712,21 +6724,18 @@ public static $zh2Hant = [
 '甜面酱' => '甜麵醬',
 '生力面' => '生力麵',
 '生于' => '生於',
-'生殖洄游' => '生殖洄游',
 '生物钟' => '生物鐘',
 '生发生' => '生發生',
 '生华发' => '生華髮',
 '生姜' => '生薑',
 '生锈' => '生鏽',
 '生发' => '生髮',
-'产卵洄游' => '產卵洄游',
 '苏醒' => '甦醒',
 '用于' => '用於',
 '用法里' => '用法裡',
 '甩发' => '甩髮',
 '田子里' => '田子里',
 '田庄英雄' => '田庄英雄',
-'田谷' => '田穀',
 '田里' => '田裡',
 '由余' => '由余',
 '由于' => '由於',
@@ -6743,6 +6752,7 @@ public static $zh2Hant = [
 '毕于' => '畢於',
 '毕业于' => '畢業於',
 '毕生发展' => '畢生發展',
+'画里' => '畫裡',
 '当准' => '當準',
 '当当丁丁' => '當當丁丁',
 '当当网' => '當當網',
@@ -7110,7 +7120,6 @@ public static $zh2Hant = [
 '吁求' => '籲求',
 '吁请' => '籲請',
 '米沈' => '米瀋',
-'米谷' => '米穀',
 '米团' => '米糰',
 '米余' => '米餘',
 '米面' => '米麵',
@@ -7190,6 +7199,7 @@ public static $zh2Hant = [
 '绑扎' => '綁紮',
 '绥棱' => '綏稜',
 '捆扎' => '綑紮',
+'经文里' => '經文裡',
 '經有云' => '經有云',
 '经有云' => '經有云',
 '综合征' => '綜合徵',
@@ -7248,7 +7258,6 @@ public static $zh2Hant = [
 '系上头' => '繫上頭',
 '系上黑' => '繫上黑',
 '系上,' => '繫上,',
-'系世' => '繫世',
 '系到' => '繫到',
 '系囚' => '繫囚',
 '系心' => '繫心',
@@ -7314,6 +7323,7 @@ public static $zh2Hant = [
 '老板' => '老闆',
 '老面皮' => '老面皮',
 '考征' => '考徵',
+'考试制度' => '考試制度',
 '耍斗' => '耍鬥',
 '耕获' => '耕穫',
 '耳余' => '耳餘',
@@ -7472,6 +7482,7 @@ public static $zh2Hant = [
 '苦里' => '苦裡',
 '苦斗' => '苦鬥',
 '苧麻' => '苧麻',
+'英文里' => '英文裡',
 '茂都淀' => '茂都澱',
 '范文同' => '范文同',
 '范文正公' => '范文正公',
@@ -7842,7 +7853,6 @@ public static $zh2Hant = [
 '西历' => '西曆',
 '西历史' => '西歷史',
 '西湖里' => '西湖里',
-'西米谷' => '西米谷',
 '西西里' => '西西里',
 '西谷米' => '西谷米',
 '西游' => '西遊',
@@ -7912,6 +7922,7 @@ public static $zh2Hant = [
 '注生娘娘' => '註生娘娘',
 '注疏' => '註疏',
 '注脚' => '註腳',
+'注里' => '註裡',
 '注解' => '註解',
 '注记' => '註記',
 '注译' => '註譯',
@@ -8004,6 +8015,7 @@ public static $zh2Hant = [
 '谈征' => '談徵',
 '请君入瓮' => '請君入甕',
 '请托' => '請託',
+'论文里' => '論文裡',
 '咨询' => '諮詢',
 '诸余' => '諸餘',
 '谋干' => '謀幹',
@@ -8027,6 +8039,7 @@ public static $zh2Hant = [
 '警报钟' => '警報鐘',
 '警示钟' => '警示鐘',
 '警钟' => '警鐘',
+'译文里' => '譯文裡',
 '译制' => '譯製',
 '译注' => '譯註',
 '护发' => '護髮',
@@ -8827,6 +8840,7 @@ public static $zh2Hant = [
 '额我略历' => '額我略曆',
 '额我略历史' => '額我略歷史',
 '颜范' => '顏範',
+'颛顼历' => '顓頊曆',
 '颠干倒坤' => '顛乾倒坤',
 '顛顛仆仆' => '顛顛仆仆',
 '颠颠仆仆' => '顛顛仆仆',
@@ -14175,7 +14189,6 @@ public static $zh2TW = [
 '打印度' => '打印度',
 '抽烟' => '抽菸',
 '抽煙' => '抽菸',
-'拉普兰' => '拉布蘭',
 '拒烟' => '拒菸',
 '拒煙' => '拒菸',
 '卷烟' => '捲菸',
@@ -14661,6 +14674,7 @@ public static $zh2TW = [
 '迈凯轮' => '麥拿輪',
 '邁凱輪' => '麥拿輪',
 '马萨诸塞' => '麻薩諸塞',
+'粘膜' => '黏膜',
 '戴安娜' => '黛安娜',
 '狄安娜' => '黛安娜',
 '点烟' => '點菸',
@@ -14689,6 +14703,7 @@ public static $zh2HK = [
 '旧金山' => '三藩市',
 '舊金山' => '三藩市',
 '上台面' => '上枱面',
+'下文里' => '下文裏',
 '下著' => '下着',
 '下著作' => '下著作',
 '下著名' => '下著名',
@@ -15174,6 +15189,8 @@ public static $zh2HK = [
 '動著錄' => '動著錄',
 '包著' => '包着',
 '北朝鲜' => '北韓',
+'区里有' => '區裏有',
+'区里的' => '區裏的',
 '南朝鲜' => '南韓',
 '波札那' => '博茨瓦納',
 '占卜' => '占卜',
@@ -15202,6 +15219,7 @@ public static $zh2HK = [
 '厄利垂亚' => '厄立特里亞',
 '厄利垂亞' => '厄立特里亞',
 '源代码' => '原始碼',
+'原文里' => '原文裏',
 '去山里' => '去山裏',
 '参数里' => '參數裏',
 '受著' => '受着',
@@ -15214,6 +15232,7 @@ public static $zh2HK = [
 '受著錄' => '受著錄',
 '丛林里' => '叢林裏',
 '口里' => '口裏',
+'古文里' => '古文裏',
 '只占' => '只佔',
 '叫著' => '叫着',
 '叫著作' => '叫著作',
@@ -16148,6 +16167,7 @@ public static $zh2HK = [
 '撞球' => '桌球',
 '梅鐸' => '梅鐸',
 '默多克' => '梅鐸',
+'条文里' => '條文裏',
 '梳著' => '梳着',
 '梳著作' => '梳著作',
 '梳著名' => '梳著名',
@@ -16178,6 +16198,7 @@ public static $zh2HK = [
 '機器人' => '機械人',
 '柜台' => '櫃枱',
 '柜里' => '櫃裏',
+'正文里' => '正文裏',
 '历史里' => '歷史裏',
 '死里求生' => '死裏求生',
 '死里逃生' => '死裏逃生',
@@ -16463,6 +16484,7 @@ public static $zh2HK = [
 '畫著名' => '畫著名',
 '畫著稱' => '畫著稱',
 '畫著者' => '畫著者',
+'画里' => '畫裏',
 '當著' => '當着',
 '當著作' => '當著作',
 '過著作' => '當著作',
@@ -16743,6 +16765,7 @@ public static $zh2HK = [
 '綁著者' => '綁著者',
 '綁著述' => '綁著述',
 '綁著錄' => '綁著錄',
+'经文里' => '經文裏',
 '网站里' => '網站裏',
 '網路' => '網絡',
 '网里' => '網裏',
@@ -16907,6 +16930,7 @@ public static $zh2HK = [
 '苦著錄' => '苦著錄',
 '苦里' => '苦裏',
 '英占' => '英佔',
+'英文里' => '英文裏',
 '共和联邦' => '英聯邦',
 '大英國協' => '英聯邦',
 '草丛里' => '草叢裏',
@@ -17099,8 +17123,10 @@ public static $zh2HK = [
 '說著者' => '說著者',
 '說著述' => '說著述',
 '數據機' => '調制解調器',
+'论文里' => '論文裏',
 '诺曼底' => '諾曼第',
 '警戒著' => '警戒着',
+'譯文里' => '譯文裏',
 '變著' => '變着',
 '變著作' => '變著作',
 '變著名' => '變著名',
index 97a8233..cb869d0 100644 (file)
        "passwordreset-emailtext-user": "احد ما (قد يكون انت$1)طلب مذكرة تفاصيل الحساب ل{{SITENAME}} ($4).المستخدم الاتي {{PLURAL:$3|الحساب هو|الحسابات هي}} قد قرن بهذا العنوان :\n\n$2\n\n{{PLURAL:$3|كلمة المرور المؤقتة|كلمات المرور المؤقة}}سينتهي في {{PLURAL:$5|يوم|ايام$5 }}\nمن الافضل ان تسجل الدخول وتختار كلمة مرور جديدة الان .\nإذا قام شخص آخر بهذا الطلب، أو إذا  تذكرت كلمة المرور الأصلية الخاصة بك،ولم تعد ترغب في تغييره، يمكنك تجاهل هذه الرسالة ومتابعة استخدام  كلمة المرورالقديمة.",
        "passwordreset-emailelement": "اسم المستخدم: \n$1\n\nكلمة السر المؤقتة: \n$2",
        "passwordreset-emailsentemail": "أرسل بريد إلكتروني تذكيري",
-       "passwordreset-emailsent-capture": "أرسل بريد إلكتروني تذكيري وهو معروض بالأسفل.",
-       "passwordreset-emailerror-capture": "ولّد بريد إلكتروني تذكيري وهو معروض بالأسفل لكن فشل إرساله للمستخدم: $1",
        "changeemail": "تغيير عنوان البريد الإلكتروني",
        "changeemail-header": "تغيير عنوان البريد الإلكتروني للحساب",
        "changeemail-no-info": "يجب تسجيل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "undo-failure": "لم يمكن استرجاع التعديل بسبب تعديلات متعارضة تمت على الصفحة.",
        "undo-norev": "فشل في الرجوع عن التعديل حيث أنه غير موجود أو تم حذفه.",
        "undo-summary": "الرجوع عن التعديل $1 بواسطة [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]])",
-       "cantcreateaccounttitle": "لا يمكن إنشاء حساب",
        "cantcreateaccount-text": "إنشاء الحسابات من عنوان الأيبي هذا ('''$1''') تم منعه بواسطة [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو ''$2''",
        "viewpagelogs": "اعرض سجلات هذه الصفحة",
        "nohistory": "لا يوجد تاريخ للتعديلات لهذه الصفحة.",
        "revdel-restore": "تغيير الرؤية",
        "pagehist": "تاريخ صفحة",
        "deletedhist": "التاريخ المحذوف",
-       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø­فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
+       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø®فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
        "revdelete-show-no-access": "خطأ في إظهار العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-no-access": "خطأ في تعديل العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-missing": "خطأ في تعديل العنصر ذا الهوية $1: العنصر مفقود من قاعدة البيانات!",
index 515dc79..ed1e509 100644 (file)
@@ -14,7 +14,8 @@
                        "Macofe",
                        "Carlos Cristia",
                        "MarcoAurelio",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Fitoschido"
                ]
        },
        "tog-underline": "Subrayar os vinclos:",
        "yourpassword": "Clau d'acceso:",
        "yourpasswordagain": "Torne a escribir a clau:",
        "createacct-yourpasswordagain": "Confirma a clau",
-       "remembermypassword": "Remerar o mío nombre d'usuario y a clau entre sesions en iste navegador (como muito por $1 {{PLURAL:$1|día|días}})",
        "yourdomainname": "Dominio:",
        "externaldberror": "Bi habió una error d'autenticación externa d'a base de datos u bien no tiene premisos ta esviellar a suya cuenta externa.",
        "login": "Encetar sesión",
        "passwordreset-emailtext-user": "L'usuario $1 en {{SITENAME}} ha demandau un recordatorio d'a información d'a suya cuenta en {{SITENAME}} ($4). {{PLURAL:$3|A cuenta d'usuario siguient ye asociata|As cuentas d'usuario siguients son asociatas}} a ista adreza de correu-e:\n\n$2\n\n{{PLURAL:$3|Ista clau d'acceso temporal circumducirá|Istas claus d'acceso temporals circumducirán}} en {{PLURAL:$5|un día|$5 días}}. Habría de connectar-se agora y trigar una nueva clau. Si ista demanda no dimana de vusté, u ya se'n ha acordau d'a suya clau inicial y ya no deseya modificar-la, puet ignorar iste mensache y continar emplegando a suya viella clau.",
        "passwordreset-emailelement": "Nombre de usuario: \n$1\n\nClau d'acceso temporal: \n$2",
        "passwordreset-emailsentemail": "S'ha ninviau un recordatorio por correu-e.",
-       "passwordreset-emailsent-capture": "Se le ha ninviau un recordatorio por correu electronico, que s'amuestra contino.",
-       "passwordreset-emailerror-capture": "S'ha chenerau un recordatorio por correu electronico, que s'amuestra contino, pero o ninvío ta l'usuario ha fallau: $1",
        "changeemail": "Cambiar l'adreza de correu-e",
        "changeemail-header": "Cambiar l'adreza de correu-e d'a cuenta",
        "changeemail-no-info": "Debe identificar-se como usuario ta poder acceder dreitament ta ista pachina.",
        "minoredit": "He feito una edición menor",
        "watchthis": "Cosirar ista pachina",
        "savearticle": "Alzar pachina",
+       "publishchanges": "Publicar os cambeos",
        "preview": "Previsualización",
        "showpreview": "Amostrar previsualización",
        "showdiff": "Amostrar cambeos",
        "undo-failure": "No se puet desfer a edición pues un atro usuario ha feito una edición intermeya.",
        "undo-norev": "No s'ha puesto desfer a edición porque no existiba u ya s'heba borrato.",
        "undo-summary": "Desfeita a edición $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|desc.]])",
-       "cantcreateaccounttitle": "No se puede creyar a cuenta",
        "cantcreateaccount-text": "A creyación de cuentas dende ixa adreza IP ('''$1''') estió bloqueyata por [[User:$3|$3]].\n\nA razón indicada por $3 ye ''$2''",
        "viewpagelogs": "Veyer os rechistros d'ista pachina",
        "nohistory": "Ista pachina no tiene un historial d'edicions.",
        "special-characters-group-lao": "Laosiano",
        "special-characters-group-khmer": "Khmer",
        "mw-widgets-dateinput-placeholder-day": "AAAA-MM-DD",
-       "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
-       "api-error-blacklisted": "Trigue un titol diferent, mas descriptivo."
+       "mw-widgets-dateinput-placeholder-month": "AAAA-MM"
 }
index 0646f21..6bc5553 100644 (file)
@@ -23,6 +23,7 @@
        "tog-hideminor": "Hȳdan lytela adihtunga in nīwra andwendinga getæle",
        "tog-hidepatrolled": "Hȳdan weardoda adihtunga in nīwra andwendinga getæle",
        "tog-newpageshidepatrolled": "Hȳdan weardode trametas in nīwra andwendinga getæle",
+       "tog-hidecategorization": "Behȳd trameta floccas",
        "tog-extendwatchlist": "Sprǣdan behealdungtæl tō īwenne ealla andwendinga, nā synderlīce þā nīwostan",
        "tog-usenewrc": "Settan andwendunga on hēapas on trametum on nīwra andwendunga getæle and behealdungtæle",
        "tog-numberheadings": "Settan rīm on forecwidas selflīce",
@@ -33,6 +34,7 @@
        "tog-watchdefault": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic adihte.",
        "tog-watchmoves": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic wege.",
        "tog-watchdeletion": "Ēacnian mīn behealdungæl mid trametum and ymelum þā ic forlēose.",
+       "tog-watchuploads": "Eacne nīwa ymelan to mīnum weardgetæle",
        "tog-minordefault": "Mearcian ealla adihtunga lytela tō gewunan",
        "tog-previewontop": "Īwan forebysene ofer adihtunge mearce",
        "tog-previewonfirst": "Īwan forebysene on forman adihtunge",
        "view-pool-error": "Wālā, þā þegntōlas nū oferlīce wyrcaþ.\nTō mænige brūcendas gesēcaþ tō sēonne þisne tramet.\nWē biddaþ þæt þū abīde scortne tīman ǣr þū gesēce to sēonne þisne tramet eft.\n\n$1",
        "pool-queuefull": "Pundfaldes forepenn is full",
        "pool-errorunknown": "Uncūþ wōh",
-       "pool-servererror": "Seo pundaldgetalere þēgnung nis gearo",
+       "pool-servererror": "Seo pundfaldgetalere þēgnung nis gearo",
        "aboutsite": "Gecȳþness ymbe {{GRAMMAR:wrēgendlīc|{{SITENAME}}}}",
        "aboutpage": "Project:Gefrǣge",
        "copyright": "Man mæg innunge under $1 findan, būton þǣr hit is elles amearcod.",
        "yourpasswordagain": "Wrītan þafungword eft:",
        "createacct-yourpasswordagain": "Asēð þafungword",
        "createacct-yourpasswordagain-ph": "Wrīt þafungword eft",
-       "remembermypassword": "Gemynan mīne inmeldunge on þissum webbsēcende (oþ $1 {{PLURAL:$1|dæg|daga}} lengest)",
        "userlogin-remembermypassword": "Ætfeolan mīnre inmeldunge",
        "yourdomainname": "Þīn geweald:",
        "password-change-forbidden": "Þū ne canst awendan þafungword on þissum wiki.",
index f07d5a0..95c7a5d 100644 (file)
@@ -65,7 +65,9 @@
                        "Izoozo",
                        "علاء",
                        "Hhaboh162002",
-                       "بدارين"
+                       "بدارين",
+                       "باسم",
+                       "Moud hosny"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
@@ -93,7 +95,7 @@
        "tog-enotifminoredits": "أرسل إلي رسالة إلكترونية بشأن التعديلات الطفيفة للصفحات والملفات",
        "tog-enotifrevealaddr": "أظهر عنوان بريدي الإلكتروني في إشعارات البريد الإلكتروني",
        "tog-shownumberswatching": "اعرض عدد المستخدمين المراقبين",
-       "tog-oldsig": "اÙ\84تÙ\88Ù\82Ù\8aع الحالي:",
+       "tog-oldsig": "تÙ\88Ù\82Ù\8aعÙ\83 الحالي:",
        "tog-fancysig": "وضع الوصلة يدوياً واستعمال نص الويكي",
        "tog-uselivepreview": "استعمال المعاينة المباشرة",
        "tog-forceeditsummary": "نبهني عند عدم إدخال ملخص تعديل",
        "tog-watchlisthideliu": "أخف تعديلات المستخدمين المسجلين في قائمة المراقبة",
        "tog-watchlistreloadautomatically": "أعد تحميل قائمة المراقبة بصفة آلية حينما يتغير مرشح ما (يتطلب جافاسكربت)",
        "tog-watchlisthideanons": "أخف تعديلات المستخدمين المجهولين في قائمة المراقبة",
-       "tog-watchlisthidepatrolled": " أخف التعديلات المراجعة في قائمة المراقبة",
+       "tog-watchlisthidepatrolled": "أخف التعديلات المراجعة في قائمة المراقبة",
        "tog-watchlisthidecategorization": "أخف تصنيف الصفحات",
        "tog-ccmeonemails": "أرسل إلي نسخا من الرسائل الإلكترونية التي أرسلها إلى المستخدمين الآخرين",
        "tog-diffonly": "لا تعرض محتوى الصفحة أسفل الفرق",
        "tog-showhiddencats": "أظهر التصنيفات المخفية",
        "tog-norollbackdiff": "عدم إظهار الاختلافات بعد تنفيذ التراجع",
        "tog-useeditwarning": "حذّرني عندما أغادر تحرير صفحة فيها تغييرات لم أحفظها",
-       "tog-prefershttps": "دائÙ\85ا Ø§Ø³ØªØ®Ø¯Ù\85 Ø§ØªØµØ§Ù\84ا Ø¢Ù\85Ù\86ا Ø¨Ø¹Ø¯ الدخول",
+       "tog-prefershttps": "دائÙ\85ا Ø§Ø³ØªØ®Ø¯Ù\85 Ø§ØªØµØ§Ù\84ا Ø¢Ù\85Ù\86ا Ø¹Ù\86د ØªØ³Ø¬Ù\8aÙ\84 الدخول",
        "underline-always": "دائما",
        "underline-never": "أبدا",
        "underline-default": "وفق المظهر أو المتصفح",
        "october-date": "تشرين الأول/أكتوبر $1",
        "november-date": "تشرين الثاني/نوفمبر $1",
        "december-date": "كانون الأول/ديسمبر $1",
-       "period-am": "صباحا",
+       "period-am": "صباحًا",
        "period-pm": "مساءً",
        "pagecategories": "{{PLURAL:$1|بلا تصنيف|تصنيف|تصنيفان|تصنيفات}}",
        "category_header": "صفحات تصنيف «$1»",
        "newwindow": "(تفتح في نافذة جديدة)",
        "cancel": "ألغِ",
        "moredotdotdot": "المزيد...",
-       "morenotlisted": "هذه القائمة غير مكتملة.",
+       "morenotlisted": "Ù\87Ø°Ù\87 Ø§Ù\84Ù\82ائÙ\85Ø© Ø±Ø¨Ù\85ا ØªÙ\83Ù\88Ù\86 ØºÙ\8aر Ù\85Ù\83تÙ\85Ù\84Ø©.",
        "mypage": "صفحة",
        "mytalk": "نقاش",
        "anontalk": "نقاش",
        "tagline": "من {{SITENAME}}",
        "help": "مساعدة",
        "search": "بحث",
-       "search-ignored-headings": "# <!-- Ø£ØªØ±Ù\83 Ù\87ذا Ø§Ù\84سطر Ù\83Ù\85ا Ù\87Ù\88 --> <pre>\n# Ø³Ù\8aتÙ\85 ØªØ¬Ø§Ù\87Ù\84 Ø§Ù\84ترÙ\88Ù\8aسات Ø®Ù\84اÙ\84 Ø¹Ù\85Ù\84Ù\8aØ© Ø§Ù\84بحث\n#ا Ù\84تغÙ\8aÙ\8aرات Ø³ØªØ£Ø®Ø° Ù\85جراÙ\87ا Ù\85ا Ø£Ù\86 Ù\8aتÙ\85 Ù\81Ù\87رسة Ø§Ù\84صÙ\81حة Ø§Ù\84تÙ\8a ØªØ­ØªÙ\88Ù\8a Ø¹Ù\84Ù\89 ØªØ±Ù\88Ù\8aسات\n# Ù\8aÙ\85Ù\83Ù\86Ù\83 Ù\81رض Ø¹Ù\85Ù\84Ù\8aØ© Ù\81Ù\87رسة Ø§Ù\84صÙ\81حة Ù\85Ù\86 Ø®Ù\84اÙ\84 ØªØ¹Ø¯Ù\8aÙ\84 Ù\81ارغ\n# Ø§Ù\84صÙ\8aغة Ù\87Ù\8a Ù\83اÙ\84أتÙ\8a:\n# * Ù\83Ù\84 Ù\85ا Ù\8aÙ\83تب Ø¨Ø¹Ø¯ \"#\" Ø¥Ù\84Ù\89 Ø¢Ø®Ø± Ø§Ù\84سطر Ù\8aعتبر ØªØ¹Ù\84Ù\8aÙ\82\n# * Ù\83Ù\84 Ø³Ø·Ø± ØºÙ\8aر Ù\81ارغ Ø³Ù\8aÙ\83Ù\88Ù\86 Ø§Ù\84عÙ\86Ù\88اÙ\86 Ø§Ù\84Ø°Ù\8a Ø³Ù\8aتÙ\85 ØªØ¬Ø§Ù\87Ù\84Ù\87 (سÙ\8aأخذ Ø§Ù\84عÙ\86Ù\88اÙ\86 Ù\83Ù\85ا Ù\87Ù\88 Ø¨Ø§Ù\84ضبط Ø¨Ø§Ù\84تشÙ\83Ù\8aÙ\84 Ù\88Ø®Ù\84اÙ\81Ù\87)\nاÙ\84Ù\85راجع\nاÙ\84Ù\88صÙ\84ات Ø§Ù\84خارجÙ\8aØ©\nØ£نظر أيضا\n#</pre><!--أترك هذا السطر كما هو -->",
+       "search-ignored-headings": "# <!-- Ø§ØªØ±Ù\83 Ù\87ذا Ø§Ù\84سطر Ù\83Ù\85ا Ù\87Ù\88 --> <pre>\n# Ø³Ù\8aتÙ\85 ØªØ¬Ø§Ù\87Ù\84 Ø§Ù\84ترÙ\88Ù\8aسات Ø®Ù\84اÙ\84 Ø¹Ù\85Ù\84Ù\8aØ© Ø§Ù\84بحث\n#اÙ\84تغÙ\8aÙ\8aرات Ø³ØªØ£Ø®Ø° Ù\85جراÙ\87ا Ù\85ا Ø£Ù\86 Ù\8aتÙ\85 Ù\81Ù\87رسة Ø§Ù\84صÙ\81حة Ø§Ù\84تÙ\8a ØªØ­ØªÙ\88Ù\8a Ø¹Ù\84Ù\89 ØªØ±Ù\88Ù\8aسات\n# Ù\8aÙ\85Ù\83Ù\86Ù\83 Ù\81رض Ø¹Ù\85Ù\84Ù\8aØ© Ù\81Ù\87رسة Ø§Ù\84صÙ\81حة Ù\85Ù\86 Ø®Ù\84اÙ\84 ØªØ¹Ø¯Ù\8aÙ\84 Ù\81ارغ\n# Ø§Ù\84صÙ\8aغة Ù\87Ù\8a Ù\83اÙ\84أتÙ\8a:\n# * Ù\83Ù\84 Ù\85ا Ù\8aÙ\83تب Ø¨Ø¹Ø¯ \"#\" Ø¥Ù\84Ù\89 Ø¢Ø®Ø± Ø§Ù\84سطر Ù\8aعتبر ØªØ¹Ù\84Ù\8aÙ\82\n# * Ù\83Ù\84 Ø³Ø·Ø± ØºÙ\8aر Ù\81ارغ Ø³Ù\8aÙ\83Ù\88Ù\86 Ø§Ù\84عÙ\86Ù\88اÙ\86 Ø§Ù\84Ø°Ù\8a Ø³Ù\8aتÙ\85 ØªØ¬Ø§Ù\87Ù\84Ù\87 (سÙ\8aأخذ Ø§Ù\84عÙ\86Ù\88اÙ\86 Ù\83Ù\85ا Ù\87Ù\88 Ø¨Ø§Ù\84ضبط Ø¨Ø§Ù\84تشÙ\83Ù\8aÙ\84 Ù\88Ø®Ù\84اÙ\81Ù\87)\nاÙ\84Ù\85راجع\nاÙ\84Ù\88صÙ\84ات Ø§Ù\84خارجÙ\8aØ©\nانظر أيضا\n#</pre><!--أترك هذا السطر كما هو -->",
        "searchbutton": "ابحث",
        "go": "اذهب",
        "searcharticle": "اذهب",
        "databaseerror": "عطل في قاعدة البيانات",
        "databaseerror-text": "حدث خطأ في إستعلام قاعدة البيانات. قد يشير هذا إلى خطأ في البرنامج.",
        "databaseerror-textcl": "حدث خطأ في إستعلام قاعدة البيانات.",
-       "databaseerror-query": "Ø¥ستعلام: $1",
+       "databaseerror-query": "استعلام: $1",
        "databaseerror-function": "دالة: $1",
        "databaseerror-error": "خطأ: $1",
+       "transaction-duration-limit-exceeded": "لتفادي إنشاء تأخير نسخ عالي، هذا الفعل تم إنهاؤه لأن فترة الكتابة ($1) تجاوزت حد $2 ثانية.\nلو أنك تغير عناصر عديدة في نفس الوقت، حاول تجربة عمليا عديدة أصغر بدلا من ذلك.",
        "laggedslavemode": "'''تحذير:''' الصفحة قد لا تحتوي على أحدث التحديثات.",
        "readonly": "قاعدة البيانات مقفلة",
        "enterlockreason": "أدخل سببا للقفل ذاكرا تقديرا لوقت إزالة الغلق",
        "missingarticle-rev": "(رقم المراجعة: $1)",
        "missingarticle-diff": "(فرق: $1، $2)",
        "readonly_lag": "تم قفل قاعدة البيانات تلقائيا حتى تستطيع الخواديم التابعة ملاحقة الخادوم الرئيسي",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP header تم إرساله لكن الطلب كان لAPI write module.",
        "internalerror": "عطل داخلي",
        "internalerror_info": "عطل داخلي: $1",
        "internalerror-fatal-exception": "استثناء مميت من النوع \"$1\"",
        "title-invalid-interwiki": "عنوان الصفحة المطلوب يتضمن وصلة لحلقة لغة وهو ما لا يمكن استخدامه في العناوين.",
        "title-invalid-talk-namespace": "عنوان الصفحة المطلوبة يشير إلى صفحة نقاش غير موجودة.",
        "title-invalid-characters": "عنوان الصفحة المطلوب يتضمن رموزًا غير صالحة: \"$1\"",
+       "title-invalid-relative": "العنوان به مسار نسبي. عنوان الصفحات النسبية (./, ../) هي غير صحيحة، لأنها ستكون غاليا لا يمكن الوصول لها عندما يتم التعامل معها بواسطة متصفح المستخدم.",
+       "title-invalid-magic-tilde": "عنوان الصفحة المطلوب يحتوي على تتابع الشر السحري غير الصحيح (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "عنوان الصفحة المطلوبة طويل جدا. يجب أن يكون ليس أطول من $1 {{PLURAL:$1|بايت}} باستخدام ترميز UTF-8.",
        "title-invalid-leading-colon": "عنوان الصفحة المطلوب يتضمن فاصلة غير صالحة في بدايته.",
        "perfcached": "البيانات التالية مخبأة و قد لا تكون محدثة. {{PLURAL:$1||نتيجة واحدة|نتيجتان|$1 نتائج|$1 نتيجة}} على الأكثر {{PLURAL:$1||مخبّأة|مخبّأتان|مخبّأة}}.",
        "perfcachedts": "البيانات التالية مخزنة، وكان آخر تحديث لها في $1. العدد الأقصى للنتائج المخزنة هو {{PLURAL:$4||نتيجة واحدة|نتيجتان|$4 نتائج|$4 نتيجة}}.",
        "yourpasswordagain": "أعد كتابة كلمة السر:",
        "createacct-yourpasswordagain": "أكد كلمة السر",
        "createacct-yourpasswordagain-ph": "أدخل كلمة المرور مرة أخرى",
-       "remembermypassword": "تذكر دخولي بهذا المتصفح (لمدة أقصاها {{PLURAL:$1||يوم واحد|يومان|$1 أيام|$1 يوما|$1 يوم}})",
        "userlogin-remembermypassword": "أبقني مسجلا للدخول",
        "userlogin-signwithsecure": "الولوج باتصّال مؤمّن",
+       "cannotlogin-title": "لا يمكن تسجيل الدخول",
+       "cannotlogin-text": "تسجيل الدخول غير ممكن.",
        "cannotloginnow-title": "لا يمكن تسجيل الدخول الآن",
        "cannotloginnow-text": "لا يمكن تسجيل الدخول عند استخدام $1.",
+       "cannotcreateaccount-title": "لا يمكن إنشاء الحسابات",
+       "cannotcreateaccount-text": "إنشاء الحسابات المباشر غير مفعل على هذه الويكي.",
        "yourdomainname": "نطاقك:",
        "password-change-forbidden": "أنت لا يمكنك تغيير كلمات السر على هذا الويكي.",
        "externaldberror": "هناك إما خطأ في دخول قاعدة البيانات الخارجية أو أنه غير مسموح لك بتحديث حسابك الخارجي.",
        "nocookiesnew": "تم إنشاء حساب المستخدم، ولكنك لست مسجل الدخول بعد.\nيستخدم {{SITENAME}} كوكيز لتسجيل الدخول.\nلديك الكوكيز معطلة.\nمن فضلك فعلها، ثم سجل الدخول باسم المستخدم وكلمة السر الجديدين.",
        "nocookieslogin": "يستخدم {{SITENAME}} الكوكيز لتسجيل الدخول.\nالكوكيز معطلة لديك.\nمن فضلك فعلها ثم حاول مرة أخرى.",
        "nocookiesfornew": "لم يتم إنشاء حساب المستخدم، لأننا لم نستطع تأكيد مصدره. \nتأكد من أن ملفات تعريف الارتباط (الكوكيز) مفعلة عندك، ثم أعد تحميل الصفحة وحاول مرة أخرى.",
+       "createacct-loginerror": "الحساب تم إنشاؤه تلقائيا لكن لم يمكن تسجيل دخولك تلقائيا. من فضلك اذهب إلى [[Special:UserLogin|تسجيل الدخول اليدوي]].",
        "noname": "لم تحدد اسم مستخدم صحيح.",
        "loginsuccesstitle": "تم الدخول",
        "loginsuccess": "'''لقد سجلت الدخول ل{{SITENAME}} باسم \"$1\".'''",
        "noemail": "لا يوجد عنوان بريد إلكتروني مسجل للمستخدم \"$1\".",
        "noemailcreate": "عليك تقديم عنوان بريد إلكتروني صالح",
        "passwordsent": "تم إرسال كلمة سر جديدة إلى عنوان البريد الإلكتروني المسجل للمستخدم \"$1\".\nمن فضلك حاول تسجيل الدخول مرة ثانية بعد استلامها.",
-       "blocked-mailpassword": "تم منع عنوان الأيبي الخاص بك من التحرير، ولمنع التخريب لا يمكنك أن تستخدم خاصية استرجاع كلمة السر.",
+       "blocked-mailpassword": "تم منع عنوان الأيبي الخاص بك من التحرير، ولمنع التخريب لا يمكنك أن تستخدم خاصية استرجاع كلمة السر من عنوان الآي بي هذا.",
        "eauthentsent": "تم إرسال رسالة تأكيد إلكترونية إلى العنوان المسمى.\nقبل إرسال أي رسالة أخرى لذلك الحساب، عليك أن تتبع التعليمات الواردة في الرسالة، لتأكيد أن هذا الحساب هو لك بالفعل.",
        "throttled-mailpassword": "تم بالفعل إرسال تذكير بكلمة السر، في ال{{PLURAL:$1||ساعة الماضية|ساعتين الماضيتين|$1 ساعات الماضية|$1 ساعة الماضية}}.\nلمنع التخريب، سيتم إرسال تذكير واحد كل {{PLURAL:$1||ساعة|ساعتين|$1 ساعات|$1 ساعة}}.",
        "mailerror": "خطأ أثناء إرسال البريد: $1",
        "changepassword-success": "تم تغيير كلمة السر !",
        "changepassword-throttled": "لديك محاولات تسجيل دخول كثيرة حديثة. من فضلك انتظر $1 قبل المحاولة ثانية.",
        "botpasswords": "كلمات مرور البوت",
+       "botpasswords-summary": "<em>كلمات سر البوت</em> يسمح بالوصول لحساب مستخدم من خلال API بدون استخدام اعتمادات تسجيل الدخول الرئيسية للحساب. صلاحيات المستخدم المتوفرة عند تسجيل الدخول باستخدام كلمة سر بوت ربما تكون مقيدة.\n\nلو أنك لا تعرف لماذا تريد فعل هذا، فأنت ينبغي على الأرجح ألا تففعله. لا أحد ينبغي أن يسألك أبدا أن تولد واحدة من هذه وإعطاؤهم إياها.",
        "botpasswords-disabled": "كلمات السر الخاصة بالبوت معطلة.",
        "botpasswords-no-central-id": "لاستخدام كلمة السر الخاصة بالبوت، يجب أن تقوم بتسجيل الدخول من خلال حساب موحد.",
        "botpasswords-existing": "كلمات مرور البوت الموجودة",
        "botpasswords-label-delete": "احذف",
        "botpasswords-label-resetpassword": "أعد ضبط كلمة السر",
        "botpasswords-label-grants": "المنح التي يمكن تطبيقها:",
+       "botpasswords-help-grants": "كل منحة تعطي وصولا لصلاحيات المستخدم المعروضة التي يمتلكها حساب المستخدم بالفعل. انظر [[Special:ListGrants|جدول المنح]] للمزيد من المعلومات.",
        "botpasswords-label-restrictions": "قيود الاستخدام:",
        "botpasswords-label-grants-column": "الممنوح",
        "botpasswords-bad-appid": "اسم البوت \"$1\" غير صحيح.",
        "botpasswords-insert-failed": "فشل في اضافة  اسم البوت \"$1\".هل اضيف بالفعل؟",
        "botpasswords-update-failed": "فشل في تحديث اسم بوت \"$1\". هل تم حذفه؟",
-       "botpasswords-created-title": "صÙ\86اعة Ù\83Ù\84Ù\85Ø© Ø³Ø± Ø£Ù\84Ù\8aØ©",
-       "botpasswords-created-body": "تم إنشاء كلمة مرور بوت \"$1\".",
-       "botpasswords-updated-title": "تحديث كلمة السر الألية",
-       "botpasswords-updated-body": "كلمة سر البوت\"$1\" قد حذفت.",
+       "botpasswords-created-title": "تÙ\85 Ø¥Ù\86شاء Ù\83Ù\84Ù\85Ø© Ø³Ø± Ø¨Ù\88ت",
+       "botpasswords-created-body": "تم إنشاء كلمة سر بوت \"$1\" للمستخدم \"$2\".",
+       "botpasswords-updated-title": "تم تحديث كلمة سر البوت",
+       "botpasswords-updated-body": "كلمة سر البوت \"$1\" للمستخدم \"$2\" تم تحديثها.",
        "botpasswords-deleted-title": "كلمة سر البوت حذفت",
-       "botpasswords-deleted-body": "كلمة سر البوت\"$1\" قد حذفت.",
+       "botpasswords-deleted-body": "كلمة سر البوت \"$1\" لمستخدم \"$2\" قد حذفت.",
+       "botpasswords-newpassword": "كلمة السر الجديدة لتسجيل الدخول ب <strong>$1</strong> هي <strong>$2</strong>. <em>من فضلك سجل هذه كمرجع في المستقبل .</em><br> (للبوتات القديمة التي تتطلب أن يكون اسم تسجيل الدخول مثل اسم المستخدم النهائي، يمكنك أيضا استخدام <strong>$3</strong> كاسم مستخدم و <strong>$4</strong> ككلمة سر.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider غير متاح.",
        "botpasswords-restriction-failed": "قيود كلمة مرور البوت تمنع هذا الولوج.",
+       "botpasswords-invalid-name": "اسم المستخدم الموفر لا يحتوي على فاصل كلمة سر البوت (\"$1\").",
+       "botpasswords-not-exist": "المستخدم \"$1\" لا يمتلك كلمة سر بوت بالاسم \"$2\".",
        "resetpass_forbidden": "كلمات السر لا يمكن تغييرها",
        "resetpass_forbidden-reason": "لا يمكن تغيير كلمة المرور: $1",
        "resetpass-no-info": "يجب أن تكون مسجل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "passwordreset-emailelement": "اسم {{GENDER:$1\n|المستخدم|المستخدمة}}: \n$1\n\nكلمة السر المؤقتة: \n$2",
        "passwordreset-emailsentemail": "إذا كان هذا العنوان البريد مرتبط بحسابك، من ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
        "passwordreset-emailsentusername": "إذا كان هناك عنوان بريد إلكتروني مرتبط بهذا المستخدم، ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|رسالة|رسائل}} البريد الإلكتروني لضبط كلمة السر تم إرسالها. {{PLURAL:$1|اسم المستخدم وكلمة السر معروضان|قائمة أسماء المستخدمين وكلمات السر معروضة}} بالأسفل.",
+       "passwordreset-emailerror-capture2": "إرسال بريد إلى {{GENDER:$2|المستخدم|المستخدمة}} فشل: $1 {{PLURAL:$3|اسم المستخدم وكلمة السر معروضان|lقائمة أسماء المستخدمين كلمات السر معروضة}} بالأسفل.",
+       "passwordreset-nocaller": "يجب أن يتم توفير مستدعي",
+       "passwordreset-nosuchcaller": "المستدعي غير موجود: $1",
+       "passwordreset-ignored": "إعادة ضبط كلمة السر لم تتم التعامل معها. ربما لا موفر تم ضبطه؟",
        "passwordreset-invalideamil": "عنوان بريد إلكتروني غير صالح",
+       "passwordreset-nodata": "لا اسم مستخدم ولا عنوان بريد الإلكتروي تم توفيره",
        "changeemail": "تغيير أو إزالة عنوان البريد الإلكتروني",
        "changeemail-header": "إكمال هذا النموذج لتغيير عنوان البريد الإلكتروني الخاص بك. إذا كنت ترغب في إزالة جمعية أي عنوان البريد الإلكتروني من حسابك، وترك الفراغ عنوان البريد الإلكتروني الجديد عند تقديم النموذج",
        "changeemail-no-info": "يجب تسجيل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "changeemail-oldemail": "عنوان البريد الإلكتروني الحالي:",
        "changeemail-newemail": "عنوان البريد الإلكتروني الجديد:",
+       "changeemail-newemail-help": "هذا الحقل ينبغي أن يترك فارغا في حالة لو كنت تريد إزالة عنوان البريد الإلكتروني الخاص بك. أنت لن تكون قادرا على إعادة ضبط كلمة سر ضائعة ولن تتلقى رسئل بريد إلكتروني من هذه الويكي لو أزيل عنوان البريد الإلكتروني.",
        "changeemail-none": "(لا شيء)",
        "changeemail-password": "كلمة سر {{SITENAME}} الخاصة بك:",
        "changeemail-submit": "غيّر البريد الإلكتروني",
        "accmailtext": "أُرسِلت كلمة سر مولدة عشوائيا ل[[User talk:$1|$1]] إلى $2. يمكن تغييرها في صفحة ''[[Special:ChangePassword|تغيير كلمة السر]]'' بعد تسجيل الدخول.",
        "newarticle": "(جديد)",
        "newarticletext": "لقد تبعت وصلة لصفحة لم يتم إنشائها بعد.\nلإنشاء هذه الصفحة ابدأ الكتابة في الصندوق بالأسفل (انظر في [$1 صفحة المساعدة] للمزيد من المعلومات).\nإذا كانت زيارتك لهذه الصفحة بالخطأ، اضغط على زر ''رجوع'' في متصفح الإنترنت لديك.",
-       "anontalkpagetext": "----''هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:CreateAccount|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.''",
+       "anontalkpagetext": "----\n<em>هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.</em>\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:CreateAccount|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.",
        "noarticletext": "الصفحة خالية. يمكنك [[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": "المراجعة #$1 من الصفحة المسماة \"{{FULLPAGENAME}}\" غير موجودة.\n\nهذا يحدث عادة عن طريق اتباع وصلة تاريخ قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "userpage-userdoesnotexist": "حساب المستخدم \"<nowiki>$1</nowiki>\" غير مسجل.\nمن فضلك تأكد أنك تريد إنشاء/تعديل هذه الصفحة.",
        "userpage-userdoesnotexist-view": "حساب المستخدم \"$1\" غير مسجل.",
        "blocked-notice-logextract": "هذا المستخدم ممنوع حاليا.\nآخر مدخلة في سجل المنع موفرة بالأسفل كمرجع:",
-       "clearyourcache": "'''ملاحظة:''' بعد الحفظ، قد تحتاج إلى إفراغ كاش متصفحك لرؤية التغييرات.\n* '''فايرفوكس / سفاري:''' اضغط ''Shift'' أثناء ضغط ''Reload''، أو اضغط أيا من ''Ctrl-F5'' أو ''Ctrl-R'' (''⌘-R'' على ماك)\n* '''جوجل كروم:''' اضغط ''Ctrl-Shift-R'' (''⌘-Shift-R'' على ماك)\n* '''إنترنت إكسبلورر:''' اضغط ''Ctrl'' أثناء ضغط ''Refresh''، أو اضغط ''Ctrl-F5''\n* '''كنكرر:''' اضغط ''Reload'' أو اضغط ''F5''\n* '''أوبرا:''' أفرغ الكاش في ''Tools → Preferences''",
+       "clearyourcache": "<strong>ملاحظة:</strong> بعد الحفظ، أنت قد تحتاج إلى إفراغ الكاش الخاص بمتصفحك لرؤية التغييرات.\n* <strong>فايرفوكس / سافاري:</strong> أمسك <em>Shift</em> أثناء ضغط <em>Reload</em>، أو اضغط على إما <em>Ctrl-F5</em> أو <em>Ctrl-R</em> (<em>⌘-R</em> على ماك)\n* <strong>جوجل كروم:</strong> اضغط <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> على ماك)\n* <strong>إنترنت إكسبلورر:</strong> أمسك <em>Ctrl</em> أثناء ضغط <em>Refresh</em>، أو اضغط <em>Ctrl-F5</em>\n* <strong>أوبرا:</strong> اذهب إلى <em>Menu → Settings</em> (<em>Opera → Preferences</em> على ماك) ثم إلى <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة CSS الجديد قبل حفظ الصفحة.",
        "userjsyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة جافاسكربت الجديدة قبل حفظ الصفحة.",
        "usercsspreview": "'''تذكر أنك تقوم بعرض الأنماط المتراصة (CSS) الخاصة بك فقط\nلم يتم حفظها بعد!'''",
        "permissionserrors": "خطأ في السماح",
        "permissionserrorstext": "لا تمتلك الصلاحية لفعل هذا، {{PLURAL:$1||للسبب التالي|للسببين التاليين|للأسباب التالية}}:",
        "permissionserrorstext-withaction": "لا تملك الصلاحيات ل$2، لل{{PLURAL:$1||سبب التالي|سببين التاليين|أسباب التالية}}:",
+       "contentmodelediterror": "أنت لا يمكنك تعديل هذه المراجعة لأن موديل محتواها هو  <code>$1</code>، والذي يختلف عن موديل المحتوى الحالي للصفحة  <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''تحذير: أنت تعيد إنشاء صفحة سبق حذفها.'''\n\nيجب عليك التيقن من أن الاستمرار بتحرير هذه الصفحة ملائم.\nسجلا الحذف والنقل لهذه الصفحة معروضان هنا للتيسير:",
        "moveddeleted-notice": "هذه الصفحة تم حذفها.\nسجلا الحذف والنقل للصفحة معروضان بالأسفل كمرجع.",
+       "moveddeleted-notice-recent": "عذرا، هذه الصفحة تم حذفها مؤخرا (في آخر 24 ساعة).\nسجلا الحذف والنقل للصفحة معروضان بالأسفل كمرجع.",
        "log-fulllog": "أظهر السجل الكامل",
        "edit-hook-aborted": "التعديل تم تركه بواسطة الخطاف.\nلم يعط تفسيرا.",
        "edit-gone-missing": "لم يمكن تحديث الصفحة.\nيبدو أنه تم حذفها.",
        "invalid-content-data": "بيانات المحتوى غير صالحة",
        "content-not-allowed-here": "\"$1\" المحتوى غير مسموح على صفحة [[$2]]",
        "editwarning-warning": "مغادرة هذه الصفحة قد تتسبب بخسارتك لأي تغييرات أجريتها.\nإذا كنت مسجل الدخول، فيمكنك تعطيل هذا التحذير في قسم \"{{int:prefs-editing}}\" في تفضيلاتك.",
+       "editpage-invalidcontentmodel-title": "موديل المحتوى غير مدعوم",
+       "editpage-invalidcontentmodel-text": "موديل المحتوى \"$1\" غير مدعوم.",
        "editpage-notsupportedcontentformat-title": "تنسيق المحتوى غير مدعوم",
        "editpage-notsupportedcontentformat-text": "تنسيق المحتوى $1 غير مدعوم بواسطة نموذج المحتوى $2.",
        "content-model-wikitext": "نص ويكي",
        "content-model-text": "نص عادي",
        "content-model-javascript": "جافاسكربت",
        "content-model-css": "CSS",
-       "content-json-empty-object": "غرض فارغ",
+       "content-json-empty-object": "كائن فارغ",
        "content-json-empty-array": "مصفوفة فارغة",
        "deprecated-self-close-category": "صفحات تستخدم وسوم أتش تي أم أل غير صالحة",
+       "deprecated-self-close-category-desc": "هذه الصفحة تحتوي على وسوم HTML مغلقة ذاتيا، مثل  <code>&lt;b/></code> أو <code>&lt;span/></code>. سلوك هذه سيتغير سريعا ليكون متوافقا مع معيار HTML5، لذا فاستخدامهم في نص الويكي ينبغي أن يتم الاستغناء عنه.",
        "duplicate-args-warning": "<strong>تنبيه:</strong> المدخل \"$3\" ل[[:$1]] المستعمل في [[:$2]] مكرر. آخر قيمة مكرر منه هي المعتمدة.",
        "duplicate-args-category": "صفحات تستعمل قالبا ببيانات مكررة",
        "duplicate-args-category-desc": "تحوي هذه الصفحة استدعاءات قالب تستخدم متغيرات مزدوجة مثل <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> أو <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "revdel-restore": "تغيير الرؤية",
        "pagehist": "تاريخ الصفحة",
        "deletedhist": "التاريخ المحذوف",
-       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø­فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
+       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø®فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
        "revdelete-show-no-access": "خطأ في إظهار العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-no-access": "خطأ في تعديل العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-missing": "خطأ في تعديل العنصر ذا الهوية $1: العنصر مفقود من قاعدة البيانات!",
        "mergehistory-go": "عرض التعديلات القابلة للدمج",
        "mergehistory-submit": "دمج المراجعات",
        "mergehistory-empty": "لا مراجعات يمكن دمجها.",
-       "mergehistory-done": "$3 {{PLURAL:$3|مراجعة|مراجعة}} من $1{{PLURAL:$3|كان|اين}} تم دمجها بنجاح في [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|مراجعة}} من $1{{PLURAL:$3|كان|اين}} تم دمجها بنجاح في [[:$2]].",
        "mergehistory-fail": "غير قادر على عمل دمج التاريخ، من فضلك أعد التحقق من محددات الصفحة والزمن.",
        "mergehistory-fail-bad-timestamp": "الختم الزمني غير صالح.",
        "mergehistory-fail-invalid-source": "الصفحة المصدر غير صالحة.",
        "localtime": "الوقت المحلي:",
        "timezoneuseserverdefault": "استخدام الويكي الافتراضي ($1)",
        "timezoneuseoffset": "آخر (حدد الفرق)",
-       "servertime": "Ù\88Ù\82ت Ø§Ù\84خادÙ\88Ù\85:",
+       "servertime": "وقت الخادم:",
        "guesstimezone": "أدخل التوقيت من المتصفح",
        "timezoneregion-africa": "أفريقيا",
        "timezoneregion-america": "أمريكا",
        "prefs-files": "ملفات",
        "prefs-custom-css": "CSS مخصص",
        "prefs-custom-js": "جافاسكربت مخصص",
-       "prefs-common-css-js": "سي إس إس وجافاسكربت مشترك لجميع المظاهر:",
+       "prefs-common-css-js": "CSS وجافاسكربت مشترك لجميع الواجهات:",
        "prefs-reset-intro": "يمكنك استخدام هذه الصفحة لإعادة تفضيلاتك للحالة الافتراضية للموقع.\nلن تستطيع استرجاع الحالة السابقة.",
        "prefs-emailconfirm-label": "تأكيد البريد الإلكتروني:",
        "youremail": "البريد:",
        "userrights-removed-self": "أزلت بنجاح صلاحياتك، ولن تتمكن من الوصول لهذه الصفحة مجددا.",
        "group": "المجموعة:",
        "group-user": "مستخدمون",
-       "group-autoconfirmed": "مستخدمون مؤكدون تلقائياً",
+       "group-autoconfirmed": "مستخدمون مؤكدون تلقائيا",
        "group-bot": "بوتات",
-       "group-sysop": "مديرو نظام",
+       "group-sysop": "إداريون",
        "group-bureaucrat": "بيروقراطيون",
        "group-suppress": "مزيلون",
        "group-all": "(الكل)",
        "group-bureaucrat-member": "{{GENDER:$1|بيروقراط}}",
        "group-suppress-member": "{{GENDER:$1|مزيل|مزيلة}}",
        "grouppage-user": "{{ns:project}}:مستخدمون",
-       "grouppage-autoconfirmed": "{{ns:project}}:مستخدمون مؤكدون تلقائياً",
+       "grouppage-autoconfirmed": "{{ns:project}}:مستخدمون مؤكدون تلقائيا",
        "grouppage-bot": "{{ns:project}}:بوتات",
        "grouppage-sysop": "{{ns:project}}:إداريون",
        "grouppage-bureaucrat": "{{ns:project}}:بيروقراطيون",
        "right-move": "نقل الصفحات",
        "right-move-subpages": "نقل الصفحات مع صفحاتها الفرعية",
        "right-move-rootuserpages": "نقل صفحات المستخدمين الأساسية",
-       "right-move-categorypages": "انقل صفحات التصنيف",
+       "right-move-categorypages": "نقل صفحات التصنيف",
        "right-movefile": "نقل الملفات",
        "right-suppressredirect": "عدم إنشاء تحويلة من الاسم القديم عند نقل صفحة",
        "right-upload": "رفع الملفات",
        "right-writeapi": "استخدام API للكتابة",
        "right-delete": "حذف الصفحات",
        "right-bigdelete": "حذف الصفحات ذات التواريخ الكبيرة",
-       "right-deletelogentry": "حذÙ\81 Ù\88اÙ\84غاء Ø­Ø°Ù\81 Ø¥Ø¯Ø®Ø§Ù\84ات Ø³Ø¬Ù\84 Ù\85عÙ\8aÙ\86",
+       "right-deletelogentry": "حذÙ\81 Ù\88Ø¥Ù\84غاء Ø­Ø°Ù\81 Ù\85دخÙ\84ات Ø³Ø¬Ù\84 Ù\85عÙ\8aÙ\86Ø©",
        "right-deleterevision": "حذف واسترجاع مراجعات معينة من الصفحات",
        "right-deletedhistory": "رؤية مدخلات التاريخ المحذوفة، بدون نصوصها المصاحبة",
        "right-deletedtext": "عرض النص المحذوف والتغييرات بين المراجعات المحذوفة",
        "right-browsearchive": "البحث في الصفحات المحذوفة",
        "right-undelete": "استرجاع صفحة",
        "right-suppressrevision": "مراجعة واسترجاع المراجعات المخفية عن مديري النظام",
-       "right-viewsuppressed": "أعرض Ø§Ù\84Ù\85راجعات Ø§Ù\84Ù\85Ø®Ù\81Ù\8aØ© Ø¨Ù\88اسطة Ø£Ù\8a Ù\85ستخدÙ\85",
+       "right-viewsuppressed": "عرض المراجعات المخفية بواسطة أي مستخدم",
        "right-suppressionlog": "رؤية السجلات السرية",
        "right-block": "منع المستخدمين الآخرين من التعديل",
        "right-blockemail": "منع مستخدم من إرسال بريد إلكتروني",
        "right-editinterface": "تعديل واجهة المستخدم",
        "right-editusercssjs": "تعديل ملفات CSS و JS للمستخدمين الآخرين",
        "right-editusercss": "تعديل ملفات CSS للمستخدمين الآخرين",
-       "right-edituserjs": "تعديل ملفات JS للمستخدمين الآخرين",
+       "right-edituserjs": "تعديل ملفات جافاسكريبت للمستخدمين الآخرين",
        "right-editmyusercss": "تعديل ملفات CSS للمستخدم نفسه",
        "right-editmyuserjs": "تعديل ملفات جافاسكربت للمستخدم نفسه",
        "right-viewmywatchlist": "عرض قائمة مراقبتك",
        "right-editmywatchlist": "حرر قائمة مراقبتك. لاحظ أن بعض الإجراءات لا تزال تضيف الصفحات حتى بدون هذا الحق.",
-       "right-viewmyprivateinfo": "إستعرض Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84Ø¥سم الحقيقي)",
-       "right-editmyprivateinfo": "حرر Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84Ø¥سم الحقيقي)",
+       "right-viewmyprivateinfo": "استعراض Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84اسم الحقيقي)",
+       "right-editmyprivateinfo": "تعدÙ\8aÙ\84 Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84اسم الحقيقي)",
        "right-editmyoptions": "تعديل تفضيلاتك",
        "right-rollback": "استرجاع تعديلات آخر مستخدم عدل صفحة معينة سريعا",
        "right-markbotedits": "التعليم على تعديلات الاسترجاع كتعديلات بوت",
        "right-import": "استيراد الصفحات من ويكيات أخرى",
        "right-importupload": "استيراد الصفحات من ملف مرفوع",
        "right-patrol": "تعليم تعديلات الآخرين بعلامة المراجعة",
-       "right-autopatrol": "عÙ\84Ù\85 ØªØ¹Ø¯Ù\8aÙ\84ات Ø§Ù\84Ù\85ستخدÙ\85 مراجعة تلقائيا",
+       "right-autopatrol": "اÙ\84تعÙ\84Ù\8aÙ\85 Ø¹Ù\84Ù\89 ØªØ¹Ø¯Ù\8aÙ\84ات Ø§Ù\84Ù\85ستخدÙ\85 Ù\83مراجعة تلقائيا",
        "right-patrolmarks": "رؤية علامات المراجعة في أحدث التغييرات",
        "right-unwatchedpages": "رؤية قائمة بالصفحات غير المراقبة",
        "right-mergehistory": "دمج تاريخ الصفحات",
        "right-override-export-depth": "تصدير الصفحات متضمنة الصفحات الموصولة حتى عمق 5",
        "right-sendemail": "إرسال رسائل بريد إلكتروني إلى مستخدمين آخرين",
        "right-passwordreset": "عرض رسائل إعادة ضبط كلمات السر",
-       "right-managechangetags": "Ø¥Ù\86شاء Ù\88حذÙ\81 [[Special:Tags|اÙ\84Ù\88سÙ\88Ù\85]] Ù\85Ù\86 Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات",
+       "right-managechangetags": "Ø¥Ù\86شاء Ù\88تعطÙ\8aÙ\84 [[Special:Tags|اÙ\84Ù\88سÙ\88Ù\85]]",
        "right-applychangetags": "تطبيق [[Special:Tags|الوسوم]]  مع التغييرات التي أجريتها.",
+       "right-changetags": "إضافة وإزالة [[Special:Tags|وسوم]] في مراجعات ومدخلات سجل فردية",
+       "right-deletechangetags": "حذف [[Special:Tags|الوسوم]] من قاعدة البيانات",
        "grant-generic": "\"$1\" حزمة الصلاحيات",
        "grant-group-page-interaction": "التفاعل مع الصفحات",
        "grant-group-file-interaction": "التفاعل مع الوسائط",
        "grant-group-high-volume": "أداء نشاط كبير الحجم",
        "grant-group-customization": "التخصيص والتفضيلات",
        "grant-group-administration": "أداء عمليات إدارية",
+       "grant-group-private-information": "الوصول للبيانات السرية المتعلقة بك",
        "grant-group-other": "نشاطات متفرقة",
        "grant-blockusers": "منع ورفع المنع عن المستخدمين",
        "grant-createaccount": "إنشاء حسابات",
        "grant-createeditmovepage": "إنشاء وتعديل ونقل الصفحات",
        "grant-delete": "حذف الصفحات والمراجعات ومدخلات السجلات",
+       "grant-editinterface": "تعديل نطاق ميدياويكي والCSS/JavaScript الخاصة بالمستخدم",
+       "grant-editmycssjs": "تعديل الCSS/JavaScript الخاصة بحسابك",
        "grant-editmyoptions": "تعديل تفضيلاتك",
        "grant-editmywatchlist": "تعديل قائمة مراقبتك",
        "grant-editpage": "تعديل صفحات موجودة",
        "grant-editprotected": "تعديل صفحات محمية",
        "grant-highvolume": "تعديل كبير الحجم",
+       "grant-oversight": "إخفاء المستخدمين وإخفاء المراجعات",
        "grant-patrol": "تغييرات دورية للصفحات",
+       "grant-privateinfo": "الوصول للمعلومات السرية",
        "grant-protect": "حماية وإزالة حماية الصفحات",
        "grant-rollback": "استرجاع التغييرات في الصفحات",
        "grant-sendemail": "إرسال بريد إلكتروني للمستخدمين الآخرين",
        "action-read": "قراءة هذه الصفحة",
        "action-edit": "تعديل هذه الصفحة",
        "action-createpage": "إنشاء هذه الصفحة",
-       "action-createtalk": "Ø¥Ù\86شاء ØµÙ\81حات Ø§Ù\84Ù\86Ù\82اش",
+       "action-createtalk": "Ø¥Ù\86شاء ØµÙ\81حة Ø§Ù\84Ù\86Ù\82اش Ù\87Ø°Ù\87",
        "action-createaccount": "إنشاء حساب المستخدم هذا",
-       "action-autocreateaccount": "تلقائيا إنشاء هذا الحساب مستخدم خارجي",
+       "action-autocreateaccount": "تلقائيا إنشاء هذا الحساب الخارجي للمستخدم",
        "action-history": "اعرض تاريخ هذه الصفحة",
        "action-minoredit": "التعليم على هذا التعديل كطفيف",
        "action-move": "نقل هذه الصفحة",
        "action-viewmyprivateinfo": "مشاهدة معلوماتك الخاصة",
        "action-editmyprivateinfo": "تعديل معلوماتك الخاصة",
        "action-editcontentmodel": "عدل عدل طريقة محتوى صفحة",
-       "action-managechangetags": "Ø¥Ù\86شاء Ù\88حذÙ\81 Ø§Ù\84Ù\88سÙ\88Ù\85 Ù\85Ù\86 Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات",
+       "action-managechangetags": "Ø¥Ù\86شاء Ù\88تعطÙ\8aÙ\84 Ø§Ù\84Ù\88سÙ\88Ù\85",
        "action-applychangetags": "تطبيق الوسوم مع تغييراتك",
+       "action-changetags": "أضف وأزل وسوما في مراجعات ومدخلات سجل فردية",
+       "action-deletechangetags": "حذف الوسوم من قاعدة البيانات",
+       "action-purge": "إفراغ كاش هذه الصفحة",
        "nchanges": "{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|منذ الزيارة الأخيرة}}",
        "enhancedrc-history": "تاريخ",
        "recentchangeslinked-page": "اسم الصفحة:",
        "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك",
        "recentchanges-page-added-to-category": "[[:$1]] أضيفت إلى التصنيف",
-       "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] و{{PLURAL:$2|صفحة واحدة|صفحتان|$2 صفحات}} إلى التصنيف",
+       "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] إلى التصنيف، [[Special:WhatLinksHere/$1|هذه الصفحة مضمنة في صفحات اخرى]]",
        "recentchanges-page-removed-from-category": "أزيلت [[:$1]] من التصنيف",
-       "recentchanges-page-removed-from-category-bundled": "أزÙ\8aÙ\84ت [[:$1]] Ù\88{{PLURAL:$2|صÙ\81حة Ù\88احدة|صÙ\81حتاÙ\86|$2 ØµÙ\81حات}} Ù\85Ù\86 Ø§Ù\84تصÙ\86Ù\8aÙ\81",
+       "recentchanges-page-removed-from-category-bundled": "أزÙ\8aÙ\84ت [[:$1]] Ù\85Ù\86 Ø§Ù\84تصÙ\86Ù\8aÙ\81Ø\8c [[Special:WhatLinksHere/$1|Ù\87Ø°Ù\87 Ø§Ù\84صÙ\81حة Ù\85ضÙ\85Ù\86Ø© Ù\81Ù\8a ØµÙ\81حات Ø£Ø®Ø±Ù\89]]",
        "autochange-username": "تغيير آلي لميدياويكي",
        "upload": "ارفع ملفا",
        "uploadbtn": "ارفع الملف",
        "file-thumbnail-no": "يبدأ الملف ب <strong>$1</strong>.\nيبدو أن الملف مصغرا لحجم أعلى ''(تصغير)''.\nإذا كانت لديك الصورة في درجة دقة كاملة قم برفعها، أو قم بتغيير اسم الملف من فضلك.",
        "fileexists-forbidden": "هناك ملف موجود بهذا الاسم بالفعل، ولا يمكن إعادة الكتابة عليه.\nلو أنك مازلت تريد رفع ملفك، من فضلك عد واستخدم اسماً جديداً. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "يوجد ملف بنفس الاسم بالفعل في مستودع الملفات المشترك.\nلو كنت مازلت تريد رفع ملفك، من فضلك ارجع واستخدم اسماً جديداً.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "الملف المرفوع هو نسخة مطابقة تمامًا للنسخة الحالية من <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "الملف المرفوع هو نسخة مطابقة من {{PLURAL:$2|نسخة أقدم|نسخ أقدم}} من <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "هذا الملف مكرر  {{PLURAL:$1|للملف|للملفات}} التالية:",
        "file-deleted-duplicate": "ملف مطابق لهذه الملف ([[:$1]]) تم حذفه من قبل. ينبغي أن تتحقق من تاريخ الحذف لهذا الملف قبل المتابعة بإعادة رفعه.",
        "file-deleted-duplicate-notitle": "سابقا تم حذف ملف مطابق لهذا الملف، وقد تم منع العنوان.\nينبغي أن تسأل شخص ما لديه القدرة على عرض بيانات الملف الممنوع لاستعراض الوضع قبل الشروع في إعادة تحميله.",
        "uploaddisabledtext": "رفع الملفات معطل.",
        "php-uploaddisabledtext": "رفع ملفات PHP معطل. من فضلك تحقق من إعدادات رفع الملفات.",
        "uploadscripted": "هذا الملف يضم كود HTML أو كود آخر يمكن أن يفسره متصفح الوب بطريقة خاطئة.",
+       "upload-scripted-pi-callback": "لا يمكن رفع ملف يحتوي على تعليمة معالجة XML-stylesheet",
+       "uploaded-script-svg": "تم العثور على عنصر سكريبت \"$1\" في ملف الSVG المرفوع.",
+       "uploaded-hostile-svg": "تم العثور على CSS غير آمن في عنصر الشكل في ملف الSVG المرفوع.",
+       "uploaded-event-handler-on-svg": "ضبط سمات معالج الأحداث <code>$1=\"$2\"</code> غير مسموح به في ملفات SVG.",
+       "uploaded-href-attribute-svg": "سمات href في ملفات SVG مسموح بوصلها فقط بأهداف http:// أو https:// ، تم العثور على <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "تم العثور على href لبيانات غير آمنة: هدف URI <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-animate-svg": "تم العثور على وسم \"animate\" الذي ربما يكون يغير href, باستخدام سمة \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-event-handler-svg": "ضبط سمات معالج الأحداث ممنوع، تم العثور على <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-href-svg": "استخدام وسم \"set\" لإضافة سمة \"href\" للعنصر الأب ممنوع.",
+       "uploaded-wrong-setting-svg": "استخدام وسم \"set\" لإضافة هدف خارجي/بيانات/سكريبت لأي سمة ممنوع. تم العثور على <code>&lt;set to=\"$1\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-handler-svg": "SVG الذي يضبط سمة \"handler\" مع خارجي/بيانات/سكريبت ممنوع. تم العثور على <code>$1=\"$2\"</code> في ملف SVG المرفوع.",
+       "uploaded-remote-url-svg": "SVG الذي يضبط أي سمة شكل مع URL خارجي ممنوع. تم العثور على <code>$1=\"$2\"</code> في ملف SVG المرفوع.",
+       "uploaded-image-filter-svg": "تم العثور على فلتر صورة مع URL: <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
        "uploadscriptednamespace": "يحتوي ملف SVG هذا على اسم نطاق غير مشروع \" $1 \"",
        "uploadinvalidxml": "تعذر تحليل XML في الملف المرفوع.",
        "uploadvirus": "الملف يحتوي على فيروس! التفاصيل: $1",
        "upload-options": "خيارات الرفع",
        "watchthisupload": "راقب هذا الملف",
        "filewasdeleted": "تم رفع ثم حذف ملف بهذا الاسم من قبل.\nمن الأفضل مراجعة $1 قبل رفعه مرة أخرى.",
+       "filename-thumb-name": "هذا يبدو وكأنه عنوان صورة مصغرة. من فضلك لا ترفع صورة مصغرة لنفس الويكي مرة ثانية. أو، من فضلك أصلح اسم الملف بحيث يكون معبرا أكثر، ولا يحتوي على بادئة الصورة المصغرة.",
        "filename-bad-prefix": "اسم الملف الذي ترفعه يبدأ ب'''\"$1\"'''، وهو اسم غير وصفي غالباً ما تخصصه الكاميرات الرقمية تلقائياً.\nمن فضلك اختر اسماً يصف ملفك بوضوح أكثر.",
        "filename-prefix-blacklist": " #<!-- اترك هذا السطر تماما كما هو --> <pre>\n# الصيغة كالتالي:\n#   * كل شيء من علامة \"#\" إلى آخر السطر هو تعليق\n#   * كل سطر غير فارغ هو بادئة لأسماء الملفات النمطية التي توضع تلقائيا بواسطة الكاميرات الرقمية\nCIMG # كاسيو\nDSC_ # نيكون\nDSCF # فوجي\nDSCN # نيكون\nDUW # بعض الهواتف المحمولة\nIMG # عام\nJD # جينوبتيك\nMGP # بينتاكس\nPICT # متنوع\n #</pre> <!-- اترك هذا السطر تماما كما هو -->",
        "upload-proto-error": "بروتوكول غير صحيح",
        "upload-too-many-redirects": "احتوى المسار تحويلات كثيرة جدا",
        "upload-http-error": "صودف خطأ HTTP: $1",
        "upload-copy-upload-invalid-domain": "رفع النسخ غير متاح من هذا الموقع",
+       "upload-foreign-cant-upload": "هذه الويكي ليست مضبوطة لرفع الملفات لمستودع الملفات الخارجي المطلوب.",
+       "upload-foreign-cant-load-config": "فشل تحميل الإعدادات للملفات المرفوعة لمستودع الملفات الخارجي.",
+       "upload-dialog-disabled": "رفع الملفات باستخدام هذا الحوار معطلة على هذه الويكي.",
        "upload-dialog-title": "رفع الملف",
        "upload-dialog-button-cancel": "إلغاء",
        "upload-dialog-button-done": "تم",
        "upload-dialog-button-upload": "رفع",
        "upload-form-label-infoform-title": "التفاصيل",
        "upload-form-label-infoform-name": "الاسم",
+       "upload-form-label-infoform-name-tooltip": "عنوان وصفي فريد للملف، والذي سيكون اسم الملف. يمكنك أن تستخدم لغة عادية مع مسافات. لا تضمن امتداد الملف.",
        "upload-form-label-infoform-description": "الوصف",
+       "upload-form-label-infoform-description-tooltip": "باختصار صف كل شيء ملحوظ حول العمل.\nلصورة، اذكر الأشياء الأساسية المصورة، المناسبة أو المكان.",
        "upload-form-label-usage-title": "الاستخدام",
        "upload-form-label-usage-filename": "اسم الملف",
        "upload-form-label-own-work": "هذا عملي الخاص",
        "upload-form-label-infoform-categories": "تصنيفات",
        "upload-form-label-infoform-date": "التاريخ",
+       "upload-form-label-own-work-message-generic-local": "أنا أؤكد أنني أقوم برفع هذا الملف مع مراعاة شروط الخدمة وسياسات الترخيص في {{SITENAME}}.",
+       "upload-form-label-not-own-work-message-generic-local": "لو أنك غير قادر على رفع هذا الملف تحت سياسات {{SITENAME}}، من فضلك أغلق هذا الحوار وجرب طريقة أخرى.",
+       "upload-form-label-not-own-work-local-generic-local": "أنت ربما تريد تجربة [[Special:Upload|صفحة الرفع الافتراضية]].",
+       "upload-form-label-own-work-message-generic-foreign": "أنا أفهم أنني أقوم برفع هذا الملف إلى مستودع مشترك. أنا أؤكد أنني أقوم بهذا بالتوافق مع شروط الخدمة وسياسات الترخيص هناك.",
+       "upload-form-label-not-own-work-message-generic-foreign": "لو أنك غير قادر على رفع هذا الملف تحت سياسات المستودع المشترك، من فضلك أغلق هذا الحوار وجرب طريقة أخرى.",
+       "upload-form-label-not-own-work-local-generic-foreign": "أنت ربما تريد أيضا تجربة [[Special:Upload|صفحة الرفع على {{SITENAME}}]]، لو أن هذا الملف يمكن رفعه هناك تحت سياساتهم.",
        "backend-fail-stream": "لا يمكن عرض الملف $1.",
        "backend-fail-backup": "لا يمكن صنع نسخة أحتياطية للملف $1.",
        "backend-fail-notexists": "الملف $1 غير موجود.",
-       "backend-fail-hashes": "لم يمكن الحصول على هاش الملف من أجل المقارنة",
-       "backend-fail-notsame": "يوجد بالفعل ملف غير متطابق في $1.",
-       "backend-fail-invalidpath": "$1 ليس مساراً صالحاً للتخزين.",
-       "backend-fail-delete": "لم يمكن حذف الملف $1.",
-       "backend-fail-describe": "Ù\84ا Ù\8aÙ\85Ù\83Ù\86 ØªØºÙ\8aÙ\8aر Ø§Ù\84بÙ\8aاÙ\86ات Ø§Ù\84تعرÙ\8aÙ\81 (metadata) Ù\84Ù\84Ù\85Ù\84Ù\81 \" $1 \".",
-       "backend-fail-alreadyexists": "الملف $1 موجود بالفعل.",
-       "backend-fail-store": "لا يمكن تخزين الملف $1 في $2 .",
-       "backend-fail-copy": "لا يمكن نسخ الملف  $1  إلى  $2 .",
-       "backend-fail-move": "تعذر نقل ملف $1 إلى $2 .",
+       "backend-fail-hashes": "لم يمكن الحصول على hashes الملفات من أجل المقارنة",
+       "backend-fail-notsame": "يوجد بالفعل ملف غير متطابق في \"$1\".",
+       "backend-fail-invalidpath": "\"$1\" ليس مساراً صالحاً للتخزين.",
+       "backend-fail-delete": "لم يمكن حذف الملف \"$1\".",
+       "backend-fail-describe": "Ù\84ا Ù\8aÙ\85Ù\83Ù\86 ØªØºÙ\8aÙ\8aر Ø¨Ù\8aاÙ\86ات Ø§Ù\84تعرÙ\8aÙ\81 metadata Ù\84Ù\84Ù\85Ù\84Ù\81 \"$1\".",
+       "backend-fail-alreadyexists": "الملف \"$1\" موجود بالفعل.",
+       "backend-fail-store": "لا يمكن تخزين الملف \"$1\" في \"$2\".",
+       "backend-fail-copy": "لا يمكن نسخ الملف  \"$1\"  إلى  \"$2\".",
+       "backend-fail-move": "تعذر نقل ملف \"$1\" إلى \"$2\".",
        "backend-fail-opentemp": "تعذّر فتح ملف مؤقت.",
        "backend-fail-writetemp": "تعذّرت كتابة ملف مؤقت.",
        "backend-fail-closetemp": "تعذّر إغلاق ملف مؤقت.",
        "backend-fail-read": "لا يمكن قراءة الملف $1.",
        "backend-fail-create": "تعذر كتابة الملف $1.",
        "backend-fail-maxsize": "تعذر كتابة الملف $1 لأنه أكبر من  {{PLURAL:$2|بايت واحد|$2 بايت}}.",
-       "backend-fail-readonly": "خلفية التخزين \"$1\" في وضعية القراءة فقط حاليا. السبب في ذلك هو: \"$2\"",
+       "backend-fail-readonly": "خلفية التخزين \"$1\" في وضعية القراءة فقط حاليا. السبب في ذلك هو:\n<em>$2</em>",
        "backend-fail-synced": "الملف \"$1\" في حالة غير متناسقة ضمن خلفية التخزين الداخلية",
        "backend-fail-connect": "تعذر ربط الإتصال بخلفية التخزين \"$1\".",
        "backend-fail-internal": "وقع خطأ غير معروف في خلفية التخزين \"$1\".",
        "uploadstash-summary": " توفر هذه الصفحة الوصول إلى الملفات التي يتم تحميلها (أو في أثناء عملية التحميل) ولكنها لم تنشر بعد. هذه الملفات هي غير مرئية لأحد إلا للمستخدم الذين تم الرفع لهم.",
        "uploadstash-clear": "مسح الملفات المخبأة",
        "uploadstash-nofiles": "ليس لديك أي ملفات مخبأة.",
-       "uploadstash-badtoken": "Ù\84Ù\85 Ù\8aÙ\86جح Ø£Ø¯Ø§Ø¡ Ø°Ù\84Ù\83 Ø§Ù\84عÙ\85Ù\84Ø\8c Ø±Ø¨Ù\85ا Ù\84Ø£Ù\86 Ù\88ثائÙ\82 ØªÙ\81Ù\88Ù\8aض Ø§Ù\84تحرÙ\8aر Ø§Ù\84خاصة Ø¨Ù\83 Ù\85Ù\86تÙ\87Ù\8aØ© Ø§Ù\84صÙ\84احÙ\8aØ©. حاول مرة أخرى.",
+       "uploadstash-badtoken": "Ù\81Ø´Ù\84 Ø£Ø¯Ø§Ø¡ Ø°Ù\84Ù\83 Ø§Ù\84عÙ\85Ù\84Ø\8c Ø±Ø¨Ù\85ا Ù\84Ø£Ù\86 Ù\88ثائÙ\82 ØªÙ\81Ù\88Ù\8aض Ø§Ù\84تحرÙ\8aر Ø§Ù\84خاصة Ø¨Ù\83 Ù\85Ù\86تÙ\87Ù\8aØ© Ø§Ù\84صÙ\84احÙ\8aØ©. Ù\85Ù\86 Ù\81ضÙ\84Ù\83 حاول مرة أخرى.",
        "uploadstash-errclear": "فشلت عملية مسح الملفات.",
        "uploadstash-refresh": "تحديث قائمة الملفات",
        "uploadstash-thumbnail": "اعرض صورة مصغرة",
+       "uploadstash-exception": "لم يمكن تخزين الرفع في الstash ($1): \"$2\".",
        "invalid-chunk-offset": "قطعة أوفست غير صالحة",
        "img-auth-accessdenied": "رفض الوصول",
        "img-auth-nopathinfo": "PATH_INFO مفقود.\nخادومك ليس مضبوطاً لتمرير هذه المعلومة.\nقد يكون مبنياً على نظام CGI ولا يمكنه دعم img_auth.\nراجع https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "استرجع",
        "filerevert-success": "'''[[Media:$1|$1]]''' تم استرجاعها [$4 للنسخة بتاريخ $3، $2].",
        "filerevert-badversion": "لا توجد نسخة محلية سابقة لهذا الملف بالتاريخ المعطى.",
+       "filerevert-identical": "الإصدار الحالي من الملف بالفعل مطابق للإصدار المحدد.",
        "filedelete": "احذف $1",
        "filedelete-legend": "احذف الملف",
        "filedelete-intro": "أنت على وشك حذف الملف '''[[Media:$1|$1]]''' مع كل تاريخه.",
        "apisandbox": "ملعب API",
        "apisandbox-jsonly": "الجافا سكريبت مطلوبة لاستخدام ملعب API",
        "apisandbox-api-disabled": "واجهة برمجة التطبيق API معطلة في هذا الموقع.",
+       "apisandbox-intro": "استخدم هذه الصفحة للتجربة ب<strong>MediaWiki web service API</strong>.\nارجع إلى [[mw:API:Main page|توثيق الAPI]] للمزيد من التفاصيل حول استخدام الAPI. مثال: [https://www.mediawiki.org/wiki/API#A_simple_example احصل على محتوى صفحة رئيسية]. اختر فعلا لترى المزيد من الأمثلة.\n\nلاحظ أنه، على الرغم من أن هذا ملعب، فالأفعال التي تقوم بها على هذه الصفحة ربما تعدل الويكي.",
        "apisandbox-fullscreen": "وسع اللوحة",
+       "apisandbox-fullscreen-tooltip": "وسع صفحة الملعب لتملأ نافذة المتصفح.",
        "apisandbox-unfullscreen": "أظهر الصفحة",
+       "apisandbox-unfullscreen-tooltip": "قلل صفحة الملعب، حتى تكون وصلات التصفح لميدياويكي متوفرة.",
        "apisandbox-submit": "عمل الطلب",
        "apisandbox-reset": "إفراغ",
        "apisandbox-retry": "أعد المحاولة",
+       "apisandbox-loading": "تحميل المعلومات لAPI module \"$1\"...",
+       "apisandbox-load-error": "حدث خطأ أثناء تحميل المعلومات لAPI module \"$1\": $2",
        "apisandbox-no-parameters": "وحدة API هذه ليس بها معاملات.",
        "apisandbox-helpurls": "وصلات المساعدة",
        "apisandbox-examples": "أمثلة",
        "apisandbox-dynamic-parameters-add-placeholder": "اسم المعامل",
        "apisandbox-dynamic-error-exists": "يوجد بالفعل معامل باسم \"$1\".",
        "apisandbox-deprecated-parameters": "معاملات مهملة",
+       "apisandbox-fetch-token": "املأ التوكين تلقائيا",
        "apisandbox-submit-invalid-fields-title": "بعض الحقول غير صالحة",
        "apisandbox-submit-invalid-fields-message": "برجاء تصحيح الحقل المعلم والمحاولة مرة أخرى.",
        "apisandbox-results": "النتائج",
        "apisandbox-sending-request": "إرسال طلب API ...",
        "apisandbox-loading-results": "استقبال طلبات API ...",
+       "apisandbox-results-error": "حدث خطأ أثناء تحميل رد استعدلام الAPI: $1.",
        "apisandbox-request-url-label": "مسار الطلب:",
        "apisandbox-request-time": "وقت الطلب: {{PLURAL:$1|$1 ms}}",
        "apisandbox-results-fixtoken": "رمز الصحيح وإعادة الموافقة",
+       "apisandbox-results-fixtoken-fail": "فشل جلب توكين \"$1\"",
        "apisandbox-alert-page": "هناك حقول غير صالحة في هذه الصفحة.",
        "apisandbox-alert-field": "قيمة هذا الحقل غير صالحة.",
        "booksources": "مصادر كتاب",
        "booksources-text": "توجد أدناه قائمة بوصلات لمواقع أخرى تبيع الكتب الجديدة والمستعملة، أيضا يمكنك أن تحصل على معلومات إضافية عن الكتب التي تبحث عنها من هناك:",
        "booksources-invalid-isbn": "رقم ISBN المعطى لا يبدو صحيحا؛ تحقق من أخطاء النسخ من المصدر الأصلي.",
        "specialloguserlabel": "المؤدي:",
-       "speciallogtitlelabel": "الهدف (عنوان أو مستخدم):",
+       "speciallogtitlelabel": "الهدف (عنوان أو {{ns:user}}:اسم المستخدم للمستخدم):",
        "log": "سجلات",
        "logeventslist-submit": "أظهر",
        "all-logs-page": "كل السجلات العامة",
        "listgrouprights-namespaceprotection-namespace": "النطاق",
        "listgrouprights-namespaceprotection-restrictedto": "الصلاحيات التي تسمح للمستخدم بالتعديل",
        "listgrants": "المنح",
+       "listgrants-summary": "التالي هو قائمة بالمنح بعمليات الوصول لصلاحيات المستخدم المصاحبة لها. المستخدمون يمكنهم إعطاء صلاحية للتطبيقات لاستخدام حساباتهم، ولكن بسماحات محدودة بناء على المنح التي أعطاها المستخدم للتطبيق. تطبيق يعمل بالنيابة عن مستخدم لا يمكنه استخدام الصلاحيات التي لا يمتلكها المستخدم بالفعل.\nربما تكون هناك [[{{MediaWiki:Listgrouprights-helppage}}|معلومات إضافية]] حول الصلاحيات الفردية.",
        "listgrants-grant": "المنحة",
        "listgrants-rights": "الصلاحيات",
        "trackingcategories": "تصانيف التتبع",
        "trackingcategories-msg": "تصانيف التتبع",
        "trackingcategories-name": "اسم الرسالة",
        "trackingcategories-desc": "معايير إدراج تصنيف",
+       "restricted-displaytitle-ignored": "الصفحات بعناوين عرض تم تجاهلها",
+       "restricted-displaytitle-ignored-desc": "هذه الصفحة تحتوي على <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> تم تجاهله لأنه لا يساوي عنوان الصفحة الفعلي.",
        "noindex-category-desc": "هذه الصفحة لا تفهرسها الروبوتات لأن فيها الكلمة السحرية <code><nowiki>__NOINDEX__</nowiki></code> ولأنها في نطاق يسمح بهذا العلم.",
        "index-category-desc": "الصفحة فيها <code><nowiki>__INDEX__</nowiki></code> (وهي في نطاق يسمح بهذا العلم) ولذا فالروبوتات تفهرسها بينما الأصل ألا تفعل.",
        "post-expand-template-inclusion-category-desc": "بعد توسيع جميع القوالب حجم الصفحة أكبر من <code><nowiki>__INDEX__</nowiki></code> ولذا فبعض القوالب لا تُوسّع.",
        "emailccsubject": "نسخة من رسالتك إلى $1: $2",
        "emailsent": "أُرسل البريد الإلكتروني",
        "emailsenttext": "أُرسلت رسالتك الإلكترونية.",
-       "emailuserfooter": "هذا البريد الإلكتروني تم إرساله بواسطة $1 إلى $2 بواسطة وظيفة \"{{int:emailuser}}\" في {{SITENAME}}.",
+       "emailuserfooter": "هذا البريد الإلكتروني {{GENDER:$1|تم إرساله}} بواسطة $1 إلى {{GENDER:$2|$2}} بواسطة وظيفة \"{{int:emailuser}}\" في {{SITENAME}}.",
        "usermessage-summary": "ترك رسالة نظام.",
        "usermessage-editor": "مراسل النظام",
        "watchlist": "قائمة مراقبتي",
        "watchnologin": "غير مسجل الدخول",
        "addwatch": "إضافة إلى قائمة المراقبة",
        "addedwatchtext": "\"[[:$1]]\" وصفحة نقاشها أضيفتا إلى [[Special:Watchlist|قائمة مراقبتك]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" وصفحتها المرافقة تمت إضافتها إلى [[Special:Watchlist|قائمة مراقبتك]].",
        "addedwatchtext-short": "أضيفت صفحة \"$1\" إلى قائمة مراقبتك.",
        "removewatch": "إزالة من قائمة المراقبة",
        "removedwatchtext": "\"[[:$1]]\" وصفحة نقاشها أزيلتا من [[Special:Watchlist|قائمة مراقبتك]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" وصفحتها المرافقة تمت إزالتها من [[Special:Watchlist|قائمة مراقبتك]].",
        "removedwatchtext-short": "أزيلت صفحة \"$1\" من قائمة مراقبتك.",
        "watch": "راقب",
        "watchthispage": "راقب هذه الصفحة",
        "deletepage": "حذف الصفحة",
        "confirm": "أكد",
        "excontent": "المحتوى كان: '$1'",
-       "excontentauthor": "المحتوى كان: '$1' (والمساهم الوحيد كان '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "المحتوى كان: \"$1\" والمساهم الوحيد كان \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|talk]])",
        "exbeforeblank": "المحتوى قبل الإفراغ كان: '$1'",
        "delete-confirm": "حذف \"$1\"",
        "delete-legend": "حذف",
        "delete-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقُيّد محذف مثل هذه الصفحات لمنع الاضطراب المفاجئة في {{SITENAME}}.",
        "delete-warning-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقد يؤدي حذفها إلى اضطراب عمليات قاعدة البيانات في {{SITENAME}}؛\nاستمر مع الحذر.",
        "deleteprotected": "لا يمكنك حذف هذه الصفحة لأنها محمية.",
-       "deleting-backlinks-warning": "[[Special:WhatLinksHere/{{FULLPAGENAME}}|تتصل صفحات أخرى]] بالصفحة التي تريد حذفها.",
+       "deleting-backlinks-warning": "<strong>تحذير:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|صفحات أخرى]] تصل إلى أو تضمن الصفحة التي أنت على وشك حذفها.",
        "rollback": "التراجع عن التعديلات",
        "rollbacklink": "استرجع",
        "rollbacklinkcount": "استرجع {{PLURAL:$1|لا تعديلات|تعديلا واحدا|تعديلين|$1 تعديلات|$1 تعديلاً|تعديل}}",
        "rollbacklinkcount-morethan": "استرجاع أكثر من {{PLURAL:$1|تعديل|تعديل|تعديلين|$1 تعديلات|$1 تعديلاً|$1 تعديل}}",
        "rollbackfailed": "لم ينجح الاسترجاع",
+       "rollback-missingparam": "محددات مطلوبة مفقودة عند الطلب.",
+       "rollback-missingrevision": "غير قادر على تحميل بيانات المراجعة.",
        "cantrollback": "لم يمكن استرجاع التعديل؛\nآخر مساهم هو المؤلف الوحيد لهذه الصفحة.",
        "alreadyrolled": "لم يمكن استرجاع آخر تعديل ل[[$1]] بواسطة [[User:$2|$2]] ([[User talk:$2|نقاش]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])؛\nشخص آخر عدل أو استرجع الصفحة بالفعل.\n\nآخر تعديل كان بواسطة [[User:$3|$3]] ([[User talk:$3|نقاش]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "ملخص التعديل كان:<em>$1</em>.",
        "revertpage": "استرجع تعديلات [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]]) حتى آخر مراجعة ل[[User:$1|$1]]",
        "revertpage-nouser": "استرجع تعديلات مستخدم مخفي حتى آخر مراجعة ل{{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "استرجع تعديلات $1؛\nاسترجع حتى آخر نسخة بواسطة $2.",
+       "rollback-success-notify": "تم استرجاع التعديلات بواسطة $1;\nتم التغيير إلى آخر مراجعة بواسطة $2. [$3 عرض التغييرات]",
        "sessionfailure-title": "فشل في الجلسة",
        "sessionfailure": "يبدو أنه هناك مشكلة في جلسة الدخول الخاصة بك؛\nلذلك فقد ألغيت هذه العملية كإجراء احترازي ضد الاختراق.\nمن فضلك اضغط على مفتاح \"رجوع\" لتحميل الصفحة التي جئت منها، ثم حاول مرة أخرى.",
        "changecontentmodel": "غير نموذج المحتوى لصفحة",
        "changecontentmodel-success-text": "نوع المحتوى ل[[:$1]] تم تغييره.",
        "changecontentmodel-cannot-convert": "المحتوى على [[:$1]] لا يمكن تحويله لنوع من $2.",
        "changecontentmodel-nodirectediting": "نموذج المحتوى $1 لا يدعم التعديل المباشر",
+       "changecontentmodel-emptymodels-title": "لا موديلات محتوى متوفرة",
+       "changecontentmodel-emptymodels-text": "المحتوى على[[:$1]] لا يمكن تغييره لأي نوع.",
        "log-name-contentmodel": "سجل تغيير نموذج المحتوى",
        "log-description-contentmodel": "الأحداث المرتبطة بنماذج المحتوى لصفحة",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|أنشأ|أنشأت}} الصفحة $3 باستخدام موديل محتوى غير قياسي \"$5\"",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|غير|غيرت}} موديل المحتوى للصفحة $3 من \"$4\" إلى \"$5\"",
        "logentry-contentmodel-change-revertlink": "استرجع",
        "logentry-contentmodel-change-revert": "استرجع",
        "protectlogpage": "سجل الحماية",
        "undeletehistorynoadmin": "هذه الصفحة تم حذفها.\nالسبب للحذف معروض في الملخص بالأسفل، إلى جانب تفاصيل المستخدمين الذين قاموا بالتعديل على هذه الصفحة قبل حذفها.\nنص المراجعات المحذوفة هذه متوفر فقط للإداريين.",
        "undelete-revision": "المراجعة المحذوفة ل$1 (بتاريخ $4، الساعة $5) بواسطة $3:",
        "undeleterevision-missing": "مراجعة غير صحيحة أو مفقودة.\nربما لديك وصلة سيئة، أو ربما المراجعة تم استرجاعها أو إزالتها من الأرشيف.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|مراجعة واحدة|$1 مراجعة}} لم يمكن استعادتها, لأن ال<code>rev_id</code> {{PLURAL:$1|الخاص بها}}  كان مستخدما بالفعل.",
        "undelete-nodiff": "لم يتم العثور على مراجعة سابقة.",
        "undeletebtn": "استرجاع",
        "undeletelink": "اعرض/استعد",
        "undeletedrevisions": "تم استرجاع {{PLURAL:$1||تعديل واحد|تعديلين|$1 تعديلات|$1 تعديلا|$1 تعديل}}",
        "undeletedrevisions-files": "أسترجعت {{PLURAL:$1||مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}}  و{{PLURAL:$2||ملف واحد|ملفان|$2 ملفات|$2 ملفًا|$2 ملف}}",
        "undeletedfiles": "أسترجع {{PLURAL:$1||ملف واحد|ملفان|$1 ملفات|$1 ملفًا|$1 ملف}}",
-       "cannotundelete": "فشل الاسترجاع؛\n$1",
+       "cannotundelete": "بعض أو كل عملية الاسترجاع فشلت:\n$1",
        "undeletedpage": "'''تم استرجاع $1'''\n\nراجع [[Special:Log/delete|سجل الحذف]] لمعاينة عمليات الحذف والاسترجاعات الحديثة.",
        "undelete-header": "انظر الصفحات المحذوفة حديثا في [[Special:Log/delete|سجل الحذف]].",
        "undelete-search-title": "البحث في الصفحات المحذوفة",
        "sp-contributions-newbies-sub": "للحسابات الجديدة",
        "sp-contributions-newbies-title": "مساهمات المستخدم للحسابات الجديدة",
        "sp-contributions-blocklog": "سجل المنع",
-       "sp-contributions-suppresslog": "مساهمات المستخدم المحذوفة",
-       "sp-contributions-deleted": "مساهمات المستخدم المحذوفة",
+       "sp-contributions-suppresslog": "مساهمات {{GENDER:$1|المستخدم|المستخدمة}} المخفية",
+       "sp-contributions-deleted": "مساهمات {{GENDER:$1|المستخدم|المستخدمة}} المحذوفة",
        "sp-contributions-uploads": "مرفوعات",
        "sp-contributions-logs": "سجلات",
        "sp-contributions-talk": "نقاش",
        "ipbexpiry": "مدة المنع:",
        "ipbreason": "السبب:",
        "ipbreason-dropdown": "*أسباب المنع الشائعة\n** كتابة معلومات زائفة\n** إزالة المحتوى من الصفحات\n** سبام وصلات لمواقع خارجية\n** كتابة كلام لا معنى له في الصفحات\n** سلوك عدواني\n** إساءة استخدام حسابات متعددة\n** اسم مستخدم غير مقبول",
-       "ipb-hardblock": "اÙ\85Ù\86ع Ø§Ù\84Ù\85ستخدÙ\85Ù\8aÙ\86 Ø§Ù\84Ù\88اÙ\84جÙ\8aÙ\86 Ù\85Ù\86 Ø§Ù\84تعدÙ\8aÙ\84 Ø¨Ø¹Ù\86Ù\88اÙ\86 Ø§Ù\84Ø¢يبي هذا",
+       "ipb-hardblock": "اÙ\85Ù\86ع Ø§Ù\84Ù\85ستخدÙ\85Ù\8aÙ\86 Ø§Ù\84Ù\88اÙ\84جÙ\8aÙ\86 Ù\85Ù\86 Ø§Ù\84تعدÙ\8aÙ\84 Ø¨Ø¹Ù\86Ù\88اÙ\86 Ø§Ù\84Ø£يبي هذا",
        "ipbcreateaccount": "امنع إنشاء الحسابات",
        "ipbemailban": "امنع المستخدم من إرسال بريد إلكتروني",
        "ipbenableautoblock": "تلقائيا امنع آخر عنوان أيبي تم استعماله بواسطة هذا المستخدم، وأي عناوين أيبي أخرى يحاول التحرير من خلالها",
        "move-page-legend": "نقل صفحة",
        "movepagetext": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك أن تترك التحويلات التي تشير إلى العنوان الأصلي كما هي لتقوم البوتات بتحديثها تلقائياً.\nإذا اخترت أن تقوم بالتحديث يدوياً، فتأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]] وقم بتصحيحها.\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه <strong>لن يتم</strong> نقل الصفحة إذا وجدت صفحة في العنوان الجديد، إلا إذا كانت صفحة تحويل، ولا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، ولا يمكنك نسخ هذه الصفحة فوق صفحة موجودة.\n\n<strong>ملاحظة:</strong>\n\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
        "movepagetext-noredirectfixer": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك تحديث التحويلات التي تشير إلى العنوان الأصلي تلقائياً.\nلو اخترت ألا تفعل، تأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]].\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه <strong>لن يتم</strong>  نقل الصفحة إذا كان هناك صفحة بنفس العنوان الجديد، إلا إذا كانت فارغة، أو تحويلة لا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، وأنك لا يمكنك الكتابة على صفحة موجودة.\n\n<strong>ملاحظة</strong> \n\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
-       "movepagetalktext": "صفحة النقاش المرفقة سيتم نقلها كذلك، '''إلا في حالة''':\n* توجد صفحة نقاش غير فارغة تحت العنوان الجديد، أو\n* قمت بإزالة اختيار الصندوق بالأسفل.\n\nوفي هذه الحالات، يجب عليك نقل أو دمج محتويات الصفحة يدويا، إذا رغب في ذلك.",
+       "movepagetalktext": "لو علمت على هذا الصندوق، فصفحة النقاش المرفقة يتم نقلها أوتوماتيكيا للعنوان الجديد، إلا لو كانت صفحة نقاش غير فارغة هناك بالفعل.\n\nفي هذه الحالة، فسيتعين عليك نقل أو دمج الصفحة يدويا لو رغبت في ذلك.",
        "moveuserpage-warning": "'''تحذير: أنت على وشك نقل صفحة مستخدم. من فضلك لاحظ أن الصفحة وحدها سوف تنقل وأن المستخدم لن يعاد تسميته.'''",
        "movecategorypage-warning": "<strong>تحذير:</strong> أنت على وشك نقل صفحة التصنيف إلى عنوان جديد؛ <em>لن</em> تنقل الصفحات المندرجة تحت التصنيف إلى العنوان الجديد.",
        "movenologintext": "يجب أن تكون مستخدماً مسجلاً وأن  [[Special:UserLogin|تسجل دخولك]] لكي تنقل صفحة.",
        "movenosubpage": "ليس لهذه الصفحة صفحات فرعية.",
        "movereason": "السبب:",
        "revertmove": "استرجع",
-       "delete_and_move_text": "==الحذف مطلوب==\nالصفحة الهدف \"[[:$1]]\" موجودة بالفعل.\nهل تريد حذفها لإفساح المجال للنقل؟",
+       "delete_and_move_text": "الصفحة الهدف \"[[:$1]]\" موجودة بالفعل.\nهل تريد حذفها لإفساح المجال للنقل؟",
        "delete_and_move_confirm": "نعم، احذف الصفحة",
        "delete_and_move_reason": "حُذِفت لإفساح مجال لنقل \"[[$1]]\"",
        "selfmove": "لا يوجد اختلاف في عنوان المصدر والهدف؛\nلا يمكن نقل الصفحة على نفسها.",
        "move-leave-redirect": "اترك تحويلة خلفك",
        "protectedpagemovewarning": "'''تحذير:''' هذه الصفحة قد تم حمايتها، فقط المستخدمون الذين يمتلكون امتيازات الإدارة يمكنهم نقلها.\nآخر مدخلة سجل موفرة بالأسفل كمرجع:",
        "semiprotectedpagemovewarning": "'''ملاحظة:''' هذه الصفحة تمت حمايتها ليتمكن المستخدمون المسجلون وحدهم من نقلها.\nآخر مدخلة سجل موفرة بالأسفل كمرجع:",
-       "move-over-sharedrepo": "== الملف موجود ==\n[[:$1]] موجود في مستودع مشترك. نقل الملف إلى هذا العنوان سوف يلغي الملف المشترك.",
+       "move-over-sharedrepo": "[[:$1]] موجود في مستودع مشترك. نقل الملف إلى هذا العنوان سوف يلغي الملف المشترك.",
        "file-exists-sharedrepo": "اسم الملف الذي اخترته مستخدم من قبل في مستودع مشترك.\nمن فضلك اختر اسماً آخر.",
        "export": "تصدير صفحات",
        "exporttext": "يمكنك تصدير النص وتاريخ تعديلات صفحة أو مجموعة صفحات في صيغة XML.\nهذا يمكن استيراده إلى ويكي آخر يستعمل ميدياويكي بواسطة [[Special:Import|صفحة الاستيراد]].\n\nلتصدير الصفحات، أدخل عناوينها في الصندوق أسفله، عنواناً واحداً في كل سطر، مع اختيار ما إذا كنت ترغب بتصدير النسخة الحالية مع جميع النسخ القديمة، أي مع كامل معلومات تاريخ الصفحة، أو فقط النسخة الحالية مع معلومات عن التعديل الأخير.\n\nفي الحالة الأخيرة يمكنك أيضاً استخدام وصلة، على سبيل المثال [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] للصفحة «[[{{MediaWiki:Mainpage}}]]».",
        "import-nonewrevisions": "لا مراجعات تم استيرادها (كل المراجعات إما أنها كانت موجودة بالفعل، وأو تم تجاوزها نتيجة أخطاء).",
        "xml-error-string": "$1 عند السطر $2، العمود $3 (بايت $4): $5",
        "import-upload": "رفع بيانات XML",
-       "import-token-mismatch": "فقد لبيانات الجلسة. من فضلك حاول مرة أخرى.",
+       "import-token-mismatch": "فقد لبيانات الجلسة.\n\nأنت لابد من أنه قد تم تسجيل خروجك. <strong>من فضلك تأكد من أنك مازلت مسجل الدخول وحاول مرة أخرى</strong>.\nلو كان مازال لا يعمل، فحاول [[Special:UserLogout|تسجيل الخروج]] وتسجيل الدخول مرة أخرى، وتحقق من أن متصفحك يسمح بالكوكيز من هذا الموقع.",
        "import-invalid-interwiki": "لم يمكن الاستيراد من الويكي المحدد.",
        "import-error-edit": "الصفحة \"$1\" لم يتم استيرادها لأنه لا يمكن لك تحريرها.",
        "import-error-create": "الصفحة \"$1\" لم يتم استيرادها لأنه لا يمكن لك استحداثها أصلا.",
        "tooltip-ca-nstab-category": "رؤية صفحة التصنيف",
        "tooltip-minoredit": "علم على هذا كتعديل طفيف",
        "tooltip-save": "احفظ تغييراتك",
+       "tooltip-publish": "انشر تغييراتك",
        "tooltip-preview": "اعرض تغييراتك، من فضلك استخدم هذا قبل الحفظ!",
        "tooltip-diff": "اعرض التغييرات التي قمت بها للنص.",
        "tooltip-compareselectedversions": "شاهد الفروق بين النسختين المختارتين من هذه الصفحة.",
        "print.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على ناتج الطباعة */",
        "noscript.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على المستخدمين الذين الجافاسكريبت لديهم معطلة */",
        "group-autoconfirmed.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على المستخدمين المؤكدين تلقائيا فقط */",
+       "group-user.css": "/* CSS المعروض هنا سيؤثر على المستخدمين المسجلين فقط */",
        "group-bot.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على البوتات فقط */",
        "group-sysop.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على الإداريين فقط */",
        "group-bureaucrat.css": "/* الأنماط المتراصة CSS المعروضة هنا ستؤثر على البيروقراطيين فقط */",
        "common.js": "/* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */",
        "group-autoconfirmed.js": "/* أي جافاسكريبت هنا سيتم تحميلها للمستخدمين المؤكدين تلقائيا فقط */",
+       "group-user.js": "/* أي JavaScript هنا سيتم تحميله للمستخدمين المسجلين فقط */",
        "group-bot.js": "/* أي جافاسكريبت هنا سيتم تحميلها للبوتات فقط */",
        "group-sysop.js": "/* أي جافاسكريبت هنا سيتم تحميلها للإداريين فقط */",
        "group-bureaucrat.js": "/* أي جافاسكريبت هنا سيتم تحميلها للبيروقراطيين فقط */",
        "pageinfo-article-id": "معرف الصفحة (ID)",
        "pageinfo-language": "لغة محتوى الصفحة",
        "pageinfo-content-model": "نموذج محتوى الصفحة",
+       "pageinfo-content-model-change": "تغيير",
        "pageinfo-robot-policy": "فهرسة الروبوتات",
        "pageinfo-robot-index": "مسموح بها",
        "pageinfo-robot-noindex": "غير مسموح بها",
        "hours-ago": "منذ {{PLURAL:$1|ساعة|$1 ساعات}}",
        "minutes-ago": "منذ {{PLURAL:$1|دقيقة|$1 دقائق}}",
        "seconds-ago": "منذ {{PLURAL:$1|ثانية|$1 ثوان}}",
-       "monday-at": "Ù\8aÙ\88Ù\85 Ø§Ù\84اثنين الساعة $1",
+       "monday-at": "Ù\8aÙ\88Ù\85 Ø§Ù\84Ø¥ثنين الساعة $1",
        "tuesday-at": "يوم الثلاثاء الساعة $1",
        "wednesday-at": "يوم الأربعاء الساعة $1",
        "thursday-at": "يوم الخميس الساعة $1",
        "exif-artist": "المؤلف",
        "exif-copyright": "مالك الحقوق",
        "exif-exifversion": "نسخة Exif",
-       "exif-flashpixversion": "نسخة فلاش بكس المدعومة",
+       "exif-flashpixversion": "نسخة Flashpix المدعومة",
        "exif-colorspace": "فضاء الألوان",
        "exif-componentsconfiguration": "معنى كل مكونة",
        "exif-compressedbitsperpixel": "طور ضغط الصورة",
        "exif-lens": "العدسة المستخدمة",
        "exif-serialnumber": "الرقم التسلسلي للكاميرا",
        "exif-cameraownername": "مالك الكاميرا",
-       "exif-label": "عÙ\84اÙ\85ة",
+       "exif-label": "اÙ\84تسÙ\85Ù\8aة",
        "exif-datetimemetadata": "آخر تعديل للبيانات التعريفية",
        "exif-nickname": "الاسم غير الرسمي للصورة",
        "exif-rating": "التقييم (من 5)",
        "exif-compression-34712": "جيه بي إي جي2000",
        "exif-copyrighted-true": "محفوظ الحقوق",
        "exif-copyrighted-false": "حالة حقوق النشر غير مُعرّفة",
+       "exif-photometricinterpretation-0": "أسود وأبيض (الأبيض هو 0)",
        "exif-photometricinterpretation-1": "أسود وأبيض (الأسود 0)",
        "exif-photometricinterpretation-2": "آر جي بي",
+       "exif-photometricinterpretation-3": "لوح الألوان",
+       "exif-photometricinterpretation-4": "قناع الشفافية",
+       "exif-photometricinterpretation-5": "مفصول (ربما CMYK)",
        "exif-photometricinterpretation-6": "واي سب سر",
+       "exif-photometricinterpretation-32803": "مصفوفة فلترة الألوان",
+       "exif-photometricinterpretation-34892": "خام خطي",
        "exif-unknowndate": "تاريخ غير معروف",
        "exif-orientation-1": "عادي",
        "exif-orientation-2": "مقلوبة أفقياً",
        "exif-iimcategory-edu": "تعليم",
        "exif-iimcategory-evn": "بيئة",
        "exif-iimcategory-hth": "صحة",
-       "exif-iimcategory-hum": "اهتمام البشرية",
+       "exif-iimcategory-hum": "اهتمام البشرية",
        "exif-iimcategory-lab": "عمل",
        "exif-iimcategory-lif": "أسلوب الحياة وأوقات الفراغ",
        "exif-iimcategory-pol": "سياسة",
        "confirmemail_body_set": "شخص ما -من المحتمل أن يكون أنت- من عنوان الأيبي $1 غيّر عنوان البريد\nالإلكتروني للحساب \"$2\" إلى عنوان البريد الإلكتروني هذا في\n{{SITENAME}}.\n\nلتأكيد أن هذا الحساب لك فعلا ولتنشيط خواص البريد الإلكتروني في\n{{SITENAME}}، افتح هذه الوصلة في متصفحك:\n\n$3\n\nإذا كان هذا الحساب *ليس* لك، اتبع هذه الوصلة لإلغاء تأكيد عنوان البريد\nالإلكتروني:\n\n$5\n\nسينتهي رمز التفعيل هذا في $4.",
        "confirmemail_invalidated": "تأكيد عنوان البريد الإلكتروني تم إلغاؤه",
        "invalidateemail": "إلغاء تأكيد البريد الإلكتروني",
+       "notificationemail_subject_changed": "عنوان البريد الإلكتروني المسجل ل{{SITENAME}} تم تغييره",
+       "notificationemail_subject_removed": "عنوان البريد الإلكتروني المسجل ل{{SITENAME}} تمت إزالته",
+       "notificationemail_body_changed": "شخص ما، غالبا أنت،  من عنوان الأيبي $1،\nقام بتغيير عنوان البريد الإلكتروني للحساب \"$2\" إلى \"$3\" في {{SITENAME}}.\n\nلو أن هذا لم يكن أنت، فاتصل بإداري للموقع حالا.",
+       "notificationemail_body_removed": "شخص ما، غالبا أنت،  من عنوان الأيبي $1،\nقام بإزالة عنوان البريد الإلكتروني للحساب \"$2\" في {{SITENAME}}.\n\nلو أن هذا لم يكن أنت، فاتصل بإداري للموقع حالا.",
        "scarytranscludedisabled": "[التضمين بالإنترويكي معطل]",
        "scarytranscludefailed": "[البحث عن القالب فشل ل$1]",
        "scarytranscludefailed-httpstatus": "[فشل جلب القالب لـ $1: HTTP $2]",
        "scarytranscludetoolong": "[المسار طويل للغاية]",
        "deletedwhileediting": "'''تحذير''': هذه الصفحة تم حذفها بعد أن بدأت أنت بتعديلها!",
-       "confirmrecreate": "حذف المستخدم [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها للسبب التالي:\n:''$2''\nالرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
-       "confirmrecreate-noreason": "حذف المستخدم [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها. الرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
+       "confirmrecreate": "{{GENDER:$1|حذف المستخدم|حذفت المستخدمة}} [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها للسبب التالي:\n: <em>$2</em>\nالرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
+       "confirmrecreate-noreason": "{{GENDER:$1|حذف المستخدم|حذفت المستخدمة}} [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها. الرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
        "recreate": "إعادة إنشاء",
        "unit-pixel": "بك",
        "confirm_purge_button": "موافق",
        "confirm-unwatch-button": "موافق",
        "confirm-unwatch-top": "إزالة هذه الصفحة من قائمة مراقبتك؟",
        "confirm-rollback-button": "موافق",
+       "confirm-rollback-top": "استرجاع التعديلات لهذه الصفحة؟",
        "semicolon-separator": "؛&#32;",
        "comma-separator": "،&#32;",
        "quotation-marks": "«$1»",
        "autoredircomment": "تحويل إلى [[$1]]",
        "autosumm-new": "أنشأ الصفحة ب'$1'",
        "autosumm-newblank": "أنشأ صفحة فارغة",
-       "size-bytes": "$1 بايت",
+       "size-bytes": "$1 {{PLURAL:$1|بايت}}",
        "size-kilobytes": "$1 كيلوبايت",
        "size-megabytes": "$1 ميجابايت",
        "size-gigabytes": "$1 جيجابايت",
        "size-exabytes": "$1 إكسابايت",
        "size-zetabytes": "$1 زيتابايت",
        "size-yottabytes": "$1 يوتابايت",
+       "size-pixel": "$1 {{PLURAL:$1|بكسل}}",
        "bitrate-bits": "$1بيت لكل ثانية",
        "bitrate-kilobits": "$1كيلوبيت لكل ثانية",
        "bitrate-megabits": "$1ميجابيت لكل ثانية",
        "timezone-local": "محلي",
        "duplicate-defaultsort": "'''تحذير:''' مفتاح الترتيب الافتراضي \"$2\" يتجاوز مفتاح الترتيب الافتراضي السابق \"$1\".",
        "duplicate-displaytitle": "<strong>تحذير:</strong> أعرض عنوان \"$2\" تجاهل العنوان المعروض سابقا \"$1\".",
+       "restricted-displaytitle": "<strong>تحذير:</strong> عنوان العرض \"$1\" تم تجاهله بما أنه لا يساوي عنوان الصفحة الفعلي.",
        "invalid-indicator-name": "<strong>خطأ:</strong> لا يجوز أن تبقى خاصية <code>name</code> لمؤشرات وضع الصفحة فارغةً.",
        "version": "نسخة",
        "version-extensions": "الامتدادات المثبتة",
        "version-libraries-description": "الوصف",
        "version-libraries-authors": "المؤلفون",
        "redirect": "تحويل حسب الملف أو المستخدم أو الصفحة أو معرف الدخول",
-       "redirect-summary": "هذه الصفحة الخاصة تحوّل إلى ملف (باسمه) أو صفحة (برقم إحدى مراجعاتها) أو إلى صفحة مستخدم (برقمه التعريفي). الاستخدام [[{{#Special:Redirect}}/file/Example.jpg]] أو [[{{#Special:Redirect}}/revision/328429]] أو [[{{#Special:Redirect}}/user/101]].",
-       "redirect-submit": "Ø­Ù\88Ù\91Ù\84",
+       "redirect-summary": "هذه الصفحة الخاصة تحوّل إلى ملف (باسمه) أو صفحة (برقم إحدى مراجعاتها) أو إلى صفحة مستخدم (برقمه التعريفي) أو إلى مدخلة سجل (برقم السجل). الاستخدام [[{{#Special:Redirect}}/file/Example.jpg]] أو [[{{#Special:Redirect}}/revision/328429]] أو [[{{#Special:Redirect}}/user/101]] أو [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-submit": "اذÙ\87ب",
        "redirect-lookup": "ابحث في:",
        "redirect-value": "الوجهة",
        "redirect-user": "رقم مستخدم",
        "tag-filter": "مرشح [[Special:Tags|الوسوم]]:",
        "tag-filter-submit": "مرشح",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1||وسم|وسمان|وسوم}}]]: $2)",
+       "tag-mw-contentmodelchange": "تغيير موديل المحتوى",
+       "tag-mw-contentmodelchange-description": "التعديلات التي [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel تغير موديل المحتوى] لصفحة",
        "tags-title": "وسوم",
        "tags-intro": "هذه الصفحة تعرض الوسوم التي ربما يعلم البرنامج تعديلا بها، ومعانيها.",
        "tags-tag": "اسم الوسم",
        "tags-actions-header": "إجراءات",
        "tags-active-yes": "نعم",
        "tags-active-no": "لا",
-       "tags-source-extension": "Ù\8aعرÙ\81Ù\87 Ø§Ù\85تداد",
+       "tags-source-extension": "Ù\85عرÙ\81 Ø¨Ù\88اسطة Ø§Ù\84برÙ\86اÙ\85ج",
        "tags-source-manual": "تم تطبيقه يدويا بواسطة المستخدمين والبوتات.",
        "tags-source-none": "لم يعد قيد الاستخدام",
        "tags-edit": "عدل",
        "tags-create-reason": "السبب:",
        "tags-create-submit": "أنشئ",
        "tags-create-no-name": "عليك أن تحدد اسم الوسم.",
+       "tags-create-invalid-chars": "أسماء الوسوم ينبغي ألا تحتوي على فواصل (<code>,</code>) أو forward slashes (<code>/</code>).",
        "tags-create-invalid-title-chars": "يجب أن لا تحتوي أسماء العلامات الأحرف التي لا يمكن استخدامها في عناوين الصفحات.",
        "tags-create-already-exists": "الوسم \"$1\" موجود بالفعل.",
+       "tags-create-warnings-above": "{{PLURAL:$2|التحذير التالي حدث|التحذيرات التالية حدثت}} عند محاولة إنشاء الوسم \"$1\":",
        "tags-create-warnings-below": "هل تود متابعة إنشاء الوسم؟",
        "tags-delete-title": "احذف الوسم",
        "tags-delete-explanation-initial": "أنت على وشك حذف الوسم \"$1\" من قاعدة البيانات.",
+       "tags-delete-explanation-in-use": "ستتم إزالته من {{PLURAL:$2|$2 مراجعة او مدخلة سجل|كل $2 مراجعات و/أو مدخلات سجل}} الذي هو مطبق عليهم حاليا.",
+       "tags-delete-explanation-warning": "هذا الفعل <strong>غير رجعي</strong> و <strong>لا يمكن التراجع عنه</strong>، ولا حتى بواسطة إداريي قاعدة البيانات. كن متاكدا من أن هذا هو الوسم الذي تريد حذفه.",
+       "tags-delete-explanation-active": "<strong>الوسم \"$1\" مازال نشطا، وسيتم الاستمرار في تطبيقه في المستقبل.</strong> لمنع هذا من الحدوث، اذهب إلى المكان(الأمكنة) حيث الوسم مضبوط ليتم تطبيقه، وعطله هناك.",
        "tags-delete-reason": "سبب:",
-       "tags-delete-submit": "إزاÙ\84Ø© هذا الوسم دون رجعة (ستدمر بعض البيانات)",
+       "tags-delete-submit": "حذÙ\81 هذا الوسم دون رجعة (ستدمر بعض البيانات)",
        "tags-delete-not-allowed": "الوسوم التي يعرفها امتداد لا يمكن حذفها إلا إذا أتاح الامتداد ذلك.",
        "tags-delete-not-found": "الوسم \"$1\" غير موجود.",
+       "tags-delete-too-many-uses": "الوسم \"$1\" مطبق في أكثر من $2 {{PLURAL:$2|مراجعة|مراجعات}}، مما يعني أنه لا يمكن حذفه.",
+       "tags-delete-warnings-after-delete": "الوسم \"$1\" تم حذفه، لكن {{PLURAL:$2|التحذير التالي حدث|التحذيرات التالية حدثت}}:",
+       "tags-delete-no-permission": "أنت لا تمتلك الصلاحية لحذف وسوم التغيير.",
        "tags-activate-title": "نشط الوسم",
        "tags-activate-question": "أنت على وشك تنشيط الوسم \"$1\".",
        "tags-activate-reason": "السبب:",
        "tags-deactivate-submit": "عطل",
        "tags-apply-no-permission": "ليس لديك إذن لتطبيق علامات التغيير جنبا إلى جنب مع التغييرات.",
        "tags-apply-blocked": "لا يمكنك تطبيق علامات التغيير جنبا إلى جنب مع التغييرات في حين منعت.",
-       "tags-apply-not-allowed-one": "السوم \"$1\" غير مسموح أن يتم تطبيقه يدويا.",
+       "tags-apply-not-allowed-one": "الوسم \"$1\" غير مسموح أن يتم تطبيقه يدويا.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|الوسم|الوسوم}} التالية غير مسموح أن يتم تطبيقها يدويا: $1",
        "tags-update-no-permission": "أنت لا تمتلك السماح لإضافة أو إزالة وسوم التغيير من المراجعات أو مدخلات السجل الفردية.",
        "tags-update-blocked": "لا يمكنك إضافة أو إزالة العلامات التغيير بينماهي محظورة.",
        "tags-edit-revision-selected": "{{PLURAL:$1|مراجعة مختارة|مراجعات مختارة}} من [[:$2]]:",
        "tags-edit-logentry-selected": "{{PLURAL:$1|حدث سجل مختار|أحداث سجل مختارة}}:",
        "tags-edit-revision-legend": "أضف أو أزل الوسوم من {{PLURAL:$1|هذه المراجعة|كل $1 المراجعات}}",
+       "tags-edit-logentry-legend": "أضف أو أزل الوسوم من {{PLURAL:$1|مدخلة السجل هذه|كل مدخلات السجل ال$1}}",
        "tags-edit-existing-tags": "الوسوم الموجودة:",
        "tags-edit-existing-tags-none": "</em>لا وسوم</em>",
        "tags-edit-new-tags": "وسوم جديدة:",
        "logentry-block-unblock": "$1 {{GENDER:$2|رفع منع}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": " {{GENDER:$2|غير|غيرت}} $1 إعدادات المنع ل{{GENDER:$4|$3}} بتاريخ انتهاء $5 $6",
        "logentry-suppress-block": "{{GENDER:$2|منع|منعت}} $1 {{GENDER:$4|$3}} لفترة زمنية مدتها $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|غير|غيرت}} إعدادات المنع ل{{GENDER:$4|$3}} بتاريخ انتهاء $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|استورد}} $3 بواسطة رفع ملف",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|استورد|استوردت}} $3 بواسطة رفع الملف ($4 {{PLURAL:$4|مراجعة|مراجعات}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|استورد|استوردت}} $3 من ويكي أخرى",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|استورد|استوردت}} $3 من $5 ($4 {{PLURAL:$4|مراجعة|مراجعات}})",
        "logentry-merge-merge": "{{GENDER:$2|دمج|دمجت}} $1 $3 إلى $4 (المراجعات حتى $5).",
        "logentry-move-move": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4",
        "logentry-move-move-noredirect": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4 دون ترك تحويلة",
        "logentry-protect-protect": "$1 {{GENDER:$2|حمى|حمت}} $3 $4",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|حمى|حمت}} $3 $4 [مضمنة]",
        "logentry-protect-modify": "{{GENDER:$2|غير|غيرت}} $1 مستوى حماية $3 $4",
-       "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3 من $4 إلى $5",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|غير|غيرت}} مستوى الحماية ل$3 $4 [مضمن]",
+       "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية {{GENDER:$6|$3}} من $4 إلى $5",
        "logentry-rights-rights-legacy": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3",
        "logentry-rights-autopromote": "تمت تلقائيا ترقية {{GENDER:$2|المستخدم|المستخدمة}} $1 من  $4 إلى $5",
        "logentry-upload-upload": " {{GENDER:$2|رفع|رفعت}} $1 $3",
        "logentry-upload-overwrite": "{{GENDER:$2|رفع|رفعت}} $1 نسخة جديدة من  $3",
        "logentry-upload-revert": "{{GENDER:$2|رفع|رفعت}} $1 $3",
        "log-name-managetags": "سجل إدارة الوسوم",
+       "log-description-managetags": "هذه الصفحة تعرض مهام الإدارة المرتعلقة ب[[Special:Tags|الوسوم]]. السجل يحتوي فقط على الافعال التي تم عملها يدويا بواسطة إداري؛ الوسوم ربما يتم إنشاؤها او حذفها بواسطة برنامج الويكي بدون تسجيل مدخلة في هذا السجل.",
        "logentry-managetags-create": "$1 {{GENDER:$2|أنشأ|أنشأت}} الوسم \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|حذف|حذفت}} الوسم \"$4\" (أزيل من $5 {{PLURAL:$5|مراجعة أو مدخلة سجل|مراجعات و/أو مدخلات سجل}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|فعل|فعلت}} الوسم \"$4\" للاستخدام بواسطة البوتات والمستخدمين",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|ألغى تفعيل|ألغت تفعيل}} الوسم \"$4\" للاستخدام بواسطة البوتات والمستخدمين",
        "log-name-tag": "سجل الوسوم",
+       "log-description-tag": "هذه الصفحة تعرض متى قام المستخدمون بإضافة أو إزالة [[Special:Tags|الوسوم]] من المراجعات أو مدخلات السجل الفردية. السجل لا يعرض أفعال الوسم عندما يحدثوا كجزء من تعديل، حذف، أو فعل مماثل.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|أضاف|أضافت}} {{PLURAL:$7|الوسم|الوسوم}} $6 للمراجعة $4 للصفحة $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|أضاف|أضافت}} {{PLURAL:$7|الوسم|الوسوم}} $6 لمدخلة السجل $5 للصفحة $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|أزال|أزالت}} {{PLURAL:$9|الوسم|الوسوم}} $8 من المراجعة $4 للصفحة $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|أزال|أزالت}} {{PLURAL:$9|الوسم|الوسوم}} $8 من مدخلة السجل $5 للصفحة $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|حدث|حدثت}} الوسوم للمراجعة $4 للصفحة $3 ({{PLURAL:$7|أضاف}} $6; {{PLURAL:$9|أزال}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|حدث|حدثت}} الوسوم على مدخلة السجل $5 للصفحة $3 ({{PLURAL:$7|أضاف}} $6; {{PLURAL:$9|أزال}} $8)",
        "rightsnone": "(لا شيء)",
        "revdelete-summary": "ملخص التعديل",
        "feedback-adding": "إضافة تعليقات إلى الصفحة...",
        "feedback-close": "تم",
        "feedback-external-bug-report-button": "أرسل تقرير علة تقنية",
        "feedback-dialog-title": "أرسل تغذية راجعة",
+       "feedback-dialog-intro": "أنت يمكنك استخدام الاستمارة السهلة بالأسفل لإرسال تعليقك. تعليقك ستتم إضافته للصفحة \"$1\"، مع اسم المستخدم الخاص بك.",
        "feedback-error-title": "خطأ",
        "feedback-error1": "خطأ: لا يمكن التعرف عليها من API",
        "feedback-error2": "خطأ: فشل في تحرير",
        "feedback-message": "الرسالة:",
        "feedback-subject": "الموضوع:",
        "feedback-submit": "إرسال",
+       "feedback-terms": "أنا أفهم أن معلومات وكيل المستخدم الخاص بي تشمل معلومات حول نسخة متصفحي ونظام التشغيل الخاص بي بالضبط وستتم مشاركتها علنيا بجانب تعليقي.",
+       "feedback-termsofuse": "أنا أوافق على توفير تعليقات بالتوافق مع شروط الاستخدام.",
        "feedback-thanks": "شكرا! أُرسلت ملاحظاتك لصفحة \"[$2 $1]\".",
        "feedback-thanks-title": "شكرا لك!",
        "feedback-useragent": "وكيل المستخدم:",
        "searchsuggest-search": "بحث",
        "searchsuggest-containing": "يحتوي...",
+       "api-error-autoblocked": "عنوان الأيبي الخاص بك تم منعه تلقائيا، لأنه تم استخدامه بواسطة مستخدم ممنوع",
        "api-error-badaccess-groups": "لا يسمح لك بتحميل الملفات إلى هذه الويكي.",
        "api-error-badtoken": "خطأ داخلي: رمز مميز غير صحيح.",
        "api-error-blocked": "لقد منعت من التحرير.",
        "api-error-copyuploaddisabled": "تم تعطيل تحميل من رابط على هذا الخادم.",
-       "api-error-duplicate": "Ù\87Ù\86اÙ\83 {{PLURAL:$1|Ù\87Ù\88 Ù\85Ù\84Ù\81 Ø¢Ø®Ø±|Ù\83Ø°Ù\84Ù\83$2 Ø¨Ø¹Ø¶ Ø§Ù\84Ù\85Ù\84Ù\81ات Ø§Ù\84أخرÙ\89}} Ù\85سبÙ\82اÙ\8b Ø¹Ù\84Ù\89 Ø§Ù\84Ù\85Ù\88Ù\82ع Ø¨Ù\86Ù\81س Ø§Ù\84Ù\85ضÙ\85Ù\88Ù\86.",
+       "api-error-duplicate": "Ù\87Ù\86اÙ\83 {{PLURAL:$1|Ù\85Ù\84Ù\81 Ø¢Ø®Ø±|بعض Ø§Ù\84Ù\85Ù\84Ù\81ات Ø§Ù\84أخرÙ\89}} Ù\85سبÙ\82ا Ø¹Ù\84Ù\89 Ø§Ù\84Ù\85Ù\88Ù\82ع Ø¨Ù\86Ù\81س Ø§Ù\84Ù\85حتÙ\88Ù\89.",
        "api-error-duplicate-archive": "هناك {{PLURAL:$1|كان ملف آخر |كذلك بعض الملفات الأخرى}} مسبقاً على الموقع بنفس المضمون، ولكن {{PLURAL:$1|أنه تم | إجراء}} الحذف لها.",
        "api-error-empty-file": "كان ملف الذي قمت بإرسال فارغة.",
        "api-error-emptypage": "إنشاء صفحات فارغة جديدة، غير مسموح به.",
        "api-error-filetype-banned-type": "$1 {{PLURAL:$4|ليس نوع ملف مسموح به|ليست أنواع ملفات مسموح بها}}. {{PLURAL:$3|نوع الملف المسموح به هو|أنواع الملفات المسموح بها هي}} $2.",
        "api-error-filetype-missing": "يفتقد الملفّ ملحق نوعيّته.",
        "api-error-hookaborted": "التعديل الذي تحاول أن تقوم به تم إحباطه",
-       "api-error-http": "خطأ Ø¯Ø§Ø®Ù\84Ù\8a: ØªØ¹Ø°Ø± Ø§Ù\84اتصاÙ\84 Ø¨Ø§Ù\84خادÙ\88Ù\85.",
+       "api-error-http": "خطأ داخلي: تعذر الاتصال بالخادم.",
        "api-error-illegal-filename": "اسم الملف غير مسموح به.",
        "api-error-internal-error": "خطأ داخلي: حدث خطأ عند معالجة التحميل الخاص بك على الويكي.",
        "api-error-invalid-file-key": "خطأ داخلي: لم يتم العثور على الملف في التخزين المؤقت.",
        "api-error-nomodule": "خطأ داخلي: لم يتم تعيين تحميل الوحدة النمطية.",
        "api-error-ok-but-empty": "خطأ داخلي : لم يكن هناك استجابة من الملقم.",
        "api-error-overwrite": "لا يسمح بالكتابة فوق ملف موجود.",
+       "api-error-ratelimited": "أنت تحاول رفع الكثير من الملفات في فترة زمنية قصيرة أقصر مما تسمح به هذه الويكي.\nمن فضلك حاول مرة ثانية خلال عدة دقائق.",
        "api-error-stashfailed": "خطأ داخلي: فشل الملقم في تخزين الملفات المؤقتة.",
        "api-error-publishfailed": "خطأ داخلي: لم ينجح الخادوم في نشر ملف مؤقت",
        "api-error-stasherror": "حدث خطأ أثناء رفع الملف لتخزينه.",
        "api-error-stashnosuchfilekey": "الملف الذي كنت تحاول الوصول اليه في مخبوائتك غير موجود.",
        "api-error-timeout": "لم يستجب الملقم في الوقت المتوقع.",
        "api-error-unclassified": "حدث خطأ غير معروف",
-       "api-error-unknown-code": "خطأ غير معروف : \" $1 \"",
+       "api-error-unknown-code": "خطأ غير معروف: \"$1\"",
        "api-error-unknown-error": "خطأ داخلي: قد حدث خطأ عند محاولة تحميل الملف الخاص بك.",
-       "api-error-unknown-warning": "تحذير غير معروف:$1",
-       "api-error-unknownerror": "خطأ غير معروف : \" $1 \"",
-       "api-error-uploaddisabled": "تم تعطيل تحميل على هذا الويكي.",
-       "api-error-verification-error": "هذا الملف قد يكون معطوباً أو يحتوي على ملحق غير صحيح.",
+       "api-error-unknown-warning": "تحذير غير معروف: \"$1\"",
+       "api-error-unknownerror": "خطأ غير معروف: \"$1\"",
+       "api-error-uploaddisabled": "تم تعطيل الرفع على هذه الويكي.",
+       "api-error-verification-error": "هذا الملف قد يكون معطوباً أو يحتوي على امتداد غير صحيح.",
+       "api-error-was-deleted": "تم رفع ملف بهذا الاسم سابقا ثم تم حذفه بعد هذا.",
        "duration-seconds": "{{PLURAL:$1|أقل من ثانية|ثانية واحدة|ثانيتان|$1 ثوانٍ|$1 ثانية}}",
        "duration-minutes": "{{PLURAL:$1|أقل من دقيقة|دقيقة واحدة|دقيقتان|$1 دقائق|$1 دقيقة}}",
        "duration-hours": "({{PLURAL:$1||ساعة واحدة|ساعتان|$1 ساعات|$1 ساعة}})",
        "expand_templates_generate_rawhtml": "أظهر خام HTML",
        "expand_templates_preview": "عرض مسبق",
        "expand_templates_preview_fail_html": "<em>عذرا! لم نستطع معالجة تعديلك بسبب فقدان بيانات الجلسة.\n\nلأن {{SITENAME}} بها HTML الخام مفعلة، العرض المسبق مخفي كاحتياط ضد هجمات الجافا سكريبت.</em>\n\n<strong>إذا كانت هذه محاولة تعديل صادقة، من فضلك حاول مرة أخرى.</strong>\nإذا كانت مازالت لا تعمل، حاول [[Special:UserLogout|تسجيل الخروج]] ثم تسجيل الدخول مجددا.و تاكد في  متصفحك من الكوكيز  الخاصة  بهذا الموقع.",
+       "expand_templates_preview_fail_html_anon": "<em>لأن {{SITENAME}} لديه الHTML الخام مفعل وأنت غير مسجل الدخول، فعملية المعاينة مخفية كاحتياط ضد عمليات هجوم الجافاسكريبت.</em>\n\n<strong>لو أن هذه عملية معاينة صحيحة، فمن فضلك  [[Special:UserLogin|سجل الدخول]] وحاول مرة أخرى.</strong>",
        "expand_templates_input_missing": "يجب تقديم بعض المدخلات النصية على الأقل.",
        "pagelanguage": "تغيير لغة الصفحة",
        "pagelang-name": "صفحة",
        "log-name-pagelang": "سجل تغيير اللغة",
        "log-description-pagelang": "هذا سجل تغيرات في صفحة اللغات.",
        "logentry-pagelang-pagelang": " {{GENDER:$2|غيَّر|غيَّرت}} $1 لغة الصفحة «$3» من $4 إلى $5.",
+       "default-skin-not-found": "أخ! الواجهة الافتراضية للويكي الخاصة بك، والمعرفة في <code dir=\"ltr\">$wgDefaultSkin</code> ك<code>$1</code>، غير متوفرة.\n\nعملية تنصيبك يبدو أنها تحتوي على {{PLURAL:$4|الواجهة|الواجهات}} التالية. انظر [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] للمعلومات حول كيف تفعل  {{PLURAL:$4|ها|هم وتختار الافتراضي}}.\n\n$2\n\n; لو أنك قمت بتنصيب ميدياويكي حالا:\n: أنت ربما قمت بالتنصيب من git, أو مباشرة من الكود المصدري باستخدام طريقة أخرى. هذا متوقع. حاول تنصيب بعض الواجهات من [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory]، عن طريق تحميل:\n:* تحميل [https://www.mediawiki.org/wiki/Download tarball installer]، والذي يأتي مع واجهات وامتدادات عديدة. أنت يمكنك نسخ مجلد ال<code>skins/</code> مباشرة منه.\n:* تحميل ال  واجهات الtarballs الفردية من [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استخدام Git لتحميل الواجهات].\n: فعل هذا ينبغي ألا يتعارض مع مستودع git الخاص بك لو أنك مطور ميدياويكي.\n\n; لو أنك قمت بترقية ميدياويكي حالا:\n: MediaWiki 1.24 وأحدث لم يعد يقوم بتفعيل الواجهات المنصبة تلقائيا (انظر [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). أنت يمكنك نسخ {{PLURAL:$5|السطر التالي|السطور التالية}} إلى <code>LocalSettings.php</code> لتفعيل {{PLURAL:$5||كل}}  {{PLURAL:$5|الواجهة|الواجهات}} المنصبة:\n\n<pre dir=\"ltr\">$3</pre>\n\n; لو أنك قمت بتعديل <code>LocalSettings.php</code> حالا:\n: فتحقق مجددا من أسماء الواجهات للأخطاء الإملائية.",
+       "default-skin-not-found-no-skins": "أخ! الواجهة الافتراضية للويكي الخاصة بك، والمعرفة في <code>$wgDefaultSkin</code> ك<code>$1</code>، غير متوفرة.\n\nأنت ليس لديك أي واجهات منصبة.\n\n; لو أنك قد قمت بتنصيب أو ترقية ميدياويكي حالا:\n: أنت على الأرجح قد قمت بالتنصيب من git، أو مباشرة من الكود المصدري باستخدام طريقة أخرى. هذا متوقع. MediaWiki 1.24 وأحدث لا يحتوي على أي واجهات في المستودع الرئيسي. حاول تنصيب بعض الواجهات من  [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory]، عن طريق تحميل [https://www.mediawiki.org/wiki/Download tarball installer], والذي يأتي مع واجهات وامتداات عديدة. أنت يمكنك نسخ مجلد ال<code>skins/</code> منه.\n:* تحميل tarballs الواجهات الفردية من [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استخدام Git لتحميل الواجهات].\n: فعل هذا ينبغي ألا يتداخل مع مستودع git الخاص بك لو أنك مطور ميدياويكي. انظر [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] للمعلومات حول كيفية تفعيل الواجهات واختيار الافتراضي.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (مفعل)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>ملغاة</strong>)",
        "mediastatistics": "إحصاءات الميديا",
        "sessionprovider-nocookies": "قد يتم تعطيل الكوكيز. تأكد من تمكين ملفات تعريف الأرتباط وأبد مرةأخرى.",
        "randomrootpage": "صفحة جذر عشوائية",
        "log-action-filter-block": "نوع المنع:",
+       "log-action-filter-contentmodel": "نوع تغيير موديل المحتوى:",
        "log-action-filter-delete": "نوع الحذف:",
        "log-action-filter-import": "نوع الاستيراد:",
        "log-action-filter-managetags": "نوع فعل إدارة الوسوم:",
        "log-action-filter-move": "نوع النقل:",
        "log-action-filter-newusers": "نوع إنشاء الحساب:",
+       "log-action-filter-patrol": "نوع الخفر:",
        "log-action-filter-protect": "نوع الحماية:",
        "log-action-filter-rights": "نوع تغيير الصلاحية",
+       "log-action-filter-suppress": "نوع الإخفاء:",
        "log-action-filter-upload": "نوع الرفع:",
        "log-action-filter-all": "الكل",
        "log-action-filter-block-block": "منع",
        "log-action-filter-block-reblock": "منع التعديل",
        "log-action-filter-block-unblock": "رفع المنع",
+       "log-action-filter-contentmodel-change": "تغيير موديل المحتوى",
+       "log-action-filter-contentmodel-new": "إنشاء صفحة بموديل محتوى غير قياسي",
        "log-action-filter-delete-delete": "حذف الصفحات",
        "log-action-filter-delete-restore": "استرجاع الصفحات",
        "log-action-filter-delete-event": "حذف السجلات",
        "log-action-filter-delete-revision": "حذف المراجعات",
        "log-action-filter-import-interwiki": "استيراد عابر للويكي",
+       "log-action-filter-import-upload": "استيراد بواسطة رفع XML",
        "log-action-filter-managetags-create": "إنشاء الوسوم",
        "log-action-filter-managetags-delete": "حذف الوسوم",
+       "log-action-filter-managetags-activate": "تفعيل الوسم",
+       "log-action-filter-managetags-deactivate": "تعطيل الوسم",
+       "log-action-filter-move-move": "نقل بدون الكتابة على التحويلات",
+       "log-action-filter-move-move_redir": "نقل مع الكتابة على التحويلات",
+       "log-action-filter-newusers-create": "إنشاء بواسطة مستخدم مجهول",
+       "log-action-filter-newusers-create2": "إنشاء بواسطة مستخدم مسجل",
        "log-action-filter-newusers-autocreate": "إنشاء آلي",
        "log-action-filter-newusers-byemail": "الإنشاء بكلمة مرور مرسلة عبر البريد الإلكتروني",
+       "log-action-filter-patrol-patrol": "خفر يدوي",
+       "log-action-filter-patrol-autopatrol": "خفر أوتوماتيكي",
        "log-action-filter-protect-protect": "حماية",
        "log-action-filter-protect-modify": "تعديل الحماية",
        "log-action-filter-protect-unprotect": "رفع الحماية",
+       "log-action-filter-protect-move_prot": "نقل الحماية",
        "log-action-filter-rights-rights": "تغيير يدوي",
+       "log-action-filter-rights-autopromote": "تغيير أوتوماتيكي",
+       "log-action-filter-suppress-event": "إخفاء السجل",
+       "log-action-filter-suppress-revision": "إخفاء المراجعة",
+       "log-action-filter-suppress-delete": "إخفاء الصفحة",
+       "log-action-filter-suppress-block": "إخفاء المستخدم بواسطة المنع",
+       "log-action-filter-suppress-reblock": "إخفاء المستخدم بواسطة إعادة المنع",
        "log-action-filter-upload-upload": "رفع جديد",
        "log-action-filter-upload-overwrite": "إعادة الرفع",
+       "authmanager-authn-not-in-progress": "عملية التحقق ليست جارية أو بينات الجلسة تم فقدها. من فضلك ابدأ مرة ثانية من البداية.",
+       "authmanager-authn-no-primary": "الاعتماد الموفر لم يمكن التحقق منه.",
+       "authmanager-authn-no-local-user": "الاعتماد الموفر غير مرتبط بأي مستخدم على هذه الويكي.",
+       "authmanager-authn-no-local-user-link": "الاعتمادات الموفرة صحيحة لكن غير مرتبطة بأي مستخدم على هذه الويكي. سجل الدخول باستخدام طريقة أخرى، أو أنشيء مستخدما جديدا، وستمتلك الاختيار لوصل اعتماداتك السابقة لذلك الحساب.",
+       "authmanager-authn-autocreate-failed": "الإنشاء التلقائي لحساب محلي فشل: $1",
+       "authmanager-change-not-supported": "الاعتمادات الموفرة لم يمكن تغييرها، حيث أن لا شيء سيستخدمها.",
        "authmanager-create-disabled": "إنشاء الحسابات معطل.",
        "authmanager-create-from-login": "لإنشاء حساب، برجاء ملء الحقول أدناه.",
+       "authmanager-create-not-in-progress": "إنشاء الحساب ليس جاريا أو بيانات الجلسة تم فقدها. من فضلك ابدأ ثانية من البداية.",
+       "authmanager-create-no-primary": "الاعتمادات الموفرة لم يمكن استخدامها لإنشاء الحساب.",
+       "authmanager-link-no-primary": "الاعتماد الموفر لم يمكن استخدامه لوصل الحسابات.",
+       "authmanager-link-not-in-progress": "وصل الحساب ليس جاريا أو بيانات الجلسة تم فقدها. من فضلك ابدأ ثانية منذ البداية.",
+       "authmanager-authplugin-setpass-failed-title": "تغيير كلمة السر فشل",
+       "authmanager-authplugin-setpass-failed-message": "إضافة التحقق رفضت تغيير كلمة السر.",
+       "authmanager-authplugin-create-fail": "إضافة التحقق رفضت إنشاء الحساب.",
+       "authmanager-authplugin-setpass-denied": "إضافة التحقق لا تسمح بتغيير كلمات السر.",
+       "authmanager-authplugin-setpass-bad-domain": "نطاق غير صحيح.",
+       "authmanager-autocreate-noperm": "إنشاء الحساب التلقائي غير مسموح به.",
+       "authmanager-autocreate-exception": "إنشاء الحسابات التلقائي تم تعطيله مؤقتا نظرا للأخطاء السابقة.",
+       "authmanager-userdoesnotexist": "حساب المستخدم \"$1\" غير مسجل.",
+       "authmanager-userlogin-remembermypassword-help": "ما إذا كانت كلمة السر ينبغي أن يتم تذكرها لأطول من مدة الجلسة.",
+       "authmanager-username-help": "اسم المستخدم للتوثيق.",
+       "authmanager-password-help": "كلمة السر للتوثيق",
+       "authmanager-domain-help": "النطاق للتوثيق الخارجي.",
        "authmanager-retype-help": "كلمة المرور مرة أخرى للتأكيد.",
        "authmanager-email-label": "البريد الإلكتروني",
        "authmanager-email-help": "عنوان البريد الإلكتروني",
        "authmanager-provider-password": "توثيق مبني على كلمة المرور",
        "authmanager-provider-password-domain": "توثيق مبني على كلمة المرور والنطاق",
        "authmanager-provider-temporarypassword": "كلمة مرور مؤقتة",
+       "authprovider-confirmlink-message": "بناء على محاولات تسجيل الدخول الحديثة الخاصة بك، فالحسابات التالية يمكن وصلها بحساب الويكي الخاص بك. وصلهم يفعل تسجيل الدخول عبر هذه الحسابات. من فضلك اختر أيهم ينبغي أن يتم وصلها.",
+       "authprovider-confirmlink-request-label": "الحسابات التي ينبغي أن يتم وصلها",
+       "authprovider-confirmlink-success-line": "$1: تم الوصل بشكل صحيح.",
+       "authprovider-confirmlink-failed": "وصل الحساب لم ينجح بشكل كامل: $1",
+       "authprovider-confirmlink-ok-help": "الاستمرار بعد عرض رسائل فشل الوصل.",
        "authprovider-resetpass-skip-label": "تخطى",
        "authprovider-resetpass-skip-help": "تخطي إعادة تعيين كلمة المرور",
+       "authform-nosession-login": "عملية التحقق كانت ناجحة، لكن متصفحك لا يمكنه \"تذكر\" أنك مسجل الدخول.\n\n$1",
+       "authform-nosession-signup": "الحساب تم إنشاؤه، لكن متصفحك لا يمكنه cannot \"تذكر\" أنك مسجل الدخول.\n\n$1",
+       "authform-newtoken": "توكين مفقود. $1",
+       "authform-notoken": "توكين مفقود",
+       "authform-wrongtoken": "توكين خاطئ",
+       "specialpage-securitylevel-not-allowed-title": "غير مسموح به",
+       "specialpage-securitylevel-not-allowed": "عذرا، أنت غير مسموح لك باستخدام هذه الصفة لأن هويتك لا يمكن التحقق منها.",
+       "authpage-cannot-login": "غير قادر على بدء عملية تسجيل الدخول.",
+       "authpage-cannot-login-continue": "غير قادر على الاستمرار في تسجيل الدخول. جلستك على الأرجح انتهت صلاحيتها.",
+       "authpage-cannot-create": "غير قادر على بدء عملية إنشاء الحساب.",
+       "authpage-cannot-create-continue": "غير قادر على الاستمرار في إنشاء الحساب. جلستك على الأرجح انتهت صلاحيتها.",
+       "authpage-cannot-link": "غير قادر على بدء عملية وصل الحسابات.",
+       "authpage-cannot-link-continue": "غير قادر على الاستمرار في وصل الحساب. جلستك على الأرجح انتهت صلاحيتها.",
        "cannotauth-not-allowed-title": "الإذن مرفوض",
+       "cannotauth-not-allowed": "أنت غير مسموح لك باستخدام هذه الصفحة",
        "changecredentials": "تغيير الاعتماد",
        "changecredentials-submit": "تغيير الاعتماد",
+       "changecredentials-invalidsubpage": "$1 ليس نوع اعتماد صحيح.",
+       "changecredentials-success": "اعتماداتك تم تغييرها.",
        "removecredentials": "إزالة الاعتماد",
        "removecredentials-submit": "إزالة الاعتماد",
+       "removecredentials-invalidsubpage": "$1 ليس نوع اعتمادات صحيح.",
+       "removecredentials-success": "اعتماداتك تمت إزالتها.",
+       "credentialsform-provider": "نوع الاعتمادات:",
        "credentialsform-account": "اسم الحساب:",
        "cannotlink-no-provider-title": "لا توجد حسابات قابلة للربط",
        "cannotlink-no-provider": "لا توجد حسابات قابلة للربط",
        "linkaccounts": "ربط الحسابات",
+       "linkaccounts-success-text": "الحساب تم وصله.",
        "linkaccounts-submit": "اربط الحسابات",
-       "unlinkaccounts": "إزالة ربط الحسابات"
+       "unlinkaccounts": "إزالة ربط الحسابات",
+       "unlinkaccounts-success": "الحساب تم فك وصله.",
+       "authenticationdatachange-ignored": "تغيير بيانات التحقق لم يتم التعامل معه. ربما لم يتم ضبط موفر؟",
+       "userjsispublic": "من فضلك لاحظ: صفحات الجافاسكريبت الفرعية لا ينبغي أن تحتوي غلى بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين.",
+       "usercssispublic": "من فضل لاحظ: صفحات الCSS الفرعية لا ينبغي أن تحتوي على بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين."
 }
index 417ca02..f12c554 100644 (file)
                        "Fitoschido",
                        "Macofe",
                        "Matma Rex",
-                       "Tokvo"
+                       "Tokvo",
+                       "Crucifunked"
                ]
        },
        "tog-underline": "Sorrayar enllaces:",
        "tog-hideminor": "Anubrir les ediciones menores nos cambeos recientes",
        "tog-hidepatrolled": "Anubrir les ediciones vixilaes nos cambeos recientes",
        "tog-newpageshidepatrolled": "Anubrir les páxines vixilaes na llista de páxines nueves",
-       "tog-hidecategorization": "Tapecer la categorización de páxines",
+       "tog-hidecategorization": "Anubrir la categorización de páxines",
        "tog-extendwatchlist": "Espander la llista de siguimientu p'amosar tolos cambeos, non solo los más recientes",
        "tog-usenewrc": "Agrupar los cambeos por páxina nos cambeos recientes y na llista de siguimientu",
        "tog-numberheadings": "Autonumberar los encabezaos",
        "period-am": "AM",
        "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoría|Categoríes}}",
-       "category_header": "Páxines na categoría «$1»",
+       "category_header": "Páxines na categoría \"$1\"",
        "subcategories": "Subcategoríes",
-       "category-media-header": "Ficheros multimedia na categoría «$1»",
-       "category-empty": "''Anguaño esta categoría nun tien nengún artículu nin ficheru multimedia.''",
+       "category-media-header": "Ficheros multimedia na categoría \"$1\"",
+       "category-empty": "<em>Anguaño esta categoría nun tien nengún artículu nin ficheru multimedia.</em>",
        "hidden-categories": "{{PLURAL:$1|Categoría anubrida|Categoríes anubríes}}",
        "hidden-category-category": "Categoríes anubríes",
        "category-subcat-count": "{{PLURAL:$2|Esta categoría tien namái la subcategoría siguiente.|Esta categoría tien {{PLURAL:$1|la siguiente subcategoría|les siguientes $1 subcategoríes}}, d'un total de $2.}}",
        "yourpasswordagain": "Escribi otra vuelta la contraseña:",
        "createacct-yourpasswordagain": "Confirmar la contraseña",
        "createacct-yourpasswordagain-ph": "Escriba nuevamente la contraseña",
-       "remembermypassword": "Recordar la mio identificación nesti restolador (un máximu {{PLURAL:$1|d'un día|de $1 díes}})",
        "userlogin-remembermypassword": "Caltener abierta la sesión",
        "userlogin-signwithsecure": "Usar una conexón segura",
+       "cannotlogin-title": "Nun pudo aniciase sesión",
+       "cannotlogin-text": "Nun ye posible aniciar sesión.",
        "cannotloginnow-title": "Nun puede aniciase sesión agora",
        "cannotloginnow-text": "Nun puede aniciase sesión cuando s'usa $1.",
+       "cannotcreateaccount-title": "Nun pueden crease cuentes",
+       "cannotcreateaccount-text": "La creación direuta de cuentes nun ta activada nesta wiki.",
        "yourdomainname": "El to dominiu:",
        "password-change-forbidden": "Nun se pueden camudar les contraseñes nesta wiki.",
        "externaldberror": "O hebo un fallu d'autenticación de la base de datos o nun tienes permisu p'anovar la to cuenta esterna.",
        "botpasswords-updated-body": "Anovóse la contraseña del bot llamáu «$1» del usuariu «$2».",
        "botpasswords-deleted-title": "Desanicióse la contraseña de bot",
        "botpasswords-deleted-body": "Desanicióse la contraseña del bot llamáu «$1» del usuariu «$2».",
-       "botpasswords-newpassword": "La nueva contraseña p'aniciar sesión con strong>$1</strong> ye <strong>$2</strong>. <em>Por favor, rexistra esto pa referencies futures.</em>",
+       "botpasswords-newpassword": "La nueva contraseña p'aniciar sesión con <strong>$1</strong> ye <strong>$2</strong>. <em>Por favor, rexistra esto pa referencies futures.</em> <br> (Pa los bots antiguos que necesiten que'l nome d'aniciu de sesión sía'l mesmu que'l nome d'usuariu, tamién pue usase <strong>$3</strong> como nome d'usuariu y <strong>$4</strong> como contraseña.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider nun ta disponible.",
        "botpasswords-restriction-failed": "Hai torgues de contraseña de bot que torgaron esti aniciu de sesión.",
        "botpasswords-invalid-name": "El nome d'usuariu especificáu nun contien el separador de contraseña de bot («$1»).",
        "invalid-content-data": "Datos del conteníu inválidos",
        "content-not-allowed-here": "El conteníu «$1» nun se permite na páxina [[$2]]",
        "editwarning-warning": "Salir d'esta páxina pue causar la perda de cualesquier cambiu fechu.\nSi anició sesión, pue desactivar esti avisu na seición «{{int:prefs-editing}}» de les preferencies.",
+       "editpage-invalidcontentmodel-title": "El modelu de conteníu nun tien sofitu",
+       "editpage-invalidcontentmodel-text": "El modelu de conteníu «$1»nun tien sofitu.",
        "editpage-notsupportedcontentformat-title": "El formatu del conteníu nun tien sofitu",
        "editpage-notsupportedcontentformat-text": "El formatu del conteníu, $1, nun tien sofitu del modelu de conteníu $2.",
        "content-model-wikitext": "testu wiki",
        "file-thumbnail-no": "El ficheru entama con <strong>$1</strong>.\nPaez ser una imaxe de tamañu menguáu ''(miniatura)''.\nSi tienes esta imaxe a resolución completa xúbila; si non, por favor camuda'l nome del ficheru.",
        "fileexists-forbidden": "Yá esiste un ficheru con esti nome, y nun se pue renomar.\nSi tovía asina quies xubir el ficheru, por favor vuelvi atrás y usa otru nome.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Yá esiste un ficheru con esti nome nel direutoriu de ficheros compartíos.\nSi tovía asina quies xubir el ficheru, por favor vuelvi atrás y usa otru nome.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "La carga ye un duplicáu exautu de la versión actual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "La carga ye un duplicáu exautu {{PLURAL:$2|d'una versión más vieya|de versiones más vieyes}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Esti ficheru ye un duplicáu {{PLURAL:$1|del siguiente ficheru|de los siguientes ficheros}}:",
        "file-deleted-duplicate": "Yá se desanició enantes un ficheru idénticu a esti ([[:$1]]).\nDeberíes revisar el historial de desaniciu del ficheru enantes de xubilu otra vuelta.",
        "file-deleted-duplicate-notitle": "Un ficheru idénticu a esti desanicióse anteriormente, y suprimióse'l títulu. Tendría de pidir a dalguién que pueda ver los datos del ficheru desaniciáu que revise la situación enantes de volver a xubilu.",
        "filerevert-submit": "Revertir",
        "filerevert-success": "'''[[Media:$1|$1]]''' foi revertida a la [$4 versión del $3 a les $2].",
        "filerevert-badversion": "Nun hai nenguna versión llocal previa d'esti archivu cola fecha conseñada.",
+       "filerevert-identical": "La versión actual del ficheru ye igual que la seleicionada.",
        "filedelete": "Desaniciar $1",
        "filedelete-legend": "Esborrar archivu",
        "filedelete-intro": "Tas a piques d'esborrar el ficheru '''[[Media:$1|$1]]''' xunto con tol so historial.",
        "rollbacklinkcount-morethan": "revertir más de $1 {{PLURAL:$1|edición|ediciones}}",
        "rollbackfailed": "Falló la reversión",
        "rollback-missingparam": "Faltan parámetros riquíos na solicitú.",
+       "rollback-missingrevision": "Nun pueden cargase los datos de la revisión.",
        "cantrollback": "Nun se pue revertir la edición; el postrer collaborador ye l'únicu autor d'esta páxina.",
        "alreadyrolled": "Nun se pue revertir la postrer edición de [[:$1]] fecha por [[User:$2|$2]] ([[User talk:$2|alderique]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\ndaquién más yá editó o revirtió la páxina.\n\nLa postrer edición foi fecha por [[User:$3|$3]] ([[User talk:$3|alderique]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resume de la edición yera: <em>$1</em>.",
        "pageinfo-article-id": "ID de la páxina",
        "pageinfo-language": "Llingua del conteníu de la páxina",
        "pageinfo-content-model": "Plantía del conteníu de la páxina",
+       "pageinfo-content-model-change": "camudar",
        "pageinfo-robot-policy": "Indexación por robots",
        "pageinfo-robot-index": "Permitío",
        "pageinfo-robot-noindex": "Torgao",
        "tag-filter": "Filtru d'[[Special:Tags|etiquetes]]:",
        "tag-filter-submit": "Peñera",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiqueta|Etiquetes}}]]: $2)",
+       "tag-mw-contentmodelchange": "cambiu nel modelu de conteníu",
+       "tag-mw-contentmodelchange-description": "Ediciones que [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel camuden el modelu de conteníu] d'una páxina",
        "tags-title": "Etiquetes",
        "tags-intro": "Esta páxina llista les etiquetes coles que'l software pue marcar una edición, y el so significáu.",
        "tags-tag": "Nome d'etiqueta",
        "tags-actions-header": "Aiciones",
        "tags-active-yes": "Sí",
        "tags-active-no": "Non",
-       "tags-source-extension": "Definida por una estensión",
+       "tags-source-extension": "Definío pol software",
        "tags-source-manual": "Aplicada a mano polos usuarios y bots",
        "tags-source-none": "Yá nun s'usa",
        "tags-edit": "editar",
        "linkaccounts-submit": "Enllazar cuentes",
        "unlinkaccounts": "Desenllazar cuentes",
        "unlinkaccounts-success": "Desenllazóse la cuenta.",
-       "authenticationdatachange-ignored": "Nun se xestionó'l cambéu de los datos d'autentificacion. ¿Seique, nun se configuró un fornidor?"
+       "authenticationdatachange-ignored": "Nun se xestionó'l cambéu de los datos d'autentificacion. ¿Seique, nun se configuró un fornidor?",
+       "userjsispublic": "Atención: les subpáxines JavaScript nun tendríen de contener datos acutaos porque son visibles pa otros usuarios.",
+       "usercssispublic": "Atención: les subpáxines CSS nun tendríen de contener datos acutaos porque son visibles pa otros usuarios."
 }
index 3432c23..add8bea 100644 (file)
        "delete-confirm": "Silinən səhifə: \"$1\"",
        "delete-legend": "Sil",
        "historywarning": "'''Xəbərdarlıq:''' Silinəcək səhifənin tarixçəsində qeyd olunmuş $1 {{PLURAL:$1|redaktə|redaktə}} var:",
+       "historyaction-submit": "Göstər",
        "confirmdeletetext": "Bu səhifə və ya fayl bütün tarixçəsi ilə birlikdə birdəfəlik silinəcək. Bunu [[{{MediaWiki:Policy-url}}|qaydalara]] uyğun etdiyinizi və əməliyyatın nəticələrini başa düşdüyünüzü təsdiq edin.",
        "actioncomplete": "Fəaliyyət tamamlandı",
        "actionfailed": "Əməliyyat yerinə yetirilmədi",
index 1a537ce..76c2859 100644 (file)
        "userlogin-loggedin": "سیر حال حاضیردا {{GENDER:$1|$1}} عونوانیندا گیریش ائدیب سیز.\nآشاغیداکی فورمودان بیر آیری ایشلدن عونوانیندا گیریش اوچون ایشلدین.",
        "userlogin-reauth": "{{GENDER:$1|$1}} اوْلدوغونوزو تأیید ائتمک اۆچون یئنه گیرمه‌لیسینیز.",
        "userlogin-createanother": "بیر باشقا حساب یارات",
-       "createacct-emailrequired": "ایمیل آدرسی",
+       "createacct-emailrequired": "ایمئیل آدرسی",
        "createacct-emailoptional": "ایمیل آدرسی (ایستگه باغلی)",
        "createacct-email-ph": "ایمیل آدرسینیزی یازین",
        "createacct-another-email-ph": "ایمیل آدرسینیزی یازین",
index 51f186e..451c046 100644 (file)
@@ -43,7 +43,7 @@
        "tog-enotifminoredits": "Паведамляць праз электронную пошту таксама пра дробныя зьмены старонак і файлаў",
        "tog-enotifrevealaddr": "Не хаваць мой адрас электроннай пошты ў паведамленьнях",
        "tog-shownumberswatching": "Паказваць колькасьць назіральнікаў",
-       "tog-oldsig": "Цяперашні подпіс:",
+       "tog-oldsig": "Ð\92аÑ\88 Ñ\86яперашні подпіс:",
        "tog-fancysig": "Апрацоўваць подпіс як вікітэкст (без аўтаматычнай спасылкі)",
        "tog-uselivepreview": "Выкарыстоўваць хуткі папярэдні прагляд",
        "tog-forceeditsummary": "Папярэджваць пра адсутнасьць кароткага апісаньня зьменаў",
        "newwindow": "(адкрываецца ў новым акне)",
        "cancel": "Скасаваць",
        "moredotdotdot": "Далей…",
-       "morenotlisted": "Гэта ня поўны сьпіс.",
+       "morenotlisted": "Гэты сьпіс можа быць няпоўным.",
        "mypage": "Старонка",
        "mytalk": "Гутаркі",
        "anontalk": "Гутаркі",
        "yourpasswordagain": "Паўтарыце пароль:",
        "createacct-yourpasswordagain": "Пацьвердзіце пароль",
        "createacct-yourpasswordagain-ph": "Увядзіце пароль зноў",
-       "remembermypassword": "Запомніць мяне на гэтым кампутары (ня больш за $1 {{PLURAL:$1|дзень|дні|дзён}})",
        "userlogin-remembermypassword": "Запомніць мяне",
        "userlogin-signwithsecure": "Скарыстацца бясьпечным злучэньнем",
+       "cannotlogin-title": "Немагчыма ўвайсьці",
+       "cannotlogin-text": "Уваход у сыстэму немагчымы.",
        "cannotloginnow-title": "Цяпер немагчыма ўвайсьці",
        "cannotloginnow-text": "Уваход у сыстэму немагчымы пры выкарыстаньні $1.",
+       "cannotcreateaccount-title": "Немагчыма стварыць рахункі",
+       "cannotcreateaccount-text": "Непасрэднае стварэньне рахункаў ня ўключана ў гэтай вікі.",
        "yourdomainname": "Ваш дамэн:",
        "password-change-forbidden": "Вы ня можаце зьмяняць паролі ў гэтай вікі.",
        "externaldberror": "Адбылася памылка аўтэнтыфікацыі з дапамогай вонкавай базы зьвестак, ці Вам не дазволена абнаўляць свой рахунак.",
        "botpasswords-updated-body": "Пароль робата для робата «$1» удзельніка «$2» быў абноўлены.",
        "botpasswords-deleted-title": "Пароль робата выдалены",
        "botpasswords-deleted-body": "Пароль робата для робата «$1» удзельніка «$2» быў выдалены.",
-       "botpasswords-newpassword": "Новы пароль для ўваходу пад <strong>$1</strong> — <strong>$2</strong>. <em>Калі ласка, запішыце яго для далейшага выкарыстаньня.</em>",
+       "botpasswords-newpassword": "Новы пароль для ўваходу пад <strong>$1</strong> — <strong>$2</strong>. <em>Калі ласка, запішыце яго для далейшага выкарыстаньня.</em><br>(Для старых робатаў, якія патрабуюць, каб імя для ўваходу было аднолькавым з патэнцыйным імем удзельніка, вы можаце таксама карыстацца <strong>$3</strong> у якасьці імя і <strong>$4</strong> у якасьці паролю.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider недаступны.",
        "botpasswords-restriction-failed": "Уваход ня выкананы праз абмежаваньні на пароль робата",
        "botpasswords-invalid-name": "Пададзенае імя ўдзельніка ня ўтрымлівае падзяляльнік для паролю робата («$1»).",
        "passwordreset-emaildisabled": "Функцыі электроннай пошты ў гэтай вікі былі адключаныя.",
        "passwordreset-username": "Імя ўдзельніка:",
        "passwordreset-domain": "Дамэн:",
-       "passwordreset-capture": "Ð\9fаказаÑ\86Ñ\8c ÐºÐ°Ð½Ñ\87аÑ\82ковы электронны ліст?",
-       "passwordreset-capture-help": "Калі Вы пазначыце гэтае поле, электронны ліст (з часовым паролем), будзе паказаны Вам як толькі ён будзе дасланы ўдзельніку.",
+       "passwordreset-capture": "Ð\9fаказаÑ\86Ñ\8c Ð²Ñ\8bнÑ\96ковы электронны ліст?",
+       "passwordreset-capture-help": "Калі Вы пазначыце гэтае поле, электронны ліст (з часовым паролем) будзе паказаны Вам, як толькі ён будзе дасланы ўдзельніку.",
        "passwordreset-email": "Адрас электроннай пошты:",
        "passwordreset-emailtitle": "Падрабязнасьці рахунку ў {{GRAMMAR:месны|{{SITENAME}}}}",
        "passwordreset-emailtext-ip": "Нехта (магчыма Вы, з IP-адрасу $1) зрабіў запыт на скіданьне вашага паролю ў {{GRAMMAR:месны|{{SITENAME}}}} ($4). {{PLURAL:$3|1=Наступны рахунак удзельніка зьвязаны|Наступныя рахункі ўдзельнікаў зьвязаныя}} з гэтым адрасам электроннай пошты:\n\n$2\n\n{{PLURAL:$3|1=Гэты часовы пароль будзе|Гэтыя часовыя паролі будуць}} дзейнічаць $5 {{PLURAL:$5|дзень|дні|дзён}}.\nЦяпер Вам неабходна ўвайсьці і выбраць новы пароль. Калі нехта іншы зрабіў гэты запыт, ці Вы ўспомнілі Ваш пачатковы пароль, які ня хочаце мяняць, Вы можаце праігнараваць гэтае паведамленьне, і працягваць выкарыстоўваць стары пароль.",
        "changeemail-submit": "Зьмяніць адрас электроннай пошты",
        "changeemail-throttled": "Вы зрабілі зашмат спробаў увайсьці ў сыстэму.\nКалі ласка, пачакайце $1 перад наступнай спробай.",
        "changeemail-nochange": "Калі ласка, увядзіце іншы новы адрас электроннай пошты",
-       "resettokens": "СкÑ\96дванÑ\8cне Ñ\82окенаÑ\9e",
-       "resettokens-text": "Тут вы можаце скінуць токены, якія даюць вамд доступ да пэўных прыватных зьвестак, асацыяваных з вашым рахункам.\n\nКалі вы выпадкова падзяліліся токенамі зь іншымі, або калі ваш рахунак быў скампрамэтаваны, скарыстайцеся гэтай магчымасьцю і скіньце токены.",
-       "resettokens-no-tokens": "Ð\9dÑ\8fма Ñ\88Ñ\82о Ñ\81кÑ\96даÑ\86Ñ\8c.",
+       "resettokens": "Скіданьне токенаў",
+       "resettokens-text": "Тут вы можаце скінуць токены, якія даюць вам доступ да пэўных прыватных зьвестак, асацыяваных з вашым рахункам.\n\nКалі вы выпадкова падзяліліся токенамі зь іншымі, або калі ваш рахунак быў скампрамэтаваны, скарыстайцеся гэтай магчымасьцю і скіньце токены.",
+       "resettokens-no-tokens": "Ð\9dÑ\8fма Ñ\82окенаÑ\9e Ð´Ð»Ñ\8f Ñ\81кÑ\96данÑ\8cнÑ\8f.",
        "resettokens-tokens": "Токены:",
        "resettokens-token-label": "$1 (бягучае значэньне: $2)",
        "resettokens-watchlist-token": "Токен стужкі (Atom/RSS) [[Special:Watchlist|зьменаў у вашым сьпісе назіраньня]]",
        "invalid-content-data": "Няслушныя зьвесткі",
        "content-not-allowed-here": "Зьмест тыпу «$1» на старонцы [[$2]] не дазволены",
        "editwarning-warning": "Пакінуўшы гэтую старонку, вы можаце страціць усе ўнесеныя зьмены.\nКалі вы ўвайшлі ў сыстэму, Вы можаце адключыць гэтае папярэджаньне ў сэкцыі «{{int:prefs-editing}}» вашых наладаў.",
+       "editpage-invalidcontentmodel-title": "Мадэль зьместу не падтрымліваецца",
+       "editpage-invalidcontentmodel-text": "Мадэль зьместу «$1» не падтрымліваецца.",
        "editpage-notsupportedcontentformat-title": "Фармат зьмесьціва не падтрымліваецца",
        "editpage-notsupportedcontentformat-text": "Фармат зьмесьціва $1 не падтрымліваецца мадэльлю зьмесьціва $2.",
        "content-model-wikitext": "вікі-тэкст",
        "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nВерагодна гэта паменшаная копія выявы ''(мініятура)''.\nКалі Вы маеце гэтую выяву ў поўным памеры, загрузіце яе, альбо зьмяніце назву файла.",
        "fileexists-forbidden": "Файл з такой назвай ужо існуе і ня можа быць перапісаны.\nКалі ласка, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл з такой назвай ужо існуе ў агульным сховішчы файлаў.\nКалі Вы жадаеце загрузіць Ваш файл, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Гэтая загрузка зьяўляецца дакладнай копіяй цяперашняй вэрсіі <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Гэтая загрузка зьяўляецца дакладнай копіяй {{PLURAL:$2|1=старой вэрсіі|старых вэрсіяў}} файлу <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Гэты файл дублюе {{PLURAL:$1|1=наступны файл|наступныя файлы}}:",
        "file-deleted-duplicate": "Падобны файл ([[:$1]]) ужо выдаляўся. Калі ласка, паглядзіце гісторыю выдаленьняў гэтага файла перад яго паўторнай загрузкай.",
        "file-deleted-duplicate-notitle": "Файл, ідэнтычны гэтаму файлу, раней ужо быў выдалены, а назва файла была забароненая.\nВам трэба зьвярнуцца да некага з правамі прагляду зьвестак забароненых файлаў, каб прааналізаваць сытуацыю перад тым, як загружаць файл ізноў.",
        "filerevert-submit": "Вярнуць",
        "filerevert-success": "'''[[Media:$1|$1]]''' быў вернуты да [вэрсіі $4 ад $3, $2].",
        "filerevert-badversion": "Не існуе папярэдняй лякальнай вэрсіі гэтага файла з пазначанай датай.",
+       "filerevert-identical": "Цяперашняя вэрсія файлу ўжо ідэнтычная абранай.",
        "filedelete": "Выдаліць $1",
        "filedelete-legend": "Выдаліць файл",
        "filedelete-intro": "Вы выдаляеце файл '''[[Media:$1|$1]]''' з усёй яго гісторыяй.",
        "watchnologin": "Вы не ўвайшлі ў сыстэму",
        "addwatch": "Дадаць ў сьпіс назіраньня",
        "addedwatchtext": "Старонка «[[:$1]]» і яе старонка абмеркаваньня былі дададзеная да Вашага [[Special:Watchlist|сьпісу назіраньня]].",
+       "addedwatchtext-talk": "«[[:$1]]» і зьвязаная зь ёй старонка дададзеныя да вашага [[Special:Watchlist|сьпісу назіраньня]].",
        "addedwatchtext-short": "Старонка «$1» была дададзеная ў ваш сьпіс назіраньня.",
        "removewatch": "Выдаліць са сьпісу назіраньня",
        "removedwatchtext": "Старонка «[[:$1]]» і яе старонка абмеркаваньня былі выдаленыя з Вашага [[Special:Watchlist|сьпісу назіраньня]].",
+       "removedwatchtext-talk": "«[[:$1]]» і зьвязаная зь ёй старонка выдаленыя з вашага [[Special:Watchlist|сьпісу назіраньня]].",
        "removedwatchtext-short": "Старонка «$1» была выдаленая з вашага сьпісу назіраньня.",
        "watch": "Назіраць",
        "watchthispage": "Назіраць за гэтай старонкай",
        "rollbacklinkcount-morethan": "адкаціць больш за $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "rollbackfailed": "Памылка адкату",
        "rollback-missingparam": "У запыце адсутнічаюць абавязковыя парамэтры.",
+       "rollback-missingrevision": "Не атрымалася загрузіць зьвесткі вэрсіі.",
        "cantrollback": "Немагчыма адкаціць зьмену; апошні рэдактар — адзіны аўтар гэтай старонкі.",
        "alreadyrolled": "Немагчыма адкаціць апошнюю зьмену [[:$1]], якую {{GENDER:$2|зрабіў|зрабіла}} [[User:$2|$2]] ([[User talk:$2|гутаркі]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); нехта іншы ўжо зьмяніў старонку альбо адкаціў зьмены.\n\nАпошнія зьмены зробленыя [[User:$3|$3]] ([[User talk:$3|гутаркі]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Кароткае апісаньне зьменаў было: <em>$1</em>.",
        "undeletehistorynoadmin": "Гэтая старонка была выдаленая.\nПрычына выдаленьня пададзена ніжэй, разам са зьвесткамі ўдзельніка, які рэдагаваў старонку перад выдаленьнем.\nТэкст выдаленай старонкі могуць глядзець толькі адміністратары.",
        "undelete-revision": "Выдаленая вэрсія $1 (ад $5 $4) ўдзельніка $3:",
        "undeleterevision-missing": "Некарэктная ці неіснуючая вэрсія.\nВерагодна Вы карысталіся няслушнай спасылкай, альбо, магчыма, вэрсія была выдаленая з архіву.",
+       "undeleterevision-duplicate-revid": "$1 {{PLURAL:$1|вэрсія ня можа быць адноўленая|вэрсіі ня могуць быць адноўленыя|вэрсіяў ня могуць быць адноўленыя}}, бо {{PLURAL:$1|1=яе|іх}} <code>rev_id</code> ужо выкарыстоўваецца.",
        "undelete-nodiff": "Папярэдняя вэрсія ня знойдзеная.",
        "undeletebtn": "Аднавіць",
        "undeletelink": "паглядзець/аднавіць",
        "pageinfo-article-id": "Ідэнтыфікатар старонкі",
        "pageinfo-language": "Мова зьместу старонкі",
        "pageinfo-content-model": "Мадэль зьместу старонкі",
+       "pageinfo-content-model-change": "зьмяніць",
        "pageinfo-robot-policy": "Індэксацыя пашукавікамі",
        "pageinfo-robot-index": "Дазволеная",
        "pageinfo-robot-noindex": "Не дазволеная",
        "notificationemail_subject_changed": "Адрас электроннай пошты на сайце {{SITENAME}} быў зьменены",
        "notificationemail_subject_removed": "Адрас электроннай пошты на сайце {{SITENAME}} быў выдалены",
        "notificationemail_body_changed": "Некім, магчыма вамі, з IP-адрасу $1,\nбыў зьменены адрас электроннай пошты «$2» на «$3» на сайце {{SITENAME}}.\n\nКалі гэта былі ня вы, неадкладна зьвяжыцеся з адміністратарам.",
+       "notificationemail_body_removed": "Некім, магчыма вамі, з IP-адрасу $1,\nбыў выдалены адрас электроннай пошты з рахунку «$2» на сайце {{SITENAME}}.\n\nКалі гэта былі ня вы, неадкладна зьвяжыцеся з адміністратарам.",
        "scarytranscludedisabled": "[Улучэньне інтэрвікі было адключанае]",
        "scarytranscludefailed": "[Памылка атрыманьня шаблёну $1]",
        "scarytranscludefailed-httpstatus": "[Памылка атрыманьня шаблёну $1: HTTP $2]",
        "timezone-local": "Мясцовы",
        "duplicate-defaultsort": "Папярэджаньне: Ключ сартыроўкі па змоўчваньні «$2» замяняе папярэдні ключ сартыроўкі па змоўчваньні «$1».",
        "duplicate-displaytitle": "<strong>Папярэджаньне:</strong> назва для адлюстраваньня «$2» перапісвае ранейшую назву для адлюстраваньня «$1».",
+       "restricted-displaytitle": "<strong>Увага:</strong> назва для адлюстраваньня «$1» была праігнараваная, бо яна не супадае зь цяперашняй назвай старонкі.",
        "invalid-indicator-name": "<strong>Памылка:</strong> атрыбут <code>name</code> індыкатараў статусу старонкі ня мусіць быць пустым.",
        "version": "Вэрсія",
        "version-extensions": "Усталяваныя пашырэньні",
        "tag-filter": "Фільтар [[Special:Tags|метак]]:",
        "tag-filter-submit": "Фільтар",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Метка|Меткі}}]]: $2)",
+       "tag-mw-contentmodelchange": "зьмена мадэлі зьместу",
+       "tag-mw-contentmodelchange-description": "Рэдагаваньні, якія [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel зьмяняюць мадэль зьместу] старонкі",
        "tags-title": "Меткі",
        "tags-intro": "На гэтай старонцы знаходзіцца сьпіс метак, якімі праграмнае забесьпячэньне можа пазначыць рэдагаваньне, і іх значэньне.",
        "tags-tag": "Назва меткі",
        "tags-actions-header": "Дзеяньні",
        "tags-active-yes": "Так",
        "tags-active-no": "Не",
-       "tags-source-extension": "Вызначаецца пашырэньнем",
+       "tags-source-extension": "Вызначаецца праграмным забесьпячэньнем",
        "tags-source-manual": "Ставіцца ўручную ўдзельнікамі і робатамі",
        "tags-source-none": "Больш не выкарыстоўваецца",
        "tags-edit": "рэдагаваць",
        "api-error-nomodule": "Унутраная памылка: ня выбраны модуль загрузкі.",
        "api-error-ok-but-empty": "Унутраная памылка: няма адказу ад сэрвэра.",
        "api-error-overwrite": "Замена існуючага файла забароненая.",
+       "api-error-ratelimited": "Вы спрабуеце загрузіць за кароткі час болей файлаў, чым дазваляе вікі.\nКалі ласка, паспрабуйце яшчэ раз празь некалькі хвілінаў.",
        "api-error-stashfailed": "Унутраная памылка: сэрвэр ня змог захаваць часовы файл.",
        "api-error-publishfailed": "Унутраная памылка: сэрвэр ня змог захаваць часловы файл.",
        "api-error-stasherror": "Падчас загрузкі файла ў сховішча адбылася памылка.",
        "api-error-unknownerror": "Невядомая памылка: «$1».",
        "api-error-uploaddisabled": "Загрузка ў гэтую вікі адключаная.",
        "api-error-verification-error": "Гэты файл можа быць пашкоджаны, ці мае няслушнае пашырэньне.",
+       "api-error-was-deleted": "Файл з такой назвай ужо загружаўся раней і быў потым выдалены.",
        "duration-seconds": "$1 {{PLURAL:$1|сэкунда|сэкунды|сэкундаў}}",
        "duration-minutes": "$1 {{PLURAL:$1|хвіліна|хвіліны|хвілінаў}}",
        "duration-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "mediastatistics": "Статыстыка мэдыяфайлаў",
        "mediastatistics-summary": "Статыстыка тыпаў загружаных файлаў. Яна ўключае толькі актуальныя вэрсіі файлаў. Старыя і выдаленыя вэрсіі ня ўлічваюцца.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Агульны памер файлу для гэтага разьдзелу: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%).",
+       "mediastatistics-allbytes": "Агульны памер усіх файлаў: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2).",
        "mediastatistics-table-mimetype": "MIME-тып",
        "mediastatistics-table-extensions": "Магчымыя пашырэньні",
        "mediastatistics-table-count": "Колькасьць файлаў",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
        "mw-widgets-titleinput-description-redirect": "перанакіраваньне на $1",
+       "sessionmanager-tie": "Немагчыма выкарыстаць адначасова некалькі тыпаў аўтэнтыфікацыі: $1.",
+       "sessionprovider-generic": "$1 сэсіі",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "сэсіі на падставе файлаў-кукі",
+       "sessionprovider-nocookies": "Файлы-кукі могуць быць адключаныя. Упэўніцеся, што ў вас уключаныя файлы-кукі і пачніце спачатку.",
        "randomrootpage": "Выпадковая карэнная старонка",
        "log-action-filter-block": "Тып блякаваньня:",
        "log-action-filter-delete": "Тып выдаленьня:",
        "log-action-filter-delete-restore": "Аднаўленьне старонкі",
        "log-action-filter-delete-event": "Выдаленьне журналу",
        "log-action-filter-delete-revision": "Выдаленьне вэрсіі",
-       "log-action-filter-managetags-create": "Стварэньне цэтлікаў",
-       "log-action-filter-managetags-delete": "Выдаленьне цэтлікаў",
-       "log-action-filter-managetags-activate": "Актывацыя цэтлікаў",
-       "log-action-filter-managetags-deactivate": "Дэактывацыя цэтлікаў",
+       "log-action-filter-managetags-create": "Стварэньне метак",
+       "log-action-filter-managetags-delete": "Выдаленьне метак",
+       "log-action-filter-managetags-activate": "Актывацыя метак",
+       "log-action-filter-managetags-deactivate": "Дэактывацыя метак",
        "log-action-filter-newusers-autocreate": "Аўтаматычнае стварэньне",
        "log-action-filter-patrol-autopatrol": "Аўтаматычнае патруляваньне",
        "log-action-filter-protect-protect": "Абарона",
index 42348b0..1ab171e 100644 (file)
@@ -56,7 +56,7 @@
        "tog-enotifminoredits": "Паведамяць мне на эл.пошту пра дробныя праўкі старонак і файлаў",
        "tog-enotifrevealaddr": "Паказваць мой адрас эл.пошты ў паведамленнях",
        "tog-shownumberswatching": "Паказваць колькасць назіральнікаў",
-       "tog-oldsig": "Ð\86Ñ\81нÑ\83Ñ\8eÑ\87Ñ\8b подпіс:",
+       "tog-oldsig": "Ð\92аÑ\88 Ñ\86Ñ\8fпеÑ\80аÑ\88нÑ\96 подпіс:",
        "tog-fancysig": "Апрацоўваць подпіс як вікі-тэкст (без аўтаматычнай спасылкі)",
        "tog-uselivepreview": "Жывы перадпаказ",
        "tog-forceeditsummary": "Папярэджваць пра пустое поле тлумачэння праўкі",
@@ -73,7 +73,7 @@
        "tog-showhiddencats": "Паказаць схаваныя катэгорыі",
        "tog-norollbackdiff": "Не паказваць розніцу ў выніку адкату",
        "tog-useeditwarning": "Папярэдзіць мяне, калі я пакідаю старонку з незахаванымі праўкамі",
-       "tog-prefershttps": "Ð\97аÑ\9eÑ\81Ñ\91дÑ\8b Ð²Ñ\8bкаÑ\80Ñ\8bÑ\81Ñ\82оÑ\9eваÑ\86Ñ\8c Ð°Ð±Ð°Ñ\80оненае Ð·Ð»Ñ\83Ñ\87Ñ\8dнне Ð¿Ð°Ñ\81лÑ\8f Ñ\9eваÑ\85одÑ\83 ў сістэму",
+       "tog-prefershttps": "Ð\97аÑ\9eÑ\81Ñ\91дÑ\8b Ð²Ñ\8bкаÑ\80Ñ\8bÑ\81Ñ\82оÑ\9eваÑ\86Ñ\8c Ð±Ñ\8fÑ\81пеÑ\87нае Ð·Ð»Ñ\83Ñ\87Ñ\8dнне Ð¿Ð° Ñ\9eваÑ\85одзе ў сістэму",
        "underline-always": "Заўсёды",
        "underline-never": "Ніколі",
        "underline-default": "Як у браўзеры",
        "newwindow": "(адкрыецца ў новым акне)",
        "cancel": "Нічога",
        "moredotdotdot": "Яшчэ...",
-       "morenotlisted": "Гэты спіс не поўны.",
+       "morenotlisted": "Ð\93Ñ\8dÑ\82Ñ\8b Ñ\81пÑ\96Ñ\81 Ð¼Ð¾Ð¶Ð° Ð±Ñ\8bÑ\86Ñ\8c Ð½Ðµ Ð¿Ð¾Ñ\9eнÑ\8b.",
        "mypage": "Старонка",
        "mytalk": "Размовы",
        "anontalk": "Размовы",
        "yourpasswordagain": "Паўтарыце пароль:",
        "createacct-yourpasswordagain": "Пацвердзіце пароль",
        "createacct-yourpasswordagain-ph": "Увядзіце пароль яшчэ раз",
-       "remembermypassword": "Памятаць мяне на гэтым камп'ютары (не даўжэй за $1 {{PLURAL:$1|дзень|дні|дзён}})",
        "userlogin-remembermypassword": "Заставацца ў сістэме",
        "userlogin-signwithsecure": "Выкарыстоўваць абароненае злучэнне",
+       "cannotlogin-title": "Немагчыма ўвайсці",
+       "cannotlogin-text": "Уваход у сістэму немагчымы.",
        "cannotloginnow-title": "Зараз немагчыма ўвайсці",
        "cannotloginnow-text": "Пры выкарыстанні $1 немагчыма прадставіцца сістэме.",
+       "cannotcreateaccount-title": "Немагчыма стварыць уліковыя запісы",
+       "cannotcreateaccount-text": "Непасрэднае стварэнне ўліковых запісаў не ўключана на гэтай вікі.",
        "yourdomainname": "Ваш дамен:",
        "password-change-forbidden": "Вы не можаце змяняць паролі на гэтай Вікі.",
        "externaldberror": "Або памылка вонкавай аўтэнтыкацыі ў базе дадзеных, або вам не дазволена абнаўляць свой вонкавы рахунак.",
        "botpasswords-updated-body": "Пароль для робата \"$1\" удзельніка \"$2\" паспяхова абноўлены.",
        "botpasswords-deleted-title": "Пароль робата сцёрты",
        "botpasswords-deleted-body": "Пароль для робата \"$1\" удзельніка \"$2\" паспяхова сцёрты.",
-       "botpasswords-newpassword": "Новы пароль для ўваходу пад <strong>$1</strong> — <strong>$2</strong>. <em>Калі ласка, запішыце яго для выкарыстання ў будучыні.</em>",
+       "botpasswords-newpassword": "Новы пароль для ўваходу пад <strong>$1</strong> — <strong>$2</strong>. <em>Калі ласка, запішыце яго для выкарыстання ў будучыні.</em> <br> (Для старых робатаў, якія патрабуюць, каб імя для ўваходу супадала з патэнцыяльным імем удзельніка, можаце таксама выкарыстоўваць <strong>$3</strong> як імя і <strong>$4</strong> у якасці пароля.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider недаступны.",
        "botpasswords-restriction-failed": "Уваход не выкананы з-за абмежаванняў на пароль робата.",
        "botpasswords-invalid-name": "Паказанае імя ўдзельніка не ўтрымлівае падзяляльнік паролю робата (\"$1\").",
        "passwordreset-emailsentemail": "Калі гэты адрас электроннай пошты злучаны з вашым уліковым запісам, будзе адпраўлены ліст пра скід пароля.",
        "passwordreset-emailsentusername": "Калі ёсць адрас электроннай пошты, злучаны з гэтым імем удзельніка, то будзе дасланы ліст пра скід пароля.",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Электронны ліст|электронныя лісты}} скіду пароля адпраўлены. {{PLURAL:$1|Імя ўдзельніка і пароль|Спіс імён удзельнікаў і паролі}} паказаны ніжэй.",
+       "passwordreset-emailerror-capture2": "Не ўдалося даслаць {{GENDER:$2|удзельніку|удзельніцы}} ліст электроннай поштай: $1 {{PLURAL:$3|Імя ўдзельніка і пароль|Спіс імён удзельнікаў і паролі}} паказаны ніжэй.",
+       "passwordreset-nocaller": "Мусіць быць указана, хто выклікае",
+       "passwordreset-nosuchcaller": "Аўтар выкліку не існуе: $1",
+       "passwordreset-ignored": "Скід пароля не быў апрацаваны. Магчыма, не настроены пастаўшчык?",
        "passwordreset-invalideamil": "Няслушны адрас электроннай пошты",
        "passwordreset-nodata": "Не былі пададзены ні імя ўдзельніка, ні адрас электроннай пошты",
        "changeemail": "Змяніць або выдаліць адрас электроннай пошты",
        "invalid-content-data": "Недапушчальнае змесціва",
        "content-not-allowed-here": "\"$1\" не дазволены на старонцы [[$2]]",
        "editwarning-warning": "Пераход на іншую старонку можа прывесці да страты правак, зробленых Вамі. \nКалі Вы ўвайшлі ў сістэму, Вы можаце адключыць гэта папярэджанне ў раздзеле \"{{int:prefs-editing}}\" Вашых настроек.",
+       "editpage-invalidcontentmodel-title": "Мадэль змесціва не падтрымліваецца",
+       "editpage-invalidcontentmodel-text": "Мадэль змесціва \"$1\" не падтрымліваецца.",
        "editpage-notsupportedcontentformat-title": "Фармат змесціва не падтрымліваецца",
        "editpage-notsupportedcontentformat-text": "Фармат змесціва $1 не падтрымліваецца мадэллю змесціва $2.",
        "content-model-wikitext": "вікі-тэкст",
        "content-json-empty-object": "Пусты аб'ект",
        "content-json-empty-array": "Пусты масіў",
        "deprecated-self-close-category": "Старонкі з недапушчальнымі самазакрытымі HTML-тэгамі",
+       "deprecated-self-close-category-desc": "Старонка ўтрымлівае недапушчальныя самазакрытыя HTML-тэгі, такія як <code>&lt;b/></code> ці <code>&lt;span/></code>. Іх паводзіны ў хуткім часе будуць зменены ў адпаведнасці з спецыфікацыяй HTML5, таму іх ужыванне ў вікітэксце лічыцца састарэлым.",
        "duplicate-args-warning": "<strong>Увага:</strong> [[:$1]] выклікае [[:$2]] з больш чым адным значэннем для параметра \"$3\". Толькі апошняе з пададзеных значэнняў будзе ўжытае.",
        "duplicate-args-category": "Старонкі, якія выкарыстоўваюць задубляваныя параметры ў шаблонах",
        "duplicate-args-category-desc": "Старонка ўтрымлівае шаблоны з задубляванымі параметрамі, напрыклад, <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> або <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "badsig": "Недапушчальны зыходны тэкст подпісу; праверце тэгі HTML.",
        "badsiglength": "Занадта доўгі подпіс. Трэба, каб ён быў карацейшым за $1 {{PLURAL:$1|знак|знакаў}}.",
        "yourgender": "Пол:",
-       "gender-unknown": "Ð\9dÑ\8fвÑ\8bзнаÑ\87аны",
+       "gender-unknown": "Ð\97гадваÑ\8eÑ\87Ñ\8b Ð²Ð°Ñ\81, Ð¿Ñ\80агÑ\80ама Ð±Ñ\83дзе Ð¿Ð° Ð¼Ð°Ð³Ñ\87Ñ\8bмаÑ\81Ñ\86Ñ\96 Ñ\9eжÑ\8bваÑ\86Ñ\8c Ð³ÐµÐ½Ð´Ð°Ñ\80на-нейÑ\82Ñ\80алÑ\8cнÑ\8bÑ\8f Ñ\81ловы",
        "gender-male": "М",
        "gender-female": "Ж",
        "prefs-help-gender": "Неабавязкова: ужываецца дзеля пола-карэктнага звяртання з боку праграм. Гэтыя звесткі могуць стацца публічна вядомымі.",
        "grant-group-high-volume": "Выконваць вялікі аб'ём дзейнасці",
        "grant-group-customization": "Настройкі і перавагі",
        "grant-group-administration": "Выконваць адміністрацыйныя дзеянні",
+       "grant-group-private-information": "Доступ да прыватных звестак пра вас",
        "grant-group-other": "Розная актыўнасць",
        "grant-blockusers": "Блакаваць і разблакаваць удзельнікаў",
        "grant-createaccount": "Ствараць уліковыя запісы",
        "grant-highvolume": "Вялікі аб'ём рэдагавання",
        "grant-oversight": "Утойваць удзельнікаў і версіі старонак",
        "grant-patrol": "Патруляваць змены старонак",
+       "grant-privateinfo": "Доступ да прыватных звестак",
        "grant-protect": "Ахоўваць і здымаць ахову старонак",
        "grant-rollback": "Адкатваць змяненні старонак",
        "grant-sendemail": "Адпраўляць электронную пошту іншым удзельнікам",
        "rightslogtext": "Журнал змяненняў у дазволах, прыпісаных удзельнікам.",
        "action-read": "чытаць гэтую старонку",
        "action-edit": "правіць гэтую старонку",
-       "action-createpage": "ствараць старонкі",
-       "action-createtalk": "ствараць размоўныя старонкі",
+       "action-createpage": "стварыць гэту старонку",
+       "action-createtalk": "стварыць гэту размоўную старонку",
        "action-createaccount": "ствараць гэты рахунак удзельніка",
        "action-autocreateaccount": "аўтаматычна ствараць гэты вонкавы ўліковы запіс удзельніка",
        "action-history": "глядзець гісторыю гэтай старонкі",
        "action-applychangetags": "прымяняць біркі з сваімі праўкамі",
        "action-changetags": "дадаваць і выдаляць адвольныя біркі да асобных версій і запісаў у журнале падзей",
        "action-deletechangetags": "выдаляць біркі з базы даных",
+       "action-purge": "ачысціць кэш гэтай старонкі",
        "nchanges": "$1 {{PLURAL:$1|змена|змены|змен}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з часу апошняга наведвання}}",
        "enhancedrc-history": "гісторыя",
        "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nТак можа называцца выява зменшанага памеру ''(драбніца)''.\nКалі гэтая выява сапраўды запісаная ў найлепшым разрозненні, якое ёсць, то ўкладайце яе, а іначай лепей памяняць назву файла.",
        "fileexists-forbidden": "Файл з такой назвай ужо ёсць, і нельга запісаць паўзверх яго. Калі вы жадаеце абавязкова ўкласці свой файл, то выберыце новую назву. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "У агульным сховішчы ўжо існуе файл з такою назвай.\nКалі вы жадаеце ўсё ж укласці свой файл, паўтарыце працэдуру ўкладання, але з іншай назвай. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Укладанне з'яўляецца дакладнай копіяй бягучай версіі <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Укладанне з'яўляецца дакладнай копіяй {{PLURAL:$2|старой версіі|старых версій}} файла <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Гэты файл з'яўляецца дублікатам наступн{{PLURAL:$1|ага файла|ых файлаў}}:",
        "file-deleted-duplicate": "Файл, падобны да гэтага ([[:$1]]), быў сцёрты некалі раней. Трэба праверыць гісторыю таго файла перад тым, як укладваць яго нанова.",
        "file-deleted-duplicate-notitle": "Файл, ідэнтычны гэтаму, быў сцёрты раней, а назва файла заглушана.\nВы мусіце спытаць каго-небудзь з магчымасцю бачыць заглушаныя звесткі па файлах пераглядзець сітуацыю перад тым, як укладваць яго нанова.",
        "uploaded-href-attribute-svg": "у SVG файлах атрыбутам href дазволены толькі мэты віду http:// або https://, знойдзена <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-href-unsafe-target-svg": "У ўкладзеным SVG файле знойдзена спасылка на небяспечныя звесткі: URI мэты <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "У ўкладзеным SVG файле знойдзены тэг \"animate\", здольны змяніць спасылку з дапамогай атрыбута \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-setting-event-handler-svg": "Устаноўка атрыбутаў апрацоўкі падзей заблакавана, у ўкладзеным SVG-файле знойдзены код <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-setting-href-svg": "Выкарыстанне тэга \"set\" для дадання атрыбута \"href\" у бацькоўскі элемент заблакавана.",
+       "uploaded-wrong-setting-svg": "Ужыванне тэга \"set\" для задання ў якасці мэты аддаленага адраса/звестак/сцэнарыя для любога атрыбута заблакавана. У ўкладзеным SVG-файле знойдзены <code>&lt;set to=\"$1\"&gt;</code>.",
        "uploadscriptednamespace": "Гэты файл SVG утрымлівае недапушчальную прастору імёнаў \"$1\".",
        "uploadinvalidxml": "Немагчыма прааналізаваць XML ва ўкладзеным файле.",
        "uploadvirus": "Файл утрымлівае вірус! Падрабязнасці: $1",
        "apisandbox-dynamic-parameters-add-placeholder": "Назва параметра",
        "apisandbox-dynamic-error-exists": "Параметр з назвай \"$1\" ужо існуе.",
        "apisandbox-deprecated-parameters": "Састарэлыя параметры",
+       "apisandbox-fetch-token": "Аўтазапаўненне токена",
        "apisandbox-submit-invalid-fields-title": "Некаторыя палі недапушчальныя",
        "apisandbox-submit-invalid-fields-message": "Калі ласка, выпраўце адзначаныя палі і паспрабуйце ізноў.",
        "apisandbox-results": "Вынікі",
        "htmlform-cloner-delete": "Сцерці",
        "htmlform-cloner-required": "Неабходна хаця б адно значэнне.",
        "htmlform-title-badnamespace": "[[:$1]] не ў прасторы назваў \"{{ns:$2}}\".",
+       "htmlform-title-not-creatable": "\"$1\" - немагчымы загаловак для старонкі",
        "htmlform-title-not-exists": "$1 не існуе.",
        "htmlform-user-not-exists": "<strong>$1</strong> не існуе.",
+       "htmlform-user-not-valid": "<strong>$1</strong> - недапушчальная назва уліковага запісу.",
        "sqlite-has-fts": "$1 з падтрымкай поўна-тэкставага пошуку",
        "sqlite-no-fts": "$1 без падтрымкі поўна-тэкставага пошуку",
        "logentry-delete-delete": "$1 {{GENDER:$2|сцёр|сцёрла}} старонку $3",
        "logentry-block-block": "$1 заблакірава{{GENDER:$2|ў|ла}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|разблакаваў|разблакавала}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} настройкі блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|заблакіраваў|заблакіравала}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} параметры блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 праз укладанне файла",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 праз укладанне файла ($4 {{PLURAL:$4|версія|версіі|версій}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 з іншай вікі",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 з $5 ($4 {{PLURAL:$4|версія|версіі|версій}})",
        "logentry-move-move": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4, не пакінуўшы перасылкі",
        "logentry-move-move_redir": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4 па-над перасылкаю",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|уклаў|уклала}} новую версію $3",
        "logentry-upload-revert": "$1 {{GENDER:$2|уклаў|уклала}} $3",
        "log-name-managetags": "Журнал кіравання біркамі",
+       "logentry-managetags-create": "$1 {{GENDER:$2|стварыў|стварыла}} бірку \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|выдаліў|выдаліла}} бірку \"$4\" (выдалена з $5 {{PLURAL:$5|версіі ці запісу ў журнале|версій і/або запісаў у журнале}})",
+       "logentry-managetags-activate": "$1 {{GENDER:$2|актываваў|актывавала}} бірку \"$4\" для выкарыстання ўдзельнікамі і робатамі",
+       "logentry-managetags-deactivate": "$1 {{GENDER:$2|дэактываваў|дэактывавала}} бірку \"$4\" для выкарыстання ўдзельнікамі і робатамі",
        "log-name-tag": "Журнал бірак",
+       "log-description-tag": "На гэтай старонцы паказана, калі ўдзельнікі дадавалі ці выдалялі [[Special:Tags|біркі]] ў асобных версіях ці запісах журнала. Журнал не захоўвае дзеянні з біркамі, калі яны былі часткай рэдагавання, выдалення ці падобных дзеянняў.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|дадаў|дадала}} {{PLURAL:$7|1=бірку|біркі}} $6 да версіі $4 старонкі $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|дадаў|дадала}} {{PLURAL:$7|1=бірку|біркі}} $6 да запісу ў журнале $5 старонкі $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|выдаліў|выдаліла}} {{PLURAL:$9|1=бірку|біркі}} $8 з версіі $4 старонкі $3",
        "rightsnone": "(няма)",
        "revdelete-summary": "тлумачэнне праўкі",
        "feedback-adding": "Даданне водгуку на старонку…",
index 41d32b5..c8eb76b 100644 (file)
@@ -54,6 +54,7 @@
        "tog-watchdefault": "Добавяне на страниците, които редактирам, в списъка ми за наблюдение",
        "tog-watchmoves": "Добавяне на преместените от мен страници и файлове към списъка ми за наблюдение",
        "tog-watchdeletion": "Добавяне на изтритите от мен страници и файлове към списъка ми за наблюдение",
+       "tog-watchuploads": "Добавяне на новите качени от мен файлове към списъка ми за наблюдение",
        "tog-watchrollback": "Добавяне на страници, в които съм {{GENDER:$1|извършвал|извършвала}} отмяна на редакции в списъка ми за наблюдениe",
        "tog-minordefault": "Отбелязване на всички промени като малки по подразбиране",
        "tog-previewontop": "Показване на предварителния преглед преди текстовата кутия",
        "history-feed-description": "Редакционна история на страницата в {{SITENAME}}",
        "history-feed-item-nocomment": "$1 в $2",
        "history-feed-empty": "Исканата страница не съществува — може да е била изтрита или преименувана. Опитайте да [[Special:Search|потърсите]] нови страници, които биха могли да са ви полезни.",
+       "history-edit-tags": "Редактиране етикетите на избраните редакции",
        "rev-deleted-comment": "(резюмето е премахнато)",
        "rev-deleted-user": "(името на автора е изтрито)",
        "rev-deleted-event": "(записът е изтрит)",
        "revdelete-unsuppress": "Премахване на ограниченията за възстановените версии",
        "revdelete-log": "Причина:",
        "revdelete-submit": "Прилагане към {{PLURAL:$1|избраната версия|избраните версии}}",
-       "revdelete-success": "'''Видимостта на версията беше променена успешно.'''",
+       "revdelete-success": "Видимостта на версията беше променена успешно.",
        "revdelete-failure": "'''Видимостта на редакцията не може да бъде обновена:'''\n$1",
        "logdelete-success": "Видимостта на дневника е установена.",
        "logdelete-failure": "'''Видимостта на дневника не може да бъде променяна:'''\n$1",
        "mergehistory-go": "Показване на редакциите, които могат да се слеят",
        "mergehistory-submit": "Сливане на редакции",
        "mergehistory-empty": "Няма редакции, които могат да бъдат слети.",
-       "mergehistory-done": "$3 {{PLURAL:$3|версия|версии}} от $1 бяха успешно слети с редакционната история на [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|версия|версии}} от $1 {{PLURAL:$3|беше успешно слята|бяха успешно слети}} с редакционната история на [[:$2]].",
        "mergehistory-fail": "Невъзможно е да се извърши сливане на редакционните истории; проверете страницата и времевите параметри.",
        "mergehistory-no-source": "Изходната страница $1 не съществува.",
        "mergehistory-no-destination": "Целевата страница $1 не съществува.",
        "userrights": "Управление на потребителските права",
        "userrights-lookup-user": "Управляване на потребителските групи",
        "userrights-user-editname": "Потребителско име:",
-       "editusergroup": "Редактиране на потребителските групи",
-       "editinguser": "Промяна на потребителските права на потребител '''[[User:$1|$1]]''' $2",
+       "editusergroup": "Редактиране на {{GENDER:$1|потребителските}} групи",
+       "editinguser": "Промяна на потребителските права на {{GENDER:$1|потребител }} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Редактиране на потребителските групи",
        "saveusergroups": "Съхраняване на потребителските групи",
        "userrights-groupsmember": "Член на:",
        "right-ipblock-exempt": "пренебрегване на блокирания по IP blocks, автоматични блокирания и блокирани IP интервали",
        "right-unblockself": "Собствено отблокиране",
        "right-protect": "променяне на нивото на защита и редактиране на защитени страници",
-       "right-editprotected": "редактиране на защитени страници (без каскадна защита)",
+       "right-editprotected": "Редактиране на страници защитени като „{{int:protect-level-sysop}}“",
        "right-editinterface": "Редактиране на потребителския интерфейс",
        "right-editusercssjs": "редактиране на CSS и JS файловете на други потребители",
        "right-editusercss": "редактиране на CSS файловете на други потребители",
index aa81ac2..e2fec0d 100644 (file)
@@ -28,7 +28,9 @@
                        "Sayma Jahan",
                        "Macofe",
                        "Bodhisattwa",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "আজিজ",
+                       "Kayser Ahmad"
                ]
        },
        "tog-underline": "সংযোগগুলির নিচে দাগ দেখানো হোক:",
@@ -56,7 +58,7 @@
        "tog-enotifminoredits": "পাতা এবং ফাইলগুলোতে অনুল্লেখ্য সম্পাদনার জন্যও আমাকে ই-মেইল করা হোক",
        "tog-enotifrevealaddr": "বিজ্ঞপ্তি মেইলে আমার ই-মেইল ঠিকানা প্রকাশ করা হোক",
        "tog-shownumberswatching": "নজরদারী করছে, এমন ব্যবহারকারীর সংখ্যা দেখানো হোক",
-       "tog-oldsig": "বর্তমান স্বাক্ষর:",
+       "tog-oldsig": "à¦\86পনার à¦¬à¦°à§\8dতমান à¦¸à§\8dবাà¦\95à§\8dষর:",
        "tog-fancysig": "স্বাক্ষরকে উইকিটেক্সট হিসেবে মনে করুন (কোন সয়ংক্রিয় লিঙ্ক ছাড়া)",
        "tog-uselivepreview": "তাৎক্ষণিক প্রাকদর্শন ব্যবহার করো",
        "tog-forceeditsummary": "খালি সম্পাদনা সারাংশ প্রবেশ করানোর সময় আমাকে জানানো হোক",
@@ -73,7 +75,7 @@
        "tog-showhiddencats": "লুকায়িত বিষয়শ্রেণীসমূহ দেখাও",
        "tog-norollbackdiff": "রোলব্যাকের পরে পার্থক্য দেখিও না",
        "tog-useeditwarning": "অসংরক্ষিত পরিবর্তনসহ কোনো পাতা ত্যাগের সময় সাবধান করো",
-       "tog-prefershttps": "যà¦\96নà¦\87 à¦ªà§\8dরবà§\87শ à¦\95রবà§\87ন সবসময় নিরাপদ সংযোগ ব্যবহার করুন",
+       "tog-prefershttps": "পà§\8dরবà§\87শ à¦\95রার à¦¸à¦®à¦¯à¦¼ সবসময় নিরাপদ সংযোগ ব্যবহার করুন",
        "underline-always": "সব সময়",
        "underline-never": "কখনো নয়",
        "underline-default": "স্কিন অথবা ব্রাউজারে যেমনভাবে নির্দিষ্ট করা আছে",
        "newwindow": "(নতুন উইন্ডোতে খুলবে)",
        "cancel": "বাতিল",
        "moredotdotdot": "আরও...",
-       "morenotlisted": "à¦\8fà¦\9fি à¦\8fà¦\95à¦\9fি à¦\85সমà§\8dপà§\82রà§\8dণ à¦¤à¦¾à¦²à¦¿à¦\95া।",
+       "morenotlisted": "à¦\8fà¦\87 à¦¤à¦¾à¦²à¦¿à¦\95াà¦\9fি à¦\85সমà§\8dপà§\82রà§\8dণ à¦¹à¦¤à§\87 à¦ªà¦¾à¦°à§\87।",
        "mypage": " পাতা",
        "mytalk": "আলোচনা",
        "anontalk": "আলাপ",
        "missingarticle-rev": "(সংস্করণ#: $1)",
        "missingarticle-diff": "(পার্থক্য: $1, $2)",
        "readonly_lag": "ডাটাবেজ স্বয়ংক্রিয়ভাবে বন্ধ করে দেয়া হয়েছে, যাতে অধীন ডাটাবেজ সার্ভারগুলি প্রধান ডাটাবেজ সার্ভারের অবস্থায় আসতে পারে।",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP শিরলেখে পাঠানো হয়েছিল কিন্তু অনুরোধটি একটি API লিখন মডিউলে ছিল।",
        "internalerror": "আভ্যন্তরীণ ত্রুটি",
        "internalerror_info": "আভ্যন্তরীণ ত্রুটি: $1",
        "internalerror-fatal-exception": "\"$1\" ধরনের মারাত্মক ব্যতিক্রম",
        "yourpasswordagain": "পাসওয়ার্ড আবার লিখুন:",
        "createacct-yourpasswordagain": "পাসওয়ার্ড নিশ্চিত করুন",
        "createacct-yourpasswordagain-ph": "আবারও পাসওয়ার্ড লিখুন",
-       "remembermypassword": "এই ব্রাউজারে আমার প্রবেশ মনে রাখা হোক (সর্বোচ্চ $1 {{PLURAL:$1|দিনের}} জন্য)",
        "userlogin-remembermypassword": "আমাকে প্রবেশ অবস্থায় রাখো",
        "userlogin-signwithsecure": "নিরাপদ সংযোগ ব্যবহার করুন",
+       "cannotlogin-title": "প্রবেশ করতে পারবেন না",
        "cannotloginnow-title": "এখন প্রবেশ করা যাবে না",
        "cannotloginnow-text": "$1 ব্যবহার করার সময় প্রবেশ করা সম্ভব নয়।",
+       "cannotcreateaccount-title": "অ্যাকাউন্ট তৈরি করা যাবে না",
        "yourdomainname": "আপনার ডোমেইন:",
        "password-change-forbidden": "আপনি এই উইকিতে পাসওয়ার্ড পরিবর্তন করতে পারবেন না।",
        "externaldberror": "হয় কোন বহিঃস্থ যাচাইকরণ ডাটাবেজ ত্রুটি ঘটেছে অথবা আপনার বহিঃস্থ অ্যাকাউন্ট হালনাগাদ করার অনুমতি নেই।",
        "createacct-email-ph": "আপনার ইমেইল ঠিকানা যোগ করুন",
        "createacct-another-email-ph": "আপনার ইমেইল ঠিকানা প্রবেশ করান",
        "createaccountmail": "একটি র‌্যান্ডম পাসওয়ার্ড নির্বাচন করুন এবং নির্ধারিত ইমেইল ঠিকানায় পাঠিয়ে দিন",
+       "createaccountmail-help": "পাসওয়ার্ড জানা ছাড়াই অন্য ব্যক্তির জন্য অ্যাকাউন্ট তৈরি করতে ব্যবহার করা যেতে পারে।",
        "createacct-realname": "আসল নাম (ঐচ্ছিক)",
        "createaccountreason": "কারণ:",
        "createacct-reason": "কারণ",
        "nocookiesnew": "ব্যবহারকারীর অ্যাকাউন্টটি সৃষ্টি করা হয়েছে, কিন্তু আপনি এখনও অ্যাকাউন্টে প্রবেশ করেননি। {{SITENAME}}-তে কুকি ব্যবহার করে ব্যবহারকারীদের অ্যাকাউন্টে প্রবেশ করানো হয়। আপনার ব্রাউজারে কুকিগুলি নিষ্ক্রিয় করা আছে। অনুগ্রহ করে কুকিগুলি সক্রিয় করুন এবং আপনার নতুন ব্যবহারকারী নাম ও পাসওয়ার্ড ব্যবহার করে অ্যাকাউন্টে প্রবেশ করুন।",
        "nocookieslogin": "ব্যবহারকারীদের প্রবেশ সম্পন্ন করতে {{SITENAME}} কুকি ব্যবহার করে। আপনার ব্রাউজারে কুকি নিষ্ক্রিয় করা আছে। কুকি চালু করে আবার চেষ্টা করুন।",
        "nocookiesfornew": "ব্যবহারকারীর অ্যাকাউন্ট তৈরি হয়নি, কারণ এর উৎস সম্পর্কে আমরা নিশ্চিত নই।\nনিশ্চিত করুন আপনার কুকি সক্রিয় রয়েছে, পাতাটি পুনরায় লোড করে আবার চেষ্টা করুন।",
+       "createacct-loginerror": "অ্যাকাউন্ট সফলভাবে তৈরি করা হয়েছে কিন্তু আপনি স্বয়ংক্রিয়ভাবে প্রবেশ করতে পারবেন না। দয়া করে [[Special:UserLogin|ম্যানুয়াল প্রবেশ]] করতে এগিয়ে যান।",
        "noname": "আপনি সঠিক ব্যবহারকারী নাম নির্দিষ্ট করেননি।",
        "loginsuccesstitle": "প্রবেশ করেছেন",
        "loginsuccess": "<strong>আপনি এইমাত্র \"$1\" নামে {{SITENAME}}-তে প্রবেশ করেছেন।</strong>",
        "sectioneditnotsupported-text": "এই সম্পাদনা পাতায় অনুচ্ছেদ সম্পাদনা সমর্থন করে না",
        "permissionserrors": "অনুমতি ত্রুটিসমূহ",
        "permissionserrorstext": "আপনার এটা করার অনুমতি নেই, নিচের {{PLURAL:$1|টি কারণের|টি কারণের}} জন্য:",
-       "permissionserrorstext-withaction": "à¦\86পনার $2 à¦\95রার à¦\85নà§\81মতি à¦¨à§\87à¦\87, à¦¯à¦¾à¦° {{PLURAL:$1|à¦\95ারণ|à¦\95ারণসমà§\82হ}} à¦¹à¦²:",
+       "permissionserrorstext-withaction": "আপনার $2 অনুমতি নেই, যার {{PLURAL:$1|কারণ|কারণসমূহ}} হল:",
        "recreate-moveddeleted-warn": "'''সতর্কীকরণ: আপনি এমন একটি পাতা পুনরায় তৈরি করছেন যা পূর্বে অপসারণ করা হয়েছিল।'''\n\nআপনি পাতাটি সম্পাদনা চালিয়ে যাওয়া ঠিক হবে কিনা, তা বিবেচনা করুন।\nআপনার সুবিধার্থে পাতাটির অপলুপ্তি লগ এখানে দেয়া হলো:",
        "moveddeleted-notice": "এই পাতাটি অপসারণ করা হয়েছে।\nসূত্র হিসেবে নিচে এ পাতার অবলুপ্তি লগ দেওয়া হলো।",
        "moveddeleted-notice-recent": "দুঃখিত, এই পাতাটি সাম্প্রতি অপসারিত হয়েছে (সর্বশেষ ২৪ ঘণ্টায়)।\nসূত্র হিসেবে নিচে এই পাতা অপসারণ ও স্থানান্তর লগ দেয়া হয়েছে।",
        "grant-editprotected": "সংরক্ষিত পাতা সম্পাদনা করুন",
        "grant-privateinfo": "ব্যক্তিগত তথ্যে প্রবেশাধিকার",
        "grant-sendemail": "অন্য ব্যবহারকারীকে ইমেইল পাঠান",
+       "grant-uploadeditmovefile": "ফাইল আপলোড, প্রতিস্থাপন এবং স্থানান্তর",
        "grant-uploadfile": "নতুন ফাইল আপলোড করুন",
        "grant-basic": "মৌলিক অধিকার",
        "grant-viewdeleted": "অপসারিত ফাইল ও পাতাগুলি দেখুন",
        "newuserlogpagetext": "এটি নতুন ব্যবহারকারী সৃষ্টির লগ",
        "rightslog": "ব্যবহারকারীর অধিকার লগ",
        "rightslogtext": "এটি ব্যবহারকারী অধিকারে আনা পরিবর্তনগুলির একটি লগ।",
-       "action-read": "এই পাতাটি পড়ুন",
-       "action-edit": "এই পাতাটি সম্পাদনা",
-       "action-createpage": "এই পাতাটি তৈরি",
-       "action-createtalk": "এই আলাপের পাতাটি তৈরি",
-       "action-createaccount": "এই ব্যবহারকারী একাউন্টটি তৈরি করো",
-       "action-history": "এই পাতার ইতিহাস দেখাও",
-       "action-minoredit": "এই সম্পাদনাটি অনুল্লেখ্য হিসেবে চিহ্নিত করো",
+       "action-read": "এই পাতাটি পড়ার",
+       "action-edit": "এই পাতাটি সম্পাদনা করার",
+       "action-createpage": "এই পাতাটি তৈরি করার",
+       "action-createtalk": "এই আলাপ পাতাটি তৈরি করার",
+       "action-createaccount": "এই ব্যবহারকারী একাউন্টটি তৈরি করার",
+       "action-autocreateaccount": "স্বয়ংক্রিয়ভাবে এই বাহ্যিক ব্যবহারকারী অ্যাকাউন্ট তৈরি করার",
+       "action-history": "এই পাতার ইতিহাস দেখার",
+       "action-minoredit": "এই সম্পাদনাটি অনুল্লেখ্য হিসেবে চিহ্নিত করার",
        "action-move": "পাতাটি সরিয়ে ফেলুন",
        "action-move-subpages": "পাতাটি এবং এর উপপাতাগুলো সরিয়ে ফেলুন",
        "action-move-rootuserpages": "root ব্যবহারকারীর পাতাগুলো সরিয়ে ফেলুন",
        "action-move-categorypages": "বিষয়শ্রেণী পাতাসমূহ স্থানান্তর করুন",
-       "action-movefile": "এই ফাইলটি সরিয়ে ফেলুন",
-       "action-upload": "à¦\8fà¦\87 à¦«à¦¾à¦\87ল à¦\86পলà§\8bড à¦\95রà§\8b",
+       "action-movefile": "এই ফাইল স্থানান্তর করার",
+       "action-upload": "à¦\8fà¦\87 à¦«à¦¾à¦\87ল à¦\86পলà§\8bড à¦\95রার",
        "action-reupload": "বিদ্যমান ফাইল প্রতিস্থাপন করো",
-       "action-reupload-shared": "শà§\87য়ারà§\8dড à¦°à¦¿à¦ªà§\8bà¦\9cিà¦\9fরà§\80তà§\87 à¦\8fà¦\87 à¦«à¦¾à¦\87লà¦\9fি à¦¹à¦¾à¦²à¦¨à¦¾à¦\97াদ à¦\95রà§\81ন",
+       "action-reupload-shared": "শà§\87য়ারà§\8dড à¦°à¦¿à¦ªà§\8bà¦\9cিà¦\9fরà§\80তà§\87 à¦\8fà¦\87 à¦«à¦¾à¦\87লà¦\9fি à¦¹à¦¾à¦²à¦¨à¦¾à¦\97াদ à¦\95রার",
        "action-upload_by_url": "কোন ইউআরএল থেকে ফাইলটি আপলোড করো",
        "action-writeapi": "রাইট এপিআই ব্যবহার করুন",
        "action-delete": "পাতাটি মুছে ফেলো",
-       "action-deleterevision": "à¦\8fà¦\87 à¦¸à¦\82শà§\8bধনà¦\9fি à¦®à§\81à¦\9bà§\87 à¦«à§\87লà§\8b",
+       "action-deleterevision": "à¦\8fà¦\87 à¦¸à¦\82শà§\8bধনà¦\9fি à¦®à§\81à¦\9bà§\87 à¦«à§\87লার",
        "action-deletedhistory": "পাতার মুছে ফেলা ইতিহাস দেখাও",
        "action-browsearchive": "অপসারিত পাতায় অনুসন্ধান করুন",
        "action-undelete": "পাতাটি পুনরুদ্ধার করো",
        "action-suppressrevision": "লুকানো সংস্করণগুলো পর্যালোচনা এবং পুনঃস্থাপন করুন",
-       "action-suppressionlog": "à¦\8fà¦\87 à¦¬à§\8dযà¦\95à§\8dতিà¦\97ত à¦²à¦\97 à¦¦à§\87à¦\96াà¦\93",
-       "action-block": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¸à¦®à§\8dপাদনা à¦\95রতà§\87 à¦¬à¦¾à¦\81ধা à¦¦à¦¾à¦\93",
-       "action-protect": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à§\81রà¦\95à§\8dষার à¦®à¦¾à¦¤à§\8dরা à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রà§\8b",
+       "action-suppressionlog": "à¦\8fà¦\87 à¦¬à§\8dযà¦\95à§\8dতিà¦\97ত à¦²à¦\97 à¦¦à§\87à¦\96ার",
+       "action-block": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¸à¦®à§\8dপাদনা à¦\95রতà§\87 à¦¬à¦¾à¦\81ধা à¦¦à§\87য়ার",
+       "action-protect": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à§\81রà¦\95à§\8dষার à¦®à¦¾à¦¤à§\8dরা à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রার",
        "action-rollback": "একটি নির্দিষ্ট পাতার সর্বশেষ ব্যবহারকারীর সম্পদনা পূর্বাবস্থায় ফিরিয়ে আনুন",
        "action-import": "অন্য উইকি থেকে পাতা আমদানী করো",
        "action-importupload": "ফাইল আপলোড থেকে পাতা আমদানী করো",
        "action-patrol": "অন্যদের সম্পাদনা পরীক্ষিত বলে চিহ্নিত করো",
        "action-autopatrol": "পরীক্ষিত বলে চিহ্নিত কি আপনি সম্পাদনা করেছেন",
        "action-unwatchedpages": "নজরতালিকা বহির্ভূত পাতাগুলির তালিকা দেখাও",
-       "action-mergehistory": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦\87তিহাস à¦\8fà¦\95তà§\8dরিত à¦\95রà§\81ন",
+       "action-mergehistory": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦\87তিহাস à¦\8fà¦\95তà§\8dরিত à¦\95রার",
        "action-userrights": "সকল ব্যবহারকারীর অধিকার সম্পাদনা করুন",
        "action-userrights-interwiki": "অন্যান্য উইকির ব্যবহারকারীদের অধিকারসমূহ সম্পাদনা করুন",
        "action-siteadmin": "ডাটাবেজ বন্ধ অথবা খুলুন",
        "action-managechangetags": "ট্যাগ তৈরি ও সক্রিয়/নিষ্ক্রিয়",
        "action-applychangetags": "আপনার পরিবর্তনগুলোর সাথে ট্যাগ সংযোজন করুন",
        "action-changetags": "নির্দিষ্ট সংস্করণ এবং দীর্ঘ সম্পাদনাগুলোতে ট্যাগ সংযোজন ও অপসারণ করুন",
-       "action-purge": "এই পাতা হালনাগাদ করুন",
+       "action-deletechangetags": "ডাটাবেজ থেকে ট্যাগ অপসরণ করার",
+       "action-purge": "এই পাতা হালনাগাদ করার",
        "nchanges": "$1টি {{PLURAL:$1|পরিবর্তন}}",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|সর্বশেষ প্রদর্শনের পর}} $1টি",
        "enhancedrc-history": "ইতিহাস",
        "file-thumbnail-no": "ফাইলের নামটি <strong>$1</strong> দিয়ে শুরু হয়েছে।\nমনে হচ্ছে এটি একটি সংকুচিত আকারের ছবি  ''(থাম্বনেইল)''।\nআপনার কাছে যদি পূর্ণ রেজোলিউশনের ছবিটি থাকে, তবে সেটি আপলোড করুন, নতুবা অনুগ্রহ করে ফাইলের নামটি পরিবর্তন করুন।",
        "fileexists-forbidden": "এই নামের একটি ফাইল ইতিমধ্যেই বিদ্যমান, এবং এটি প্রতিস্থাপনযোগ্য নয়।\nআপনি যদি এখনো ফাইলটি আপলোড করতে চান, তবে অনুগ্রহপূর্বক পেছনে গিয়ে একটি নতুন নামে ফাইলটি আপলোড করুন।\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "অংশীদারী ফাইল ভাণ্ডারে এই নামের একটি ফাইল ইতিমধ্যেই বিদ্যমান।\nআপনি যদি এখনো ফাইলটি আপলোড করতে চান, তবে অনুগ্রহপূর্বক পেছনে গিয়ে একটি নতুন নামে ফাইলটি আপলোড করুন।[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "আপলোডটি <strong>[[:$1]]</strong>-এর বর্তমান সংস্করণের হুবহু প্রতিলিপি।",
+       "fileexists-duplicate-version": "এই আপলোডটি <strong>[[:$1]]</strong>-এর একটি {{PLURAL:$2|পুরনো সংস্করণের}} হুবহু প্রতিলিপি।",
        "file-exists-duplicate": "এই ফাইলটি নিচের {{PLURAL:$1|ফাইল|ফাইলগুলির}} অনুলিপি:",
        "file-deleted-duplicate": "এই ফাইলটির মত একটি ফাইল ([[:$1]]) পূর্বে অপসারণ করা হয়েছে।\nপুনরায় আপলোড করার পূর্বে আপনার উচিত আগের ফাইলটির অপসারণের কারণ জানা।",
        "uploadwarning": "আপলোড সতর্কবাণী",
        "uploadscripted": "এই ফাইলে এমন HTML বা স্ক্রিপ্ট কোড আছে যা একটি ওয়েব ব্রাউজার ভুল বুঝতে পারে।",
        "uploaded-script-svg": "আপলোডকৃত SVG ফাইলে স্ক্রিপ্টযোগ্য উপাদান \"$1\" পাওয়া গেছে।",
        "uploaded-hostile-svg": "আপলোড করা SVG ফাইলের শৈলী উপাদানে অনিরাপদ সিএসএস পাওয়া গেছে।",
+       "uploaded-href-unsafe-target-svg": "অনিরাপদ উপাত্তে href পাওয়া গেছে: আপলোডকৃত SVG ফাইলে URI লক্ষ্য ছিল <code>&lt;$1 $2=\"$3\"&gt;</code>।",
        "uploaded-image-filter-svg": "আপলোডকৃত SVG ফাইলে URL: <code>&lt;$1 $2=\"$3\"&gt;</code> সহ ছবি পরিশোধক পাওয়া গেছে।",
        "uploadscriptednamespace": "এই SVG ফাইলে অবৈধ নামস্থান \"$1\" রয়েছে",
        "uploadinvalidxml": "আপলোডকৃত ফাইলে XML পার্স করা যাবে না।",
        "upload-form-label-own-work": "এটি আমার নিজের কাজ",
        "upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
        "upload-form-label-infoform-date": "তারিখ",
+       "upload-form-label-own-work-message-generic-local": "আমি নিশ্চিত করছি যে আমি {{SITENAME}}-এর পরিষেবা এবং লাইসেন্সকরণ নীতির শর্তাবলী অনুসরণ করে এই ফাইল আপলোড করছি।",
        "upload-form-label-not-own-work-local-generic-local": "এছাড়াও আপনি [[Special:Upload|ডিফল্ট আপলোডের পাতা]] চেষ্টা করতে পারেন।",
        "upload-form-label-not-own-work-local-generic-foreign": "এছাড়াও আপনি [[Special:Upload|{{SITENAME}}-এর আপলোডের পাতা]] ব্যবহার করার চেষ্টা করতে পারেন, যদি এই ফাইলটি তাদের নীতিমালা অধীনে সেখানে আপলোড করা যায়।",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-read": "$1 ফাইলটি ওপেন করা যাচ্ছে না।",
        "backend-fail-create": "$1 ফাইলটি তৈরী করা যাচ্ছে না।",
        "backend-fail-maxsize": "\"$1\" ফাইলে লেখা যাচ্ছে না কারণ এটি {{PLURAL:$2|এক বাইট|$2 বাইট}} থেকে বড়।",
-       "backend-fail-readonly": "\"$1\" à¦¸à§\8dà¦\9fà§\8bরà§\87à¦\9c à¦¬à§\8dযাà¦\95à¦\8fনà§\8dড à¦¥à§\87à¦\95à§\87 à¦¬à¦°à§\8dতমানà§\87 à¦²à§\87à¦\96া à¦¯à¦¾à¦\9aà§\8dà¦\9bà§\87 à¦¨à¦¾à¥¤ à¦\95ারণ: \"''$2''\"",
+       "backend-fail-readonly": "\"$1\" à¦¸à§\8dà¦\9fà§\8bরà§\87à¦\9c à¦¬à§\8dযাà¦\95à¦\8fনà§\8dড à¦¥à§\87à¦\95à§\87 à¦¬à¦°à§\8dতমানà§\87 à¦¶à§\81ধà§\81-পঠনà§\87 à¦°à¦¯à¦¼à§\87à¦\9bà§\87। à¦ªà§\8dরদতà§\8dত à¦\95ারণ: <em>$2</em>",
        "backend-fail-synced": "\"$1\" ফাইলটি ইন্টারনাল স্টোরেজ ব্যকএন্ডের সাথে অসামঞ্জস্যপূর্ণ",
        "backend-fail-connect": "স্টোরেজ ব্যাকেন্ড \"$1\" এর সাথে যোগাযোগ করা যাচ্ছে না।",
        "backend-fail-internal": "\"$1\" স্টোরেজ ব্যাকেন্ডে কোনো অজানা ত্রুটি হয়েছে।",
        "uploadstash-errclear": "ফাইলগুলো পরিষ্কারকরণ ব্যর্থ হয়েছে।",
        "uploadstash-refresh": "ফাইলের তালিকা রিফ্রেশ করুন",
        "uploadstash-thumbnail": "থাম্বনেইল দেখুন",
+       "uploadstash-exception": "স্টাসে আপলোড সঞ্চয় করা যায়নি ($1): \"$2\"।",
        "invalid-chunk-offset": "ত্রুটিপূর্ণ চাংক অফসেট",
        "img-auth-accessdenied": "প্রবেশাধিকার নাই",
        "img-auth-nopathinfo": "PATH_INFO পাওয়া যাচ্ছে না।\nআপনার সার্ভার থেকে এই তথ্য পাঠানোর জন্য কনফিগার করা হয়নি।\nএটি হয়তো CGI ভিত্তিক এবং img_auth সমর্থন করে না।\nবিস্তারিত দেখুন https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization।",
        "apisandbox-fullscreen": "প্যানেল সম্প্রসারণ করুন",
        "apisandbox-fullscreen-tooltip": "ব্রাউজারের উইন্ডো পূরণ করতে খেলাঘরের প্যানেল প্রসারিত করুন।",
        "apisandbox-unfullscreen": "পাতা দেখাও",
+       "apisandbox-unfullscreen-tooltip": "খেলাঘরের প্যানেল হ্রাস করুন, তাহলে মিডিয়াউইকি পরিভ্রমণ করার সংযোগগুলি পাওয়া যাবে।",
        "apisandbox-submit": "অনুরোধ রাখুন",
        "apisandbox-reset": "পরিস্কার",
        "apisandbox-retry": "পুনঃচেষ্টা করুন",
        "apisandbox-loading": "\"$1\" এপিআই মডিউলের জন্য তথ্য লোড হচ্ছে...",
        "apisandbox-load-error": "\"$1\" এপিআই মডিউলের জন্য তথ্য লোড করার সময় একটি ত্রুটি ঘটেছে: $2",
+       "apisandbox-no-parameters": "এই API মডিউলের কোন প্যারামিটার নেই।",
        "apisandbox-helpurls": "সাহায্যকারী লিঙ্কসমূহ",
        "apisandbox-examples": "উদাহরণ",
        "apisandbox-dynamic-parameters": "অতিরিক্ত প্যারামিটার",
        "listgrouprights-removegroup-self-all": "নিজের অ্যাকাউন্ট থেকে সকল দল অপসারণ",
        "listgrouprights-namespaceprotection-header": "নামস্থান নিষেধাজ্ঞাসমূহ",
        "listgrouprights-namespaceprotection-namespace": "নামস্থান",
+       "listgrants": "কার্যভার",
+       "listgrants-summary": "নিম্নে ব্যবহারকারী অধিকারের সাথে যুক্ত প্রবেশাধিকারসহ তাদের কার্যভারের একটি তালিকা দেয়া হয়েছে। ব্যবহারকারীরা তাদের অ্যাকাউন্ট ব্যবহার করতে অ্যাপ্লিকেশনকে অনুমোদন দিতে পারে, কিন্তু কার্যভারের উপর ভিত্তি করে সীমিত অনুমতি ব্যবহারকারীরা অ্যাপ্লিকেশনকে দিতে পারবেন। মূলত, একটি অ্যাপ্লিকেশন একজন ব্যবহারকারীর দেয়া অধিকারের অতিরিক্ত অধিকার ব্যবহার করতে পারবে না। পৃথক অধিকার সম্পর্কে [[{{MediaWiki:Listgrouprights-helppage}}|অতিরিক্ত তথ্য]] দেখুন।",
+       "listgrants-grant": "কার্যভার",
        "listgrants-rights": "অধিকারসমূহ",
        "trackingcategories": "বিষয়শ্রেণীসমূহ অনুসরণ করা হচ্ছে",
        "trackingcategories-msg": "বিষয়শ্রেণী অনুসরণ করা হচ্ছে",
        "restricted-displaytitle-ignored": "উপেক্ষিত প্রদর্শন শিরোনামসহ পাতা",
        "restricted-displaytitle-ignored-desc": "পাতাটি একটি <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> উপেক্ষা করেছে কারণ এটা পাতাটির আসল শিরোনামের সাথে সমতুল্য নয়।",
        "broken-file-category-desc": "এই পাতায় একটি ভাঙ্গা ফাইলের লিঙ্ক রয়েছে (একটি ফাইল এম্বেড করার জন্য একটি লিঙ্ক যখন ফাইলটির অস্তিত্ব নেই)",
+       "hidden-category-category-desc": "এই বিষয়শ্রেণীটির পাতার বিষয়বস্তুর মধ্যে <code><nowiki>__HIDDENCAT__</nowiki></code> উপস্থিত রয়েছে, যা পূর্ব-নির্ধারিতভাবে পাতার বিষয়শ্রেণীর সংযোগে দেখায় না।",
        "trackingcategories-nodesc": "কোন বর্ণনা নেই।",
        "trackingcategories-disabled": "বিষয়শ্রেণীটি বিকল",
        "mailnologin": "প্রাপকের ঠিকানা নেই",
        "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "rollbacklinkcount-morethan": "$1টির বেশি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "rollbackfailed": "রোলব্যাক ব্যর্থ",
+       "rollback-missingparam": "অনুরোধে প্রয়োজনীয় প্যারামিটারগুলি অনুপস্থিত।",
+       "rollback-missingrevision": "সংশোধনের উপাত্ত লোড করতে অক্ষম।",
        "cantrollback": "পূর্বের সংস্করণে ফেরত যাওয়া সম্ভব হল না, সর্বশেষ সম্পাদনাকারী এই নিবন্ধটির একমাত্র লেখক।",
        "alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) দ্বারা সম্পাদিত সর্বশেষ [[:$1]] সম্পাদনাটি পুনর্বহাল করা যাচ্ছে না;\nঅন্য কোন ব্যবহারকারী এই পাতা ইতিমধ্যে সম্পাদনা বা পুনর্বহাল করেছেন।\n\nএই পাতায় সর্বোশেষে [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) দ্বারা সম্পাদিত।",
        "editcomment": "সম্পাদনা সারাংশ ছিল: \"''$1''\"।",
        "undeletedrevisions": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} পুনরুদ্ধার করা হয়েছে",
        "undeletedrevisions-files": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} এবং {{PLURAL:$2|১টি ফাইল|$2টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
        "undeletedfiles": "{{PLURAL:$1|১টি ফাইল|$1টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
-       "cannotundelete": "মà§\81à¦\9bà§\87 à¦«à§\87লা à¦¬à¦¾à¦¤à¦¿à¦² করা যায়নি:\n$1",
+       "cannotundelete": "à¦\95িà¦\9bà§\81 à¦¬à¦¾ à¦¸à¦¬ à¦ªà§\81নরà§\81দà§\8dধার করা যায়নি:\n$1",
        "undeletedpage": "'''$1 পুনরুদ্ধার করা হয়েছে'''\n\nসাম্প্রতিক মুছে ফেলা ও পুনরুদ্ধারের ঘটনাগুলির জন্য [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
        "undelete-header": "সাম্প্রতিক সময়ে মুছে ফেলা পাতাগুলি দেখতে [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
        "undelete-search-title": "অপসারিত পাতা অনুসন্ধান করো",
        "sp-contributions-newbies-sub": "নতুন অ্যাকাউন্টের জন্য",
        "sp-contributions-newbies-title": "নতুন অ্যাকাউন্টের ব্যবহারকারী অবদান",
        "sp-contributions-blocklog": "বাধা দানের লগ",
-       "sp-contributions-suppresslog": "মà§\81à¦\9bà§\87 à¦«à§\87লা à¦¬à§\8dযবহারà¦\95ারà§\80 অবদান",
-       "sp-contributions-deleted": "মুছে ফেলা ব্যবহারকারী অবদান",
+       "sp-contributions-suppresslog": "à¦\97à§\8bপনà¦\95à§\83ত {{GENDER:$1|বà§\8dযবহারà¦\95ারà§\80র}} অবদান",
+       "sp-contributions-deleted": "মুছে ফেলা {{GENDER:$1|ব্যবহারকারীর}} অবদান",
        "sp-contributions-uploads": "আপলোডসমূহ",
        "sp-contributions-logs": "লগসমূহ",
        "sp-contributions-talk": "আলোচনা",
        "move-page-legend": "পাতা স্থানান্তর",
        "movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
        "movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
-       "movepagetalktext": "পাতাà¦\9fির à¦¸à¦¾à¦¥à§\87 à¦¸à¦¾à¦¥à§\87 à¦¸à¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা à¦ªà¦¾à¦¤à¦¾à¦\9fিà¦\93 à¦¸à§\8dবয়à¦\82à¦\95à§\8dরিয়ভাবà§\87 à¦¸à¦°à¦¾à¦¨à§\8b à¦¹à¦¬à§\87 '''যদি à¦¨à¦¾:'''\n*à¦\96ালি à¦¨à¦¯à¦¼ à¦\8fমন à¦\8fà¦\95à¦\9fি à¦\86লাপ à¦ªà¦¾à¦¤à¦¾ à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 à¦¬à¦¿à¦¦à§\8dযমান à¦¥à¦¾à¦\95à§\87, à¦\85থবা\n*à¦\86পনি à¦¨à¦¿à¦\9aà§\87র à¦¬à¦¾à¦\95à§\8dসà¦\9fি à¦¥à§\87à¦\95à§\87 à¦\9fিà¦\95 à¦¸à¦°à¦¿à¦¯à¦¼à§\87 à¦¨à¦¿à¦¤à§\87 à¦ªà¦¾à¦°à§\87ন।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
+       "movepagetalktext": "à¦\86পনি à¦¯à¦¦à¦¿ à¦\8fà¦\87 à¦¬à¦¾à¦\95à§\8dসà§\87 à¦\9fিà¦\95 à¦¦à§\87ন, à¦¤à¦¾à¦¹à¦²à§\87 à¦ªà¦¾à¦¤à¦¾à¦\9fির à¦¸à¦¾à¦¥à§\87 à¦¸à¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা à¦ªà¦¾à¦¤à¦¾à¦\9fিà¦\93 à¦¸à§\8dবয়à¦\82à¦\95à§\8dরিয়ভাবà§\87 à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà§\87 à¦¸à¦°à¦¾à¦¨à§\8b à¦¹à¦¬à§\87 à¦¯à¦¦à¦¿ à¦¨à¦¾ à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 à¦\8fà¦\95à¦\9fি à¦¬à¦¿à¦¦à§\8dযমান à¦¥à¦¾à¦\95à§\87।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
        "moveuserpage-warning": "'''সতর্কতা:''' আপনি একটি ব্যবহারকারী পাতা স্থানান্তর করছেন। অনুগ্রহ করে লক্ষ্য করুন যে এর মাধ্যমে কেবলমাত্র পাতাটি স্থানান্তর হবে, কিন্তু পাতার নাম পরিবর্তন হবে ''না''।",
        "movecategorypage-warning": "<strong>সতর্কীকরণ:</strong> আপনি একটি বিষয়শ্রেণীর পাতা স্থানান্তর করতে চলেছেন। দয়া করে মনে রাখবেন যে এতে শুধুমাত্র পাতাটি স্থানান্তরিত হবে এবং পুরাতন বিষয়শ্রেণীতে থাকা কোন পাতা নতুনটিতে পুনঃশ্রেণীকরণ করা হবে <em>না</em>।",
        "movenologintext": "কোন পাতা সরিয়ে ফেলতে চাইলে আপনাকে অবশ্যই একজন নিবন্ধিত ব্যবহারকারী হতে হবে ও অ্যাকাউন্টে [[Special:UserLogin|প্রবেশ]] করতে হবে।",
        "pageinfo-article-id": "পাতার আইডি",
        "pageinfo-language": "পাতার তথ্যের ভাষা",
        "pageinfo-content-model": "পাতার বিষয়বস্তুর মডেল",
+       "pageinfo-content-model-change": "পরিবর্তন",
        "pageinfo-robot-policy": "রোবটের মাধ্যমে ইন্ডেক্স করা হচ্ছে",
        "pageinfo-robot-index": "অনুমোদিত",
        "pageinfo-robot-noindex": "অনুনমোদিন",
        "tags-actions-header": "কার্যসমূহ",
        "tags-active-yes": "হ্যাঁ",
        "tags-active-no": "না",
-       "tags-source-extension": "à¦\8fà¦\95à¦\9fি à¦\8fà¦\95à§\8dসà¦\9fà§\87নশন দ্বারা সংজ্ঞায়িত",
+       "tags-source-extension": "সফà¦\9fà¦\93য়à§\8dযার দ্বারা সংজ্ঞায়িত",
        "tags-source-manual": "ব্যবহারকারী এবং বট দ্বারা ম্যানুয়ালি প্রয়োগ",
        "tags-source-none": "আর ব্যবহার করা হচ্ছে না",
        "tags-edit": "সম্পাদনা",
        "tags-delete-submit": "অপরিবর্তনীয় এই ট্যাগ অপসারন করো",
        "tags-delete-not-found": "\"$1\" ট্যাগ বিদ্যমান নয়।",
        "tags-activate-title": "সক্রিয় ট্যাগ",
+       "tags-activate-question": "আপনি ট্যাগ \"$1\" সক্রিয় করতে চলেছেন।",
        "tags-activate-reason": "কারণ:",
+       "tags-activate-not-allowed": "ট্যাগ \"$1\" সক্রিয় করা সম্ভব নয়।",
+       "tags-activate-not-found": "\"$1\" ট্যাগের অস্তিত্ব নেই।",
        "tags-activate-submit": "চালু",
        "tags-deactivate-title": "নিষ্ক্রিয় ট্যাগ",
+       "tags-deactivate-question": "আপনি ট্যাগ \"$1\" নিষ্ক্রিয় করতে চলেছেন।",
        "tags-deactivate-reason": "কারণ:",
+       "tags-deactivate-not-allowed": "ট্যাগ \"$1\" নিষ্ক্রিয় করা সম্ভব নয়।",
        "tags-deactivate-submit": "নিষ্ক্রিয়",
        "tags-edit-title": "ট্যাগ সম্পাদনা করুন",
        "tags-edit-manage-link": "ট্যাগ পরিচালনা করুন",
        "mediastatistics-header-office": "অফিস",
        "mediastatistics-header-archive": "সংকুচিত বিন্যাস",
        "mediastatistics-header-total": "সকল ফাইল",
+       "json-warn-trailing-comma": "JSON থেকে $1টি সর্বশেষ {{PLURAL:$1|কমা}} সরানো হয়েছে",
        "json-error-unknown": "JSON-এ একটি সমস্যা রয়েছে। ত্রুটি: $1",
+       "json-error-depth": "সর্বাধিক স্ট্যাকের গভীরতা অতিক্রম হয়েছে",
        "json-error-state-mismatch": "অকার্যকর বা ত্রুটিপূর্ণ JSON",
        "json-error-ctrl-char": "অক্ষর নিয়ন্ত্রণ ত্রুটি, সম্ভবত ভুল এনকোডকৃত",
        "json-error-syntax": "সিনট্যাক্স ত্রুটি",
        "log-action-filter-block": "বাধাদানের ধরন:",
        "log-action-filter-delete": "অপসারণের ধরন:",
        "log-action-filter-import": "আমদানির ধরন:",
+       "log-action-filter-managetags": "ট্যাগ ব্যবস্থাপনা কার্যের ধরন:",
+       "log-action-filter-move": "স্থানান্তরের ধরন:",
+       "log-action-filter-newusers": "অ্যাকাউন্ট তৈরির ধরন:",
        "log-action-filter-patrol": "টহলের ধরন:",
        "log-action-filter-protect": "সুরক্ষার ধরন:",
+       "log-action-filter-rights": "অধিকার পরিবর্তনের ধরন:",
        "log-action-filter-upload": "আপলোডের ধরন:",
        "log-action-filter-all": "সব",
        "log-action-filter-block-block": "বাধাদান",
        "log-action-filter-delete-revision": "সংশোধন অপসারণ",
        "log-action-filter-import-interwiki": "আন্তঃউইকি আমদানি",
        "log-action-filter-import-upload": "XML আপলোড কর্তৃক আমদানি",
+       "log-action-filter-managetags-create": "ট্যাগ সৃষ্টিকরণ",
+       "log-action-filter-managetags-delete": "ট্যাগ অপসারণ",
+       "log-action-filter-managetags-activate": "ট্যাগ সক্রিয়করণ",
+       "log-action-filter-managetags-deactivate": "ট্যাগ নিষ্ক্রিয়করণ",
        "log-action-filter-newusers-create": "বেনামী ব্যবহারকারী দ্বারা সৃষ্টি",
        "log-action-filter-newusers-create2": "নিবন্ধিত ব্যবহারকারী দ্বারা সৃষ্টি",
        "log-action-filter-newusers-autocreate": "স্বয়ংক্রিয় সৃষ্টি",
        "log-action-filter-rights-autopromote": "স্বয়ংক্রিয় পরিবর্তন",
        "log-action-filter-upload-upload": "নতুন আপলোড",
        "log-action-filter-upload-overwrite": "পুনঃআপলোড",
+       "authmanager-authn-no-primary": "সরবরাহকৃত পরিচয়পত্রের অনুমোদন যাচাই করা যায়নি।",
+       "authmanager-create-disabled": "অ্যাকাউন্ট সৃষ্টিকরণ নিষ্ক্রিয় করা হয়েছে।",
        "authmanager-create-from-login": "আপনার একাউন্ট তৈরি করতে, নীচের ক্ষেত্রগুলি পূরণ করুন।",
        "authmanager-authplugin-setpass-failed-title": "পাসওয়ার্ড পরিবর্তন ব্যর্থ হয়েছে",
        "authmanager-authplugin-setpass-bad-domain": "অবৈধ ডোমেইন।",
        "authprovider-confirmlink-success-line": "$1: সংযোগ করা সফল হয়েছে।",
        "authprovider-resetpass-skip-label": "উপেক্ষা করো",
        "authprovider-resetpass-skip-help": "পাসওয়ার্ড পুনঃস্থাপন করা উপেক্ষা করুন।",
+       "authform-newtoken": "টোকেন অনুপস্থিত। $1",
+       "authform-notoken": "টোকেন অনুপস্থিত",
        "authform-wrongtoken": "ভুল টোকেন",
        "specialpage-securitylevel-not-allowed-title": "অনুমতি নেই",
+       "specialpage-securitylevel-not-allowed": "দুঃখিত, আপনি এই পাতা ব্যবহার করতে অনুমতিপ্রাপ্ত নন কারণ আপনার পরিচয় যাচাই করা যায়নি।",
+       "authpage-cannot-login": "প্রবেশ শুরু করা সম্ভন নয়।",
        "cannotauth-not-allowed-title": "অনুমতি অস্বীকৃত",
        "cannotauth-not-allowed": "আপনি এই পাতাটি ব্যবহার করতে অনুমতিপ্রাপ্ত নন।",
        "changecredentials": "পরিচয়পত্র পরিবর্তন করুন",
        "removecredentials-success": "আপনার পরিচয়পত্র সরানো হয়েছে।",
        "credentialsform-provider": "পরিচয়পত্রের ধরন:",
        "credentialsform-account": "অ্যাকাউন্টের নাম:",
-       "linkaccounts": "অ্যাকাউন্ট সংযোগ করুন"
+       "linkaccounts": "অ্যাকাউন্ট সংযোগ করুন",
+       "linkaccounts-submit": "অ্যাকাউন্ট সংযুক্ত করুন",
+       "unlinkaccounts": "অ্যাকাউন্ট সংযোগ বিচ্ছিন্ন করুন",
+       "unlinkaccounts-success": "অ্যাকাউন্টের সংযোগ বিচ্ছিন্ন করা হয়েছে।",
+       "userjsispublic": "অনুগ্রহ করে লক্ষ্য করুন: জাভাস্ক্রিপ্টের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।",
+       "usercssispublic": "অনুগ্রহ করে লক্ষ্য করুন: সিএসএসের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।"
 }
index 8c3c3d8..5bea10a 100644 (file)
        "october-date": "$1 a viz Here",
        "november-date": "$1 a viz Du",
        "december-date": "$1 a viz Kerzu",
+       "period-am": "mintin",
+       "period-pm": "goude-merenn",
        "pagecategories": "{{PLURAL:$1|Rummad |Rummad }}",
        "category_header": "Niver a bennadoù er rummad \"$1\"",
        "subcategories": "Isrummadoù",
        "yourpasswordagain": "Skrivit ho ker-tremen en-dro",
        "createacct-yourpasswordagain": "Kadarnaat ar ger-tremen",
        "createacct-yourpasswordagain-ph": "Skrivit ar ger-tremen adarre",
-       "remembermypassword": "Derc'hel soñj eus ma ger-tremen war an urzhiataer-mañ (evit $1 devezh{{PLURAL:$1||}} d'ar muiañ)",
        "userlogin-remembermypassword": "Derc'hel ac'hanon kevreet",
        "userlogin-signwithsecure": "Implijout ur gevreadenn suraet",
        "yourdomainname": "Ho tomani",
        "passwordreset-emailtext-user": "Goulennet en deus an implijer $1 war  {{SITENAME}} e vefe degaset soñj dezhañ eus titouroù e gont evit {{SITENAME}} ($4). Emañ liammet {{PLURAL:$3|ar gont implijer|ar c'hontoù implijer}} da-heul gant ar chomlec'h postel-mañ :\n\n$2\n\nMont a raio da get {{PLURAL:$3|ar ger-tremen da c'hortoz|ar gerioù-tremen da c'hortoz}} a-benn {{PLURAL:$5|un devezh|$5 deiz}}.\nMat e vefe deoc'h kevreañ ha dibab ur ger-tremen nevez bremañ. Mard eo bet goulennet kement-se gant unan bennak all pe m'hoc'h eus soñj eus ho ker-tremen orin ha mar ne fell ket deoc'h e cheñch ken, na daolit ket evezh ouzh ar gemennadenn-mañ ha dalc'hit d'ober gant ho ker-tremen kozh.",
        "passwordreset-emailelement": "Anv implijer :           \n$1\n\nGer-tremen da c'hortoz : \n$2",
        "passwordreset-emailsentemail": "Kaset ez eus bet ur postel deoc'h da adderaouekaat ho ker-tremen.",
-       "passwordreset-emailsent-capture": "Ur postel evit aderaouekaat ho ker-tremen, evel diskouezet amañ dindan, zo bet kaset.",
-       "passwordreset-emailerror-capture": "Kaset ez eus bet ur postel degas da soñj evel m'emañ diskouezet amañ dindan met c'hwitet eo bet ar gasadenn d'an {{GENDER:$2|implijer|implijerez}} : $1",
        "changeemail": "Kemmañ ar chomlec'h postel",
        "changeemail-header": "Kemmañ chomlec'h postel ar gont",
        "changeemail-no-info": "Ret eo deoc'h bezañ kevreet a-benn mont d'ar bajenn-se war-eeun.",
        "minoredit": "Kemm dister",
        "watchthis": "Evezhiañ ar pennad-mañ",
        "savearticle": "Enrollañ ar bajenn",
+       "savechanges": "Enrollañ ar c'hemmoù",
+       "publishpage": "Embann ar bajenn",
+       "publishchanges": "Embann ar c'hemmoù",
        "preview": "Rakwelet",
        "showpreview": "Rakwelet",
        "showdiff": "Diskouez ar c'hemmoù",
        "undo-nochange": "War a seblant eo bet nullet ar c'hemm dija.",
        "undo-summary": "Dizober kemmoù $1 a-berzh [[Special:Contributions/$2|$2]] ([[User talk:$2|kaozeal]])",
        "undo-summary-username-hidden": "Dizober ar reizhadenn $1 gant un implijer kuzhet",
-       "cantcreateaccounttitle": "Dibosupl krouiñ ar gont",
        "cantcreateaccount-text": "Stanket eo bet ar c'hrouiñ kontoù adal ar chomlec'h IP ('''$1''') gant [[User:$3|$3]].\n\nAn abeg roet gant $3 zo ''$2''",
        "viewpagelogs": "Gwelet ar marilhoù evit ar bajenn-mañ",
        "nohistory": "Ar bajenn-mañ n'he deus tamm istor ebet.",
        "mostrevisions": "Pennadoù bet kemmet ar muiañ",
        "prefixindex": "An holl bajennoù a grog gant...",
        "prefixindex-namespace": "An holl bajennoù enno ur rakger (esaouenn anv $1)",
+       "prefixindex-submit": "Diskouez",
        "prefixindex-strip": "Lemel ar rakger er roll",
        "shortpages": "Pennadoù berr",
        "longpages": "Pennadoù hir",
        "usereditcount": "$1 {{PLURAL:$1|kemm|kemm}}",
        "usercreated": "{{GENDER:$3|Krouet}} d'an $1 da $2",
        "newpages": "Pajennoù nevez",
+       "newpages-submit": "Diskouez",
        "newpages-username": "Anv implijer :",
        "ancientpages": "Pennadoù koshañ",
        "move": "adenvel",
        "apisandbox": "Poull-traezh API",
        "apisandbox-api-disabled": "Diweredekaet eo API war al lec'hienn-mañ.",
        "apisandbox-intro": "Grit gant ar bajenn-mañ evit amprouiñ '''servij Web API MediaWiki'''.\nKit da deuler ur sell war [https://www.mediawiki.org/wiki/API:Main_page titouroù an API] evit gouzout hiroc'h war an doare da embreger API. Da skouer :\n[https://www.mediawiki.org/wiki/API#A_simple_example gwelet danvez ur bennbajenn]. Dibabit un oberiadenn bennak evit gwelet skouerioù all",
+       "apisandbox-unfullscreen": "Diskouez ar bajenn",
        "apisandbox-submit": "Sevel ar goulenn",
        "apisandbox-reset": "Riñsañ",
-       "apisandbox-examples": "Skouer",
-       "apisandbox-results": "Disoc'h",
+       "apisandbox-retry": "Klask en-dro",
+       "apisandbox-examples": "Skouerioù",
+       "apisandbox-dynamic-parameters": "Arventenn ouzhpenn",
+       "apisandbox-dynamic-parameters-add-label": "Ouzhpennañ un arventenn:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Anv an arventenn",
+       "apisandbox-results": "Disoc'hoù",
        "apisandbox-request-url-label": "Goulenn URL :",
        "apisandbox-request-time": "Pad ar goulenn: $1",
        "booksources": "Oberennoù dave",
        "specialloguserlabel": "Implijer :",
        "speciallogtitlelabel": "Bukadenn (titl pe implijer) :",
        "log": "Marilhoù",
+       "logeventslist-submit": "Diskouez",
        "all-logs-page": "An holl varilhoù foran",
        "alllogstext": "Diskwel a-gevret an holl varilhoù hegerz war {{SITENAME}}.\nGallout a rit strishaat ar mod diskwel en ur zibab ar marilh, an anv implijer (diwallit ouzh ar pennlizherennoù) pe ar bajenn a fell deoc'h (memes tra).",
        "logempty": "Goullo eo istor ar bajenn-mañ.",
        "log-title-wildcard": "Klask an titloù a grog gant an destenn-mañ",
        "showhideselectedlogentries": "Diskouez/kuzhat penngerioù ar marilh bet diuzet",
+       "checkbox-select": "Diuzañ : $1",
+       "checkbox-all": "An holl",
+       "checkbox-none": "Hini ebet",
+       "checkbox-invert": "Eilpennañ",
        "allpages": "An holl bajennoù",
        "nextpage": "Pajenn war-lerc'h ($1)",
        "prevpage": "Pajenn gent ($1)",
        "cachedspecial-viewing-cached-ts": "Emaoc'h o sellet ouzh ur stumm krubuilhet eus ar bajenn-mañ a c'hall bezañ dispredet un disterañ.",
        "cachedspecial-refresh-now": "Gwelet an hini nevesañ.",
        "categories": "Roll ar rummadoù",
+       "categories-submit": "Diskouez",
        "categoriespagetext": "Er {{PLURAL:$1|rummad|rummadoù}} da-heul ez eus pajennoù pe restroù media.\nNe ziskouezer ket amañ ar [[Special:UnusedCategories|Rummadoù dizimplij]].\nGwelet ivez ar [[Special:WantedCategories|rummadoù goulennet a vank]].",
        "categoriesfrom": "Diskouez ar rummadoù en ur gregiñ gant :",
        "deletedcontributions": "Degasadennoù diverket un implijer",
        "wlheader-showupdated": "E '''tev''' emañ merket ar pajennoù bet kemmet abaoe ar wezh ziwezhañ hoc'h eus sellet outo",
        "wlnote": "Setu aze {{PLURAL:$1|ar c'hemm diwezhañ|ar '''$1''' kemm diwezhañ}} c'hoarvezet e-kerzh an {{PLURAL:$2|eurvezh|'''$2''' eurvezh}} diwezhañ, d'an $3 da $4.",
        "wlshowlast": "Diskouez an $1 eurvezh $2 devezh diwezhañ",
+       "watchlist-hide": "Kuzhat",
+       "watchlist-submit": "Diskouez",
+       "wlshowhideminor": "kemmoù dister",
+       "wlshowhidebots": "robotoù",
+       "wlshowhideliu": "implijerien enrollet",
+       "wlshowhideanons": "implijerien dizanv",
        "wlshowhidemine": "ma c'hemmoù",
        "watchlist-options": "Dibarzhioù ar roll evezhiañ",
        "watching": "Heuliet...",
        "whatlinkshere-hidelinks": "$1 liamm",
        "whatlinkshere-hideimages": "$1 ar restroù liammet",
        "whatlinkshere-filters": "Siloù",
+       "whatlinkshere-submit": "Mont",
        "autoblockid": "Emstankañ #$1",
        "block": "Stankañ an implijer",
        "unblock": "Distankañ an implijer",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|adweladenn}} enporzhiet eus $2",
        "javascripttest": "Amprouadenn JavaScript",
        "javascripttest-qunit-intro": "Sellet ouzh [$1 an teulioù amprouiñ] e mediawiki.org.",
-       "tooltip-pt-userpage": "Ho pajenn implijer",
+       "tooltip-pt-userpage": "{{GENDER:|Ho pajenn}} implijer",
        "tooltip-pt-anonuserpage": "Ar bajenn implijer evit ar c'homlec'h IP implijet ganeoc'h",
-       "tooltip-pt-mytalk": "Ho pajenn gaozeal",
+       "tooltip-pt-mytalk": "{{GENDER:|Ho}} pajenn gaozeal",
        "tooltip-pt-anontalk": "Kaozeadennoù diwar-benn ar c'hemmoù graet adal ar chomlec'h-mañ",
-       "tooltip-pt-preferences": "Ma fenndibaboù",
+       "tooltip-pt-preferences": "{{GENDER:|Ma}} fenndibaboù",
        "tooltip-pt-watchlist": "Roll ar pajennoù evezhiet ganeoc'h.",
        "tooltip-pt-mycontris": "Roll ho tegasadennoù",
        "tooltip-pt-login": "Daoust ma n'eo ket ret, ec'h aliomp deoc'h kevreañ",
        "tooltip-t-recentchangeslinked": "Roll ar c'hemmoù diwezhañ war ar pajennoù liammet ouzh ar bajenn-mañ",
        "tooltip-feed-rss": "Magañ ar red RSS evit ar bajenn-mañ",
        "tooltip-feed-atom": "Magañ ar red Atom evit ar bajenn-mañ",
-       "tooltip-t-contributions": "Gwelet roll degasadennoù an implijer-mañ",
+       "tooltip-t-contributions": "Gwelet roll degasadennoù {{GENDER:$1|this user}} an implijer-mañ",
        "tooltip-t-emailuser": "Kas ur postel d'an implijer-mañ",
        "tooltip-t-upload": "Enporzhiañ ur skeudenn pe ur restr media war ar servijer",
        "tooltip-t-specialpages": "Roll an holl bajennoù dibar",
        "tooltip-ca-nstab-category": "Gwelet pajenn ar rummad",
        "tooltip-minoredit": "Merkañ ar c'hemm-mañ evel dister",
        "tooltip-save": "Enrollañ ho kemmoù",
+       "tooltip-publish": "Embann ho kemmoù",
        "tooltip-preview": "Rakwelet ar c'hemmoù; trugarez d'ober gantañ a-raok enrollañ!",
        "tooltip-diff": "Diskouez ar c'hemmoù degaset ganeoc'h en destenn.",
        "tooltip-compareselectedversions": "Sellet ouzh an diforc'hioù zo etre daou stumm diuzet ar bajenn-mañ.",
        "confirm-watch-top": "Ouzhpennañ ar bajenn-mañ d'ho roll evezhiañ",
        "confirm-unwatch-button": "Mat eo",
        "confirm-unwatch-top": "Lemel ar bajenn-mañ a-ziwar ho roll evezhiañ",
+       "confirm-rollback-button": "Mat eo",
        "quotation-marks": "« $1 »",
        "imgmultipageprev": "&larr; pajenn gent",
        "imgmultipagenext": "pajenn war-lerc'h &rarr;",
        "iranian-calendar-m11": "11vet miz Jalāli",
        "iranian-calendar-m12": "12vet miz Jalāli",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|kaozeal]])",
+       "timezone-local": "Lec'hel",
        "duplicate-defaultsort": "Diwallit : Frikañ a ra an alc'hwez dre ziouer \"$2\" an hini a oa a-raok \"$1\".",
        "version": "Stumm",
        "version-extensions": "Astennoù staliet",
        "tags-source-header": "Mammenn",
        "tags-active-header": "Oberiant ?",
        "tags-hitcount-header": "Kemmoù balizennet",
+       "tags-actions-header": "Oberoù",
        "tags-active-yes": "Ya",
        "tags-active-no": "Ket",
        "tags-edit": "aozañ",
        "special-characters-group-thai": "Thai",
        "special-characters-group-lao": "Laoseg",
        "special-characters-group-khmer": "Khmer",
-       "api-error-blacklisted": "Dibabit un titl deskrivañ all",
        "randomrootpage": "Pajenn wrizienn dargouezhek"
 }
index ee07df4..159f7e7 100644 (file)
@@ -41,6 +41,7 @@
        "tog-watchdefault": "Dodaj stranice i datoteke koje uređujem na moj spisak praćenih članaka",
        "tog-watchmoves": "Dodaj stranice i datoteke koje premjestim na moj spisak praćenih članaka",
        "tog-watchdeletion": "Dodaj stranice i datoteke koje izbrišem na moj spisak praćenih članaka",
+       "tog-watchuploads": "Dodaj datoteke koje postavim na moj spisak praćenja",
        "tog-watchrollback": "Sve stranice za koje sam izvršio povrat izmjena stavi na spisak praćenja",
        "tog-minordefault": "Označi sve izmjene manjim isprva",
        "tog-previewontop": "Prikaži pregled iznad okvira za uređivanje",
        "yourpasswordagain": "Ponovo upišite lozinku:",
        "createacct-yourpasswordagain": "Potvrdite lozinku",
        "createacct-yourpasswordagain-ph": "Unesite lozinku opet",
-       "remembermypassword": "Zapamti moju lozinku na ovom pregledniku (najduže $1 {{PLURAL:$1|dan|dana}})",
        "userlogin-remembermypassword": "Ostavi me prijavljenog/-u",
        "userlogin-signwithsecure": "Koristite sigurnu konekciju",
        "yourdomainname": "Vaš domen:",
        "minoredit": "Ovo je manja izmjena",
        "watchthis": "Prati ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
+       "savechanges": "Sačuvaj izmjene",
        "publishpage": "Objavi stranicu",
        "publishchanges": "Objavi izmjene",
        "preview": "Pregled stranice",
        "rev-deleted-user": "(korisničko ime uklonjeno)",
        "rev-deleted-event": "(stavka zapisa obrisana)",
        "rev-deleted-user-contribs": "[korisničko ime ili IP adresa uklonjeni - izmjena sakrivena u spisku doprinosa]",
-       "rev-deleted-text-permission": "Revizija ove stranice je '''obrisana'''.\nDetalje možete vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisu brisanja].",
+       "rev-deleted-text-permission": "Revizija ove stranice je '''obrisana'''.\nDetalje možete vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
        "rev-suppressed-text-permission": "Revizija ove stranice je <strong>prekrivena</strong>.\nDetalji se mogu naći u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisniku prekrivanja].",
-       "rev-deleted-text-unhide": "Revizija ove stranice je '''obrisana'''.\nDetalje o tome može se vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].\nVi je i dalje možete [$1 vidjeti ovu reviziju] ako želite da nastavite.",
-       "rev-suppressed-text-unhide": "Ova revizija stranice je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisu uklanjanja].\nVi je i dalje možete [$1 vidjeti ovu reviziju] ako želite.",
-       "rev-deleted-text-view": "Revizija ove stranice je '''obrisana'''.\nVi je možete vidjeti; detalji o tome se mogu vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisu brisanja].",
+       "rev-deleted-text-unhide": "Izmjena ove stranice je <strong>obrisana</strong>.\nDetalje možete vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].\nIpak možete [$1 vidjeti ovu izmjenu] ako želite nastaviti.",
+       "rev-suppressed-text-unhide": "Ova revizija stranice je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisu uklanjanja].\nVi i dalje možete [$1 vidjeti ovu reviziju] ako želite.",
+       "rev-deleted-text-view": "Revizija ove stranice je '''obrisana'''.\nVi je možete vidjeti; detalji o tome mogu se vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
        "rev-suppressed-text-view": "Ova revizija stranice je '''uklonjena'''.\nVi je možete vidjeti; možete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisu uklanjanja].",
        "rev-deleted-no-diff": "Ne možete vidjeti ovu razliku jer je jedna od izmjena '''obrisana'''.\nDetalji se nalaze u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
        "rev-suppressed-no-diff": "Ne možete vidjeti ove razlike jer je jedna od revizija '''obrisana'''.",
-       "rev-deleted-unhide-diff": "Jedna od revizija u ovom pregledu razlika je '''obrisana'''.\nMožete pregledati detalje u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].\nVi još uvijek možete [$1 vidjeti ove razlike] ako želite da nastavite.",
-       "rev-suppressed-unhide-diff": "edna od revizija ove razlike je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisniku uklanjanja].\nVi i dalje možete [$1 vidjeti ove razlike] ako želite da nastavite.",
-       "rev-deleted-diff-view": "Jedna od revizija u ovoj razlici je '''obrisana'''.\nVi možete vidjeti ovu razliku; detalji o tome se mogu vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
+       "rev-deleted-unhide-diff": "Jedna od revizija u ovom pregledu razlika je '''obrisana'''.\nMožete pregledati detalje u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].\nVi još uvijek možete [$1 vidjeti ove razlike] ako želite nastaviti.",
+       "rev-suppressed-unhide-diff": "Jedna od revizija ove razlike je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisniku uklanjanja].\nVi i dalje možete [$1 vidjeti ove razlike] ako želite nastaviti.",
+       "rev-deleted-diff-view": "Jedna od revizija u ovoj razlici je '''obrisana'''.\nVi možete vidjeti ovu razliku; detalji o tome mogu se vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
        "rev-suppressed-diff-view": "Jedna od revizija u ovoj razlici je '''sakrivena'''.\nVi možete vidjeti ovu razliku; detalji se mogu vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku sakrivanja].",
        "rev-delundel": "pokaži/sakrij",
        "rev-showdeleted": "prikaži",
index 8e71647..9e9e6b6 100644 (file)
        "yourpasswordagain": "Escriviu una altra vegada la contrasenya",
        "createacct-yourpasswordagain": "Confirmeu la contrasenya",
        "createacct-yourpasswordagain-ph": "Introduïu de nou la contrasenya",
-       "remembermypassword": "Recorda la contrasenya entre sessions (per un màxim de $1 {{PLURAL:$1|dia|dies}})",
        "userlogin-remembermypassword": "Mantén-me connectat",
        "userlogin-signwithsecure": "Connexió segura",
        "cannotloginnow-title": "Ara no es pot iniciar la sessió",
        "content-model-json": "JSON",
        "content-json-empty-object": "Objecte buit",
        "content-json-empty-array": "Matriu buida",
+       "deprecated-self-close-category": "Pàgines que usen etiquetes HTML autotancades no vàlides",
        "duplicate-args-warning": "<strong>Avís:</strong> [[:$1]] crida [[:$2]] amb més d'un valor pel paràmetre «$3». Només s'utilitzarà el darrer valor proporcionat.",
        "duplicate-args-category": "Pàgines amb arguments duplicats en utilització de plantilles",
        "duplicate-args-category-desc": "La pàgina conté crides a plantilles que fan servir duplicats d'arguments, com ara <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "trackingcategories-msg": "Categoria de seguiment",
        "trackingcategories-name": "Nom del missatge",
        "trackingcategories-desc": "Criteris d'inclusió de categoria",
+       "restricted-displaytitle-ignored": "Pàgines amb títols a mostrar ignorats",
        "noindex-category-desc": "La pàgina conté una paraula màgica <code><nowiki>__NOINDEX__</nowiki></code> (i és en un espai de noms on està permesa) i per tant no està indexada per robots.",
        "index-category-desc": "La pàgina conté un <code><nowiki>__INDEX__</nowiki></code> (i és en un espai de noms on està permès) i per tant està indexat per robots quan normalment no ho seria.",
        "post-expand-template-inclusion-category-desc": "La mida de la pàgina és més gran que <code>$wgMaxArticleSize</code> un cop expandides totes les plantilles, per tant algunes plantilles no s'han expandit.",
        "rollbacklinkcount": "reverteix $1 {{PLURAL:$1|edició|edicions}}",
        "rollbacklinkcount-morethan": "reverteix més de $1 {{PLURAL:$1|edició|edicions}}",
        "rollbackfailed": "No s'ha pogut revocar",
+       "rollback-missingrevision": "No es poden carregar les dades de revisió.",
        "cantrollback": "No s'han pogut revertir les edicions; el darrer col·laborador és l'únic autor de la pàgina.",
        "alreadyrolled": "No es pot revertir la darrera modificació de [[:$1]]\nde l'usuari [[User:$2|$2]] ([[User talk:$2|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]). Algú altre ja ha modificat o revertit la pàgina.\n\nLa darrera modificació l'ha fet l'usuari [[User:$3|$3]] ([[User talk:$3|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resum d'edició és: <em>$1</em>.",
        "changecontentmodel-success-text": "S'ha canviat el tipus de contingut de [[:$1]].",
        "changecontentmodel-cannot-convert": "El contingut a [[:$1]] no es pot convertir a un tipus de $2.",
        "changecontentmodel-nodirectediting": "El model de contingut $1 no permet l'edició directa",
+       "changecontentmodel-emptymodels-title": "No hi ha models de contingut",
+       "changecontentmodel-emptymodels-text": "El contingut a [[:$1]] no pot convertir-se a cap tipus.",
        "log-name-contentmodel": "Registre de canvis del model de contingut",
        "log-description-contentmodel": "Esdeveniments relacionats amb els models de contingut d'una pàgina",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|ha canviat}} el model de contingut de la pàgina $3 de «$4» a «$5»",
        "lockdbsuccesstext": "S'ha bloquejat la base de dades.<br />\nRecordeu-vos de [[Special:UnlockDB|treure el bloqueig]] quan hàgiu acabat el manteniment.",
        "unlockdbsuccesstext": "S'ha desbloquejat la base de dades del projecte {{SITENAME}}.",
        "lockfilenotwritable": "No es pot modificar el fitxer de la base de dades de bloquejos. Per a blocar o desblocar la base de dades, heu de donar-ne permís de modificació al servidor web.",
+       "databaselocked": "La bases de dades ja està bloquejada.",
        "databasenotlocked": "La base de dades no està bloquejada.",
        "lockedbyandtime": "(per $1 el $2 a les $3)",
        "move-page": "Reanomena $1",
        "redirect-page": "ID de pàgina",
        "redirect-revision": "Revisió de la pàgina",
        "redirect-file": "Nom del fitxer",
+       "redirect-logid": "ID de registre",
        "redirect-not-exists": "No s'ha trobat el valor",
        "fileduplicatesearch": "Cerca fitxers duplicats",
        "fileduplicatesearch-summary": "Cerca fitxers duplicats d'acord amb el seu valor de resum.",
        "tags-edit-title": "Modifica les etiquetes",
        "tags-edit-manage-link": "Gestiona les etiquetes",
        "tags-edit-revision-selected": "{{PLURAL:$1|Revisió seleccionada|Revisions seleccionades}} de [[:$2]]:",
+       "tags-edit-revision-legend": "Afegeix o suprimeix etiquetes {{PLURAL:$1|d'aquesta revisió|de totes les $1 revisions}}",
        "tags-edit-logentry-legend": "Afegeix o suprimeix etiquetes {{PLURAL:$1|d'aquesta entrada del registre|de totes les entrades del registre}}",
        "tags-edit-existing-tags": "Etiquetes existents:",
        "tags-edit-existing-tags-none": "<em>Cap</em>",
index dcc4c5c..088c746 100644 (file)
        "yourpasswordagain": "Юха язъе пароль:",
        "createacct-yourpasswordagain": "Бакъе пароль",
        "createacct-yourpasswordagain-ph": "Кхин цкъа язъе пароль",
-       "remembermypassword": "Даглаца сан дӀаяздар хӀокху компьютеран тӀехь (цхьан $1 {{PLURAL:$1|дийнахь}})",
        "userlogin-remembermypassword": "Системин чохь Ӏойла",
        "userlogin-signwithsecure": "Ларийна цхьаьнакхетар",
        "cannotloginnow-title": "ХӀинца чудаха таро яц",
        "minoredit": "Жима хийцам",
        "watchthis": "ХӀара агӀо тергаме могӀанан юкъатоха",
        "savearticle": "АгӀо дӀаязъян",
+       "savechanges": "Ӏалашбе хийцамаш",
+       "publishpage": "АгӀо кхолла",
        "preview": "Хьалххе хьажар",
        "showpreview": "Хьалха хьажар",
        "showdiff": "Бина болу хийцамашка хьажар",
        "page_last": "тlаьххьара",
        "histlegend": "Кхетор: (хӀинцалера.) — йолучу башхон къастам; (хьалх.) — хьалхалерчу башхон къастам; '''ж''' — жимо бозалца болу хийцам.",
        "history-fieldset-title": "АгӀона хийцамаш",
-       "history-show-deleted": "Ð\94Ó\80аÑ\8fÑ\85инарш",
+       "history-show-deleted": "Ð\94Ó\80аÑ\8fÑ\8cÑ\85нарш",
        "histfirst": "ширниш",
        "histlast": "хьалхарниш",
        "historysize": "($1 {{PLURAL:$1|байт}})",
        "searchprofile-articles-tooltip": "$1 чохь лахар",
        "searchprofile-images-tooltip": "Файлаш лахар",
        "searchprofile-everything-tooltip": "Массо агӀонашкахь лахар (дийцаре агӀонашца)",
-       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð°Ð½ашкахь лахар",
+       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар",
        "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешнаш}})",
        "search-result-category-size": "$1 {{PLURAL:$1|юкъаяр}} ($2 {{PLURAL:$2|1=бухара категори|бухара категореш}}, $3 {{PLURAL:$3|1=файл|файлаш}}).",
        "search-redirect": "(дӀасахьажийна $1)",
        "search-showingresults": "{{PLURAL:$4|Карийна <strong>$1</strong> — цхьаъ агӀо|И дош карийна <strong>$3</strong> агӀонашкахь, царех гойту $2 агӀо}}",
        "search-nonefound": "Дехаре терра цхьа хӀума ца карийна.",
        "powersearch-legend": "Шуьйра лахар",
-       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð°Ð½ашкахь лахар:",
+       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар:",
        "powersearch-togglelabel": "Билгалдан:",
        "powersearch-toggleall": "Массо",
        "powersearch-togglenone": "ХӀумма цаоьшу",
        "tooltip-ca-nstab-category": "Категорешан агӀо",
        "tooltip-minoredit": "Къастам бé хӀокху хийцамна кӀеззиг болуш санна",
        "tooltip-save": "Хьан хийцамаш Ӏалашбой",
+       "tooltip-publish": "Гучубаха хьой бина хийцамаш",
        "tooltip-preview": "Дехар до, агӀо Ӏалаш ярал хьалха хьажа муха ю из!",
        "tooltip-diff": "Гайта долуш долу йозанах бина болу хийцам.",
        "tooltip-compareselectedversions": "ХӀокху агӀона шина хаьржина версийн башхалле хьажар.",
index 1fb2401..71a2c6b 100644 (file)
        "actionthrottled": "چالاکی پێشی پێ گیرا",
        "actionthrottledtext": "بە مەبەستی پێشگریی لە سپەم، ڕێگە نادرێت تۆ لە ماوەیەکی کورت دا لە سەر یەک ئەمە زۆر جار ئەنجام بدەی، وە ئیستا تۆ لە ڕادە بەدەرت کردووە.\nتکایە پاش چەند خولەک دووبارە تاقی بکەوە.",
        "protectedpagetext": "بۆ بەرگری لە دەستکاریکردن یان چالاکییەکانی تر ئەم پەڕەیە پارێزراوە.",
-       "viewsourcetext": "دەتوانی سەرچاوەی ئەم پەڕە ببینی و کۆپیی بکەی:",
-       "viewyourtext": "دەتوانی ژێدەری '''دەستکارییەکەت''' لەم پەڕەیەدا ببینی و کۆپی بکەی:",
+       "viewsourcetext": "دەتوانی سەرچاوەی ئەم پەڕە ببینی و کۆپیی بکەی٫",
+       "viewyourtext": "دەتوانی ژێدەری <strong>دەستکارییەکەت</strong> لەم پەڕەیەدا ببینی و کۆپی بکەی.",
        "protectedinterface": "ئەم پەڕەیە دەقی ڕواڵەتی نەرمامێری ئەم ویکییە نیشان دەدات و بۆ بەرگری لە خراپکاری پارێزراوە.\nبۆ زیادکردن یان گۆڕینی وەرگێڕانەکان بۆ ھەموو ویکییەکان، تکایە لە [https://translatewiki.net/ translatewiki.net]، پرۆژەی ناوچەیی کردنی میدیاویکی کەڵک وەربگرە.",
        "editinginterface": "<strong>ھۆشیار بە:</strong> خەریکی دەستکاریی پەڕەیەک دەکەیت کە بۆ دابین کردنی دەقی ڕووکاری نەرمامێر بەکاردێت.\nگۆڕانکارییەکان لەم پەڕەیەدا لە سەر ڕواڵەتی پەڕەکان بۆ بەکارھێنەرانی تر لەم ویکییەدا کاریگەر دەبێت.",
        "cascadeprotected": "ئەم لاپەڕە پارێزراوە لە دەستکاریی، چونکا خراوەتە سەر ڕیزی ئەم {{PLURAL:$1|لاپەڕانه‌، کە}} که‌ به‌ هه‌ڵکردنی بژارده‌ی داڕژان هه‌ڵکراوه‌:\n$2",
        "yourpasswordagain": "دیسان تێپەڕوشەکە بنووسەوە:",
        "createacct-yourpasswordagain": "تێپەروشە پشتڕاست بکەرەوە",
        "createacct-yourpasswordagain-ph": "تێپەروشە دیسان بنووسەوە",
-       "remembermypassword": "چوونە ژوورەوەم لەسەر ئەم کۆمپیوتەرە پاشەکەوت بکە (ئەو پەڕی $1 {{PLURAL:$1|ڕۆژ}}ە)",
        "userlogin-remembermypassword": "چوونەژوورەوەکەم ڕابگرە",
        "userlogin-signwithsecure": "پەیوەندیی دڵنیا بەکاربھێنە",
        "yourdomainname": "دۆمەینەکەت:",
        "newpassword": "تێپەڕوشەی نوێ:",
        "retypenew": "تێپەڕوشەی نوێ دوبارە بنووسەوە:",
        "resetpass_submit": "تێپەڕوشە رێکخە و بچۆ ژوورەوە",
-       "changepassword-success": "تێپەروشەکەت بە سەرکەوتوویی گۆڕدرا!",
+       "changepassword-success": "تێپەڕەوشەکەت  گۆڕدرا!",
        "botpasswords-label-create": "دروستکردن",
        "botpasswords-label-update": "نوێکردنەوە",
        "botpasswords-label-cancel": "ھەڵوەشاندنەوە",
        "minoredit": "ئەمە دەستکارییەکی بچووکە",
        "watchthis": "ئەم پەڕەیە بخە ژێر چاودێری",
        "savearticle": "پەڕەکە پاشەکەوت بکە",
+       "savechanges": "پاشەکەوتکردنی گۆڕانکارییەکان",
        "preview": "پێشبینین",
        "showpreview": "پێشبینین نیشان بدە",
        "showdiff": "گۆڕانکارییەکان نیشان بدە",
        "prefs-watchlist-token": "ڕەمزی لیستی چاودێری:",
        "prefs-misc": "جۆراوجۆر",
        "prefs-resetpass": "تێپەڕوشە بگۆڕە",
-       "prefs-changeemail": "ئەدرەسی ئیمەیل بگۆڕە",
+       "prefs-changeemail": "ئەدرەسی ئیمەیل بگۆڕە یان لایبەرە",
        "prefs-setemail": "ناونیشانێکی ئیمەیل دیاری بکە",
        "prefs-email": "ھەڵبژاردەکانی ئیمەیل",
        "prefs-rendering": "ڕواڵەت",
        "rollbackfailed": "گەڕاندنەوە سەرکەوتوو نەبوو",
        "cantrollback": "دەستکاریەکان ناگەڕێندرێتەوە؛\nدوایین هاوبەش تەنها ڕێکخەری ئەم لاپەڕەیە.",
        "alreadyrolled": "دوایین گۆڕانکارییەکان لەسەر [[:$1]] لە لایەن [[User:$2|$2]] ناگەڕێندرێنەوە ([[User talk:$2|لێدوان]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])؛ کەسێکی تر لە پێشدا دەستکاریی کردووە یان گەڕاندوویەتەوە.\n\nدوایین دەستکاری ئەم پەڕە [[User:$3|$3]] کردوویە ([[User talk:$3|لێدوان]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
-       "editcomment": "پوختەی دەستکاری \"''$1''\" بوو.",
+       "editcomment": "پوختەی دەستکاری <em>$1</em> بوو.",
        "revertpage": "گەڕاندنەوەی دەستکارییەکانی [[Special:Contributions/$2|$2]] ([[User talk:$2|لێدوان]]) بۆ دوایین پێداچوونەوەی [[User:$1|$1]]",
        "revertpage-nouser": "دەستکارییەکانی بەکارھێنەرێکی شاڕدراوە بۆ دوایین پێداچوونەوەی {{GENDER:$1|[[User:$1|$1]]}} گەڕێنرایەوە.",
        "rollback-success": "دەستکارییەکانی $1 وەرگێرایەوە؛<br />\nگۆڕدرا بۆ دوایین پێداچوونەوەی $2.",
        "sp-contributions-newbies-sub": "بۆ ھەژمارە نوێکان",
        "sp-contributions-newbies-title": "بەشدارییەکانی بەکارھێنەر بۆ ھەژمارە نوێکان",
        "sp-contributions-blocklog": "لۆگی بەربەستن",
-       "sp-contributions-deleted": "بەشدارییە سڕاوەکان",
+       "sp-contributions-deleted": "بەشدارییە سڕاوەکانی {{GENDER:$1|بەکارھێنەر}}",
        "sp-contributions-uploads": "بارکردنەکان",
        "sp-contributions-logs": "لۆگەکان",
        "sp-contributions-talk": "لێدوان",
        "whatlinkshere-next": "{{PLURAL:$1|دیکە|$1ی تر}}",
        "whatlinkshere-links": "← بەستەرەکان",
        "whatlinkshere-hideredirs": "ڕەوانەکەرەکان $1",
-       "whatlinkshere-hidetrans": "$1 ھێنانەناوەوەکان",
+       "whatlinkshere-hidetrans": "ھێنانەناوەوەکان $1",
        "whatlinkshere-hidelinks": "$1 بەستەر",
        "whatlinkshere-hideimages": "$1 بەستەرەکانی پەڕگە",
        "whatlinkshere-filters": "پاڵێوکەکان",
        "tooltip-feed-rss": "RSS feed بۆ ئەم پەڕە",
        "tooltip-feed-atom": "Atom feed بۆ ئەم پەڕە",
        "tooltip-t-contributions": "پێڕستی بەشدارییەکانی {{GENDER:$1|ئەم بەکارھێنەرە}}",
-       "tooltip-t-emailuser": "ئیمەیلێک بنێرە بۆ ئەم بەکارھێنەرە",
+       "tooltip-t-emailuser": "ئیمەیڵێک بنێرە بۆ {{GENDER:$1|ئەم بەکارھێنەرە}}",
        "tooltip-t-upload": "پەڕگە بار بکە",
        "tooltip-t-specialpages": "پێڕستی ھەموو پەڕە تایبەتەکان",
        "tooltip-t-print": "وەشانی چاپی ئەم پەڕەیە",
        "lastmodifiedatby": "ئەم پەڕە دواجار لە $2ی $1 بە دەستی $3 گۆڕدراوە.",
        "othercontribs": "لەسەر بنەمای کاری $1.",
        "others": "ئەوانی دیکە",
-       "siteusers": "{{PLURAL:$2|بەکارھێنەری|بەکارھێنەرانی}} {{SITENAME}} $1",
+       "siteusers": "{{SITENAME}} {{PLURAL:$2|{{GENDER:$1|بەکارھێنەری}}|بەکارھێنەرانی}} $1",
        "anonusers": "{{PLURAL:$2|بەکارھێنەر|بەکارھێنەر}}ی نامۆی {{SITENAME}} $1",
        "creditspage": "بایەخەکانی لاپەڕە",
        "nocredits": "هیچ زانیارییەکی بایەخ لەبەردەست‌دا نیە بۆ ئەم لاپەڕە.",
        "fileduplicatesearch-result-n": "پەڕگەی «$1» {{PLURAL:$2|١ دووپاتکراوەی کوتوموتی|$2 دووپاتکراوەی کوتوموتی}} ھەیە.",
        "fileduplicatesearch-noresults": "پەڕگەیەک بە ناوی «$1» نەدۆزرایەوە.",
        "specialpages": "پەڕە تایبەتەکان",
-       "specialpages-note": "* Ù¾Û\95Ú\95Û\95 ØªØ§Û\8cبÛ\95تÛ\95 Ø¦Ø§Ø³Ø§Û\8cÛ\8cÛ\8cÛ\95کاÙ\86.\n* <span class=\"mw-specialpagerestricted\">Ù¾Û\95Ú\95Û\95 ØªØ§Û\8cبÛ\95تÛ\95 Ø¨Û\95رگرÛ\8câ\80\8cÙ\84Û\8eکراÙ\88Û\95کاÙ\86.</span>",
+       "specialpages-note": "* Ù¾Û\95Ú\95Û\95 ØªØ§Û\8cبÛ\95تÛ\95 Ø¦Ø§Ø³Ø§Û\8cÛ\8cÛ\95کاÙ\86.\n* <span class=\"mw-specialpagerestricted\">Ù¾Û\95Ú\95Û\95 ØªØ§Û\8cبÛ\95تÛ\95 Ø¨Û\95رگرÛ\8cÙ\84Û\8eکراÙ\88Û\95کاÙ\86.</span>",
        "specialpages-group-maintenance": "ڕاپۆرتەکانی چاکسازی",
        "specialpages-group-other": "پەڕە تایبەتەکانی دیکە",
        "specialpages-group-login": "چوونەژوورەوە / دروستکردنی ھەژمار",
        "compare-invalid-title": "ئەم سەردێڕە دەستنیشانت کردووە نادروستە.",
        "dberr-problems": "ببورە! ئەم ماڵپەڕە ئێستا خەریک ئەزموونێکی کێشەی تەکنیکیە.",
        "dberr-again": "چەن خولک ڕاوەستە و نوێی بکەوە.",
-       "dberr-info": "(Ù¾Û\95Û\8cÙ\88Û\95Ù\86دÛ\8c Ù\84Û\95Ú¯Û\95Úµ Ú\95اÚ\98Û\95کارÛ\8c Ø¨Ù\86Ú©Û\95دراÙ\88 Ù¾Û\8eÚ©Ù\86اÛ\8cÛ\95ت: $1)",
+       "dberr-info": "(Ù\86اتÙ\88اÙ\86Û\8cت Ø¨Ú¯Û\95Û\8cت Ø¨Û\95 Ø¨Ù\86Ú©Û\95دراÙ\88: $1)",
        "dberr-usegoogle": "دەتوانی هاوکات هەوڵی گەڕان بە گووگڵ بدەیت.",
        "dberr-outofdate": "لەیادت بێ لەوانەیە پێرستەکەیان سەبارەت نە ناوەڕۆک ئەم ماڵپەڕە ماوە بەسەرچوو بێت.",
        "dberr-cachederror": "ئەمە ڕوونووسێکی کاش‌کراوی لاپەڕەی داواکراوە و لەوانەیە بەڕۆژ نەبێت.",
        "logentry-newusers-autocreate": "ھەژماری بەکارھێنەریی $1 بە شێوەی خۆگەڕ {{GENDER:$2|دروست کرا}}",
        "logentry-protect-protect": "$1 $3ی {{GENDER:$2|پاراست}} $4",
        "logentry-protect-modify": "$1 ئاستی پاراستنی $3ی {{GENDER:$2|گۆڕی}} $4",
-       "logentry-rights-rights": "$1 ئەندامێتیی $3ی لە $4 بۆ $5 {{GENDER:$2|گۆڕی}}",
+       "logentry-rights-rights": "$1 ئەندامێتیی {{GENDER:$6|$3}}ی لە $4 بۆ $5 {{GENDER:$2|گۆڕی}}",
        "logentry-upload-upload": "$1 $3ی {{GENDER:$2|بار کرد}}",
        "logentry-upload-overwrite": "$1 وەشانێکی نوێی $3ی {{GENDER:$2|بار کرد}}",
        "rightsnone": "(ھیچ)",
index 7e9eecf..83a72b4 100644 (file)
        "yourpasswordagain": "Zopakujte heslo:",
        "createacct-yourpasswordagain": "Potvrzení hesla",
        "createacct-yourpasswordagain-ph": "Zadejte heslo ještě jednou",
-       "remembermypassword": "Zapamatovat si mé přihlášení na tomto počítači (maximálně $1 {{PLURAL:$1|den|dny|dní}})",
        "userlogin-remembermypassword": "Přihlásit trvale",
        "userlogin-signwithsecure": "Používat zabezpečené připojení",
+       "cannotlogin-title": "Nelze se přihlásit",
+       "cannotlogin-text": "Přihlášení není možné.",
        "cannotloginnow-title": "Momentálně se nelze přihlásit",
        "cannotloginnow-text": "Přihlášení není možné, když se používají $1.",
+       "cannotcreateaccount-title": "Nelze zakládat uživatelské účty",
+       "cannotcreateaccount-text": "Přímé zakládání účtů není na této wiki povoleno.",
        "yourdomainname": "Vaše doména",
        "password-change-forbidden": "Na této wiki nemůžete měnit hesla.",
        "externaldberror": "Buď nastala chyba externí autentizační databáze, nebo nemáte dovoleno měnit svůj externí účet.",
        "botpasswords-updated-body": "Heslo pro bota jménem „$1“ {{GENDER:$2|uživatele|uživatelky}} „$2“ bylo aktualizováno.",
        "botpasswords-deleted-title": "Heslo pro bota smazáno",
        "botpasswords-deleted-body": "Heslo pro bota jménem „$1“ {{GENDER:$2|uživatele|uživatelky}} „$2“ bylo smazáno.",
-       "botpasswords-newpassword": "Nové přihlašovací heslo pro bota <strong>$1</strong> je <strong>$2</strong>. <em>Zaznamenejte si je pro budoucí použití.</em>",
+       "botpasswords-newpassword": "Nové přihlašovací heslo pro bota <strong>$1</strong> je <strong>$2</strong>. <em>Zaznamenejte si je pro budoucí použití.</em> <br> (Pro staré boty, vyžadující, aby přihlašovací jméno bylo stejné jako následné uživatelské jméno, můžete také jako uživatelské jméno použít <strong>$3</strong> a jako heslo <strong>$4</strong>.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider není dostupný.",
        "botpasswords-restriction-failed": "Toto přihlášení bylo zamítnuto omezením hesel pro boty.",
        "botpasswords-invalid-name": "Uvedené uživatelské jméno neobsahuje oddělovač hesel pro boty („$1“).",
        "invalid-content-data": "Obsažená data jsou chybná",
        "content-not-allowed-here": "Obsah typu $1 není na stránce [[$2]] dovolen.",
        "editwarning-warning": "Opuštěním této stránky se mohou veškeré provedené změny ztratit.\nPřihlášení uživatelé si mohou toto varování vypnout na záložce „{{int:prefs-editing}}“ v uživatelském nastavení.",
+       "editpage-invalidcontentmodel-title": "Nepodporovaný model obsahu",
+       "editpage-invalidcontentmodel-text": "Model obsahu „$1“ není podporován.",
        "editpage-notsupportedcontentformat-title": "Nepodporovaný formát obsahu",
        "editpage-notsupportedcontentformat-text": "Model obsahu $2 nepodporuje formát obsahu $1.",
        "content-model-wikitext": "wikitext",
        "file-thumbnail-no": "Jméno souboru začíná na <strong>$1</strong>.\nMožná to je obrázek ve zmenšené velikosti ''(náhled)''.\nNačtěte soubor v plném rozlišením, pokud je k dispozici, nebo změňte jméno souboru.",
        "fileexists-forbidden": "Soubor s tímto názvem již existuje a není dovoleno ho přepsat.\nPokud chcete přesto soubor načíst, vraťte se a zvolte jiný název.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Soubor s tímto názvem již existuje ve sdíleném úložišti. Pokud přesto chcete váš soubor načíst, vraťte se a zvolte jiný název. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Načítaný soubor je přesným duplikátem aktuální revize souboru <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Načítaný soubor je přesným duplikátem {{PLURAL:$2|starší revize|starších revizí}} souboru <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Tento soubor je duplikát {{PLURAL:$1|následujícího souboru|následujících souborů}}:",
        "file-deleted-duplicate": "Identický soubor k tomuto ([[:$1]]) byl již dříve smazán. Před tím, než soubor znovu nahrajete, byste měli zkontrolovat záznamy o předchozím smazání.",
        "file-deleted-duplicate-notitle": "Identický soubor k tomuto byl již dříve smazán a název byl utajen.\nPřed tím, než soubor znovu nahrajete, byste měli požádat někoho, kdo může prohlížet utajené soubory, aby situaci zkontroloval.",
        "filerevert-submit": "Vrátit zpět",
        "filerevert-success": "Soubor '''[[Media:$1|$1]]''' byl vrácen zpět na [$4 verzi z $3 $2].",
        "filerevert-badversion": "Není dostupná předchozí verze tohoto souboru s odpovídající časovou značkou.",
+       "filerevert-identical": "Aktuální verze souboru se již od vybrané verze neliší.",
        "filedelete": "Smazání souboru $1",
        "filedelete-legend": "Smazat soubor",
        "filedelete-intro": "Chystáte se smazat soubor '''[[Media:$1|$1]]''' i s celou historií.",
        "rollbacklinkcount-morethan": "vrácení více než $1 {{PLURAL:$1|editace|editací}} zpět",
        "rollbackfailed": "Nešlo vrátit zpět",
        "rollback-missingparam": "V požadavku chybí povinné parametry.",
+       "rollback-missingrevision": "Nepodařilo se načíst data revize.",
        "cantrollback": "Nelze vrátit zpět poslední editaci, neboť poslední přispěvatel je jediným autorem této stránky.",
        "alreadyrolled": "Nelze vrátit zpět poslední editaci [[:$1]] od uživatele [[User:$2|$2]] ([[User talk:$2|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), protože někdo jiný již stránku editoval nebo vrátil tuto změnu zpět.\n\nPoslední editaci této stránky {{GENDER:$3|provedl|provedla|provedl uživatel}} [[User:$3|$3]] ([[User talk:$3|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Shrnutí editace bylo: <em>$1</em>.",
        "changecontentmodel-nodirectediting": "Model obsahu $1 nepodporuje přímou editaci",
        "changecontentmodel-emptymodels-title": "Nejsou k dispozici žádné modely obsahu",
        "changecontentmodel-emptymodels-text": "Obsah stránky [[:$1]] nelze zkonvertovat na žádný typ.",
-       "log-name-contentmodel": "Kniha změny modelů obsahu",
+       "log-name-contentmodel": "Kniha změn modelů obsahu",
        "log-description-contentmodel": "Události týkající se modelů obsahu stránek",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|založil|založila}} stránku $3 za použití nestandardního modelu obsahu „$5“",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|změnil|změnila}} model obsahu stránky $3 z „$4“ na „$5“",
        "pageinfo-article-id": "ID stránky",
        "pageinfo-language": "Jazyk obsahu stránky",
        "pageinfo-content-model": "Model obsahu stránky",
+       "pageinfo-content-model-change": "změnit",
        "pageinfo-robot-policy": "Indexování roboty",
        "pageinfo-robot-index": "Dovoleno",
        "pageinfo-robot-noindex": "Zakázáno",
        "tag-filter": "Filtr podle [[Special:Tags|značek]]:",
        "tag-filter-submit": "Filtrovat",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Značka|Značky}}]]: $2)",
+       "tag-mw-contentmodelchange": "změna modelu obsahu",
+       "tag-mw-contentmodelchange-description": "Editace, které [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel mění obsah modelu] stránky",
        "tags-title": "Značky",
        "tags-intro": "Tato stránka obsahuje seznam značek, kterými může software označovat jednotlivé editace, a jejich významy.",
        "tags-tag": "Název značky",
        "tags-actions-header": "Akce",
        "tags-active-yes": "Ano",
        "tags-active-no": "Ne",
-       "tags-source-extension": "Definována rozšířením",
+       "tags-source-extension": "Definována softwarem",
        "tags-source-manual": "Přidávána ručně uživateli a boty",
        "tags-source-none": "Už nepoužívána",
        "tags-edit": "editovat",
        "linkaccounts-submit": "Propojit účty",
        "unlinkaccounts": "Zrušení propojení účtů",
        "unlinkaccounts-success": "Propojení účtu bylo zrušeno.",
-       "authenticationdatachange-ignored": "Změna autentizačních údajů nebyla zpracována. Možná není nakonfigurován žádný poskytovatel?"
+       "authenticationdatachange-ignored": "Změna autentizačních údajů nebyla zpracována. Možná není nakonfigurován žádný poskytovatel?",
+       "userjsispublic": "Uvědomte si prosím, že podstránky s JavaScriptem by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům.",
+       "usercssispublic": "Uvědomte si prosím, že podstránky s CSS by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům."
 }
index 4fb3f7b..c6bd656 100644 (file)
        "yourpasswordagain": "Gentag adgangskode",
        "createacct-yourpasswordagain": "Bekræft adgangskode",
        "createacct-yourpasswordagain-ph": "Indtast adgangskode igen",
-       "remembermypassword": "Husk mit brugernavn i denne browser (højst $1 {{PLURAL:$1|dag|dage}})",
        "userlogin-remembermypassword": "Husk mig",
        "userlogin-signwithsecure": "Brug sikker forbindelse",
        "cannotloginnow-title": "Kan ikke logge ind på nuværende tidspunkt",
        "minoredit": "Dette er en mindre ændring",
        "watchthis": "Overvåg denne side",
        "savearticle": "Gem side",
+       "savechanges": "Gem ændringer",
        "publishpage": "Offentliggør side",
        "publishchanges": "Offentliggør ændringer",
        "preview": "Forhåndsvisning",
        "rightslogtext": "Dette er en log over ændringer i brugeres rettigheder.",
        "action-read": "se denne side",
        "action-edit": "redigere denne side",
-       "action-createpage": "oprette sider",
-       "action-createtalk": "oprette diskussionssider",
+       "action-createpage": "oprette denne side",
+       "action-createtalk": "oprette denne diskussionsside",
        "action-createaccount": "Oprette denne brugerkonto",
        "action-history": "se historik for denne side",
        "action-minoredit": "markere denne redigering som mindre",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Navn",
        "upload-form-label-infoform-description": "Beskrivelse",
+       "upload-form-label-usage-title": "Anvendelse",
        "upload-form-label-usage-filename": "Filnavn",
        "upload-form-label-infoform-categories": "Kategorier",
        "upload-form-label-infoform-date": "Dato",
        "log-title-wildcard": "Søg i titler som begynder med teksten",
        "showhideselectedlogentries": "Vis/skjul de markerede loghændelser",
        "log-edit-tags": "Rediger tags i valgte logposter",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Ingen",
        "allpages": "Alle sider",
        "nextpage": "Næste side ($1)",
        "prevpage": "Forrige side ($1)",
        "listgrouprights-namespaceprotection-header": "Navnerumsbegrænsninger",
        "listgrouprights-namespaceprotection-namespace": "Navnerum",
        "listgrouprights-namespaceprotection-restrictedto": "Rettighed(er) der giver brugeren mulighed for at redigere",
+       "listgrants-rights": "Rettigheder",
        "trackingcategories": "Sporingskategorier",
        "trackingcategories-summary": "Denne side viser sporing af de kategorier, som er automatisk udfyldt af MediaWiki-softwaren. Deres navne kan ændres ved at ændre de relevante system-beskeder i {{ns:8}}-navnerummet.",
        "trackingcategories-msg": "Sporingskategori",
index 5e989ef..d5f52f2 100644 (file)
        "tog-enotifminoredits": "Auch bei kleinen Änderungen an Seiten und Dateien E-Mails senden",
        "tog-enotifrevealaddr": "Meine E-Mail-Adresse in Benachrichtigungs-E-Mails anzeigen",
        "tog-shownumberswatching": "Anzahl der beobachtenden Benutzer anzeigen",
-       "tog-oldsig": "Vorhandene Signatur:",
+       "tog-oldsig": "Deine vorhandene Signatur:",
        "tog-fancysig": "Signatur als Wikitext behandeln (ohne automatische Verlinkung)",
        "tog-uselivepreview": "Vorschau sofort anzeigen",
        "tog-forceeditsummary": "Warnen, sofern beim Speichern die Zusammenfassung fehlt",
        "tog-showhiddencats": "Versteckte Kategorien anzeigen",
        "tog-norollbackdiff": "Unterschied nach dem Zurücksetzen nicht anzeigen",
        "tog-useeditwarning": "Warnen, sofern eine zur Bearbeitung geöffnete Seite verlassen wird, die nicht gespeicherte Änderungen enthält",
-       "tog-prefershttps": "Wenn angemeldet, immer eine sichere Verbindung benutzen.",
+       "tog-prefershttps": "Während angemeldet, immer eine sichere Verbindung benutzen.",
        "underline-always": "immer",
        "underline-never": "nie",
        "underline-default": "abhängig von der Benutzeroberfläche oder Browsereinstellung",
        "category-empty": "''Diese Kategorie enthält zurzeit keine Seiten oder Medien.''",
        "hidden-categories": "{{PLURAL:$1|Versteckte Kategorie|Versteckte Kategorien}}",
        "hidden-category-category": "Versteckte Kategorien",
-       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Unterkategorie:|{{PLURAL:$1|Folgende Unterkategorie ist eine von insgesamt $2 Unterkategorien in dieser Kategorie:|Es werden $1 von insgesamt $2 Unterkategorien in dieser Kategorie angezeigt.}}}}",
+       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Unterkategorie.|Diese Kategorie enthält {{PLURAL:$1|folgende Unterkategorie|die folgende $1 Unterkategorien}}, von $2 insgesamt.}}",
        "category-subcat-count-limited": "Diese Kategorie enthält folgende {{PLURAL:$1|Unterkategorie|$1 Unterkategorien}}:",
-       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Seite:|{{PLURAL:$1|Folgende Seite ist eine von insgesamt $2 Seiten in dieser Kategorie:|Es werden $1 von insgesamt $2 Seiten in dieser Kategorie angezeigt.}}}}",
-       "category-article-count-limited": "Folgende {{PLURAL:$1|Seite ist|$1 Seiten sind}} in dieser Kategorie enthalten:",
-       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Datei:|{{PLURAL:$1|Folgende Datei ist eine von insgesamt $2 Dateien in dieser Kategorie:|Es werden $1 von insgesamt $2 Dateien in dieser Kategorie angezeigt:}}}}",
+       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält nur die folgende Seite.|Folgende {{PLURAL:$1|Seite ist| $1 Seiten sind}} in dieser Kategorie, von $2 insgesamt.}}",
+       "category-article-count-limited": "{{PLURAL:$1|Folgende Seite ist|Die folgenden $1 Seiten sind}} in dieser Kategorie enthalten:",
+       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Datei.|Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie, von $2 insgesamt.}}",
        "category-file-count-limited": "Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie enthalten:",
        "listingcontinuesabbrev": "(Fortsetzung)",
        "index-category": "Indexierte Seiten",
        "newwindow": "(wird in einem neuen Fenster geöffnet)",
        "cancel": "Abbrechen",
        "moredotdotdot": "Mehr …",
-       "morenotlisted": "Diese Liste ist nicht vollständig.",
+       "morenotlisted": "Diese Liste könnte nicht vollständig sein.",
        "mypage": "Eigene Seite",
        "mytalk": "Diskussion",
        "anontalk": "Diskussionsseite",
        "yourpasswordagain": "Passwort wiederholen:",
        "createacct-yourpasswordagain": "Passwort bestätigen",
        "createacct-yourpasswordagain-ph": "Gib das Passwort erneut ein",
-       "remembermypassword": "Mit diesem Browser dauerhaft angemeldet bleiben (maximal $1 {{PLURAL:$1|Tag|Tage}})",
        "userlogin-remembermypassword": "Angemeldet bleiben",
        "userlogin-signwithsecure": "Sichere Verbindung verwenden",
+       "cannotlogin-title": "Die Anmeldung ist nicht möglich.",
+       "cannotlogin-text": "Die Anmeldung ist nicht möglich.",
        "cannotloginnow-title": "Anmeldung nicht erfolgreich",
        "cannotloginnow-text": "Eine Anmeldung ist mit Verwendung von $1 nicht möglich.",
+       "cannotcreateaccount-title": "Die Erstellung von Benutzerkonten ist nicht möglich.",
+       "cannotcreateaccount-text": "Die direkte Erstellung von Benutzerkonten ist auf diesem Wiki nicht aktiviert.",
        "yourdomainname": "Deine Domain:",
        "password-change-forbidden": "Du kannst auf diesem Wiki keine Passwörter ändern.",
        "externaldberror": "Entweder liegt ein Fehler bei der externen Authentifizierung vor oder du darfst dein externes Benutzerkonto nicht aktualisieren.",
        "botpasswords-updated-body": "Das Botpasswort für den Botnamen „$1“ des Benutzers „$2“ wurde aktualisiert.",
        "botpasswords-deleted-title": "Botpasswort gelöscht",
        "botpasswords-deleted-body": "Das Botpasswort für den Botnamen „$1“ des Benutzers „$2“ wurde gelöscht.",
-       "botpasswords-newpassword": "Das neue Passwort zur Anmeldung mit <strong>$1</strong> ist <strong>$2</strong>. <em>Bitte halte dies für die Zukunft fest.</em>",
+       "botpasswords-newpassword": "Das neue Passwort zur Anmeldung mit <strong>$1</strong> ist <strong>$2</strong>. <em>Bitte halte dies für die Zukunft fest.</em><br>Für alte Bots, die erfordern, dass der Anmeldename mit dem späteren Benutzernamen identisch ist, kannst du auch <strong>$3</strong> als Benutzernamen und <strong>$4</strong> als Passwort verwenden.",
        "botpasswords-no-provider": "BotPasswordsSessionProvider ist nicht verfügbar.",
        "botpasswords-restriction-failed": "Beschränkungen des Botpassworts verhindern diese Anmeldung.",
        "botpasswords-invalid-name": "Der angegebene Benutzername enthält keinen Botpassworttrenner („$1“).",
        "invalid-content-data": "Ungültige Inhaltsdaten",
        "content-not-allowed-here": "Der Inhalt „$1“ ist auf der Seite [[$2]] nicht erlaubt",
        "editwarning-warning": "Das Verlassen dieser Seite kann dazu führen, dass deine Änderungen verloren gehen.\nWenn du angemeldet bist, kannst du das Anzeigen dieser Warnung im Bereich „{{int:prefs-editing}}“ deiner Einstellungen abschalten.",
+       "editpage-invalidcontentmodel-title": "Das Inhaltsmodell wird nicht unterstützt.",
+       "editpage-invalidcontentmodel-text": "Das Inhaltsmodell „$1“ wird nicht unterstützt.",
        "editpage-notsupportedcontentformat-title": "Das Inhaltsformat wird nicht unterstützt",
        "editpage-notsupportedcontentformat-text": "Das Inhaltsformat $1 wird vom Inhaltsmodell $2 nicht unterstützt.",
        "content-model-wikitext": "Wikitext",
        "nextn-title": "{{PLURAL:$1|Folgendes Ergebnis|Folgende $1 Ergebnisse}}",
        "shown-title": "Zeige $1 {{PLURAL:$1|Ergebnis|Ergebnisse}} pro Seite",
        "viewprevnext": "Zeige ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse anzeigen.}}",
+       "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse:}}",
        "searchmenu-new": "<strong>Erstelle die Seite „[[:$1]]“ in diesem Wiki.</strong> {{PLURAL:$2|0=|Siehe auch die über deine Suche gefundene Seite.|Siehe auch die gefundenen Suchergebnisse.}}",
        "searchprofile-articles": "Inhaltsseiten",
        "searchprofile-images": "Multimedia",
        "file-thumbnail-no": "Der Dateiname beginnt mit <strong>$1</strong>. Dies deutet auf ein Bild verringerter Größe ''(Minitatur)'' hin.\nBitte prüfe, ob du das Bild in voller Auflösung vorliegen hast und lade dieses unter dem Originalnamen hoch.",
        "fileexists-forbidden": "Unter diesem Namen existiert bereits eine Datei und sie kann nicht überschrieben werden. Bitte gehe zurück und lade die Datei unter einem anderen Namen hoch. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Unter diesem Namen existiert bereits eine Datei im zentralen Medienarchiv.\nWenn du diese Datei trotzdem hochladen möchtest, gehe bitte zurück und ändere den Namen.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Die hochgeladene Datei ist ein exaktes Duplikat der aktuellen Version von <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Die hochgeladene Datei ist ein exaktes Duplikat {{PLURAL:$2|einer älteren Version|von älteren Versionen}} von <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Diese Datei ist ein Duplikat der folgenden {{PLURAL:$1|Datei|$1 Dateien}}:",
        "file-deleted-duplicate": "Eine mit dieser identische Datei ([[:$1]]) wurde früher gelöscht. Sieh das Lösch-Logbuch ein, bevor du sie hochlädst.",
        "file-deleted-duplicate-notitle": "Eine identische Datei wurde kürzlich gelöscht und der Titel wurde unterdrückt.\nDu solltest jemanden fragen, der die Möglichkeit hat, die unterdrückten Dateidaten anzusehen, um die Situation vor dem erneuten Hochladen zu überprüfen.",
        "filerevert-submit": "Zurücksetzen",
        "filerevert-success": "'''[[Media:$1|$1]]''' wurde auf die [$4 Version vom $2, $3 Uhr] zurückgesetzt.",
        "filerevert-badversion": "Es gibt keine Version der Datei zu dem angegebenen Zeitpunkt.",
+       "filerevert-identical": "Die aktuelle Version der Datei ist bereits mit der ausgewählten Version identisch.",
        "filedelete": "Lösche „$1“",
        "filedelete-legend": "Lösche Datei",
        "filedelete-intro": "Du löschst die Datei '''„[[Media:$1|$1]]“''' inklusive ihrer Versionsgeschichte.",
        "rollbacklinkcount-morethan": "Mehr als {{PLURAL:$1|eine Version|$1 Versionen}} zurücksetzen",
        "rollbackfailed": "Zurücksetzen gescheitert",
        "rollback-missingparam": "In der Anfrage fehlen erforderliche Parameter.",
+       "rollback-missingrevision": "Die Versionsdaten konnten nicht geladen werden.",
        "cantrollback": "Die Änderung kann nicht zurückgesetzt werden, da es keine früheren Autoren gibt.",
        "alreadyrolled": "Das Zurücksetzen der Änderungen von [[User:$2|$2]] ([[User talk:$2|Diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) an [[:$1]] ist gescheitert, da in der Zwischenzeit ein anderer Benutzer die Seite geändert hat.\n\nDie letzte Änderung stammt von [[User:$3|$3]] ([[User talk:$3|Diskussion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Die Änderungszusammenfassung lautet: <em>$1</em>.",
        "revertmove": "zurück verschieben",
        "delete_and_move_text": "Die Seite „[[:$1]]“ existiert bereits.\nMöchtest du diese löschen, um die Seite verschieben zu können?",
        "delete_and_move_confirm": "Ja, Seite löschen",
-       "delete_and_move_reason": "gelöscht, um Platz für die Verschiebung von „[[$1]]“ zu machen",
+       "delete_and_move_reason": "Gelöscht, um Platz für die Verschiebung von „[[$1]]“ zu machen",
        "selfmove": "Ursprungs- und Zielname sind gleich.\nEine Seite kann nicht auf sich selbst verschoben werden.",
        "immobile-source-namespace": "Seiten des „$1“-Namensraums können nicht verschoben werden",
        "immobile-target-namespace": "Seiten können nicht in den „$1“-Namensraum verschoben werden",
        "pageinfo-article-id": "Seitenkennnummer",
        "pageinfo-language": "Seiteninhaltssprache",
        "pageinfo-content-model": "Seiteninhaltsmodell",
+       "pageinfo-content-model-change": "ändern",
        "pageinfo-robot-policy": "Indizierung durch Suchmaschinen",
        "pageinfo-robot-index": "Erlaubt",
        "pageinfo-robot-noindex": "Nicht erlaubt",
        "tag-filter": "[[Special:Tags|Markierungs]]-Filter:",
        "tag-filter-submit": "Filter",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Markierung|Markierungen}}]]: $2)",
+       "tag-mw-contentmodelchange": "Änderung des Inhaltsmodells",
+       "tag-mw-contentmodelchange-description": "Bearbeitungen, die [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel das Inhaltsmodell einer Seite ändern]",
        "tags-title": "Markierungen",
        "tags-intro": "Diese Seite zeigt alle Markierungen, die für Bearbeitungen verwendet wurden, sowie deren Bedeutung. \n\nBei entsprechender Einstellung können die Missbrauchfilter beliebige Markierungen in die Versionsgeschichte setzen. Man kann die Versionsgeschichte dann nach den Markierungen filtern.",
        "tags-tag": "Markierungsname",
        "tags-actions-header": "Aktionen",
        "tags-active-yes": "Ja",
        "tags-active-no": "Nein",
-       "tags-source-extension": "Definiert von einer Erweiterung",
+       "tags-source-extension": "Definiert von der Software",
        "tags-source-manual": "Manuell von Benutzern und Bots eingesetzt",
        "tags-source-none": "Nicht mehr in Verwendung",
        "tags-edit": "bearbeiten",
index 6445fad..9e89037 100644 (file)
@@ -76,7 +76,7 @@
        "editfont-monospace": "Terzê nusteyê sabıtcagırewtoği",
        "editfont-sansserif": "Fontê Sans-serifi",
        "editfont-serif": "Font (çêşıdê nuştey) Serif",
-       "sunday": "Kırê (Bazar)",
+       "sunday": "Kırê",
        "monday": "Dışeme",
        "tuesday": "Sêşeme",
        "wednesday": "Çarşeme",
        "oct": "Tşv",
        "nov": "Tşp",
        "dec": "Kan",
-       "january-date": "Çele  $1",
-       "february-date": "Sıbate $1",
-       "march-date": "Adar $1",
-       "april-date": "Nisane $1",
-       "may-date": "Gulane $1",
-       "june-date": "{{PLURAL:$1|1=1ᵉ|$1}} Heziran",
-       "july-date": "Temuz $1",
-       "august-date": "Tebaxe $1",
-       "september-date": "Keşkelun $1",
-       "october-date": "Tışrino Verên $1",
-       "november-date": "Tışrino Peyên $1",
-       "december-date": "Kanun $1",
+       "january-date": "$1 Çele",
+       "february-date": "$1 Sıbate",
+       "march-date": "$1 Adar",
+       "april-date": "$1 Nisane",
+       "may-date": "$1 Gulane",
+       "june-date": "$1 Heziran",
+       "july-date": "$1 Temuze",
+       "august-date": "$1 Tebaxe",
+       "september-date": "$1 Keşkelun",
+       "october-date": "$1 Tışrino Verên",
+       "november-date": "$1 Tışrino Peyên",
+       "december-date": "$1 Kanun",
        "period-am": "AM",
        "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategoriy}}",
        "category-empty": "''Ena kategoriye de hewna qet nuştey ya zi medya çıniyê.''",
        "hidden-categories": "{{PLURAL:$1|Kategoriya nımıtiye|Kategoriyê nımıtey}}",
        "hidden-category-category": "Kategoriyê nımıtey",
-       "category-subcat-count": "{{PLURAL:$2|Na kategoriya de $1 bınkategoriyay estê.|$2 kategoriyan ra $1 bınkategoriyay asenê.}} \n(K) Kategori (D) Dosya (P) Pela",
-       "category-subcat-count-limited": "Na kategoriya de {{PLURAL:$1|ena kategoriya bınên est a|enê $1 kategoriyay bınêni est ê}}.",
-       "category-article-count": "{{PLURAL:$2|Na kategoriye de teyna ena pele esta.|Ebe $2 ra pêro piya {{PLURAL:$1|ena pela na kategoriye dera|$1 enê peli na kategoriye derê.}}}}",
+       "category-subcat-count": "{{PLURAL:$2|Na kategoriye de tenya na bınkategoriye esta.|Na kategoriye de, $2 ra pêro piya, {{PLURAL:$1|bınkategoriye esta|$1 bınkategoriy estê}}.}}",
+       "category-subcat-count-limited": "Na kategoriye de {{PLURAL:$1|na kategoriya bınêne esta|nê $1 kategoriyê bınêni estê}}.",
+       "category-article-count": "{{PLURAL:$2|Na kategoriye de teyna ena pele esta.|Ebe $2 ra pêro piya {{PLURAL:$1|ena pele na kategoriye dera|$1 enê peli na kategoriye derê}}.}}",
        "category-article-count-limited": "{{PLURAL:$1|Pela cêrêne|$1 Pelê cêrêni}} na kategoriye derê.",
-       "category-file-count": "<noinclude>{{PLURAL:$2|Na kategoriya tenya dosyanê cêrênan muhtewa kena.}}</noinclude>\n*Na kategoriya de $2 dosyan ra {{PLURAL:$1|yew dosya tenêka esta| $1 dosyay asenê}}.",
+       "category-file-count": "{{PLURAL:$2|Na kategori tenya dosya ya cêri muhtewa kena.|Na kategori de $2 ra pêro piya {{PLURAL:$1|1 dosya est a|$1 dosyey est ê}}.}}",
        "category-file-count-limited": "{{PLURAL:$1|Dosya cêrêne|$1 Dosyê cêrêni}} na kategoriye derê.",
        "listingcontinuesabbrev": "dewam...",
        "index-category": "Pelê endeksıni",
        "anontalk": "Werênayış",
        "navigation": "Pusula",
        "and": "&#32;u",
-       "qbfind": "Bıvin",
+       "qbfind": "Bıvêne",
        "qbbrowse": "Çım ra viyarne",
        "qbedit": "Bıvurne",
        "qbpageoptions": "Ena pele",
        "qbmyoptions": "Pelê mı",
-       "faq": "PZP",
+       "faq": "PVP",
        "faqpage": "Project: PZP",
        "actions": "Hereketi",
        "namespaces": "Heruna nameyan",
        "view-foreign": "$1 de bıvêne",
        "edit": "Bıvurne",
        "edit-local": "Şınasnayışê lokali bıvurne",
-       "create": "Bıvıraz",
+       "create": "Vıraze",
        "create-local": "Şınasnayışê lokali cı ke",
        "editthispage": "Ena pele bıvurne",
        "create-this-page": "Na pele bınuse",
        "specialpage": "Pela xısusiye",
        "personaltools": "Hacetê şexsiy",
        "articlepage": "Pera zerreki bıvin",
-       "talk": "Hurênayış",
+       "talk": "Vatene",
        "views": "Asayışi",
        "toolbox": "Haceti",
        "userpage": "Pela karberi bıvêne",
        "viewhelppage": "Pera peşti bıvin",
        "categorypage": "Pela kategoriya bıasne",
        "viewtalkpage": "Werênayışi bıvêne",
-       "otherlanguages": "Tayna zıwanan dı",
+       "otherlanguages": "Zıwananê binan de",
        "redirectedfrom": "($1 ra kırışı yê)",
        "redirectpagesub": "Pela berdışi",
        "redirectto": "Beno hetê:",
        "viewsourcelink": "çımey bıvêne",
        "editsectionhint": "Leteyo ke bıvuriyo: $1",
        "toc": "Sernameyê meselan",
-       "showtoc": "bıasene",
+       "showtoc": "bımocne",
        "hidetoc": "bınımne",
-       "collapsible-collapse": "Teng ke",
+       "collapsible-collapse": "Teng kı",
        "collapsible-expand": "Hera ke",
-       "confirmable-confirm": "{{GENDER:$1|Şıma }} do emeli?",
+       "confirmable-confirm": "{{GENDER:$1|Şıma}} pêbawerê?",
        "confirmable-yes": "Eya",
        "confirmable-no": "Nê",
        "thisisdeleted": "Bıvêne ya zi $1 peyser biya?",
        "nstab-main": "Pele",
        "nstab-user": "Pela karberi",
        "nstab-media": "Pela medya",
-       "nstab-special": "Pera spesiyal",
+       "nstab-special": "Pela xısusiye",
        "nstab-project": "Pela proceyi",
        "nstab-image": "Dosya",
        "nstab-mediawiki": "Mesac",
        "cannotlogoutnow-title": "Enewke ronıştışo nêracneyêno",
        "welcomeuser": "Ğeyr amey, $1!",
        "welcomecreation-msg": "Hesabê şıma abiyo.\n[[Special:Preferences|{{SITENAME}} vurnayişê tercihanê xo]], xo vir ra mekere.",
-       "yourname": "Nameyê karberi:",
-       "userlogin-yourname": "Nameyê karberi",
+       "yourname": "Namey karberi:",
+       "userlogin-yourname": "Namey karberi",
        "userlogin-yourname-ph": "Nameyê xoyê karberi cı kewe",
        "createacct-another-username-ph": "Nameyê karberi cı kewe",
        "yourpassword": "Parola",
        "yourpasswordagain": "Parola reyna bınusne:",
        "createacct-yourpasswordagain": "Parola tesdiq ke",
        "createacct-yourpasswordagain-ph": "Parola fına cıkewe",
-       "remembermypassword": "Parola mı nê cıgeyrayoği de biya xo viri (seba tewr zêde $1 {{PLURAL:$1|roce|rocan}})",
        "userlogin-remembermypassword": "Mı biya xo viri",
        "userlogin-signwithsecure": "Ebe teqdimkerê asayişın cıkewe",
        "cannotloginnow-title": "Enewke ronıştışo nêabeno",
        "cannotloginnow-text": "$1 karkerdışa ronıştış akerdış mıkum niyo.",
-       "yourdomainname": "Nameyê şıma yo meydani",
+       "yourdomainname": "Yewdestê şıma:",
        "password-change-forbidden": "Şıma na wiki de nêşenê parola bıvurnê.",
        "externaldberror": "Ya database de xeta esta ya zi heqê şıma çino şıma no hesab bıvurni.",
        "login": "Cı kewe",
        "createacct-another-continue-submit": "Hesab vıraştışi rê dewam ke",
        "createacct-benefit-heading": "{{SITENAME}} meş de merduman şi",
        "createacct-benefit-body1": "{{PLURAL:$1|vurnayış|vurnayışi}}",
-       "createacct-benefit-body2": "{{PLURAL:$1|wesiqe|wesiqey}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|pele|peli}}",
        "createacct-benefit-body3": "{{PLURAL:$1|iştıraqkerdoğo nıkayên|iştıraqkerdoğê nıkayêni}}",
        "badretype": "Parolayê ke şıma nuşti yewbini nêtepışneni.",
        "usernameinprogress": "Qandê nê karberi hesab vıraştışondewamnkeno.  Tay bıpawê",
        "botpasswords-label-appid": "Nameyê boti:",
        "botpasswords-label-create": "Vıraze",
        "botpasswords-label-update": "Rocane ke",
-       "botpasswords-label-cancel": "İbtal ke",
+       "botpasswords-label-cancel": "Bıtexelne",
        "botpasswords-label-delete": "Bestere",
        "botpasswords-label-resetpassword": "Parola raçarne",
        "botpasswords-label-grants-column": "Dayen",
        "resetpass_forbidden": "parolayi nêvuryayi",
        "resetpass-no-info": "şıma gani hesab akere u hona bıeşke bırese cı",
        "resetpass-submit-loggedin": "Parola bıvurne",
-       "resetpass-submit-cancel": "İbtal ke",
+       "resetpass-submit-cancel": "Bıtexelne",
        "resetpass-wrong-oldpass": "parolayo parola maqbul niyo.\nşıma ya parolaye xo vurnayo ya zi parolayo muwaqqat waşto.",
        "resetpass-recycled": "Parolaya şımaya newiye wa paroloya şımaya verêne ra ferqıne bo.",
        "resetpass-temp-emailed": "E postaya rışyayê yubkoda şıma ronıştış akerdo.  Ronıştışi xo temammkerdışi rê yu parolaya newi lazım a",
        "summary": "Xulasa:",
        "subject": "Mewzu:",
        "minoredit": "Vurriyayışo werdiyo",
-       "watchthis": "Seyr kı",
-       "savearticle": "Qeyd kı",
+       "watchthis": "Na pele seyr ke",
+       "savearticle": "Qeyd ke",
        "savechanges": "Vurnayışan qeyd ke",
        "publishpage": "Perer bıhesırne",
        "publishchanges": "Vurnayışa vıla ke",
        "preview": "Verqayt",
-       "showpreview": "Verasayışi bıvin",
-       "showdiff": "Vuryayışa bıasne",
+       "showpreview": "Verqayti bıvêne",
+       "showdiff": "Vurriyayışan bımocne",
        "anoneditwarning": "<strong>İqaz:</strong> Şıma be hesabê xo nêkewtê cı. \nAdresê şımayê IP tarixê vırnayışê na pele de do qeyd bo. Eke şıma <strong>[$1 cıkewê]</strong> ya zi <strong>[$2 hesab vırazê]</strong>, vurnayışê şıma be zewbina kare ra nameyê şıma rê bar beno.",
        "anonpreviewwarning": "\"Şıma be hesabê xo nêkewtê cı. Eke qeyd kerê, adresê şımaê IP tarixê vırnayışê na pele de do qeyd bo.\"",
        "missingsummary": "'''DİQET:''' Şıma jû xulasa nênuşte.\nEke şıma \"{{int:savearticle}}\" reyna bıtıknê, vırnayışê şıma bê xulasa qeyd beno.",
        "template-protected": "(kılit biyo)",
        "template-semiprotected": "(nimey ena pele kılit biya)",
        "hiddencategories": "Ena per de {{PLURAL:$1|1 kategoriyo nımıte|$1 kategoriyê nımıtey}} muhtewa benê:",
-       "edittools": "<!-- Text here will be shown below edit and upload forms. -->",
+       "edittools": "<div id=\"specialcharss\" class=\"toccolours specialchars\" style=\"margin-top:.5em; padding: .3em .5em; font-size: 100%; color:#aaa; text-align:left;\" title=\"{{int:bw-edittools-tooltip}}\">\n<p class=\"specialbasic\" id=\"Standard\">\n'''{{int:bw-edittools-lead-in}}''' \n<charinsert>Á á É é Í í Ó ó Ú ú Ý ý</charinsert> –\n<charinsert>À à È è Ì ì Ò ò Ù ù </charinsert> –\n<charinsert> â Ê ê Î î Ô ô Û û </charinsert> –\n<charinsert>Ä ä Ë ë Ï ï Ö ö Ü ü Ÿ ÿ</charinsert> –\n<charinsert>Æ æ Ø ø Œ œ ẞ ß </charinsert> –\n<charinsert>Å å Ů ů </charinsert> –\n<charinsert>àã Ẽ ẽ ɛ̃ Ĩ ĩ Ñ ñ Õ õ ɔ̃ Ũ ũ </charinsert> –\n<charinsert>Рð Þ þ </charinsert> –\n<charinsert>Ç ç Ģ ģ Ķ ķ Ļ ļ Ņ ņ Ŗ ŗ Ş ş Ţ ţ </charinsert> –\n<charinsert>Ć ć Ĺ ĺ Ń ń Ŕ ŕ Ś ś Ý ý Ź ź </charinsert> –\n<charinsert>Č č Ď ď Ľ ľ Ň ň Ř ř Š š Ť ť Ž ž </charinsert> –\n<charinsert>Ǎ ǎ Ě ě Ǐ ǐ Ǒ ǒ Ǔ ǔ </charinsert> –\n<charinsert>Ā ā Ē ē Ī ī Ō ō Ū ū </charinsert> –\n<charinsert>ǖ ǘ ǚ ǜ </charinsert> –\n<charinsert>Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŵ ŵ Ŷ ŷ </charinsert> –\n<charinsert>Ă ă Ğ ğ Ŭ ŭ </charinsert> –\n<charinsert>Ċ ċ Ė ė Ġ ġ Għ għ İ ı Ż ż </charinsert> –\n<charinsert>Ą ą Ę ę Į į Ų ų </charinsert> –\n<charinsert>Ő ő Ű ű </charinsert> –\n<charinsert>Đ đ Ħ ħ Ł ł Ŀ ŀ </charinsert> –\n<charinsert>Ɖ ɖ Ɛ ɛ Ƒ ƒ Ɣ ɣ Ŋ ŋ Ɔ ɔ Ʋ ʋ </charinsert> -\n<charinsert>Ə ə </charinsert> –\n<charinsert>– — ’</charinsert> –\n<charinsert>~ | ° ¹ ² ³ ⅛ ¼ ⅓ ⅜ ½ ⅝ ¾ ⅔ ⅞ € $ ¥ £ † × ← → ↔ ↑ ± ≠ © ® ™ ‰ «+» ‹+› „+“ „+” ‚+‘ ¡ ¿ …</charinsert> –\n<charinsert>&amp;nbsp; &nbsp; [[Category:+]] #REDIRECT[[+]] {{msg-mw|+|notext=1}} &#33;!FUZZY!! ~~~~  &lt;nowiki>+</nowiki></charinsert>\n<charinsert>ڈ ڑ ٹ </charinsert>\n<charinsert>ټ څ ځ ډ ړ ږ ښ ڼ ؤ ي ې ۍ ئ </charinsert>\n<charinsert>{{{+}}} {{+}} {{subst:+}} <noinclude>+</noinclude></charinsert>\n<charinsert>&lt;!--&nbsp;+&nbsp;--> &lt;br&nbsp;/></charinsert>\n</p></div>",
        "edittools-upload": "-",
        "nocreatetext": "{{SITENAME}}, Pelê neweyi vıraştış re destur çino.\nşıma eşkeni tepiya şêri u eke şıma qayd biyaye yê [[Special:UserLogin|şıma eşkeni hesab akeri]], eke niye [[Special:UserLogin|şıma eşkeni qayd bıbiy]].",
        "nocreate-loggedin": "Desturê şıma çıniyo ke pelanê neweyan vırazê.",
        "parser-unstrip-recursion-limit": "Sinorê limit dê qayış dê ($1) ravêrya",
        "converter-manual-rule-error": "Rehberê zıwan açarnayışi dı xırabin tesbit biya",
        "undo-success": "No vurnayiş tepeye geryeno. pêverronayişêyê cêrıni kontrol bıkeri.",
-       "undo-failure": "Sebayê pêverameyişê vurnayişan karo tepêya gırewtış nêbı.",
+       "undo-failure": "Poxta pëverameyişa vurnayişan ra  peyd grotışë kari në bı",
        "undo-norev": "Vurnayiş tepêya nêgeryeno çunke ya vere cû hewna biyo ya zi ca ra çino.",
        "undo-summary": "Vırnayışê $1'i [[Special:Contributions/$2|$2i]] ([[User talk:$2|Werênayış]]) peyser gırot",
        "undo-summary-username-hidden": "Rewizyona veri $1'i hewada",
        "historysize": "({{PLURAL:$1|1 bayt|$1 bayti}})",
        "historyempty": "(veng)",
        "history-feed-title": "Tarixê çımraviyarnayışi",
-       "history-feed-description": "Wiki de tarixê çımraviyarnayışê na pele",
+       "history-feed-description": "Wiki de tarixê çım ra viyarnayışë na perer",
        "history-feed-item-nocomment": "$1 miyanê $2i de",
        "history-feed-empty": "Pela cıgeyrayiye çıniya.\nBeno ke ena esteriya, ya zi namê cı vuriyo.\nSeba pelanê muhimanê newan [[Special:Search|cıgeyrayışê wiki de]] bıcerebne.",
        "history-edit-tags": "Etiketa weçinaye rewizyoni timar ke",
        "rev-suppressed-unhide-diff": "Nê Timarkerdışi ra yewi '''çap biyo'''.\n[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rocaneyê vındertışi] de teferru'ati esti.\nEke şıma serkari u devam bıkeri [$1 no vurnayiş şıma eşkeni bıvini].",
        "rev-deleted-diff-view": "Jew timarkerdışê ena versiyon '''wedariyayo''.\nÎdarekarî şenê ena versiyon bivîne; belki tiya de [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wedarnayişî] de teferruat esto.",
        "rev-suppressed-diff-view": "Jew timarkerdışê ena versiyon '''Ploxneyış'' biyo.\nÎdarekarî eşkeno ena dif bivîne; belki tiya de [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ploxnayış] de teferruat esto.",
-       "rev-delundel": "bıasne/bınımne",
-       "rev-showdeleted": "bıasene",
+       "rev-delundel": "bımocne/bınımne",
+       "rev-showdeleted": "bımocne",
        "revisiondelete": "Çımraviyarnayışan bestere/peyser biya",
        "revdelete-nooldid-title": "Çımraviyarnayışo waşte nêvêreno",
        "revdelete-nooldid-text": "Şıma vıraştışê nê fonksiyoni rê ya yew çımraviyarnayışo waşte diyar nêkerdo, çımraviyarnayışo diyarkerde çıniyo, ya ki şıma wazenê ke çımraviyarnayışê nıkayêni bınımnê.",
        "mergehistory-box": "revizyonê pelanî yew bike:",
        "mergehistory-from": "Pela çımey:",
        "mergehistory-into": "Pela destinasyonî",
-       "mergehistory-list": "tarixê vurnayîşî ke eşkeno yew bi.",
+       "mergehistory-list": "Tarixê vurnayışiyo yewbiyaye",
        "mergehistory-merge": "[[:$1]] qey ney revizyonê cêrini [[:$2]] şıma ekeni piyawani. Benatê wexto muwaqqet de piyayanayişê rezizyonan de tuşa radyo bıxebitne.",
        "mergehistory-go": "Yew bıyaye vurriyayışa bıasne",
        "mergehistory-submit": "revizyonî yew bike",
        "skin-preview": "Verqayt",
        "datedefault": "Tercih çıniyo",
        "prefs-labs": "Xacetê labs",
-       "prefs-user-pages": "Pela Karberi",
+       "prefs-user-pages": "Pelê karberi",
        "prefs-personal": "Pela karberi",
        "prefs-rc": "Vurriyayışê peyêni",
        "prefs-watchlist": "Lista seyrkerdışi",
        "action-managechangetags": "Vıraz u etiketa aktiv (me) ke",
        "action-applychangetags": "Vurnayışana piya etiket kerdışi zi dezge fi",
        "action-purge": "Ane perer newe ke",
-       "nchanges": "$1 {{PLURAL:$1|fın vurna|fıni vurna}}",
-       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ra yok wazino}}",
+       "nchanges": "$1 {{PLURAL:$1|vurnayış|vurnayışi}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ziyaretê peyêni ra nata}}",
        "enhancedrc-history": "tarix",
        "recentchanges": "Vurriyayışê peyêni",
        "recentchanges-legend": "Tercihê vurnayışanê peyênan",
        "recentchanges-legend-heading": "<strong>Kıtabekê Vurriyayışê peyêni:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} Şıma şenê ([[Special:NewPages|Listey peranê  newan]] zi bıvinê)",
        "recentchanges-legend-plusminus": "''(±123)''",
-       "recentchanges-submit": "Bıasne",
+       "recentchanges-submit": "Bımocne",
        "rcnotefrom": "Cêr de <strong>$2</strong> ra nata {{PLURAL:$5|vurnayışiyê}} asenê (tewr vêşi <strong>$1</strong> asenê) <strong>$3, $4</strong>",
        "rclistfrom": "$3 $2 ra tepiya vurnayışanê neweyan bımocne",
        "rcshowhideminor": "vurriyayışê werdi $1",
        "rcshowhidebots-show": "Bımocne",
        "rcshowhidebots-hide": "Bınımne",
        "rcshowhideliu": "karberê qeydbiyayeyi $1",
-       "rcshowhideliu-show": "Bıasne",
+       "rcshowhideliu-show": "Bımocne",
        "rcshowhideliu-hide": "Bınımne",
        "rcshowhideanons": "karberê bênameyi $1",
        "rcshowhideanons-show": "Bımocne",
        "rcshowhideanons-hide": "Bınımne",
        "rcshowhidepatr": "$1 vurnayışê ke dewriya geyrayê",
-       "rcshowhidepatr-show": "Bıasne",
+       "rcshowhidepatr-show": "Bımocne",
        "rcshowhidepatr-hide": "Bınımne",
        "rcshowhidemine": "vurnayışanê mı $1",
        "rcshowhidemine-show": "Bımocne",
        "rcshowhidemine-hide": "Bınımne",
        "rcshowhidecategorization": "kategorizasyonê pele $1",
-       "rcshowhidecategorization-show": "Bıasne",
+       "rcshowhidecategorization-show": "Bımocne",
        "rcshowhidecategorization-hide": "Bınımne",
        "rclinks": "Peyniya $2 rocan de $1 vurriyayışan ra <br />$3 asenê",
        "diff": "ferq",
        "boteditletter": "b",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 ho seyr keno {{PLURAL:$1|karber|karberî}}]",
-       "rc_categories": "Pelê ke kategoriyan ra (be „|“ ciya biyê):",
+       "rc_categories": "Kategoriyan rêz kı ( \"|“ ya ciya yo):",
        "rc_categories_any": "Weçinayiyan ra her yew",
        "rc-change-size": "$1",
        "rc-change-size-new": "Vurnayışa dıma $1 {{PLURAL:$1|bayt|bayt}}",
        "mimesearch": "MIME bigêre",
        "mimesearch-summary": "Na pele, dosyayanê MIME goreyê tewran ra parzûn kena. Cıkewtış: tewrê zerreki/tewro bınên ya zi tewrê zerreki/*, nımune: <code>image/jpeg</code>.",
        "mimetype": "Babetê NIME",
-       "download": "bar ke",
+       "download": "Bar ke",
        "unwatchedpages": "Pelanê seyrnibiyeyî",
        "listredirects": "Listeya Hetenayışan",
        "listduplicatedfiles": "Lista dosyeyanê ke kopyaya cı vêniyena",
        "withoutinterwiki": "Pelê ke zıwananê binan rê gıreyê cı çıniyo",
        "withoutinterwiki-summary": "Enê pelî ke versiyonê ziwanî binî ra link nidano.",
        "withoutinterwiki-legend": "Verole",
-       "withoutinterwiki-submit": "Bıasene",
+       "withoutinterwiki-submit": "Bımocne",
        "fewestrevisions": "Pelê be senık çımraviyarnayışi",
        "nbytes": "$1 {{PLURAL:$1|bayt|bayti}}",
        "ncategories": "$1 {{PLURAL:$1|Kategori|Kategoriy}}",
        "mostrevisions": "Pelan ke tewr zaf revizyonî biyê.",
        "prefixindex": "Veroleya peley pêro",
        "prefixindex-namespace": "Peleyê Veroleyıni ($1 cay nami)",
-       "prefixindex-submit": "Bıasene",
+       "prefixindex-submit": "Bımocne",
        "prefixindex-strip": "Listeya réz bıyayışi",
        "shortpages": "Pelê kılmeki",
        "longpages": "Pelê dergeki",
        "protectedpages-unknown-performer": "Karbero nêzanaye",
        "protectedtitles": "Sernameyê pawıteyi",
        "protectedtitlesempty": "pê ney parametreyan sernuşteyê pawite çinê",
-       "protectedtitles-submit": "Sernaman bımocne",
+       "protectedtitles-submit": "Sernameyan bımocne",
        "listusers": "Listeyê Karberan",
        "listusers-editsonly": "Teyna karberan bimucne ke ey nuştê",
        "listusers-creationsort": "goreyê wextê vıraştışi rêz ker",
        "usereditcount": "$1 {{PLURAL:$1|vurnayîş|vurnayîşî}}",
        "usercreated": "$2 de $1 {{GENDER:$3|viraziya}}",
        "newpages": "Pelê newey",
-       "newpages-submit": "Bıasene",
+       "newpages-submit": "Bımocne",
        "newpages-username": "Nameyê karberi:",
        "ancientpages": "Pelê kehenêri",
        "move": "Bıkırışe",
        "specialloguserlabel": "Kerdoğ:",
        "speciallogtitlelabel": "Meqsed (sername ya zi {{ns:user}}:karberi rê nameyê karberi):",
        "log": "Qeydi",
-       "logeventslist-submit": "Bıasene",
+       "logeventslist-submit": "Bımocne",
        "all-logs-page": "Umumi qeydi pêro",
        "alllogstext": "qey {{SITENAME}}i mocnayişê heme rocaneyani.\ntipa rocaneyi, nameyê karberi (herfa pil u qıci re hessas a), ya zi peli (reyna hessasiyê herfa pil u qıciyi) bıweçine u esayiş qıc kerê.",
        "logempty": "Qeydan dı malumato unasin çıni yo.",
        "cachedspecial-viewing-cached-ts": "Na pela raşt niya, şımayê enewke versiyonê verhafızada na pela vinenê.",
        "cachedspecial-refresh-now": "Peyêni bıvin.",
        "categories": "Kategoriy",
-       "categories-submit": "Bıasene",
+       "categories-submit": "Bımocne",
        "categoriespagetext": "{{PLURAL:$1|Kategoriya cêrene|Kategoriyanê cêrênan}} de peli ya zi medya estê.\n[[Special:UnusedCategories|Kategoriyê ke nêxebetiyenê]] tiya de nêmocniyayê.\n[[Special:WantedCategories|Kategoriyanê waşteyeyan]] de zi bıvêne.",
        "categoriesfrom": "Kategoriyê ke be ninan dest pêkenê, bımocne:",
        "deletedcontributions": "İştırakê karberi esterdi",
        "linksearch-line": "$1, $2 ra link biya",
        "linksearch-error": "jokeri têna nameyê makina ya serekini de aseni/eseni.",
        "listusersfrom": "karber ê ke pey ıney detpêkeni ramocın:",
-       "listusers-submit": "Bıasene",
+       "listusers-submit": "Bımocne",
        "listusers-noresult": "karber nêdiyayo/a.",
        "listusers-blocked": "(blok biy)",
        "activeusers": "Listey karberan de aktivan",
        "emailsubject": "Mewzu:",
        "emailmessage": "Mesac:",
        "emailsend": "Bırışe",
-       "emailccme": "kopyayekê mesaji mı re bıerşaw",
+       "emailccme": "Ju kopya ya mesaci bırş mı rê?",
        "emailccsubject": "$2 kopyaya mesaj a ke şıma erşawıto/a $1:",
        "emailsent": "E-poste rışna",
        "emailsenttext": "e-mailê şıma erşawiya/ruşiya",
        "emailuserfooter": "na e-posta hetê ıney ra $1 erşawiya $2 no/na karberi/e re. pê fonksiyonê \"Karberi/e re e-posta bıerşaw\" no {{SITENAME}} keyepeli erşawiya.",
        "usermessage-summary": "Mesacê sistemi caverde.",
-       "usermessage-editor": "Mesaj berdoxe sistemi",
+       "usermessage-editor": "Xeberdarê sistemi",
        "usermessage-template": "MediaWiki:UserMessage",
-       "watchlist": "Lista seyrkerdışi",
-       "mywatchlist": "Lista seyrkerdışi",
+       "watchlist": "Listey pawıteyan",
+       "mywatchlist": "Listey pawıteyan",
        "watchlistfor2": "Qandê $1 ($2)",
        "nowatchlist": "listeya temaşa kerdıişê şıma de yew madde zi çina.",
        "watchlistanontext": "qey vurnayişê maddeya listeya temaşakerdiş ronıştış akerê",
        "removedwatchtext": "Ena pela \"[[:$1]]\" biya wedariya [[Special:Watchlist|listeyê seyr-kerdışi şıma]].",
        "removedwatchtext-short": "Pera $1`i listeya seyran de şıma ra wedari yê",
        "watch": "Seyr ke",
-       "watchthispage": "Seyr kı",
+       "watchthispage": "Na pele seyr ke",
        "unwatch": "Teqib meke",
        "unwatchthispage": "temaşa kerdışê peli vındarn.",
        "notanarticle": "mebhesê peli niyo",
        "wlnote": "$3 saete $4 ra dıme {{PLURAL:$2|yew saete de|'''$2''' saetan de}} {{PLURAL:$1|vurnayışo peyên|vurnayışê '''$1''' peyêni}} cêrderê.",
        "wlshowlast": "Peyni de  $1 seata u $2 roca  bıasne",
        "watchlist-hide": "Bınımne",
-       "watchlist-submit": "Bıasene",
+       "watchlist-submit": "Bımocne",
        "wlshowtime": "Periyoda zemani asenayışi:",
        "wlshowhideminor": "vurriyayışê werdi",
        "wlshowhidebots": "boti",
        "delete-confirm": "\"$1\" bestere",
        "delete-legend": "Bestere",
        "historywarning": "'''Teme:''' Pela ke şıma esterenê tede yew viyarte be teqriben $1 {{PLURAL:$1|versiyon esto|versiyoni estê}}:",
-       "historyaction-submit": "Bıasene",
+       "historyaction-submit": "Bımocne",
        "confirmdeletetext": "Tı ho yew pele u tarixê pele wederneno.\nTı ra rica keno, tı zani tı ho sekeno, tı zani neticeyanê eno wedarnayışi u tı zani tı ser [[{{MediaWiki:Policy-url}}|poliçe]] kar keno.",
        "actioncomplete": "Kar bi temam",
        "actionfailed": "kar nêbı",
        "namespace_association": "Heruna nameyanê elaqedaran",
        "tooltip-namespace_association": "Herunda canemiya elekeyın nışan kerdışi sero qıse kerdışi yana zerre dekerdışi rê ena dora tesdiqi nışan kerê",
        "blanknamespace": "(Ser)",
-       "contributions": "İştirakê {{GENDER:$1|karber}}i",
+       "contributions": "İştırakê {{GENDER:$1|karber}}i",
        "contributions-title": "Dekerdenê karber de $1",
        "mycontris": "İştıraki",
        "anoncontribs": "İştıraki",
        "sp-contributions-newbies-title": "Îştîrakê karberî ser hesabê neweyî",
        "sp-contributions-blocklog": "qeydê kılitbiyayeyi",
        "sp-contributions-deleted": "iştırakê karberi esterdi",
-       "sp-contributions-uploads": "barkerdey",
+       "sp-contributions-uploads": "Barkerdışi",
        "sp-contributions-logs": "qeydi",
        "sp-contributions-talk": "werênayış",
        "sp-contributions-userrights": "idareyê heqanê karberan",
        "cant-move-user-page": "desturê şıma çino, şıma pelanê karberani bıkırışi (bê pelê cerıni).",
        "cant-move-to-user-page": "desturê şıma çino, şıma yew peli bıkırışi pelê yew karberi.",
        "newtitle": "Sernameyo newe:",
-       "move-watch": "Peler seyr ke",
+       "move-watch": "Pela çıme u meqsedi seyr ke",
        "movepagebtn": "Pele bere",
        "pagemovedsub": "Berdışi kerd temam",
        "movepage-moved": "'''\"$1\" berd \"$2\"'''",
        "tooltip-pt-anonuserpage": "pelê karberê IPyi",
        "tooltip-pt-mytalk": "Pela {{GENDER:|toya}} werênayışi",
        "tooltip-pt-anontalk": "vurnayiş ê ke no Ipadresi ra biyo muneqeşa bıker",
-       "tooltip-pt-preferences": "Tercihê {{GENDER:|şıma}}",
+       "tooltip-pt-preferences": "Tercihê {{GENDER:|to}}",
        "tooltip-pt-watchlist": "Lista pelanê ke to gırewtê seyrkerdış",
        "tooltip-pt-mycontris": "Yew lista iştırakanê {{GENDER:|şıma}}",
        "tooltip-pt-login": "Mayê şıma ronıştış akerdışi rê dawet keme; labelê ronıştış mecburi niyo",
        "tooltip-pt-logout": "Bıveciye",
        "tooltip-pt-createaccount": "Şıma rê tewsiyey ma xorê jew hesab akerê. Fına zi hesab akerdış mecburi niyo.",
-       "tooltip-ca-talk": "Zerrekê pele sero werênayış",
+       "tooltip-ca-talk": "Heqa zerrekê pele de werênayış",
        "tooltip-ca-edit": "Ena pele bıvurne",
        "tooltip-ca-addsection": "Zu bınnusteya newi ak",
        "tooltip-ca-viewsource": "Ena pele kılit biya.\nŞıma şenê çımeyê aye bıvênê",
        "tooltip-n-mainpage-description": "Şo pela seri",
        "tooltip-n-portal": "Heqa proceyi de, çı şenay bıkerê, çı koti vêniyeno",
        "tooltip-n-currentevents": "Vurnayışanê peyênan de melumatê pey bıvêne",
-       "tooltip-n-recentchanges": "Wiki de lista vurnayışanê peyênan",
+       "tooltip-n-recentchanges": "Wiki de yew lista vurriyayışanê peyênan",
        "tooltip-n-randompage": "Pelê da raştameyiye bar ke",
        "tooltip-n-help": "Cayê peştigırewtışi",
        "tooltip-t-whatlinkshere": "Lista pelanê wikiya pêroina ke tiya gırê bena",
        "tooltip-ca-nstab-main": "Pela zerreki bıvêne",
        "tooltip-ca-nstab-user": "Pela karberi bıvêne",
        "tooltip-ca-nstab-media": "Pela medya bıvêne",
-       "tooltip-ca-nstab-special": "Na pelaya xas a, şıma nêşenê sero vurnayış bıkerê",
+       "tooltip-ca-nstab-special": "Na yew pela xasa, şıma nêşenê sero vurnayış bıkerê",
        "tooltip-ca-nstab-project": "Pela proceyi bıvêne",
        "tooltip-ca-nstab-image": "Pera dosyayer bıvin",
-       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bıasne",
+       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bımocne",
        "tooltip-ca-nstab-template": "Şabloni bıvêne",
        "tooltip-ca-nstab-help": "Pela peşti bıvêne",
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "pageinfo-header-edits": "Veréna timar kerdışi",
        "pageinfo-header-restrictions": "Sıtarkerdışê pele",
        "pageinfo-header-properties": "Xısusiyetê pele",
-       "pageinfo-display-title": "Sernuştey bımocne",
+       "pageinfo-display-title": "Sernuşteyo ke mosneyêno",
        "pageinfo-default-sort": "Hesıbyaye mırfeyo kılm",
        "pageinfo-length": "Derdeya pela (bayti heta)",
        "pageinfo-article-id": "Kamiya pele",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Kategoriya nımıtiye|Kategoriyê nımıtey}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Şablono|Şablonê}} ke mocniyenê ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|1 Pele|$1 Pelan}} de bestiya pıra",
-       "pageinfo-toolboxlink": "Zanayışa perer",
+       "pageinfo-toolboxlink": "Melumatê pele",
        "pageinfo-redirectsto": "Beno hetê",
        "pageinfo-redirectsto-info": "melumat",
        "pageinfo-contentpage": "Zey jû pela zerreki hesebiyena",
        "exif-pixelydimension": "Berzeya resimi",
        "exif-usercomment": "Mışewreyê karberi",
        "exif-relatedsoundfile": "Derhekê dosya yê vengi",
-       "exif-datetimeoriginal": "Zeman u tarixê data varaziyayişi",
-       "exif-datetimedigitized": "Zeman u tarixê dicital kerdişi",
+       "exif-datetimeoriginal": "Demê afernayışê dayeyo sıfteyıni",
+       "exif-datetimedigitized": "Zeman û tarixê dicitalkerdışi",
        "exif-subsectime": "ZemanTarix saniyeyibini",
        "exif-subsectimeoriginal": "ZemanTarixOricinal saniyeyibini",
        "exif-subsectimedigitized": "ZemanTarixDicital saniyeyibini",
        "version": "Versiyon",
        "version-extensions": "Ekstensiyonî ke ronaye",
        "version-skins": "Bar kerde bejni",
-       "version-specialpages": "Pelanê xasiyan",
+       "version-specialpages": "Pelê xısusiyi",
        "version-parserhooks": "Çengelê Parserî",
        "version-variables": "Vurnayeyî",
        "version-antispam": "Spam vındarnayış",
        "redirect-value": "Erc:",
        "redirect-user": "Kamiya Karberi:",
        "redirect-page": "Kamiya pele",
-       "redirect-revision": "Çımraviyarnayışê pele",
+       "redirect-revision": "Çım ra viyarnayışê perer",
        "redirect-file": "Namey dosya",
        "redirect-logid": "Qeydé  ID",
        "redirect-not-exists": "Erc nêvineyê",
        "fileduplicatesearch-result-1": "Dosyayê ''$1î'' de hem-kopya çini yo.",
        "fileduplicatesearch-result-n": "Dosyayê ''$1î'' de {{PLURAL:$2|1 hem-kopya|$2 hem-kopyayî'}} esto.",
        "fileduplicatesearch-noresults": "Ebe namey \"$1\" ra dosya nêdiyayê.",
-       "specialpages": "Page bağsey",
+       "specialpages": "Pelê xısusiyi",
        "specialpages-note-top": "Kıtabek",
        "specialpages-note": "* Pelê xasê normali.\n* <span class=\"mw-specialpagerestricted\">Pelê xasê nımıtey.</span>",
        "specialpages-group-maintenance": "Raporê pawıtışi",
-       "specialpages-group-other": "Pelê xasiyê bini",
+       "specialpages-group-other": "Pelê xısusiyê bini",
        "specialpages-group-login": "Dekew / hesab vıraz",
        "specialpages-group-changes": "Vurnayışê peyêni û qeydi",
        "specialpages-group-media": "Raporê medya û barkerdışi",
        "tag-filter-submit": "Avrêc",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiket|Etiketi}}]]: $2)",
        "tags-title": "Etiketan",
-       "tags-intro": "Eno pel de listeyê eyiketî este ke belki software pê ey edit kenî.",
+       "tags-intro": "Ena per şena ju vurnayışa etiketinu maney nustekeni liste  kena",
        "tags-tag": "Nameyê etiketi",
        "tags-display-header": "Listeyê vurnayîşî de esayîş",
        "tags-description-header": "Tam arezekerdışê maneyê cı",
        "tags-actions-header": "Kerdışi",
        "tags-active-yes": "Eya",
        "tags-active-no": "Nê",
-       "tags-source-extension": "Teref dê yo dergeneki ra şınasiyayo",
+       "tags-source-extension": "Kışta ju dergeneki ra şınasêna",
        "tags-edit": "bıvurne",
        "tags-delete": "bestere",
        "tags-activate": "Aktiv ke",
        "feedback-bugcheck": "Harika! Sadece [xırabina ke $1 ] çınyayışê cı kontrol keno.",
        "feedback-bugnew": "Mı qontrol ke. Xetaya newi xeber ke",
        "feedback-bugornote": "Jew mersela teferruato teknik esta şıma reca malumatê şıma hazıro se [ $1  jew xırab rapor] bıvinê.Zewbi zi, formê cerê xo rê şenê karfiyê. Vatışê xo pela da \"[ $3  $2 ]\", namey karber dê xoya piya u wasteriya karfiye.",
-       "feedback-cancel": "İbtal kı",
+       "feedback-cancel": "Bıtexelne",
        "feedback-close": "Biya star",
        "feedback-error1": "Xeta: API ra neticey ne vıcyay",
        "feedback-error2": "Xeta: Timar kerdış nebı",
index 61e06c0..7dd59f3 100644 (file)
@@ -6,14 +6,15 @@
                        "रमेश सिंह बोहरा",
                        "राम प्रसाद जोशी",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Nirajan pant"
                ]
        },
        "tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
        "tog-hideminor": "अहिलका मामूली सम्पादनलाई लुकाउन्या",
        "tog-hidepatrolled": "गस्ती(patrolled)सम्पादनलाई लुकाउन्या",
        "tog-newpageshidepatrolled": "गस्ती गरिया पानानलाई नयाँ पाना  सूचीबठेई लुकाउन्या",
-       "tog-hidecategorization": "पà¥\83षà¥\8dठहरà¥\81को श्रेणीकरण हटाया",
+       "tog-hidecategorization": "पà¥\83षà¥\8dठहरà¥\82को श्रेणीकरण हटाया",
        "tog-extendwatchlist": "निगरानी सूचीलाई सबै परिवर्तन धेकुन्या गरी बढुन्या , ऐईलका बाहेक",
        "tog-usenewrc": "पानाका अहिलका  परिवर्तन र अवलोकन सूचीका आधारमी सामूहिक परिवर्तनहरू",
        "tog-numberheadings": "शीर्षकहरूलाई स्वत:अङ्कित गर",
        "tog-watchlisthidebots": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
        "tog-watchlisthideminor": "मेरा सम्पादनहरू ध्यान सूचीबाट लुकाउन्या",
        "tog-watchlisthideliu": "प्रवेश गरेका प्रयोगकर्ताहरूको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
+       "tog-watchlistreloadautomatically": "जज्ज्याँलै फिल्टर बदेलिन्छ इच्छासूची आफुइ रिलोड अर: (जावास्क्रिप्ट चायीन्छ)",
        "tog-watchlisthideanons": "अज्ञात प्रयोगकर्ताहरूबाट गरिएको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
        "tog-watchlisthidepatrolled": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
-       "tog-watchlisthidecategorization": "पà¥\83षà¥\8dठहरà¥\81को श्रेणीकरण लुकौन्या",
+       "tog-watchlisthidecategorization": "पà¥\83षà¥\8dठहरà¥\82को श्रेणीकरण लुकौन्या",
        "tog-ccmeonemails": "मुईले अन्य प्रयोगकर्ताहरूलाई पठाउन्या इ-मेलको प्रतिलिपि मुईलाई पठाउन्या",
        "tog-diffonly": "तलका पानाहरुको भिन्नहरू सामग्री नदेखाउन्या",
        "tog-showhiddencats": "लुकाइएका श्रेणीहरू धेखाउन्या",
        "editfont-serif": "सेरिफ फन्ट",
        "sunday": "आइतबार",
        "monday": "सौउबार",
-       "tuesday": "माà¤\82à¤\97लबार",
+       "tuesday": "माà¤\99लबार",
        "wednesday": "बुधबार",
-       "thursday": "बिपैबार",
+       "thursday": "बà¥\80पैबार",
        "friday": "शुकबार",
        "saturday": "छन्चरबार",
        "sun": "आइत",
        "mon": "सौउ",
        "tue": "मांगल",
-       "wed": "बà¥\81ध",
-       "thu": "बिपै",
+       "wed": "बà¥\8bऽ",
+       "thu": "बà¥\80पै",
        "fri": "शुक",
        "sat": "छन्चर",
        "january": "जनवरी",
        "october-gen": "अक्टोबर",
        "november-gen": "नोभेम्बर",
        "december-gen": "डिसेम्बर",
-       "jan": "जनवरी",
+       "jan": "जन",
        "feb": "फेब्रुअरी",
        "mar": "मार्च",
-       "apr": "अप्रि",
+       "apr": "अप्रि",
        "may": "मे",
        "jun": "जुन",
-       "jul": "जुलाई",
-       "aug": "अगस्ट",
-       "sep": "सेप्टेम्बर",
-       "oct": "अक्टोबर",
-       "nov": "नोभेम्बर",
-       "dec": "डिसेम्बर",
+       "jul": "जुल",
+       "aug": "अग",
+       "sep": "सेप्ट",
+       "oct": "अक्ट",
+       "nov": "नोभ",
+       "dec": "डिस",
        "january-date": "जनवरी $1",
        "february-date": "फेब्रुअरी $1",
        "march-date": "मार्च $1",
        "december-date": "डिसेम्बर $1",
        "period-am": "रात १२ बज्या बठे छाकला सम्म",
        "period-pm": "छाकला बठे रात १२ बज्या सम्म",
-       "pagecategories": "{{PLURAL:$1|शà¥\8dरà¥\87णà¥\80|शà¥\8dरà¥\87णà¥\80हरà¥\82}}",
-       "category_header": "\"$1\" श्रेणीमी भया लेखहरू",
+       "pagecategories": "{{PLURAL:$1|शà¥\8dरà¥\87णà¥\80|शà¥\8dरà¥\87णà¥\80न}}",
+       "category_header": "\"$1\" श्रेणीमी भयाऽ लेखअन",
        "subcategories": "उपश्रेणीहरू",
        "category-media-header": "\"$1\" श्रेणीमी भया लेखहरू",
        "category-empty": "''यै श्रेणीमी हाल कोइलै पाना या मिडिया रया नाइँथिन ।''",
        "mypage": "पानो",
        "mytalk": "मेरी कुरडी",
        "anontalk": "कुरडी",
-       "navigation": "à¤\96à¥\8bà¤\9c",
-       "and": "&#32;र",
+       "navigation": "पथपà¥\8dरदरà¥\8dशन",
+       "and": "&#32;र",
        "qbfind": "तम जाण",
        "qbbrowse": "ब्राउज गर्न्या",
        "qbedit": "सम्पादन",
        "faqpage": "Project:भौत सोधिएका प्रश्नहरु",
        "actions": "कार्यहरू",
        "namespaces": "नेमस्पेस",
-       "variants": "बहà¥\81रà¥\81पहरà¥\82",
+       "variants": "बहà¥\81रà¥\81पà¤\85न",
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "त्रुटी",
        "returnto": "$1 मी फर्क।",
-       "tagline": "{{SITENAME}}बाट",
-       "help": "सहायता",
-       "search": "खोज",
-       "searchbutton": "खोज",
+       "tagline": "{{SITENAME}} बठेइ",
+       "help": "मद्दत",
+       "search": "खोजी",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\n#   * Everything from a \"#\" character to the end of the line is a comment.\n#   * Every non-blank line is the exact title to ignore, case and everything.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
+       "searchbutton": "खोज:",
        "go": "जाने",
        "searcharticle": "जाओ",
        "history": "पाना इतिहास",
        "history_short": "पानाको इतिहास",
        "updatedmarker": "मेरो अन्तिम घुमाई पछि अद्यतन गरियाको",
-       "printableversion": "à¤\9bापà¥\8dनसà¤\95िनà¥\87 संस्करण",
+       "printableversion": "à¤\9bापà¥\8dद à¤®à¤¿à¤²à¥\8dलà¥\8dया संस्करण",
        "permalink": "स्थायी लिङ्क",
        "print": "छाप",
        "view": "अवलोकन गर",
        "unprotectthispage": "यै पानाको सुरक्षा परिवर्तन गर",
        "newpage": "नयाँ पाना",
        "talkpage": "यै पानाका बारेमी छलफल गर",
-       "talkpagelinktext": "à¤\95à¥\81रडà¥\80",
+       "talkpagelinktext": "à¤\95à¥\81रणि",
        "specialpage": "खास पानो",
-       "personaltools": "वà¥\8dयà¤\95à¥\8dतिà¤\97त à¤\94à¤\9cारहरà¥\82",
+       "personaltools": "वà¥\8dयà¤\95à¥\8dतिà¤\97त à¤\94à¤\9cारà¤\85न",
        "articlepage": "कन्टेन्ट पानो हेर",
-       "talk": "à¤\95à¥\81रडà¥\80 à¤\95ानी",
-       "views": "अवलोकन गर",
-       "toolbox": "à¤\94à¤\9cारहरà¥\82",
+       "talk": "à¤\95à¥\81रणिà¤\95ाà¤\86नी",
+       "views": "अवलोकन गर",
+       "toolbox": "à¤\94à¤\9cारà¤\85न",
        "userpage": "प्रयोगकर्ता पाना हेर्न्या",
        "projectpage": "प्रोजेक्ट पानो हेर्न्या",
        "imagepage": "चित्र पानो हेर",
        "viewhelppage": "सहायता पानो हेर्ने",
        "categorypage": "श्रेणी पानो हेर",
        "viewtalkpage": "छलफल हेर",
-       "otherlanguages": "à¤\85नà¥\8dय à¤­à¤¾à¤·à¤¾मी",
-       "redirectedfrom": "($1 à¤¬à¤¾à¤\9f à¤ªà¤ à¤¾à¤\87याà¤\95à¥\8b)",
+       "otherlanguages": "à¤\94र à¤­à¤·à¤¾à¤\85नमी",
+       "redirectedfrom": "($1 à¤¬à¤ à¥\87à¤\87 à¤ªà¥\81न:निरà¥\8dदà¥\87शित)",
        "redirectpagesub": "अनुप्रेषित पानो",
        "redirectto": "पठाएको पाना:",
-       "lastmodifiedat": "यà¥\88 à¤ªà¤¾à¤¨à¤¾à¤²à¤¾à¤\88 à¤\86नà¥\8dतिम à¤ªà¤\9fà¤\95 $2, $1 à¤®à¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिया थ्यो।",
+       "lastmodifiedat": "यà¥\88 à¤ªà¤¨à¥\8dनालाà¤\88 à¤\9bाडà¥\8dडà¥\80बाऱ $2 à¤¬à¤\9cà¥\87, $1 à¤®à¥\80 à¤¹à¥\87रफà¥\87र à¤\97रियाऽ थ्यो।",
        "viewcount": "यो पाना हेरियाको थियो {{PLURAL:$1|एकपटक|$1 पटक}}",
        "protectedpage": "सुरक्षित गर्याका पानाहरू",
-       "jumpto": "यà¥\88मà¥\80 à¤\9cाà¤\93:",
-       "jumptonavigation": "भ्रमण गर",
-       "jumptosearch": "खोज",
+       "jumpto": "यà¥\88मà¥\80 à¤«à¤\9fà¥\8dà¤\9fाà¤\95:",
+       "jumptonavigation": "भ्रमण गर",
+       "jumptosearch": "खोज",
        "view-pool-error": "माफ गर्या , अहिल सर्भरहरूमी कामको भार भौत रह्या छ।\nभौत भौत प्रयोगकर्ताहरू यै पाना हेद्या प्रयास गरी रह्या छन्।\nकृपया यो पाना पुन: हेर्नु अगाडि थोक्कै पख ।\n\n$1",
        "generic-pool-error": "माफ गर्या , अहिल सर्भरहरूमी कामको भार भौत रह्या छ।\nभौत भौत प्रयोगकर्ताहरू यै पाना हेद्या प्रयास गरी रह्या छन् ।\nकृपया यो पाना पुन: हेर्नु अगाडि थोक्कै पख ।",
        "pool-timeout": "समय सकियो बन्द गर्ने प्रतीक्षामी",
        "pool-errorunknown": "अज्ञात गल्ती",
        "pool-servererror": "पुल काउन्टर सेवा उपलब्ध नाइथिन् ($1)।",
        "poolcounter-usage-error": "प्रयोग गल्ती:$1",
-       "aboutsite": "{{SITENAME}}à¤\95à¥\8b बारेमी",
+       "aboutsite": "{{SITENAME}}à¤\86 बारेमी",
        "aboutpage": "Project:बारेमी",
        "copyright": "सामाग्री $1 अनुसार उपलब्ध छ, खुलाइएको अवस्था बाहेकका हकमी ।",
-       "copyrightpage": "{{ns:project}}:पà¥\8dरतिलिपà¥\80 à¤\85धिà¤\95ारहरà¥\82",
-       "currentevents": "आजभोलका घटनाहरू",
-       "currentevents-url": "Project:आजभोलका घटनाहरू",
-       "disclaimers": "à¤\85सà¥\8dविà¤\95ारà¥\8bà¤\95à¥\8dतिहरà¥\82",
+       "copyrightpage": "{{ns:project}}:पà¥\8dरतिलिपà¥\80 à¤\85धिà¤\95ारà¤\85न",
+       "currentevents": "आजभोलका घटना",
+       "currentevents-url": "Project:आजभोलका घटना",
+       "disclaimers": "à¤\85सà¥\8dविà¤\95ारà¥\8bà¤\95à¥\8dतà¥\80न",
        "disclaimerpage": "Project:सामान्य अस्वीकारोक्ति",
        "edithelp": "सम्पादन सहायता",
        "helppage-top-gethelp": "सहायता",
-       "mainpage": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
-       "mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
+       "mainpage": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
+       "mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
        "policy-url": "Project:निति",
        "portal": "सामाजिक पोर्टल",
        "portal-url": "Project:सामाजिक पोर्टल",
        "versionrequired": "MediaWiki संस्करण $1 चाईन्या",
        "versionrequiredtext": "ये पाना प्रयोग गर्नका लागि MediaWiki $1 संस्करण चाहिन्छ ।\nहेर  [[Special:Version|version page]]",
        "ok": "भयो",
-       "retrievedfrom": " \"$1\" बठे निकालिया",
-       "youhavenewmessages": "तमखी लेखा($3)मी $1($2) छ ।",
-       "youhavenewmessagesfromusers": "तमखी लेखा {{PLURAL:$3|प्रयोगकर्ता|$3 प्रयोगकर्तान}}($2)बठे$1",
+       "retrievedfrom": " \"$1\" बठे निकालिया",
+       "youhavenewmessages": "{{PLURAL:$3|तम सित छन}} $1 ($2)।",
+       "youhavenewmessagesfromusers": "{{PLURAL:$3|अर्खा प्रयोगकर्ता|$3 प्रयोगकर्ताअन}} ($2) मी है {{PLURAL:$4|तम सित}} $1 छन।",
        "youhavenewmessagesmanyusers": "तमलाई धेरै प्रयोगकर्ताहरू($2) बठे $1 छ ।",
        "newmessageslinkplural": "{{PLURAL:$1|एक नौलो रैबार|999=नौला रैबारहरू}}",
        "newmessagesdifflinkplural": "छाड्डीबारको {{PLURAL:$1|परिवर्तन|999=परिवर्तनहरू}}",
        "viewsourceold": "स्रोत हेर",
        "editlink": "सम्पादन",
        "viewsourcelink": "स्रोत हेर",
-       "editsectionhint": "खण्ड: $1 सम्पादन गर",
+       "editsectionhint": "खण्ड: $1 सम्पादन गर",
        "toc": "विषयसूची",
        "showtoc": "धेकाउन्या",
        "hidetoc": "लुकाउन्या",
        "feed-invalid": "अमान्य फिड प्रकार ग्राह्याता ।",
        "feed-unavailable": "सिन्डीकेसन फिडहरु उपलब्ध नाइथिन्",
        "site-rss-feed": "$1 आरएसएस फिड",
-       "site-atom-feed": "$1 à¤\8fà¤\9fम à¤«à¤¿ड",
+       "site-atom-feed": "$1 à¤\8fà¤\9fम à¤«à¥\80ड",
        "page-rss-feed": "\"$1\" आरएसएस फिड",
        "page-atom-feed": "\"$1\" एटम फिड",
-       "red-link-title": "$1 (पाना उपलब्ध नाइँथिन)",
+       "red-link-title": "$1 (पनà¥\8dना उपलब्ध नाइँथिन)",
        "sort-descending": "अवरोहण क्रममी मिलाउन्या",
        "sort-ascending": "आरोहण क्रममी मिलाउन्या",
-       "nstab-main": "लà¥\87à¤\96",
+       "nstab-main": "पनà¥\8dना",
        "nstab-user": "प्रयोगकर्ता पानो",
        "nstab-media": "माध्यम पाना",
        "nstab-special": "खास पानो",
        "nstab-template": "ढाँचा",
        "nstab-help": "सहायता पानो",
        "nstab-category": "श्रेणी",
-       "mainpage-nstab": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
+       "mainpage-nstab": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
        "nosuchaction": "यसो काम हैन",
        "nosuchactiontext": "URL ले खुलाएको काम मान्य छैन ।\nतमीले URL गलत टाइपगरेका हौ , वा गलत लिंकक पछाडी लागेका हुनसक्देहौ ।\nयो {{SITENAME}}ले सफ्टवेयरमी भयाको गल्ति देखायाको लै हुनसक्छ ।",
        "nosuchspecialpage": "तसो विशेष पानो छैन",
        "laggedslavemode": "<strong>चेतावनी:</strong> पानामी हालका अद्यतनहरू नहुनस्कदान ।",
        "readonly": "डेटाबेस बन्द गरिया छ",
        "enterlockreason": "ताल्चा मार्नुको कारण दिया, साथै ताल्चा हटाउने समयको अवधि अनुमान लगा।",
-       "readonlytext": "समà¥\8dभवतà¤\83 à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¥\87à¤\9fाबà¥\87स à¤°à¤\96-रà¤\96ाà¤\89à¤\95à¥\8b à¤\95ारण à¤\85हिलà¥\87लाà¤\88 à¤¨à¤¯à¤¾à¤\81 à¤¡à¥\87à¤\9fाबà¥\87स à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤° à¤\85नà¥\8dय à¤¸à¤\82शà¥\8bधनहरà¥\82  à¤¬à¤¨à¥\8dद à¤°à¤¾à¤\96िया à¤\9b, à¤\9cà¤\88लाà¤\88 à¤ªà¤\9bि à¤¬à¤ à¥\87 à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\8dया à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cà¤\88लà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\8dयाà¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤¯à¤¾à¤\95ाà¤\9bनà¥\8d: $1",
+       "readonlytext": "नà¥\8cला à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤°à¥\87 à¤\94र à¤¸à¤\82शà¥\8bधनà¤\85न à¤\96िलाà¤\88 à¤¡à¤¾à¤\9fाबà¥\87स à¤\85à¤\87ल à¤¬à¤¨à¥\8dद à¤\85रà¥\80रà¥\88à¤\9b़, à¤¸à¤®à¥\8dभबत: à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¤¾à¤\9fाबà¥\87स à¤°à¤\96-रà¤\96ाà¤\89 à¤\96िलाà¤\87, à¤\9cà¥\88 à¤ªà¤\9bा à¤¯à¥\8b à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\85वसà¥\8dथा à¤®à¥\80 à¤\86सलà¥\8b। \n\nवà¥\8dयवसà¥\8dथापà¤\95 à¤\9cà¤\88लà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\85रिराà¤\87à¤\9b à¤\89नलà¥\87 à¤¯à¥\87à¤\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¥\80राà¤\87à¤\9b़: $1",
        "missing-article": "नाम \"$1\" $2 भयाको भेटिनु पड्डे पानो पाठ डेटाबेसले  भेटाएन, \n\nयिसो प्राय: मिति नाघिसक्याको भिन्न वा इतिहास वा कुनै मेटिसक्याको पानाको लिंक पहिल्याउनाले हुन्छ ।\n\nयदि यसो भया नाइँहो भणे सफ्टवेयरको गल्ती लै हुनसकुन्छ ।\nकृपया यैको url खुलाइ [[Special:ListUsers/sysop|प्रबन्धक]]लाई उजुरी गर",
        "missingarticle-rev": "(संशोधन #: $1)",
        "missingarticle-diff": "(भिन्नता: $1, $2)",
        "readonly_lag": "डेटाबेस स्वतः बन्द गरिया छ जबकि अधिनस्थ डेटाबेस सर्वरले मूल पहिल्याउँनाछ।",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP हेडर पठाइयाथ्यो तर अनुरोध API write module खिलाइ थ्यो।",
        "internalerror": "भित्रका गल्ती",
        "internalerror_info": "भित्रका गल्ती: $1",
        "internalerror-fatal-exception": "प्रकारको गम्भीर अपवाद \"$1\"",
        "title-invalid-interwiki": "अनुरोध गरियाको शिर्षकमी अन्तर विकि लिङ्क छ जइलाई शिर्षकमी प्रयोग गद्द नाइपाइनो ।",
        "title-invalid-talk-namespace": "निवेदन गरियाको पानाको शिर्षकले उपलब्ध नभएका कुरडी पानालाई सन्दर्भको रूपमी राख्याको छ ।",
        "title-invalid-characters": "निवेदन गरियाको यै पानाको शिर्षकमी अवैध अक्षर रयाको छः \"$1\" ।",
+       "title-invalid-magic-tilde": "अनुरोध अरिया: पन्ना: शीर्षकमी अमान्य म्याजिक टिल्ड शृङ्खला छ (<nowiki>~~~</nowiki>)।",
+       "title-invalid-too-long": "अनुरोध अरिया: पन्ना: शीर्षक भौत लामु छ। यो UTF-8 इनकोडिङमी $1 {{PLURAL:$1|byte|bytes}} है लामु हुनु हुनैन।",
        "title-invalid-leading-colon": "निवेदन गरिया पृष्ठको शिर्षकको शुरूमी अवैध कोलोन रया छ ।",
        "perfcached": "तलका डाटाहरू क्याचमी रया कुराहरू हुन्। अपटुडेट नहुन लाई सक्दान। बर्ति {{PLURAL:$1|नतिजा|$1 नतिजाहरू}} क्याचमी उपलब्ध छ।",
        "perfcachedts": "तलतिरको आँकडा क्याच हो र $1 पहिला अद्यतन गरिया थ्यो। येई क्याचमी उपलब्ध {{PLURAL:$4|एउटा कारण हो|$4 कारणहरू हुन्}}।",
+       "querypage-no-updates": "येइ पन्ना: अद्दतन कार्य ऐल़ निस्क्रिय अरीरैछ।\nयाँ को डाटा ऐलखिलाइ ताजो अरिन्या आथिन।",
        "viewsource": "स्रोत हेर",
        "viewsource-title": " $1 को स्रोत हेर",
        "actionthrottled": "कार्य रोकिईयो",
        "viewsourcetext": "तम ये पृष्ठको स्रोत हेद्दु सकुन्छौ और उईको नक्कल उताद्दु सकुन्छौ |",
        "viewyourtext": "यै पानामी रह्याका '''तमरा सम्पादनहरू''' हेद्द या प्रतिलिपी गद्द सक्द्या हौ :",
        "editinginterface": "<strong>चेतावनी:</strong> तमी यै पानालाई सम्पादन गद्द लाग्याछौ, जनले सफ्टवेयरको लागि \nइन्टरफेस सामग्रीहरू प्रदान गरन्छ।\nयै पानामी गरियाको परिवर्तनले यै विकिमी अरु प्रयोगकर्तानको इन्टरफेसको प्रदर्शनमी प्रभाव पडन्छ ।",
+       "translateinterface": "सप्पै विकिइनखिलाइ अनुवाद थप्दाइ या बदेल्लाइ, कृपया [https://translatewiki.net/ translatewiki.net]को प्रयोग अर:, मिडियाविकि क्षेत्रीयकरण परियोजना:।",
        "namespaceprotected": "तमलाई '''$1'''  नेमस्पेसमी रह्याका पानाहरू सम्पादन गद्या अनुमति छैन ।",
        "customcssprotected": "तमलाई यो  पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
        "customjsprotected": "तमलाई यो जाभास्कृप्ट पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
        "mypreferencesprotected": "तमसंग तमरो अभिरुचीहरू सम्पादन ग्द्दे अनुमती छैन |",
        "ns-specialprotected": "विशेष पृष्ठहरू सम्पादन अद्दु नाइँ सकिनो।",
        "titleprotected": "[[User:$1|$1]]द्वारा ये शीर्षक निर्माणहुनबठे जोगाइया छ।\nकारण <em>$2</em> हो ।",
-       "filereadonlyerror": "फाइल \"$1\" लाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार  \"$2\" केवल पढ्ने स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित गर्ने प्रवन्धकले  यो कारण दियाकाछन् : ''$3''।",
+       "filereadonlyerror": "\"$1\" फाइललाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार \"$2\" केवल पड्ड्या स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित अद्द्या प्रवन्धकले यो कारण दीराइछ: ''$3''।",
+       "invalidtitle-knownnamespace": "\"$2\" नाउँबार रे \"$3\" पाठ भया: अमान्य शीर्षक",
+       "invalidtitle-unknownnamespace": "अपछ्याणो नाउँबार अङ्क $1 रे पाठ \"$2\" भया: अमान्य शीर्षक",
        "exception-nologin": "प्रवेश (लग ईन) नगरिएको",
+       "exception-nologin-text": "येए पन्नालाई चलुनाइ या केयि काम खिलाइ सक्षम हुनाइ लगइन अर:।",
+       "virus-badscanner": "गट्टी मिलजुल: अपछ्याणो भाइरस खोज्ज्या: <em>$1</em>",
        "virus-scanfailed": "जँचाई असफल(कोड $1)",
        "virus-unknownscanner": "थानभया एन्टीभाइरस:",
        "logouttext": "<strong>तमी अहिल बाहिर निस्क्याका  छौ।</strong>\n\nयाद राख्या तमीले ब्राउजरको क्याच खालि नगर्यासम्म कुनै पानाहरूमी तमी अझैं प्रवेश गरिरख्याको धेकाउन सक्छ।",
        "createacct-yourpassword-ph": "पासवर्ड लेख",
        "yourpasswordagain": "पासवर्ड फेरि टाईप गर",
        "createacct-yourpasswordagain": "पासवर्ड निश्चित गर",
-       "createacct-yourpasswordagain-ph": "à¤\86à¤\9cà¥\80 à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤²à¥\87à¤\96",
+       "createacct-yourpasswordagain-ph": "à¤\86à¤\81à¤\9cि à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤­à¤°à¤½",
        "userlogin-remembermypassword": "मुलाई अघाडी झान्या काम गराइराख्या",
        "userlogin-signwithsecure": "सुक्षित जडान प्रयोग गद्द्या",
+       "cannotlogin-title": "अईल भितर झान नाइँ पाईनो",
+       "cannotlogin-text": "येइमी लगइन सम्भव नाइथिन।",
        "cannotloginnow-title": "अईल भितर झान नाइँ पाईनो",
        "cannotloginnow-text": "भितर जान असंभव छ जब प्रयोग $1|",
+       "cannotcreateaccount-title": "खाता बनौन नाइसक्दो",
+       "cannotcreateaccount-text": "प्रत्यक्ष खाता बनौन एइ विकि मी सक्षम अरीयाऽ आथिन।",
        "yourdomainname": "तमरो ज्ञानक्षेत्र(डोमेन):",
        "password-change-forbidden": "ये विकिमी पासवर्ड परिवर्तन गर्न सक्नुहुन्न।",
+       "externaldberror": "या त याँ प्रमाणीकरण डाटाबेस त्रुटी थी या त तमलाई अफुना बाइल्ला खातालाई अद्यतन अद्देइ अनुमति आथिन।",
        "login": "प्रवेश (लगईन)",
        "login-security": "तमरो पहिचान जाचँ गर",
        "nav-login-createaccount": "प्रवेश गर्ने/नयाँ खाता बनाउन्या",
        "userlogin-resetpassword-link": "पासवर्ड भुलिगया?",
        "userlogin-helplink2": "प्रवेश गद्दलाई सहयोग",
        "userlogin-loggedin": "तमी {{GENDER:$1|$1}}को रूपमी प्रवेश (लग इन) भइ सक्यौ ।\nअर्को प्रयोगकर्ताको रूपमी प्रवेश (लग इन) गर्न तलको फारम प्रयोग गर ।",
+       "userlogin-reauth": "तम {{GENDER:$1|$1}} हो भणिबर पुष्टि अद्दाइ दोसर्‍याँ लगइन अर:।",
        "userlogin-createanother": "दोसरो खाता खोल",
        "createacct-emailrequired": "इमेल ठेगाना",
        "createacct-emailoptional": "इमेल ठेगाना (ऐच्छिक)",
-       "createacct-email-ph": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤°à¤¯à¤¾",
+       "createacct-email-ph": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤°à¤½",
        "createacct-another-email-ph": "इमेल ठेगाना भर",
        "createacct-realname": "वास्तविक नाम (ऐच्छिक)",
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण",
        "createacct-reason-ph": "क्याई तम नयाँ खाता खोल्ला छौ?",
-       "createacct-submit": "तमरà¥\8b à¤\96ाता à¤¸à¤¿à¤°à¥\8dà¤\9cना à¤\97र",
+       "createacct-submit": "तमरà¥\8b à¤\96ाता à¤¬à¤¨à¤¾à¤½",
        "createacct-another-submit": "खाता खोल",
        "createacct-continue-submit": "खाता खोल्लु जारि राख",
        "createacct-another-continue-submit": "खाता खोल्लु जारि राख",
        "mailmypassword": "पासवर्ड पूर्वनिर्धारित गर",
        "passwordremindertitle": "{{SITENAME}}का लागि नयाँ अस्थायी पासवर्ड",
        "passwordremindertext": "कसैले (सायद तमी, IP ठेगाना $1 बाट), {{SITENAME}}($4) को लागि नौलो पासवर्ड अनुरोध गर्या छ । प्रयोगकर्ता \"$2\" को लागि नौलो अस्थायी पासवर्ड \"$3\"तयार पारिया छ । यदि यो तमरो इच्छामी भयाको भया अहिले तमीले लगइन गरीबर नौलो पासवर्ड छान्नु पड्ड्या हुन्छ ।\nतमरो अस्थायी पासवर्ड  {{PLURAL:$5|एक दिन|$5 दिनहरू पछि}} अमान्य हुन्याछ ।\n\nयदि कोही अरुले नै अनुरोध गर्याको हो भण्या , या तमीले आफ्नो पासवर्ड सम्झ्यौ भण्या, अथवा\nत्यैलाई परिवर्तन गर्न चाहन्नौ भण्या, तमीले यो सन्देसको वेवास्ता गद्दसक्द्याहौ र पुरानै पासवर्ड प्रयोग गरिरहन सक्द्याहौ ।",
-       "blocked-mailpassword": "तमरà¥\8b IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤\97ायाà¤\95à¥\8b à¤\9b, à¤° à¤¤à¥\8dयसà¥\88लà¥\87 à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\95à¥\8b à¤²à¤¾à¤\97ि à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97दà¥\8dया à¤\85नà¥\81मति à¤\9bà¥\88न ।",
+       "blocked-mailpassword": "तमरा IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤¾à¤\87राà¤\87à¤\9b। à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\87, à¤¤à¤®à¤°à¤¾ IP à¤ à¥\87à¤\97ाना à¤¬à¤ à¥\87à¤\87 à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\85दà¥\8dदà¥\8dया à¤\85नà¥\81मति à¤\86थिन।",
        "mailerror": " चिठी :$1 पठाउँदा गल्ती भयो",
        "noemailprefs": "निम्न सुविधाहरू राम्डरी काम गद्दको लागि तमरो रोजाईमी आफ्नो ई-मेल ठेगाना खुलाओ ।",
        "emailconfirmlink": "तमरो ई-मेल ठेगाना पक्का गर",
        "pt-login": "प्रवेश (लग ईन)",
        "pt-login-button": "प्रवेश",
        "pt-login-continue-button": "प्रवेश जारी राख",
-       "pt-createaccount": "नयाँ खाता खोल",
+       "pt-createaccount": "नयाँ खाता खोल:",
        "pt-userlogout": "बाहिर निस्कन्या (लग आउट)",
        "php-mail-error-unknown": "PHP मेल() क्रियामा अज्ञात गल्ती",
        "user-mail-no-addy": "इमेल ठेगाना बिनाई इमेल पठाउन खोजिया थ्यो।",
        "passwordreset-email": "इमेल ठेगाना:",
        "passwordreset-emailtitle": "{{SITENAME}}मा खाता विवरण",
        "passwordreset-emailelement": "प्रयोगकर्ताको नाम: \n$1\n\nअस्थाई पासवर्ड: \n$2",
-       "passwordreset-emailsentemail": "पासवरà¥\8dड à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95ा à¤²à¤¾à¤\97ि à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87या à¤\9b।",
+       "passwordreset-emailsentemail": "यदि à¤¯à¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¤à¤® à¤¸à¤¿à¤¤ à¤¸à¤®à¥\8dबनà¥\8dधित à¤\9b à¤­à¤£à¥\8dया, à¤¤à¤¬ à¤¯à¤\95 à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤°à¤¿à¤¸à¥\87à¤\9f à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\8fलà¥\8b।",
        "passwordreset-invalideamil": "अबैध ई-मेल ठेगाना",
        "changeemail": "इमेल ठेगाना बदेल वा हटा",
-       "changeemail-header": "à¤\86फà¥\8dनà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97दà¥\8dद à¤¯à¥\8b à¤«à¤¾à¤°à¤® à¤­à¤° à¥¤ à¤¯à¥\88लाà¤\88 à¤ªà¥\81षà¥\8dà¤\9fि à¤\97दà¥\8dद à¤¤à¤®à¥\80लà¥\87 à¤\86फà¥\8dनà¥\8b à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¹à¤¾à¤²à¥\8dनà¥\81 à¤ªà¤¡à¤¨à¥\8dà¤\9b।",
+       "changeemail-header": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¬à¤¦à¥\87लà¥\8dलाà¤\87 à¤\8fà¤\87 à¤«à¤¾à¤°à¤¾à¤® à¤ªà¥\81राà¤\87 à¤­à¤°à¤½à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤® à¤¤à¤®à¤°à¤¾ à¤\96ाता à¤¬à¤ à¥\87à¤\87 à¤\95सà¥\88 à¤²à¥\88 à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¸à¤¿à¤¤à¥\8bऽ à¤¸à¤®à¥\8dबनà¥\8dध à¤¹à¤\9fà¥\8cन à¤\9aाहनà¥\8dà¤\9bऽ à¤­à¤£à¥\8dया, à¤«à¤¾à¤°à¤¾à¤® à¤¬à¥\81à¤\9cà¥\8cनà¥\8dà¤\9cà¥\8dयाà¤\81 à¤¨à¥\8cलà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤£à¥\8dणà¥\8dया à¤ à¥\8cर à¤\96ालि à¤\9bाणà¥\8dयाऽ।",
        "changeemail-oldemail": "अईलको इमेल-ठेगाना:",
        "changeemail-newemail": "नयाँ इमेल-ठेगाना:",
        "changeemail-none": "(के लै नाइँ)",
        "changeemail-password": "तमरो {{SITENAME}} पासवर्ड:",
        "changeemail-submit": "इमेल परिवर्तन गद्या",
        "changeemail-throttled": "तमले अलै भौत फेर प्रवेशका निम्ति प्रयास गरया छौ।\nकृपया $1 पर्खेर मात्र प्रयास गर।",
+       "changeemail-nochange": "कृपया नौलो जुदोइ इमेल ठेगाना राख:।",
        "resettokens": "टोकन पूर्वरुपमी फर्काउन्या",
        "resettokens-text": "जो टोकन तमरो खातासँग सम्बद्ध केहि विशिष्ट व्यक्तिगत जानकारी प्रदान गर्छन, तम त्यसलाई यहाँ रिसेट गद्द सक्द्या हौ।\n\nयदि तमले तिनलाई भुलवस कैकनै देखाईदिया छौ वा तमरो खाता ह्याक भइसक्याको छ भन्या तम यसलाई रिसेट गर्या ।",
        "resettokens-no-tokens": "पूर्वरुमी फर्काउन्या कोई लै टोकन नाइथिन् ।",
        "anontalkpagetext": "----''यो कुरडी पानो अज्ञात प्रयोगकर्ताको हो जनले अहिलसम्म खाता बनायाकै छैन, अथवा जनले यै पानाको उपयोग गर्दैन।\nयस कारण हामीले उनलाई उनरो आइ पी (IP) ठेगानाले चिन्न सकन्छौ। \nयस्तो आइ पी (IP) ठेगाना धेरै प्रयोगकर्तानको साझा हुनसकन्छ ।\nयदि तमी अज्ञात प्रयोगकर्ता हौ र तमलाई अचाहिँदो टिप्पणी भयाको अनुभव गद्दा छौ भण्या भविष्यमी अन्य अज्ञात प्रयोगकर्तासँगको भ्रमबाट बाँच्न कृपया [[Special:CreateAccount|खाता खोल]] अथवा [[Special:UserLogin|प्रवेश गर]] ''",
        "noarticletext": "यै लेखमी अहिल क्यै पन पाठ नाइथी  ।\nतमले और पृष्ठमी\n[[Special:Search/{{PAGENAME}}|यस पृष्ठको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}}  यै पानालाई सम्पादन गद्या]</span>.",
        "noarticletext-nopermission": "यै लेखमी अहिल केइ पन पाठ नाइथी  ।\nतमले और पानामी\n[[Special:Search/{{PAGENAME}}|यै पानाको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज्न],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}}  यै पानालाई सम्पादन गद्द] सकन्छौ</span>.",
+       "userpage-userdoesnotexist": "\"$1\" प्रयोगकर्ता खाता दर्ता अरीया: आथिन।\nयेइ पान्नो बनुन/सम्पादन अद्द चाहन्छ: भण्या विचार अर:।",
        "userpage-userdoesnotexist-view": "प्रयोगकर्ता खाता \"$1\" दर्ता गरिया छैन।",
+       "blocked-notice-logextract": "यो प्रयोगकर्ता अच्याल प्रतिवन्धित छ।\nसब है पछा: प्रतिबन्ध लग प्रविष्टि सन्दर्भ खिलाइ तल्तिर दियीरैछ:",
+       "sitecsspreview": "<strong>येइ CSSलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
+       "sitejspreview": "<strong>येइ जावास्क्रिप्ट कोडलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
        "userinvalidcssjstitle": "<strong>चेतावनी:</strong> यहाँ कोइपनि \"$1\" नामको खोल नाइथिन् ।\nप्रचलित .css तथा .js पानाहरूले निम्नपद शीर्षक प्रयोग गद्दान्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामी {{ns:user}}:Foo/vector.css",
        "updated": "नौला",
        "note": "'''सूचना:'''",
+       "previewnote": "<strong>फाम अर: कि यो यक पूर्वावलोकन मात्तरी हो।</strong>\nतमले अर्‍या फेरबदेली आँजि सङ्ग्रहित भया: आथिन!",
        "continue-editing": "सम्पादन क्षेत्रमी जाओ",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\85रà¥\80नà¥\8dनाà¤\9b़",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझ्यो: $1",
        "yourtext": "तमरा पाठहरू",
        "template-protected": "(सुरक्षित)",
        "template-semiprotected": "(अर्ध-सुरक्षित)",
        "hiddencategories": "यो पानो निम्न {{PLURAL:$1|1 लुकाइयाको श्रेणी|$1 लुकाइयाका श्रेणीहरू}}को हिस्सादार(सदस्य) हो :",
+       "nocreate-loggedin": "नौला पन्ना बनुनाइ तम सित अधिकार आथिन।",
        "sectioneditnotsupported-title": "खण्ड सम्पादन असमर्थित",
        "sectioneditnotsupported-text": "ये पृष्ठमी खण्ड सम्पादन असमर्थित",
        "permissionserrors": "अधिकारमी त्रुटी",
+       "permissionserrorstext": "तइ काम अद्दाइ तम सित अधिकार आथिन, यिन {{PLURAL:$1|कारण|कारणअन}}ले अद्दा:",
        "permissionserrorstext-withaction": "$2 कि लेखा तमलाईँ अनुमति नाइथिन , यिन {{PLURAL:$1|कारणले|कारणहरुले}} गद्दा :",
        "moveddeleted-notice": "पानो मेटियाको छ।\nमेटियाका और सारियाका पानाहरूको सूची तल्तिर सन्दर्भखी लेखा दियाको छ।",
        "log-fulllog": "पूरा लग हेर",
        "revdelete-unsuppress": "पुनर्स्थापित पुनरावृत्तिबठे बन्देज हटाउन्या",
        "revdelete-log": "कारण:",
        "revdelete-submit": "{{PLURAL:$1|छानिया संशोधन|छान्निया संशोधनहरू}}मी प्रयोग गर्न्या",
-       "revdelete-success": "'''संशोधन दृश्यता सफलतापूर्वक अद्यतन भयो।'''",
+       "revdelete-success": "संशोधन दृश्यता अद्यतन अरियो।",
        "revdelete-failure": "'''संशोधन दृश्यता अद्यतन गर्न सकिएन:'''\n$1",
        "logdelete-success": "लग दृष्टि मिलाइयो ।",
        "logdelete-failure": "'''लग दृष्टि मिलाउन सकिएन :'''\n$1",
        "nextn-title": "यै पछाका $1 {{PLURAL:$1|नतिजा |नतिजाहरू}}",
        "shown-title": "धेखाउने $1 {{PLURAL:$1|नतिजा|नतिजाहरू}} प्रति पाना",
        "viewprevnext": "हेर ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "''' \"[[:$1]]\" नाम गरया पाना  ये विकीमी रह्या छ'''",
+       "searchmenu-exists": "<strong>\"[[:$1]]\" नाउँ अरियाऽ पन्ना ये विकीमी छ।</strong>{{PLURAL:$2|0=| पाइयाऽ और खोजी नतिजाअन लै तकऽ।}}",
        "searchmenu-new": "<strong>\"[[:$1]]\"  पानो इसै विकिमी बनाओ !</strong> {{PLURAL:$2|0=|तमले खोज अरी भेटियाको पानो पन सङ्ङै जोड्या काम अर ।|तमरो खोज परिणाम पन हेर।}}",
-       "searchprofile-articles": "सामà¤\97à¥\8dरà¥\80 à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "searchprofile-articles": "सामà¤\97à¥\8dरà¥\80 à¤ªà¤¨à¥\8dनाà¤\85न",
        "searchprofile-images": "मल्टिमिडिया(श्रव्य दृश्य)",
        "searchprofile-everything": "सबै थोक",
        "searchprofile-advanced": "उन्नत",
        "searchprofile-articles-tooltip": "$1 मी खोज्या",
-       "searchprofile-images-tooltip": "फाइलहरू खोज्ज्या",
+       "searchprofile-images-tooltip": "फाइल कि लेखा खोजऽ",
        "searchprofile-everything-tooltip": "सबै सामग्री खोज्या (वार्तालाप लै )",
        "searchprofile-advanced-tooltip": "अनुकुल नेमस्पेसमा खोज्या",
-       "search-result-size": "$1 ({{PLURAL:$2|1 à¤¶à¤¬à¥\8dद|$2 à¤¶à¤¬à¥\8dदहरà¥\82}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 à¤\86à¤\81à¤\96र|$2 à¤\86à¤\81à¤\96र}})",
        "search-result-category-size": "{{PLURAL:$1|एक सदस्य|$1 सदस्यहरू}} ({{PLURAL:$2|1 उपश्रेणी|$2  उपश्रेणीहरू}}, {{PLURAL:$3|एउटा फाइल|$3 फाइलहरू}})",
        "search-redirect": "(जान्या $1)",
        "search-section": "(खण्ड $1)",
        "search-interwiki-more": "(आजी)",
        "search-relatedarticle": "सम्बन्धित",
        "searchrelated": "सम्बन्धित",
-       "searchall": "सबै",
+       "searchall": "सपà¥\8dपै",
        "showingresults": "धेखाउँदै  {{PLURAL:$1|'''१''' नतिजा|'''$1''' नतिजाहरू }} , #'''$2''' बठे सुरुहुन्या ।",
        "showingresultsinrange": "देखाई रह्या छ{{PLURAL:$1|<strong>1</strong> result|<strong>$1</strong> परिणाम}} सम्म पहुँच  #<strong>$2</strong> देखि #<strong>$3</strong> मी।",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> मै बठे <strong>$1</strong> परिणाम|<strong>$3</strong> मै बठे परिणाम <strong>$1 - $2</strong>}}",
        "prefs-editwatchlist-raw": "कच्चा अवलोकनसूची सम्पादन गद्दा",
        "prefs-editwatchlist-clear": "तमरो अवलोकनसूची मेटा",
        "prefs-watchlist-days": "ध्यान सूचीमी धेकाउने दिनहरू:",
-       "prefs-watchlist-days-max": "भà¥\8cत $1 {{PLURAL:$1|दिन|दिन}}",
+       "prefs-watchlist-days-max": "बरà¥\8dतà¥\80 à¤¹à¥\88 à¤¬à¤°à¥\8dतà¥\80 $1 {{PLURAL:$1|दिन|दिनà¤\85न}}",
        "prefs-watchlist-edits": "उच्चतम परिवर्तन संख्या बढाइएको निगरानी सूचीमी  धकाउनका लागि :",
        "prefs-watchlist-edits-max": "सबै है ज्यादा संख्या : १०००",
        "prefs-watchlist-token": "अवलोकन सूची टोकन:",
        "stub-threshold-sample-link": "उदाहरण",
        "stub-threshold-disabled": "निष्क्रिय",
        "recentchangesdays": "हालको परिवर्तनमी धेकाउने दिनहरू:",
-       "recentchangesdays-max": "à¤\85धिà¤\95तम $1 {{PLURAL:$1|दिन|दिन}}",
+       "recentchangesdays-max": "à¤\9cà¥\87दा à¤¹à¥\88 à¤\9cà¥\87दा $1 {{PLURAL:$1|दिन|दिनà¤\85न}}",
        "timezonelegend": "समय क्षेत्र :",
        "localtime": "स्थानिय समय:",
        "timezoneuseserverdefault": "विकि मूल  ($1) रुपमी प्रयोग गर्ने",
        "userrights-groupsmember": "को सदस्य:",
        "userrights-groupsmember-auto": "अंतर्निहित सदस्य:",
        "userrights-reason": "कारण:",
+       "userrights-changeable-col": "तमले परिवर्तन गद्द सक्दया समूहअन",
        "userrights-unchangeable-col": "तमीले परिवर्तन गद्द नसक्ने समूहहरू",
        "userrights-conflict": "प्रयोगकर्ताको अधिकार परिवर्तनमी मतभेद भयो ! कृपया तमरो परिवर्तन पुनरावलोकन तथा पुष्टि गर ।",
-       "userrights-removed-self": "तमà¥\80लà¥\87 à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤\86फà¥\8dनà¥\8b à¤\85धिà¤\95ारहरà¥\82लाà¤\88 à¤®à¥\87à¤\9fायà¥\8c à¥¤ à¤¤à¥\8dयà¥\88 à¤\95ारण à¤¤à¤®à¥\80 à¤\85ब यो पानो हेद्द नाइसक्दा ।",
+       "userrights-removed-self": "तमलà¥\87 à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤\86फनà¥\8b à¤\85धिà¤\95ारहरà¥\82लाà¤\88 à¤®à¥\87à¤\9fाया à¥¤ à¤¤à¥\8dयà¥\88 à¤\95ारण à¤¤à¤® à¤\86ब यो पानो हेद्द नाइसक्दा ।",
        "group": "समूह:",
        "group-user": "प्रयोगकर्ताहरू",
        "group-autoconfirmed": "स्वत स्थापित प्रयोगकर्ताहरू",
        "group-sysop-member": "{{GENDER:$1|प्रबन्धक}}",
        "group-bureaucrat-member": "{{GENDER:$1|प्रशासक}}",
        "group-suppress-member": "{{GENDER:$1|दबाउन्या}}",
-       "grouppage-user": "{{एनयस:आयोजना}}:प्रयोगकर्ताहरू",
-       "grouppage-autoconfirmed": "{{एनयस:आयोजना}}:स्वनिर्धारित प्रयोगकर्ताहरू",
-       "grouppage-bot": "{{एनयस:आयोजना}}:बोटहरु",
-       "grouppage-sysop": "{{एनयस:आयोजना}}:प्रबन्धकहरु",
-       "grouppage-bureaucrat": "{{एनयस:आयोजना}}:प्रशासकहरू",
-       "grouppage-suppress": "{{एनयस:आयोजना}}:लुकौन्या",
+       "grouppage-user": "{{ns:project}}:प्रयोगकर्ताहरू",
+       "grouppage-autoconfirmed": "{{ns:project}}:स्वतःपुष्टि भयाऽ प्रयोगकर्ताअन",
+       "grouppage-bot": "{{ns:project}}:बोटअन",
+       "grouppage-sysop": "{{ns:project}}:प्रबन्धकहरू",
+       "grouppage-bureaucrat": "{{ns:project}}:प्रशासकअन",
+       "grouppage-suppress": "{{ns:project}}:लुकौन्या",
        "right-read": "पृष्ठहरू पढ",
        "right-edit": "पृष्ठहरू सम्पादन गर",
        "right-createpage": "पृष्ठ निर्माण गर(छलफल पृष्ठहरू बाहेक)",
        "right-upload_by_url": "URL बठे फाइल उर्ध्वभरण गर्ने",
        "right-purge": "साइटको क्याश( cache) निश्चित नगरिकनै पर्ज(Purge) गर्ने",
        "right-writeapi": "लेखन API प्रयोग गद्य्या",
+       "right-delete": "पृष्ठहरू मेट्ने",
        "right-bigdelete": "लामो इतिहास भयाका पानाहरू मेट्ट्या",
        "right-deleterevision": "खुलाइयाको पानाहरू मेटाउन्या र मेटायाको रद्द गद्या",
        "right-deletedtext": "मेट्याका संशोधन बीचका मेट्याका पाठ र परिवर्तनहरू हेद्या",
+       "right-undelete": "मेट्याको पाना फर्काउने",
        "right-suppressionlog": "व्यक्तिगत लगहरू हेद्या",
        "right-block": "अरु प्रयोगकर्तानलाई सम्पादन गद्दाकी ब्लक गर",
        "right-unblockself": "आफुलाई खुल्ला गर ।",
        "right-userrights-interwiki": "अन्य विकिहरूमी प्रयोगकर्ताहरूको अधिकार सम्पादन गद्या",
        "right-override-export-depth": "गहिराइ ५ सम्म लिंक गरियाका पानाहरू सहित निर्यात गद्या",
        "right-sendemail": "अन्य प्रयोगकर्तानलाई इमेल पठाउन्या",
-       "grant-editmycssjs": "तमरो प्रयोगकर्ता CSS/JavaScript सम्पादन गर",
-       "grant-editmyoptions": "तमरा à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤\85भिरà¥\82à¤\9aà¥\80हरà¥\82लाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97र",
+       "grant-editmycssjs": "तमरो प्रयोगकर्ता CSS/JavaScript सम्पादन गर",
+       "grant-editmyoptions": "तमरा à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤\85भिरà¥\81à¤\9aà¥\80à¤\87नलाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97रऽ",
        "grant-editmywatchlist": "तमरो अवलोकनसूची सम्पादन गर",
        "grant-editpage": "भैरया पृष्ठहरू सम्पादन गर",
        "grant-editprotected": "सुरक्षित पृष्ठ सम्पादन",
        "recentchanges-legend": "अच्यालैका परिवर्तन विकल्पहरू",
        "recentchanges-summary": "विकिका यैल्लैका फेरबदललाई यै पानामि पहिल्याउन्या",
        "recentchanges-label-newpage": "यै सम्पादनले नौलो पानो बनायाको छ",
-       "recentchanges-label-minor": "यो नानो सम्पादन हो",
+       "recentchanges-label-minor": "यà¥\8b à¤¨à¤¾à¤½à¤¨à¥\8b à¤¸à¤®à¥\8dपादन à¤¹à¥\8b",
        "recentchanges-label-bot": "यो सम्पादन बोटबठे गरियाको थ्यो",
        "recentchanges-label-unpatrolled": "यो सम्पादन यैलसम्म गस्ती गरियाको नाइथी",
        "recentchanges-label-plusminus": "यति बाइटहरू संख्याले पानाको आकार फेरबदल  भयाको छ",
        "rclistfrom": "$3 $2 देखिका नयाँ परिवर्तनहरू देखाउन्या",
        "rcshowhideminor": "$1 सानतिनो सम्पादन",
        "rcshowhideminor-show": "धेकाइदिय",
-       "rcshowhideminor-hide": "लà¥\81à¤\95ाà¤\89नà¥\8dया",
+       "rcshowhideminor-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidebots": "$1 बोटहरू",
        "rcshowhidebots-show": "धेकाइदिय",
        "rcshowhidebots-hide": "लुकाइदिय",
        "rcshowhideliu": "$1 दर्ता अर्याका प्रयोगकर्ताहरू",
        "rcshowhideliu-hide": "लुकाउन्या",
        "rcshowhideanons": "$1 नपछेण्याका प्रयोगकर्ता",
-       "rcshowhideanons-show": "धà¥\87à¤\95ाà¤\87दिय",
-       "rcshowhideanons-hide": "लà¥\81à¤\95ाà¤\89नà¥\8dया",
+       "rcshowhideanons-show": "धà¥\87à¤\95ाऽ",
+       "rcshowhideanons-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidepatr": "$1 पट्रोल गर्याका सम्पादनहरू",
        "rcshowhidemine": "$1 मेरा सम्पादनहरू",
        "rcshowhidemine-show": "धेकाइदिय",
-       "rcshowhidemine-hide": "लà¥\81à¤\95ाà¤\87दिय",
+       "rcshowhidemine-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidecategorization-show": "धेकाउन्या",
        "rcshowhidecategorization-hide": "लुकाउन्या",
        "rclinks": "पछिल्ला $1 परिवर्तनहरू पछाडिका $2 दिनहरूमी<br />$3",
        "newpageletter": "नौ",
        "boteditletter": "बो",
        "rc_categories": "श्रेणीहरूमी सीमित (\"|\" ले छुट्याओ)",
-       "rc-change-size-new": "$1 {{PLURAL:$1|बाà¤\87à¤\9f|बाà¤\87à¤\9fस}}फà¥\87रबदलपाछा",
+       "rc-change-size-new": "$1 {{PLURAL:$1|बाà¤\87à¤\9f|बाà¤\87à¤\9fà¥\8dस}}फà¥\87रबदल पाछा",
        "recentchangeslinked": "सम्बन्धित फेरबदल",
        "recentchangeslinked-toolbox": "सम्बन्धित फेरबदल",
        "recentchangeslinked-title": "\"$1\" सित सम्बन्धित परिवर्तन",
        "recentchangeslinked-summary": "यो सूची निर्दिष्ट पाना (वा निर्दिष्ट श्रेणी)सित जोडियाका अल्लै परिवर्तन भयाका पानाको  हो। [[Special:Watchlist|तमरो ध्यानसूची]]का पानाहरू <strong>गाढा अक्षरमी</strong> छन्।",
        "recentchangeslinked-page": "पाना नाम:",
        "recentchangeslinked-to": "यैको सट्टा यो पानासित जोडियाका पानानको परिवर्तन धेकाउन्या",
-       "upload": "à¤\9aितà¥\8dर à¤\85पलà¥\8bड à¤\97र",
+       "upload": "फाà¤\87ल à¤\85पलà¥\8bड à¤\97रऽ",
        "uploadbtn": "फाइल अपलोड गर्न्या",
        "upload-recreate-warning": "'''चेतावनी: त्यस नाममी रह्याका फाइलहरू सारियाको या हटायाको छ।'''\n\nयै पानाको सारियाको र हटायाको लग तमरो सहजताको लागि दियाको छ।",
        "filedesc": "सारांश:",
        "large-file": "यो सिफारिस गर्याछकि फाइलहरूको आकार $1 भन्दा ठूला हुनु हुँदैन;\nयै फाइलको आकार $2 छ ।",
        "emptyfile": "तमीले अपलोड गर्याको फाइल रित्तो छ ।\nयो फाइल नाम गलत राख्याका कारणले भयाको हुनसकन्छ\nयो फाइल साँच्चै अपलोड गद्दे कुरडीमी निश्चित होइजाओ ।",
        "fileexists": "यै नामको फाइल पैल्ली नैं छ, यदि तम परिवर्तन गद्या कुरडीमू सुनिश्चित छैनौ भण्या कृपया <strong>[[:$1]]</strong> जाँच गर।\n[[$1|thumb]]",
+       "fileexists-no-change": "अपलोड <strong>[[:$1]]</strong>का अच्यालआ संस्करणो ठ्याक्कै नकल हो।",
+       "fileexists-duplicate-version": "अपलोड <strong>[[:$1]]</strong> को {{PLURAL:$2|पुरानु संस्करण|पुरानु संस्करणअन}}ओ नकल हो।",
        "filewasdeleted": "यै नामको एक फाइल पहिली पनि अपलोड गरिबर पछि हटाई सकियाको छ।\nपुनः अपलोड गद्दु पूर्व तम $1 लाई निक्करी जाँच गर ।",
        "upload-dialog-title": "चित्र अपलोड गर",
        "upload-dialog-button-cancel": "रद्द",
        "uploadstash-nofiles": "तमरा कोइ पनि स्टाश गर्याका फाइलहरू नाइथिन् ।",
        "uploadstash-badtoken": "त्यो कार्य असफलभयो , सायद तमरो सम्पादन अधिकार समाप्त भयो । पुन: प्रयास गर ।",
        "uploadstash-refresh": "फाइलहरूको सूची ताजा गर्न्या",
-       "license-header": "à¤\95à¥\8bà¤\87 à¤\95à¥\87à¤\87 à¤¨à¤¾à¤\87थिन",
+       "license-header": "à¤\86à¤\9cà¥\8dà¤\9eापतà¥\8dर à¤¦à¤¿à¤¨à¥\8dनाà¤\9b़",
        "listfiles-summary": "यै खास पानाले अपलोड गर्याका सबै फाइलहरू धेकाउन्छ ।",
        "imgfile": "चित्र",
        "listfiles_count": "संस्करणहरू",
        "filehist-thumb": "थम्बनेल",
        "filehist-thumbtext": "थम्बनेल $1 संस्करणको रुपमी",
        "filehist-user": "प्रयोगकर्ता",
-       "filehist-dimensions": "à¤\86à¤\95ारहरà¥\82",
+       "filehist-dimensions": "à¤\86याम",
        "filehist-comment": "टिप्पणी",
        "imagelinks": "फाइलको प्रयोगहरु",
        "linkstoimage": "यै चित्रमी निम्न{{PLURAL:$1|पाना जोडिनान{{PLURAL:$1|}}|$1 पानाहरू जोडिनान्}}:",
        "filedelete-intro-old": "तमी <strong>[[Media:$1|$1]]</strong> को संस्करणलाई [$4 $3, $2] हुन्या गरि मेट्ट लाग्याछौ ।",
        "filedelete-maintenance": "रखरखाव चलिरह्याको हुनाले अस्थायी रुपमी फाइलहरू मेट्ट्या र मेट्याकोलाई पुनर्बहाली गर्न निष्क्रिय गरियाकोछ।",
        "mimesearch-summary": "MIME-प्रकार अनुसार फाइलहरू खोज्न यै पानाको प्रयोग गद्द सकिन्याछ ।\nइनपुट: फाइलको प्रकार/उपप्रकार, उदा. <code>image/jpeg</code>।",
-       "randompage": "à¤\95à¥\8bà¤\87 à¤\8fà¤\95 à¤²à¥\87à¤\96",
+       "randompage": "à¤\95à¥\8dरमरहित à¤ªà¤¨à¥\8dना",
        "statistics-header-pages": "पानानको तथ्याङ्क",
        "statistics-header-edits": "सम्पादनहरूको तथ्याङ्क",
        "statistics-files": "अपलोड गर्याका फाइलहरू",
        "deadendpagestext": "निम्न पानाहरू {{SITENAME}}मी रह्याका अरु पानाहरूसँग जोडिदाइनन् ।",
        "protectedpagesempty": "यै बेला यी नियम बठे कुनै पाना लै शुरक्षित नाइथिन्",
        "usereditcount": "$1 {{PLURAL:$1|सम्पादन|सम्पादनहरू}}",
-       "newpages": "नयाà¤\81 à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "newpages": "नà¥\8cला à¤ªà¤¨à¥\8dनाà¤\85न",
        "move": "नाम बदल",
        "movethispage": "पानाको नाम बदल्न्या",
        "notargettext": "यै कार्यका लेखाई तमीले कुनै लक्षित पानो वा प्रयोगकर्ता निर्दिष्ट गर्याको छैनौ ।",
        "booksources-text": "तल दियाको सूची नौला तथा पूराना किताब बेच्न्या लगायत तमीले खोज्याका किताबका बारेमी थप जानकारी भयाका अन्य साइटका लिंकहरू हुन् ।",
        "log": "लगहरू",
        "all-logs-page": "सब्बै सार्वजनिक लगहरू",
-       "allarticles": "सबà¥\8dबà¥\88 à¤²à¥\87à¤\96हरà¥\82",
-       "allpagessubmit": "à¤\9cानà¥\8dया",
+       "allarticles": "सपà¥\8dपà¥\88 à¤ªà¤¨à¥\8dनाà¤\85न",
+       "allpagessubmit": "à¤\9cाऽ",
        "allpagesprefix": "यी सुरुका अक्षरसहितका पानाहरू हेद्या:",
        "categories": "श्रेणीहरू",
        "listusers-noresult": "प्रयोगकर्ता भेटियानन्",
        "blanknamespace": "(मुख्य)",
        "contributions": "{{GENDER:$1|प्रयोगकर्ता}}को योगदान",
        "mycontris": "मेरो योगदानहरू",
+       "anoncontribs": "योगदान",
        "month": "महिना बठे (लै पैल्ली):",
        "year": "वर्ष बठे( लौ पैल्ली):",
        "sp-contributions-toponly": "नवीनतम संशोधनका सम्पादनहरू मात्र धेकाओ",
-       "whatlinkshere": "याà¤\81à¤\96ाà¤\87 à¤\95à¥\80 à¤\9cà¥\81डन्छ",
-       "whatlinkshere-title": "$1 à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाà¤\95ा à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "whatlinkshere": "याà¤\81à¤\96ाà¤\87 à¤\95ि à¤\9cà¥\8bणà¥\80न्छ",
+       "whatlinkshere-title": "$1 à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाऽ à¤ªà¤¨à¥\8dनाà¤\85न",
        "whatlinkshere-page": "पानो",
        "linkshere": "निम्न पानाहरू '''[[:$1]]''' मी जुडन्छ :",
        "nolinkshere-ns": "चुनियाको नामस्थानमी '''[[:$1]]''' सित जुड्न्या पानाहरू नाइथिन्।",
        "whatlinkshere-prev": "{{PLURAL:$1|पैलो|पैलो $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अर्को|अर्को $1}}",
        "whatlinkshere-links": "← लिंकहरू",
-       "whatlinkshere-hideredirs": "$1 à¤\85नà¥\81पà¥\8dरà¥\87षित हुन्छ",
-       "whatlinkshere-hidetrans": "$1 à¤ªà¤¾à¤°à¤¦à¤°à¥\8dशन",
-       "whatlinkshere-hidelinks": "$1 लिङ्कहरु",
-       "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
+       "whatlinkshere-hideredirs": "$1 à¤ªà¥\81न:निरà¥\8dदà¥\87शित हुन्छ",
+       "whatlinkshere-hidetrans": "$1 à¤¸à¤®à¥\8dमà¥\80ल",
+       "whatlinkshere-hidelinks": "$1 लिङ्क",
+       "whatlinkshere-hideimages": "$1 फाइलआ लिङ्कअन",
        "whatlinkshere-filters": "छानियाका",
        "ipbreason-dropdown": "* ब्लक गर्नुका समान्य कारणहरू\n** झूटो सूचना दियाको\n** पानानबठे सामाग्रीहरू हटायाको\n** बाहिरी जालक्षेत्र (sites)सित नचाहिंदो लिङ्क गर्याको \n** पानानमी बकवास/गाली-गलौच हाल्याको\n** भै धेकाउने व्यवहार/उत्पीडन (सताउने कार्य) गर्याको\n** धेरै गलत खाताहरू बनायाको\n** प्रयोगकर्ता नाम अस्वीकार्य",
        "ipboptions": "२ घण्टाहरू:2 hours,१ दिन :1 day,३ दिनहरू:3 days,१ हप्ता:1 week,२ हप्ताहरू:2 weeks,१ महिना:1 month,३ महिनाहरू:3 months,६ महिनाहरू:6 months,१ वर्ष:1 year,अनगिन्ती:infinite",
        "ipblocklist": "ब्लक गर्याका प्रयोगकर्ताहरू",
        "ipblocklist-legend": "ब्लक गर्याका प्रयोगकर्ताहरू खोज",
        "blocklink": "रोक्न्या",
-       "contribslink": "यà¥\8bà¤\97दानहरà¥\82",
+       "contribslink": "यà¥\8bà¤\97दानà¤\85न",
        "block-log-flags-anononly": "नाम नभयाका प्रयोकर्ताहरू मात्र",
        "proxyblockreason": "तमरो IP ठेगानामी रोक लगायाको छ किनकी यो खुला प्रोक्सी हो ।\nकृपया तमरो इन्टरनेट सेवा प्रदायक या प्राविधिक सहायतासँग सम्पर्क गरीबर यै सुरक्षा समस्याका बारेमी जानकारी गराओ ।",
        "sorbsreason": "तमरो IP ठेगाना खुल्ला प्रोक्सीको रुपमी  DNSBL मा सूचीकरण गरिएको छ यैलाई{{SITENAME}}ले प्रयोगमी ल्यायाको छ।",
        "import-error-edit": "तमलाई सम्पादन गद्या अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-error-create": "तमलाई नयाँ बनाउने अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनहरू}} आयात भयो",
-       "tooltip-pt-userpage": "तमरो प्रयोगकर्ता पानो",
+       "tooltip-pt-userpage": "{{GENDER:|तमरो प्रयोगकर्ता}} पान्नो",
        "tooltip-pt-anonuserpage": "तमी जो IP ठेगानाको रुपमी सम्पादन गद्दै छौ , त्यैको प्रयोगकर्ता पानो निम्न छ :",
-       "tooltip-pt-mytalk": "तमरो कुरडीकानी पानो",
-       "tooltip-pt-preferences": "तमरा अभिरुचिहरू",
+       "tooltip-pt-mytalk": "{{GENDER:|तमरो}} कुरडीकानी पानो",
+       "tooltip-pt-preferences": "{{GENDER:|तमरी}} अभिरुचि",
        "tooltip-pt-watchlist": "पृष्ठहरूको सूची जैका फेरबदलहरुलाई तमले पहरा गरिराखेका छौ ।",
-       "tooltip-pt-mycontris": "तमरो योगदानको सूची",
-       "tooltip-pt-login": "तमलाई प्रवेशगद्द सुझाव दिइन्छ ; याद अर यो जरुरी आथिन भण्या ।",
+       "tooltip-pt-mycontris": "{{GENDER:|तमरा}} योगदानअनऐ सूची",
+       "tooltip-pt-login": "तमलाई प्रवेशगद्द सुझाव दिइन्छ ; याद अर यो जरुरी आथिन भण्या ।",
        "tooltip-pt-logout": "बाहिर निस्कन्या (लग आउट)",
-       "tooltip-pt-createaccount": "तमलाई खाता बनौन लै लग इन अद्द हम हौसला अद्दाउ; काइकि, यो अनिवार्य नाइथी भण्या ।",
-       "tooltip-ca-talk": "सामाà¤\97à¥\8dरà¥\80 à¤ªà¥\83षà¥\8dठबारà¥\87मà¥\80 à¤\9bलफल",
-       "tooltip-ca-edit": "ये पाना सम्पादन गर",
+       "tooltip-pt-createaccount": "तमलाई खाता बनौन लै लगइन अद्द लै हम हौसला अद्दाउ; यद्यपि, यो अनिवार्य नाइथीन।",
+       "tooltip-ca-talk": "सामाà¤\97à¥\8dरà¥\80 à¤ªà¥\83षà¥\8dठबारà¥\87मà¥\80 à¤\95à¥\81रणिà¤\95ाà¤\86नà¥\80",
+       "tooltip-ca-edit": "येइ पन्ना सम्पादन गरऽ",
        "tooltip-ca-addsection": "नयाँ खण्ड सुरु अरिदिय",
        "tooltip-ca-viewsource": "यो पानो सुरक्षित अरियाको छ। यैको श्रोत हेद्द सकन्छौ ।",
-       "tooltip-ca-history": "यà¥\88 à¤ªà¥\83षà¥\8dठà¤\95ा à¤ªà¥\88लà¥\8dलिà¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\81",
+       "tooltip-ca-history": "यà¥\88 à¤ªà¤¨à¥\8dनाऽ à¤ªà¥\88लà¥\8dलिà¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नà¤\85न",
        "tooltip-ca-undelete": "मेट्याको भया पनि यै पानाको सम्पादनहरू पुन:प्राप्त गर",
        "tooltip-ca-move": "यो पानालाई अर्खिठौर सार",
        "tooltip-ca-watch": "यै पानालाई तमरा ध्यानसूचीमि थपिदिय",
-       "tooltip-search": "{{SITENAME}}मी खोज",
-       "tooltip-search-go": "यदि à¤¯à¥\8b à¤¨à¤¾à¤®à¤\95à¥\8b à¤ªà¥\83षà¥\8dठ à¤°à¤¯à¤¾à¤\95à¥\8b à¤\9b à¤­à¤£à¥\8dया à¤¤à¥\88मà¥\80 à¤\9cानà¥\8dया ।",
-       "tooltip-search-fulltext": "यà¥\88 à¤ªà¤¾à¤ à¤\95ा à¤²à¤¾à¤\97ि à¤ªà¤¾à¤¨à¤¾मी खोज",
-       "tooltip-p-logo": "à¤\96ास à¤ªà¤¾à¤¨à¥\8b",
+       "tooltip-search": "{{SITENAME}}मी खोज",
+       "tooltip-search-go": "यदà¥\80 à¤ à¥\8dयाà¤\95à¥\8dà¤\95à¥\88 à¤¯à¥\87à¤\87 à¤¨à¤¾à¤\89à¤\81 à¤­à¤¯à¤¾: à¤ªà¤¨à¥\8dना à¤°à¥\88à¤\9b à¤­à¤\81णà¥\8dया à¤¤à¥\88 à¤®à¥\80 à¤\9cा:।",
+       "tooltip-search-fulltext": "यà¥\88 à¤ªà¤¾à¤ à¤\95ा à¤²à¤¾à¤\97ि à¤ªà¤¨à¥\8dनाà¤\85नमी खोज",
+       "tooltip-p-logo": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dनामà¥\80 à¤¹à¥\87रऽ",
        "tooltip-n-mainpage": "खास पानामी झान्या",
-       "tooltip-n-mainpage-description": "à¤\96ास à¤ªà¤¾à¤¨à¤¾à¤®à¥\80 à¤\9dा",
+       "tooltip-n-mainpage-description": "à¤\96ास à¤ªà¤¨à¥\8dनामà¥\80 à¤\9dाऽ",
        "tooltip-n-portal": "आयोजनाका बारेमी , तम कि अद्द सकन्छौ , समान काखाइ  भेटौन्या",
        "tooltip-n-currentevents": "हालैका घटनाको बारेमी पृष्ठभूमि जानकारी पत्ता लागाइदिय",
-       "tooltip-n-recentchanges": "विà¤\95िमा à¤\85रियाà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤­à¤¯à¤¾ à¤«à¥\87रबदलà¤\95ा शुचि ।",
-       "tooltip-n-randompage": "à¤\9cà¥\8b à¤\95à¥\8bà¤\87 à¤ªà¤¾à¤¨à¥\8b à¤\96à¥\8bलà¥\8dया",
-       "tooltip-n-help": "खोज्जु पड्या ठौर ।",
-       "tooltip-t-whatlinkshere": "यà¥\8b à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाà¤\95ा à¤¸à¤¬à¥\8dबà¥\88 à¤µà¤¿à¤\95ि à¤ªà¤¾à¤¨à¤¾à¤¨à¤\95à¥\8b à¤¸à¥\82à¤\9aà¥\80",
+       "tooltip-n-recentchanges": "विà¤\95िमà¥\80 à¤¹à¤¾à¤²à¥\88 à¤\85रियाà¤\95ा à¤«à¥\87रबदलà¥\88 शुचि ।",
+       "tooltip-n-randompage": "à¤\95à¥\8dरमरहित à¤ªà¤¨à¥\8dना à¤\96à¥\8bलऽ",
+       "tooltip-n-help": "à¤\96à¥\8bà¤\9cà¥\8dà¤\9cà¥\81 à¤ªà¤¡à¥\8dडà¥\8dया à¤ à¥\8cर à¥¤",
+       "tooltip-t-whatlinkshere": "सपà¥\8dपà¥\88 à¤µà¤¿à¤\95ि à¤ªà¤¨à¥\8dनाà¤\85नà¥\88 à¤¶à¥\81à¤\9aि à¤\9cà¥\8b à¤¯à¤¾à¤\81à¤\96ाà¤\87 à¤\9cà¥\8bणà¥\80à¤\9cान",
        "tooltip-t-recentchangeslinked": "यै पानामी जोडियाका पानामी अहिलको परिवर्तन",
        "tooltip-feed-atom": "यै पानाकी लेखा एक एटम फिड",
-       "tooltip-t-contributions": "{{GENDER:$1|यिन à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता}}à¤\95ा à¤¯à¥\8bà¤\97दानहरà¥\82à¤\95à¥\8b à¤¸à¥\82à¤\9aà¥\80 à¤¹à¥\87रपà¥\81à¤\88",
-       "tooltip-t-upload": "à¤\9aितà¥\8dर à¤\85पà¥\8dलà¥\8bड à¤\85र",
-       "tooltip-t-specialpages": "सब्बै खास खास पानानको शुचि ।",
-       "tooltip-t-print": "यà¥\8b à¤ªà¤¾à¤¨à¤¾à¤\95à¥\8b à¤\9bापिन्या संस्करण",
+       "tooltip-t-contributions": "{{GENDER:$1|यिन à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता}}ऽ à¤¯à¥\8bà¤\97दानन à¤\90 à¤¶à¥\81à¤\9aि",
+       "tooltip-t-upload": "फाà¤\87ल à¤\85पà¥\8dलà¥\8bड à¤\85रऽ",
+       "tooltip-t-specialpages": "सब्बै खास-खास पन्नाअनै सूची",
+       "tooltip-t-print": "यà¥\87à¤\87 à¤ªà¤¨à¥\8dनाऽ à¤\9bापà¥\8dद à¤®à¤¿à¤²à¥\8dल्या संस्करण",
        "tooltip-t-permalink": "पृष्ठको यो पुनरावलोकनकि लेखा स्थाई लिङ्क",
        "tooltip-ca-nstab-main": "सामाग्री पानो हेरिदिय",
        "tooltip-ca-nstab-user": "प्रयोगकर्ता पानो हेरिदिय",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|प्रयोगकर्ता|प्रयोगकर्ताहरू}} $1",
        "anonusers": "{{SITENAME}} का नाम नभयाका {{PLURAL:$2| प्रयोगकर्ता|प्रयोगकर्ताहरू}} $1",
        "simpleantispam-label": "ऐन्टी-स्प्याम जाँच।\nयैलाई <strong>नाइँ</strong> भद्य्या!",
-       "pageinfo-toolboxlink": "यà¥\88 à¤ªà¤¾à¤¨à¤¾à¤\95à¥\8b à¤\9cाणकारी",
+       "pageinfo-toolboxlink": "पनà¥\8dनाà¤\87 à¤\9cानकारी",
        "rcpatroldisabled": "अहिलका परिवर्तनहरू गस्ती निष्क्रिय पार्याको छ ।",
        "rcpatroldisabledtext": "अहिलका परिवर्तनहरू गस्ती गुण अहिलको लागि निष्कृय पारियाको छ ।",
        "markedaspatrollederror-noautopatrol": "तमी आफ्नै सम्पादनलाई गस्ती गरियाको भनि चिनो लगाउन नाइसक्दा ।",
        "watchlistedit-clear-done": "तमरो ध्यान सूची खाली गरीयाको छ।",
        "watchlisttools-view": "आधारित फेरबदलीहरू हेर",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|कुरडी]])",
-       "specialpages": "à¤\96ास à¤ªà¤¾à¤¨à¥\8b",
+       "specialpages": "à¤\96ास à¤ªà¤¨à¥\8dनाà¤\85न",
        "specialpages-note": "* साधारण खास पानाहरू।\n* <span class=\"mw-specialpagerestricted\">निषेधित खास पानाहरू।</span>",
        "specialpages-group-changes": "अल्लैका परिवर्तन लगहरू",
        "tags": "मान्य परिवर्तन ट्यागहरू",
        "logentry-newusers-create": "प्रयोगकर्ता खाता $1 {{GENDER:$2|खोलियो}}",
        "logentry-upload-upload": "$1 ले $3 {{GENDER:$2|अपलोड अरेका छन्}}",
        "feedback-bugornote": "यदि तमी कुनै प्राविधिक समस्यालाई विस्तारले सम्झाउन तयार छौ भण्या कृपया [$1 बग राख]।\nयदि हैन, भण्या तमी तल दियाको सरल फारमको प्रयोग गद्दसक्द्याहौ । तमरो टिप्पणी, तमरो प्रयोगकर्ता नाम र तमरो ब्राउजरको नाम सहित \"[$3 $2]\" पानामी जोडिन्याछ ।",
-       "searchsuggest-search": "खोज",
+       "searchsuggest-search": "खोज:",
        "api-error-duplicate": "यै साइटमी पहिलीबठे यस्तै सामग्री {{PLURAL:$1|भयाको अर्को फाइल छ|भयाका  केहि अरु फाइलहरू छन्}} ।",
        "api-error-duplicate-archive": "यै साइटमी पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भयाको अर्को फाइल थियो|भयाका केहि अरु फाइलहरू थिए}} ।\nतर {{PLURAL:$1|यो मेट्याको थियो|यी मेटायाका थिए}} ।",
        "expand_templates_preview_fail_html": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्ववावलोकन प्रयास हो भण्या पुन प्रयास गर ।</strong>\nयदि यसले कार्य पूर्ण भएन भण्या [[Special:UserLogout|लग आउट गरिबर]] फेरी लग इन गर्या ।",
index 0c6b8ef..e338987 100644 (file)
@@ -33,7 +33,7 @@
        "tog-enotifminoredits": "Mândom un avîş ânca p'r al mudéfichi céchi ed pàgini e file",
        "tog-enotifrevealaddr": "Fà vèder al mé indirés ed la pôsta eletrônica int i mesâg 'd avîş",
        "tog-shownumberswatching": "Fà vèder al nómer ed j utèint che gh'àn la pàgina sòta uservasiòun",
-       "tog-oldsig": "La fîrma 'd adèsa",
+       "tog-oldsig": "La tó fîrma 'd adèsa",
        "tog-fancysig": "Trâta la fîrma cme wikitèst (sèinsa colegamèint avtomâtich)",
        "tog-uselivepreview": "Permèt la funsiòun \"Live preview\" (guêrda préma 'd salvêr in dirèta)",
        "tog-forceeditsummary": "Dmânda s'l'è vèira che al câmp argumèint l' é vōd",
@@ -48,7 +48,7 @@
        "tog-showhiddencats": "Fà vèder al categoréi lughêdi",
        "tog-norollbackdiff": "An fêr mia vèder al cunfrûnt tr' al versiòun dôp avèir fât un \"rollback\"",
        "tog-useeditwarning": "Avîşom quând a vâgh fōra da 'na pàgina d' mudéfica e an n'ò mìa salvê al mudéfichi fâti",
-       "tog-prefershttps": "Drōva sèimper un colegamèint sicûr quând ét fê l'ingrès",
+       "tog-prefershttps": "Drōva sèimper un colegamèint sicûr mèinter té coleghê.",
        "underline-always": "Sèimper",
        "underline-never": "Mài",
        "underline-default": "Mantî al j impustasiòun dal navigadōr o 'd la skin",
        "category-file-count-limited": "In cla categoréia ché a gh'é {{PLURAL:$1|al file nutê|i $1 file nutê}} ché 'd sègvit.",
        "listingcontinuesabbrev": "cunt.",
        "index-category": "Pàgini gancêdi",
-       "noindex-category": "Pàgini mìa gancêdi",
+       "noindex-category": "Pàgini mìa coleghêdi.",
        "broken-file-category": "Pàgini cun dèinter di file ch' an gh'în mìa.",
        "about": "Infumasiòun",
        "article": "Còl che gh'é int la pàgina",
        "newwindow": "(a s'arvés 'na fnèstra nōva)",
        "cancel": "Scanşèla",
        "moredotdotdot": "Êter...",
-       "morenotlisted": "Cl'elèinch ché an n'é mìa finî.",
+       "morenotlisted": "Cl'elèinch ché 'l pré èser mìa finî.",
        "mypage": "Pàgina",
        "mytalk": "Al mē discusiòun",
-       "anontalk": "Discusiòun per cl' IP ché",
+       "anontalk": "Discusiòun",
        "navigation": "Navigasiòun",
        "and": "&#32;e",
        "qbfind": "Câta",
        "laggedslavemode": "'''Atèinti:''' la pàgina la pré avèir mìa al revisiòun pió nōv.",
        "readonly": "'Database' bluchê",
        "enterlockreason": "Scréver al mutîv dal blôch, precişêr quând a 's pèinsa che 'l vègna tôt via.",
-       "readonlytext": "In cól mumèint ché al databêş l'é bluchê e an 's pōlen fêr né zûnti né mudéfichi. Al blôch ed sôlit l'é lighê a 'na revişiòun normêla e quând la srà finîda al gnirà sbluchê. \n\nL'aminitradōr dal sistēma ch' al l'à bluchê l'à dê cla spiegasiòun ché: $1",
+       "readonlytext": "In cól mumèint ché al databêş l'é bluchê e an 's pōlen fêr né zûnti né mudéfichi. Al blôch ed sôlit l'é lighê a 'na revişiòun normêla e quând la srà finîda al gnirà sbluchê. \n\nL'aminitradōr dal sistēma ch' l'à urdnê 'l blôch l'à dê cla spiegasiòun ché: $1",
        "missing-article": "Al datebêş an n'à mìa catê al tèst ed 'na pàgina ch' l' aré duvû catêres sòt' al nòm \"$1\" $2. Ed sôlit còst a sucēd quând a vîn arciamê, a partîr da la stòria dal mudéfichi o dal cunfrûnt tra versiòun, un colegamèint a 'na pàgina scanşlêda, a un cunfrûnt tra versiòun che gh'în mìa o a un cunfrûnt tra versiòun cun la stòria dal mudéfichi scanşlêda. In chês cuntrâri, a s'é pubabilmèint catê un erōr int al prugrâma ed Media Wiki. A se dmânda al piaşèir ed comunichêr còl ch'é sucès a un [[Special:ListUsers/sysop|amministadōr]] e comunichêregh l'indirés (URL) in quistiòun.",
        "missingarticle-rev": "(nómer ed la versiòun: $1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "viewsource": "Guêrda la surzéia",
        "viewsource-title": "Guêrda la surzéia 'd $1",
        "actionthrottled": "L'asiòun la vîn tardêda.",
-       "actionthrottledtext": "Cme mişûra 'd sicurèsa cûnt'r al spam soquânti operasiòun a vînen limitêdi a 'n nómer mâsim ed vôlti in un precîş peréiod ed tèimp, in cól chêş ché a s'é bèle andê d'ed là 'd cól lémit. A se dmânda ed turnêr a pruvêr tra soquânt minût.",
+       "actionthrottledtext": "Cme mişûra 'd sicurèsa cûntra l'abûş soquânti operasiòun a vînen limitêdi a 'n nómer mâsim ed vôlti in un precîş peréiod ed tèimp, in cól chêş ché a 's é bèle andê d'ed là 'd cól lémit. A 's e-dmânda 'd turnêr a pruvêr tra soquânt minût.",
        "protectedpagetext": "Cla pàgina ché l'é stêda prutèta per impidîr la mudéfica o êtri operasiòun.",
        "viewsourcetext": "L'é pusébil vèder e cupiêr al côdis surzéia ed cla pàgina ché.",
        "viewyourtext": "L'é pusébil vèder e cupiêr al côdis surzéia dal <strong>tō mudéfichi</strong> ed cla pàgina ché.",
        "protectedinterface": "Cla pàgina ché la gh'à 'n elemèint ch' al fa pêrt dal colegamèint tra utèint e al progrâma 'd cól sît ché e l'é prutèta per schivşêr pusébil abûş. Per zuntêr o mudufichêr tradusiòun per tót i sistēma wiki druvêr [https://translatewiki.net/ translatewiki.net], al prugèt 'd adatamèint a ògni léngva 'd MediaWiki.",
        "editinginterface": "<strong>Atèinti:</strong> Al tèst ed cla pàgina ché 'l fa pêrt dal colegamèint tra utèint e 'l prugrâma dal sît.  Tót' al modéfichi fâti a cla pàgina ché a se spècen in sém a i mesâg vést per tót j utèint ed cól wiki ché.",
        "translateinterface": "Per zuntêr o mudifichêr al tradusiòun vâlidi in sém a tót i wiki, drōva [https://translatewiki.net/ translatewiki.net], al prugèt Media Wiki p'r al léngui di divêrs pôst.",
-       "cascadeprotected": "Insém a cla pàgina ché an n'é mìa pusébil fêr dal mudéfichi perchè l'é dèinter {{PLURAL:$1|int la pàgina sgnêda ché  'd sègvit, ch' l'é stêda prutèta|int al pàgini sgnêdi ché  'd sègvit, ch' în stêdi prutèti}} cun la prutesiòun ch' la 's arfà in cuntinvasiòun:\n$2",
+       "cascadeprotected": "In sém a cla pàgina ché an n'é mìa pusébil fêr dal mudéfichi perchè l'é dèinter {{PLURAL:$1|int la pàgina sgnêda ché  'd sègvit, ch' l'é stêda prutèta|int al pàgini sgnêdi ché  'd sègvit, ch' în stêdi prutèti}} cun la prutesiòun ch' la 's arfà in cuntinvasiòun:\n$2",
        "namespaceprotected": "An 's gh'à mìa i permès necesâri per mudifichêr al pàgini dal spâsi di nòm <strong>$1</strong>.",
        "customcssprotected": "An 's gh'à mìa i permès necesâri per mudifichêr cla pàgina CSS ché, perchè la gh'à dèinter al j impustasiòun personêli 'd n' êter utèint.",
        "customjsprotected": "An 's gh'à mìa i permès necesâri per mudifichêr cla pàgina JavaScript ché, perchè la gh'à dèinter al j impustasiòun personêli 'd n' êter utèint.",
        "mypreferencesprotected": "An 's gh'à mìa i permès necesâri per cambiêr al preferèinsi personêli.",
        "ns-specialprotected": "An n'é mìa pusébil mudifichêr al pàgini specêli.",
        "titleprotected": "Al tétol ed cla pagina ché l'é stê bluchê da [[User:$1|$1]].\nCòst l'é al mutîv: <em>$2</em>.",
-       "filereadonlyerror": "An n'é mìa stê pusébil mudifichêr al file \"$1\" perchè al depôsit di file \"$2\" a 's pōl sōl lēzer.\n\nL'aministradōr ch' al l'à bluchê l'à dê cla spiegasiòun ché:\"$3\".",
+       "filereadonlyerror": "An n'é mìa stê pusébil mudifichêr al file \"$1\" perchè al depôsit di file \"$2\" a 's pōl sōl lēzer.\n\nL'aministradōr dal sistēma ch' al l'à bluchê l'à dê cla spiegasiòun ché:\"$3\".",
        "invalidtitle-knownnamespace": "Tétol mìa vâlid cme spâsi di nòm \"$2\" e tèst \"$3\"",
        "invalidtitle-unknownnamespace": "Tétol mìa vâlid cun spâsi di nòm mìa cgnusû \"$1\" e tèst \"$2\"",
        "exception-nologin": "An t'é mìa gnû dèinter",
        "yourpasswordagain": "Scrév incòra la cêva 'd ingrès:",
        "createacct-yourpasswordagain": "Cunfērma la cêva 'd ingrès",
        "createacct-yourpasswordagain-ph": "Tōrna mèter dèinter la cêva 'd ingrès",
-       "remembermypassword": "Tîn a mèint la cêva 'd ingrès insém a cól navigadōr ché (per un mâsim ed $1{{PLURAL:$1|dé}}).",
        "userlogin-remembermypassword": "Sèimper coleghê",
        "userlogin-signwithsecure": "Drōva un colegamèint sicûr",
        "yourdomainname": "Precişêr al duméni:",
        "createacct-reason": "Mutîv",
        "createacct-reason-ph": "Perchè ét drē fêr 'n' êtra utèinsa",
        "createacct-submit": "Fà la tó utèinsa",
-       "createacct-another-submit": "Fà 'n' êtra utèinsa.",
+       "createacct-another-submit": "Fà l' utèinsa.",
        "createacct-benefit-heading": "{{SITENAME}} crès grâsia a persòuni cme té.",
        "createacct-benefit-body1": "{{PLURAL:$1|mudéfica|mudéfichi}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pàgina|pàgini}}",
        "nocookieslogin": "Per fêr l'ingrès a {{SITENAME}} a 's dēv druvêr i cookie che rişûlten bluchê. Tōrna fêr l'ingrès dôp avèir şbluchê i cookie int al tó navigadōr.",
        "nocookiesfornew": "L'iscrisiòun utèint an n'é mìa stêda fâta, perchè òm mìa prû cunfermêr la só urégin. Veréfica 'd avèir şbluchê i cookie, tōrna carghêr cla pàgina ché e prōva incòra.",
        "noname": "Al nòm utèint scrét an n'é mìa vâlid.",
-       "loginsuccesstitle": "Ingrès fât.",
+       "loginsuccesstitle": "Fât l'ingrès.",
        "loginsuccess": "''' T'é stê coleghê al terminêl {{SITENAME}} cun al nòm utèint '''$1'''.",
-       "nosuchuser": "An n'é mìa registrê nisûn utèint cun al nòm \"$1\". I nòm utèin în sensébil al lètri grândi. Veréfica al nòm scrét o [[Special:CreateAccount|fà un nōv ingrès]].",
+       "nosuchuser": "An n'é mìa registrê nisûn utèint cun al nòm \"$1\". I nòm utèin în sensébil al lètri grândi. Veréfica al nòm scrét o [[Special:CreateAccount|fà 'na nōva utèinsa]].",
        "nosuchusershort": "An gh'é mìa registrê un utèint ciamê ''$1''. Veréfica al nòm scrét.",
        "nouserspecified": "L'é necesâri precişêr un nòm utèint.",
        "login-userblocked": "Cl'utèinsa ché l'é bluchêda. An n'é pusébil fêr l'ingrès.",
        "noemail": "Nisûn indirés ed pôsta eletrônica registrê per l'utèint $1.",
        "noemailcreate": "L'é necesâri dêr un 'indirés ed pôsta eletrônica vâlid.",
        "passwordsent": "'Na nōva cêva 'd ingrès l'é stêda spidîda a l'indiré ed pôsta eletrônica per l'utèint \"$1\". Per piaşèir, fà un ingrès apèina 't la ricēv.",
-       "blocked-mailpassword": "Per pervèder abûş, an n'é mìa permès druvêr la funsiòun \"spidés 'na nōva cêva 'd ingrès\" da un indirés IP bluchê.",
+       "blocked-mailpassword": "Al tó indirés IP l’è bluchê int la mudéfica. Per pervèder abûş, an n'é mìa permès druvêr la funsiòun ed recóper ed la cêva ‘d ingrès da cl’indirés IP ché.",
        "eauthentsent": "Un mesâg ed cunfèirma l'é stê spidî a l'indirés ed pôsta eletrônica sgnê ché. L'utèint per prèir inviêr di mesâg ed pôsta eletrônica al dēv andêr a drē al j istrusiòun scréti, in môd da cunfermêr ch' l'é ló al legétim proprietâri 'd l'indirés.",
        "throttled-mailpassword": "Un mesâg ed pôsta eletrônica 'd arnōv ed la cêva 'd ingrès l'é bèle stê inviê da mēno 'd {{PLURAL:$1|1 ōra|$1 ōri}}. Per pervèder abûş, la funziòun 'd arnōv ed la cêva 'd ingrès la pōl èser druvêda sōl 'na vôlta ògni {{PLURAL:$1|1 ōra|$1 ōri}}.",
        "mailerror": "Erōr int la spedisiòun dal mesâg $1",
        "createaccount-title": "Per fêr un inscrisiòun a {{SITENAME}}",
        "createaccount-text": "Quelchidûn l'à fât un inscrisiòun a  {{SITENAME}} ($4) a nòm ed $2 coleghê a cl'indirés ed pôsta eletrônica ché. La cêva 'd ingrès per l'utèint \"$2\" l'é  impustêda a \"$3\". \nÉ necesâri fêr un ingrès préma ch' es pôl e cambiêr subét la cêva 'd ingrès. \nSe l'inscrisiòun l'é stêda fâta per şbâli, es pōl scanşlêr sté mesâg.",
        "login-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
-       "login-abort-generic": "An t'é mìa stê arcgnusû - Scanşlê",
+       "login-abort-generic": "Al tó ingrès an n’è mia stê fât – Al vîn scanşlê",
        "login-migrated-generic": "La tó utèinsa l'é stêda spustêda, e al tó nòm utèint al gh'é mìa pió in sém a cla wiki ché.",
        "loginlanguagelabel": "Léngva: $1",
        "suspicious-userlogout": "La tó dmânda per destachêret l'é stēda rifiutêda perchè la sèmbra spidîda da un navigadōr ch' al funsiòuna mìa o da un proxy di caching.",
        "newpassword": "Nōva cêva 'd ingrès:",
        "retypenew": "Scrév incòra la nōva cêva 'd ingrès:",
        "resetpass_submit": "Scrév la cêva 'd ingrès e và dèinter al sît",
-       "changepassword-success": "La cêva 'd ingrès l'é stêda nudifichêda!",
+       "changepassword-success": "La tó cêva 'd ingrès l'é stêda mudifichêda!",
        "changepassword-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
        "resetpass_forbidden": "An 'né mìa pusébil mudifichêr la cêva 'd ingrès",
        "resetpass-no-info": "Per andêr dèinter a cla pàgina ché 't gh'ê da fêr l'ingrès.",
        "resetpass-submit-loggedin": "Câmbia la cêva 'd ingrès",
        "resetpass-submit-cancel": "Scanşèla",
-       "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór n'in pré èser stê dmandê 'na nōva pruvişôria.",
+       "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès an n'é mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór in pré èser stê dmandê 'na nōva pruvişôria.",
        "resetpass-recycled": "Mèt dèinter 'na cêva 'd ingrès divêrsa da còla 'd adès.",
        "resetpass-temp-emailed": "L'ingrès l'é stê fât cun un côdis pruvişôri. Per finîr la registrasiòun, l'é necesâri impustêr 'na nōva cêva 'd ingrès ché:",
        "resetpass-temp-password": "Cêva 'd ingrès pruvişôria:",
        "passwordreset-emailtext-ip": "Quelchidûn (prubabilmèint té, cun l'indirés IP $1) l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n \n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia.",
        "passwordreset-emailtext-user": "L'utèint $1 ed {{SITENAME}} l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n\n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria ché la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia",
        "passwordreset-emailelement": "Nòm utèint: \n$1\n.\nCêva 'd ingrès pruvişôria: \n$2",
-       "passwordreset-emailsentemail": "É stê spidî un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès.",
-       "passwordreset-emailsent-capture": "É stê spidî un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès, ché sòta a gh'é al tèst che gh'é scrét.",
-       "passwordreset-emailerror-capture": "É stê fât un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès, scréta ché 'd sègvit. La spedisiòun {{GENDER:$2|a l'utèint}} an n'é mia 'riusîda:$1",
-       "changeemail": "Câmbia l'indirés ed la pôsta eletrônica",
-       "changeemail-header": "Câmbia l'indirés ed la pôsta eletrônica 'd la tó inscrisiòun.",
+       "passwordreset-emailsentemail": "Se cl’indirés ed pôsta eletrônica ché l’è unî a la tó utèinsa, alōra a gnirà spidî ‘na lètra per per turnêr a impustêr la cêva ‘d ingrès.",
+       "changeemail": "Mudéfica o tó via l'indirés ed pôsta eletrônica",
+       "changeemail-header": "Finés cól fòj ché per cambiêr al tó indirés ed pôsta eletrônica, 'S an 't vō mia avèir nisûn indirés coleghê a la tó utèinsa lêsa vōd al spâsi per l'indirés nōv quând té spidés al fòj.",
        "changeemail-no-info": "Per andêr dèinter diretamèint a cla pàgina ché 't gh'ê da fêr l'ingrès.",
        "changeemail-oldemail": "L'indirés ed la pôsta eletrànica 'd adès.",
        "changeemail-newemail": "Nōv indirés ed pàsta eletrônica:",
        "sig_tip": "Fîrma cun la dâta e l'ōra",
        "hr_tip": "Rîga spiâna (drōva cun giudési)",
        "summary": "Ogèt:",
-       "subject": "Argumèint (tétol):",
+       "subject": "Argumèint:",
        "minoredit": "Còsta l'é 'na mudéfica céca",
        "watchthis": "Tîn adrē a cla pàgina ché",
        "savearticle": "Sêlva la pàgina",
        "missingsummary": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda cun al mutîv vōd.",
        "selfredirect": "<strong>Ateinti:</strong>t'é drē fêr un rinvéi a l'istèsa pàgina. Ét prés avèir şbaliê sgnêr al pôst dal rinvéi o t'é drē mudifichêr la pàgina şbaliêda. S'ét fê cléch incòra in sém a \"{{int:savearticle}}\", al rinvéi al gnirà fât in tót' al manēri.",
        "missingcommenttext": "Scréver un cumèint ché sòta.",
-       "missingcommentheader": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv/al tétol de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa tétol.",
+       "missingcommentheader": "<strong>Atensiòun:<strong> an n'é mìa stê precişê l'argumèint de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa.",
        "summary-preview": "Guêrda préma sûnt:",
-       "subject-preview": "Guêrda préma argumèint/tétol:",
+       "subject-preview": "Guêrda préma l'argumèint:",
        "previewerrortext": "A gh'é stê 'n erōr mèinter a s'é serchê ed guardêr al lavōr préma 'd salvêrel.",
        "blockedtitle": "Utèint bluchê",
        "blockedtext": " '''Al tō nòm utèint o indirés IP l'é stê bluchê.'''\n\nAl blôch l'é stê fât da $1. Al mutîv dal blôch l'é còst:  ''$2''.\n\n*Inési dal blôch: $8\n*Scadèinsa dal blôch: $6\n*Intervâl ed blôch: $7\n\nS' ét vō, l'é pusébil mètres in cuntât cun $1 o 'n êter [[{{MediaWiki:Grouppage-sysop}}|aministradōr]] per discóter dal blôch.\n\nGuêrda che la funsiòun 'Scrév a l'utèint' an n'é mìa in ôvra s' an n'é mìa stê registrtê un indirés ed pôsta eletrônica vâlid int al tō [[Special:Preferences| preferèinsi]] o se sté funsiòun l'é stêda bluchêda. L'indirés IP 'd adèsa l'é $3, al nóme ID dal blôch l'é #$5. T'é perghê ed precişêr tót j elemèint ed préma per ògni dmânda de spiegasiòun",
        "accmailtext": "'Na cêva 'd ingrés l'è stêda fâta a chêş per [[User talk:$1|$1]] e l'è stêda spidîda a $2. Cla cêva 'd ingrès ché la pōl èser cambiêda int la pàgina per ''[[Special:ChangePassword|cambiêr la cêva 'd ingrès]]'' subét dôp avèir fât l'ingrès.",
        "newarticle": "(Nōv)",
        "newarticletext": "Al colegamèint apèina fât al cumbîna cun 'na pàgina ch' an n'é mìa incòra stêda fâta. S'ét vō fêr la pàgina adès, l'é asê cumincêr a scréver al tèst int la caşèla ché sòt (per vedèr infurmasiòun pió precîşi guêrda la [$1 pàgina 'd ajót]). Se al colegamèint  l'é stê avêrt per erōr, l'é asê clichêr al pulsânt \"Indrē\" dal tó navigadōr.",
-       "anontalkpagetext": "----'' Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla. Per arcgnòsrel l'è dòunca necesâri druvê al só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'è un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa tè, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr,  'd èser cunfûş cun 'd j êter utèint sèinsa nòm.''",
-       "noarticletext": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] opór  [{{fullurl:{{FULLPAGENAME}}|action=edit}} mudifichêr la pàgina adèsa]</span>.",
+       "anontalkpagetext": "----\n<em>Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla.</em> Per arcgnòsrel l'è dòunca necesâri druvê al nóme dal só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'é un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa té, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr,  'd èser cunfûş cun 'd j êter utèint sèinsa nòm.",
+       "noarticletext": "In cól mumèint ché la pàgina serchêda l'é vōda.Ét pō\n[[Special:Search/{{PAGENAME}}|serchêr cól tétoi ché]] int al j êtri pàgini dal sît, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] opór  [{{fullurl:{{FULLPAGENAME}}|action=edit}} e fêr cla pàgina ché]</span>.",
        "noarticletext-nopermission": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît o<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] <span>, mó an 't gh'ê mìa al permès ed fêr cla pàgina ché.",
        "missing-revision": "La revişiòun #$1 'd la pagina \"{{FULLPAGENAME}}\" l' an gh'è mìa. Còst, ed sôlit, a sucēd mèint'r as va drē a 'n colegamèint a 'na pàgina scanşlêda, in 'na stòria, di lavōr fât, mìa arnuvêda. I particulêr a 's pōlen catêr int al [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} regéster dal scanşladûri].",
        "userpage-userdoesnotexist": "L'inscrisiòun \"<nowiki>$1</nowiki>\" la cumbîna mìa cun 'n utèint registrê. Ét sicûr ed vrèir fêr o mudifichêr cla pàgina ché.",
        "userpage-userdoesnotexist-view": "L'utèin \"$1\" an n'à mìa fât l'inscrisiòun.",
        "blocked-notice-logextract": "Cl'utèint ché adèsa l'é bluchê. \nPer infurmasiòun l'ûltem elemèint dal regéster di blôch l'é scrét ché sòta:",
-       "clearyourcache": "'''Nôta:''' dôpa vèir salvê a pré èser necesâri pulîr la memôria pruvişôria dal navigadôr per vèder i cambiamèint.\n*'''Firefox / Safari''': tgnîr cucê al tâst dal lètri grândi e clichêr insém a \"Ricarica\" opór cucêr i tâst ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' insém a un Mac)\n*'''Google Chrome''': cucêr i tâst ''Ctrl-Shift-R'' (''⌘-Shift-R'' insém a un Mac) \n*'''Internet Explorer''': tgnîr cucê al tâst ''Ctrl'' mènter es fà cléch insém a ''Refresh'', opór cucêr ''Ctrl-F5'' \n*'''Opera''': svudêr dal tót la memôria pruvişôria 'd la lésta ''Strumenti → Preferenze''",
+       "clearyourcache": "<strong>Nôta:</strong> dôpa vèir salvê a pré èser necesâri pulîr la memôria pruvişôria dal navigadôr per vèder i cambiamèint.\n*< strong >Firefox / Safari /<strong>: tgnîr cucê al tâst dal lètri grândi <em>Shift</em> e clichêr in sém a <em>Ricarica</em>, opór cucêr i tâst <em>Ctrl-F5</em> o <em>Ctrl-R</em> (<em>⌘-R</em> in sém a ‘n Mac)\n*<strong>Google Chrome:</strong>: cucêr i tâst <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> in sém a ‘n Mac) \n*<strong>Internet Explorer:</strong>: tgnîr cucê al tâst<em>Ctrl</em> e fêr cléch in sém a em>Aggiorna</em>, opór clichêr <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Và int al <em>Menu → Impostazioni</em> (<em>Opera → Preferenze</em> in séma 'n Mac)  e pó in <em>Privacy & sicurezza → Pulisci dati del browser → Immagini e file nella cache</em>.",
        "usercssyoucanpreview": "'''Cunséli:''' drōva al tâst 'Guêrda préma' per pruvêr al tó nōv CSS préma 'd salvêrel'''",
        "userjsyoucanpreview": "'''Cunséli:''' drōva al tâst 'Guêrda préma' per pruvêr al tó nōv  JavaScript préma 'd salvêrel'''",
        "usercsspreview": "'''Còsta l'é sōl 'na guardêda al tó CSS préma 'd salvêr al mudéfichi ch'în stêdi fâti.Ricôrdet che al mudéfichi în mìa incòra stêdi salvêdi!'''",
        "previewnote": "'''Ricôrdet che còsta l'é sōl 'na guardêda préma 'd salvêr.'''\nAl tō mudéfichi în MIA incòra stêdi salvêdi.",
        "continue-editing": "Và int la zôna 'd mudéfica",
        "previewconflict": "La vésta la cumbîna cun al tèst int la zôna 'd mudéfica tèst ché d'ed sōver e l'é cme la srà la pàgina s'ed decéd ed clichêr insém a \"Sêlva la pàgina\" in cól mumèint ché.",
-       "session_fail_preview": "'''An n'é mìa stê pusébil registrêr la mudéfica perchè a s' în pêrsi al j infurmasiòun relatîvi a la sesiòun. Tōrna a pruvêr. Se al prublēma al cunténva, a 's pōl pruvêr [[Special:UserLogout|ed coleghêres]] e fêr un ingrès nōv.'''",
-       "session_fail_preview_html": "'''An n'é mìa stê pusébil registrêr la mudéfica perchè în andêdi persi al j infurmasiòun relatîvi a la sesiòun.'''\n\n''Pôst che in {{SITENAME}} a gh'é al permès ed druvêr l' HTML sèinsa lémit, an 's pōl mìa guardêr préma la pàgina mudifichêda; a 's trâta ed 'n'amzûra 'd sicurèsa cûntra j atâch JavaScript.''\n\n''' Se còst l'é un tentatîv legétim ed mudéfica, pruvêr incòra. Se al prublēma l'armâgn, a 's pōl pruvêr a [[Special:UserLogout|sarêr al colegamèint]] e fêr un nōv ingrès.'''",
+       "session_fail_preview": "A's în dispiêş. An n'é mìa stê pusébil registrêr la mudéfica perchè a 's în pêrsi al j infurmasiòun relatîvi a la sesiòun. Ét prés èser stê destachê. <strong>Contròla s' t'é incòra coleghê</strong>. Se al problēma 'l cunténva, a 's pōl pruvêr [[Special:UserLogout|ed coleghêres]] e fêr un ingrès nōv, contròla ânch se al tó navigadōr l' acèta i cookie da cól sît ché.",
+       "session_fail_preview_html": "A's în deispiêş. An n'é mìa stê pusébil registrêr la mudéfica perchè în andêdi persi al j infurmasiòun relatîvi a la sesiòun. \n\n<em> Dâto che {{SITENAME}} al gh'à un HTML grēz inviê a a 'ss è pêrs dal j infurmasiòun ed la sesiòun, la vésta préma ed salvêr l'è lughêda per prudèinsa cûntra j atâch JavaScript.</em>\n\n<strong>Se ' s trâta 'd un tentatîv normêl ed vèder còl che t'è fât préma 'd salvêrel, tōrna pruvêr.</strong>\nSe gh'è incòra al problēma, ét pō pruvêr a [[Special:UserLogout|scoleghêret]] e fêr un nōv ingrès, mó préma contròla che 'l tó navigadōr al tóga i cookie da cól sît ché.",
        "token_suffix_mismatch": "'''La mudéfica an n'é mìa stêda salvêda perchè al ''client'' l'à fât vèder ed gestîr in môd e-sbaliê i carâter di pûn e dal virgûli int al ''token'' lighê a la mudéfica. Per schivşêr di pusébil erōr int al tèst ed la pàgina, è stê rifiutê tóta la mudéfica. Dla vôlti cla situasiòun ché la pōl sucēder quând a vînen druvê soquânt servési ''proxy'' sèinsa nòm via internèt che preşèinten di ''bug''.'''",
        "edit_form_incomplete": "'''Soquânti pêrt dal môdul ed mudéfica în mìa rivêdi al ''server''; controlêr che al mudéfichi sién intâti e turnêr a pruvêr'''",
        "editing": "Mudéfica ed $1",
        "copyrightwarning": "Per piaşèir tîn cûnt che tót al colaborasiòun a {{SITENAME}} a vînen cunsidrêdi publichêdi sòta la licèinsa $2 (per i particulêr guêrda $1). S' an 't vō mìa che i tō tèst a pôsen èser cambiê e turnê a publichêr da tót sèinsa lémit, an publichêri mìa ché.<br /> In pió, se 't  i póblich ché, a 't dichiâr, sòta la tó responsabilitê, che còl ch' è stê scrét a 't l'ê scrét té personalmèint opór l'é ste cupiê da documèint sèinsa ch' al sìa quacê da nisûn dirét 'd autōr. <strong> Ché insém an pubblichêr mìa materiêl quacê da dirét 'd autōr sèinsa autorişâsiòun! </strong>",
        "copyrightwarning2": "Per piaşèir tîn cûnt che tót al colaborasiòun a {{SITENAME}} a pōlen èser mudifichê, arversê o scanşlê da êtra gînta cla dà 'na mân. S' an 't vō mìa che i tō tèst a pôsen èser cambiê alōra an publichêri mìa ché.<br />In pió, se 't  i póblich ché, a 't dichiâr, sòta la tó responsabilitê, che còl ch' è stê scrét a 't l'ê scrét té personalmèint opór l'é ste cupiê da documèint sèinsa ch' al sìa quacê da nisûn dirét 'd autōr (per i particulêr guêrda $1). <strong> Ché insém an pubblichêr mìa materiêl quacê da dirét 'd autōr sèinsa autorişâsiòun! </strong>",
        "longpageerror": "<strong> Erōr: al tèst spidî l'é lòngh {{PLURAL:$1|1|$1}} kilobyte, ch'l'é pió grôs ed l'amzûra mâsima permésa ({{PLURAL:$2|1|$2}} kilobyte). </strong> Al tèst al pôl mìa èser salvê.",
-       "readonlywarning": "<strong>Atensiòun: al databêş l'é stê bluchê per justadûri e dòunca l'é impusébil salvê al mudéfichi in cól mumèint ché.</strong> P'r an pêrdi mìa, l'é pusébil cupiêri còl ch' é stê més dèinter fîn a dès int la caşèla di cambiamèint, incolêrel in un prugrâma tèst e salvêrel per spetêr al şblôch dal databêş. \n\nL'aministradōr ch' l'à bluchê al databêş l'à dê cla spiegasiòun ché: $1",
+       "readonlywarning": "<strong>Atensiòun</strong>: al databêş l'é l'é bluchê per justadûri e per adès l'é impusébil salvê al mudéfichi fâti. P'r an pêrdi mìa,  côpi in un file tèst e sêlvel mèinter té spèt al şblôch dal databêş. \n\nL'aministradōr ch' l'à bluchê al databêş l'à dê cla spiegasiòun ché: $1",
        "protectedpagewarning": "<strong> Atensiòun: cla pàgina ché l'é stêda bluchêda in môd che sōl j utèint cun i privilèg 'd aministradōr a pôsen cambiêrla.</strong> \nPer infurmasiòun ché 'd sègvit a vîn scrét l'ûltem elemèint dal regéster:",
        "semiprotectedpagewarning": "<strong>Nôta:</strong> cla pàgina ché l'é stêda bluchê in môd che sōl j utèint registrê a pôsen cambiêrla. \nPer infurmasiòun ché 'd sègvit è scrét l'ûltem elemèint dal regéster.",
        "cascadeprotectedwarning": "<strong> Atensiòun: </strong>cla pàgina ché l'é stêda bluchêda in môd che sōl j utèint cun i privilèg 'd aministradō a pôsen cambiêrla. Còst a sucēd perchè la pàgina l'é dèinter in cl'elèinch sòta protesiòun ché 'd sègvit {{PLURAL:$1|pàgina|pàgini}}",
        "undo-nochange": "A sèmbra che la mudéfica la sìa bèle stêda scanşlêda.",
        "undo-summary": "Scanşlê la mudéfica $1 ed [[Special:Contributions/$2|$2]]\n([[User talk:$2|discusiòun]])",
        "undo-summary-username-hidden": "Scanşlê la modéfica $1 ed 'n utèin lughê",
-       "cantcreateaccounttitle": "Impusébil registrêr un utèint",
        "cantcreateaccount-text": "La registrasiòun ed cl'indirés IP ché ('''$1''') l'é stêda bluchêda da [[User:$3|$3]]. \n\nAl mutîv dal blôch dê da $3 l'é còst: ''$2''.",
        "cantcreateaccount-range-text": "La registrasiòun da indirés IP int l'intervâl <strong>$1</strong>, in dó gh'é dèinter al tó  (<strong>$4</strong>), l'é stêda bluchêda da [[User:$3|$3]]. \n\nAl mutîv dê da $3 l'é <em>$2</em>",
        "viewpagelogs": "Guêrda la stòria 'd cla pàgina ché",
        "revdelete-unsuppress": "Tó via i lémit al revisiòun armési",
        "revdelete-log": "Mutîv:",
        "revdelete-submit": "Drōva {{PLURAL:$1|int la revisiòun sernîda|int al revisiòun sernîdi}}",
-       "revdelete-success": "Vésta ed la revisòun arnuvêda int al môd gióst.",
+       "revdelete-success": "Vésta ed la revisòun arnuvêda.",
        "revdelete-failure": "La vésta 'd la versiòun l'an pōl mìa èser arnuvêda:\n$1",
-       "logdelete-success": "Vésta dal fât impustêda int al môd gióst.",
+       "logdelete-success": "Vésta impustêda dal fât .",
        "logdelete-failure": "La vésta dal fât l'an pōl mìa èser impustêda:\n$1",
        "revdel-restore": "Câmbia la vidûda.",
        "pagehist": "Stòria 'd la pàgina",
        "prefs-watchlist-token": "Token tgnî d'ôc specêl:",
        "prefs-misc": "Divêrs",
        "prefs-resetpass": "Câmbia la cêva 'd ingrès",
-       "prefs-changeemail": "Câmbia l'indirés ed la pôsta eletrônica",
+       "prefs-changeemail": "Câmbia o scanşèla l'indirés ed la pôsta eletrônica",
        "prefs-setemail": "Impôsta un indirés ed pôsta eletrônica",
        "prefs-email": "Siēlta pôsta eletrônica",
        "prefs-rendering": "Aspèt",
        "rows": "Rîghi",
        "columns": "Clòuni:",
        "searchresultshead": "Sērca",
-       "stub-threshold": "Valōr ménim per i <a href=\"#\" class=\"stub\">colegamèint a i stub</a>, in byte:",
+       "stub-threshold": "Pôrta per i colegamèint a j abòs ($1):",
        "stub-threshold-disabled": "Bluchê",
        "recentchangesdays": "Nómer di dé da fêr vèder int al j ûltmi mudéfichi:",
        "recentchangesdays-max": "Mâsim $1 {{PLURAL:$1|dé}}",
index 12f6e28..a684a3f 100644 (file)
        "yourpasswordagain": "Επαναπληκτρολόγηση κωδικού:",
        "createacct-yourpasswordagain": "Επιβεβαίωση κωδικού",
        "createacct-yourpasswordagain-ph": "Εισαγωγή κωδικού ξανά",
-       "remembermypassword": "Απομνημόνευση της σύνδεσής μου σε αυτόν τον περιηγητή (για μέγιστο $1 {{PLURAL:$1|ημέρα|ημέρες}})",
        "userlogin-remembermypassword": "Να διατηρούμαι μόνιμα σε σύνδεση",
        "userlogin-signwithsecure": "Χρησιμοποιείστε ασφαλή σύνδεση",
+       "cannotlogin-title": "Δεν μπορώ να συνδεθώ",
+       "cannotlogin-text": "Η σύνδεση δεν είναι δυνατή.",
        "cannotloginnow-title": "Δεν μπορείτε να συνδεθείτε τώρα",
        "cannotloginnow-text": "Η σύνδεση δεν είναι δυνατή όταν χρησιμοποιείτε την $1.",
        "yourdomainname": "Το domain σας:",
        "previewnote": "'''Να θυμάστε ότι αυτή είναι μόνο μια προεπισκόπηση.'''\nΟι αλλαγές σας δεν έχουν ακόμη αποθηκευτεί!",
        "continue-editing": "Μεταβείτε στην περιοχή επεξεργασίας",
        "previewconflict": "Αυτή η προεπισκόπηση απεικονίζει το κείμενο στην επάνω περιοχή επεξεργασίας κειμένου, όπως θα εμφανιστεί εάν επιλέξετε να το αποθηκεύσετε.",
-       "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
+       "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να [[Special:UserLogout|αποσυνδεθείτε]] και να συνδεθείτε πάλι.'''",
        "session_fail_preview_html": "'''Λυπούμαστε! Δεν μπορέσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.'''\n\n''Επειδή το {{SITENAME}} επιτρέπει την εισαγωγή ακατέργαστου HTML, η προεπισκόπηση είναι κρυμμένη ως προφύλαξη ενάντια σε επιθέσεις με Javascript.''\n\n'''Αν αυτή είναι μια έγκυρη προσπάθεια επεξεργασίας, παρακαλώ προσπαθήστε ξανά. Αν πάλι δε δουλεύει, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
        "token_suffix_mismatch": "'''Η επεξεργασία σας απορρίφθηκε γιατί το πρόγραμμα-πελάτη σας κατακρεούργησε τους χαρακτήρες στίξης στο κουπόνι επεξεργασίας. Η επεξεργασία απορρίφθηκε για να αποφευχθεί η παραφθορά του κειμένου της σελίδας.\nΑυτό μερικές φορές συμβαίνει όταν χρησιμοποιείται ένας ανώνυμος διακομιστής μεσολάβησης διαθέσιμος μέσω του παγκόσμιου ιστού με σφάλματα.'''",
        "edit_form_incomplete": "'''Ορισμένα τμήματα της φόρμας επεξεργασίας δεν έφθασαν στο διακομιστή. Ελέγξτε ότι οι αλλαγές σας είναι άθικτες και προσπαθήστε ξανά.'''",
        "mergehistory-fail-bad-timestamp": "Η χρονική σήμανση δεν είναι έγκυρη.",
        "mergehistory-fail-invalid-source": "Η πηγή σελίδας δεν είναι έγκυρη.",
        "mergehistory-fail-invalid-dest": "Η σελίδα προορισμού δεν είναι έγκυρη.",
+       "mergehistory-fail-permission": "Μη επαρκή δικαιώματα για τη συγχώνευση του ιστορικού.",
+       "mergehistory-fail-self-merge": "Η πηγή και ο προορισμός των σελίδων είναι ο ίδιος.",
        "mergehistory-fail-toobig": "Δεν είναι δυνατό να πραγματοποιηθεί η συγχώνευση ιστορικών, καθώς πάνω από $1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}} θα μετακινούνταν.",
        "mergehistory-no-source": "Η σελίδα πηγής $1 δεν υπάρχει.",
        "mergehistory-no-destination": "Η σελίδα προορισμού $1 δεν υπάρχει.",
        "grant-group-high-volume": "Εκτέλεση υψηλής έντασης δραστηριότητας",
        "grant-group-customization": "Ρυθμίσεις και προτιμήσεις",
        "grant-group-administration": "Εκτέλεση διαχειριστικών ενεργειών",
+       "grant-group-private-information": "Πρόσβαση σε ιδιωτικά δεδομένα σχετικά με εσάς",
+       "grant-group-other": "Διάφορες δραστηριότητες",
        "grant-blockusers": "Φραγή και αναίρεση φραγής χρηστών",
        "grant-createaccount": "Δημιουργία λογαριασμών",
        "grant-createeditmovepage": "Δημιουργία, επεξεργασία και μετακίνηση σελίδων",
        "grant-highvolume": "Υψηλής έντασης επεξεργασία",
        "grant-oversight": "Απόκρυψη χρηστών και καταστολή αναθεωρήσεων",
        "grant-patrol": "Περιπολία αλλαγών σε σελίδες",
+       "grant-privateinfo": "Πρόσβαση σε προσωπικές πληροφορίες",
        "grant-protect": "Προστασία και κατάργηση προστασίας σελίδων",
        "grant-rollback": "Η επαναφορά αλλαγών σε σελίδες",
        "grant-sendemail": "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου σε άλλους χρήστες",
        "action-applychangetags": "εφαρμογή ετικετών μαζί με τις αλλαγές σας",
        "action-changetags": "πρόσθεση και αφαίρεση αυθαίρετων ετικετών σε μεμονωμένες εκδόσεις και καταχωρήσεις καταγραφών",
        "action-deletechangetags": "διαγράψετε ετικέτες από τη βάση δεδομένων",
+       "action-purge": "εκκαθάριση αυτής της σελίδας",
        "nchanges": "$1 {{PLURAL:$1|αλλαγή|αλλαγές}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|από την τελευταία επίσκεψη}}",
        "enhancedrc-history": "ιστορικό",
        "uploadstash-summary": "Η σελίδα παρέχει πρόσβαση σε αρχεία που είναι  επιφορτωμένα  (ή στη διαδικασία της επιφόρτωσης) αλλά δεν έχει ακόμη δημοσιευθεί για το wiki. Αυτά τα αρχεία δεν είναι ορατά σε  οποιονδήποτε, αλλά στο χρήστη που τα επιφόρτωσε.",
        "uploadstash-clear": "Καθαρά διατηρημένα αρχεία",
        "uploadstash-nofiles": "Δεν έχετε κρυμμένα αρχεία.",
-       "uploadstash-badtoken": "Î\95κÏ\84έλεÏ\83η Ï\84ηÏ\82 ÎµÎ½ Î»Ï\8cγÏ\89 ÎµÎ½Î­Ï\81γειαÏ\82  Î®Ï\84αν Î±Î½ÎµÏ\80ιÏ\84Ï\85Ï\87ήÏ\82, Î¯Ï\83Ï\89Ï\82 ÎµÏ\80ειδή Ï\84α Î´Î¹Î±Ï\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά ÎµÏ\80εξεÏ\81γαÏ\83ίαÏ\82  Ï\83αÏ\82 Î­Ï\87οÏ\85ν Î»Î®Î¾ÎµÎ¹. Î\94οκίμαστε ξανά.",
-       "uploadstash-errclear": "Î\97 ÎµÎºÎºÎ±Î¸Î¬Ï\81ιÏ\83η Ï\84Ï\89ν Î±Ï\81Ï\87είÏ\89ν Î®Ï\84αν Î±Î½ÎµÏ\80ιÏ\84Ï\85Ï\87ήÏ\82.",
+       "uploadstash-badtoken": "Î\95κÏ\84έλεÏ\83η Ï\84ηÏ\82 ÎµÎ½ Î»Ï\8cγÏ\89 ÎµÎ½Î­Ï\81γειαÏ\82  Î±Ï\80έÏ\84Ï\85Ï\87ε, Î¯Ï\83Ï\89Ï\82 ÎµÏ\80ειδή Ï\84α Î´Î¹Î±Ï\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά ÎµÏ\80εξεÏ\81γαÏ\83ίαÏ\82  Ï\83αÏ\82 Î­Ï\87οÏ\85ν Î»Î®Î¾ÎµÎ¹. Î Î±Ï\81ακαλοÏ\8dμε Î´Î¿ÎºÎ¹Î¼Î¬στε ξανά.",
+       "uploadstash-errclear": "Î\97 ÎµÎºÎºÎ±Î¸Î¬Ï\81ιÏ\83η Ï\84Ï\89ν Î±Ï\81Ï\87είÏ\89ν Î±Ï\80έÏ\84Ï\85Ï\87ε.",
        "uploadstash-refresh": "Ανανεώσετε τη λίστα των αρχείων",
+       "uploadstash-thumbnail": "προβολή μικρογραφίας",
        "invalid-chunk-offset": "Άκυρο κομμάτι όφσετ",
        "img-auth-accessdenied": "Δεν επετράπη η πρόσβαση",
        "img-auth-nopathinfo": "Λείπει το PATH_INFO.\nΟ διακομιστής σας δεν είναι ρυθμισμένος για να περάσει αυτές τις πληροφορίες.\nΜπορεί να είναι βασισμένος σε CGI και να μην υποστηρίζει img_atuh.\nΔείτε https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "apisandbox-helpurls": "Σύνδεσμοι βοήθειας",
        "apisandbox-examples": "Παραδείγματα",
        "apisandbox-dynamic-parameters": "Πρόσθετες παράμετροι",
+       "apisandbox-dynamic-parameters-add-label": "Προσθήκη παραμέτρου:",
        "apisandbox-dynamic-parameters-add-placeholder": "Ονομασία παραμέτρου",
        "apisandbox-dynamic-error-exists": "Η παράμετρος με την ονομασία \"$1\" υπάρχει ήδη",
        "apisandbox-submit-invalid-fields-title": "Κάποια από τα πεδία δεν είναι έγκυρα",
        "listgrouprights-namespaceprotection-header": "Περιορισμοί ονοματοχώρων",
        "listgrouprights-namespaceprotection-namespace": "Ονοματοχώρος",
        "listgrouprights-namespaceprotection-restrictedto": "Δικαίωμα(τα) που επιτρέπει(ουν) σε χρήστη να επεξεργαστεί",
+       "listgrants": "Επιχορηγήσεις",
+       "listgrants-grant": "Επιχορήγηση",
        "listgrants-rights": "Δικαιώματα",
        "trackingcategories": "Κατηγορίες παρακολούθησης",
        "trackingcategories-summary": "Αυτή η σελίδα εμφανίζει τις κατηγορίες παρακολούθησης το περιεχόμενο των οποίων συμπληρώνεται αυτόματα από το λογισμικό MediaWiki. Τα ονόματά τους μπορεί να αλλαχθούν με την αλλαγή των σχετικών μηνυμάτων συστήματος στον ονοματοχώρο {{ns:8}}.",
        "delete-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή τέτοιων σελίδων έχει περιοριστεί για την αποφυγή τυχαίας αναστάτωσης του {{SITENAME}}.",
        "delete-warning-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή της μπορεί να αναστατώσει τη λειτουργία της βάσης δεδομένων του {{SITENAME}}. Συνιστούμε μεγάλη προσοχή.",
        "deleteprotected": "Δεν μπορείτε να διαγράψετε αυτή τη σελίδα επειδή είναι προστατευόμενη.",
-       "deleting-backlinks-warning": "\"'Προσοχή:\"' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
+       "deleting-backlinks-warning": "<strong>Προσοχή:</strong>  [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
        "rollback": "Επαναφορά επεξεργασιών",
        "rollbacklink": "αναστροφή",
        "rollbacklinkcount": "Επαναφορά $1 {{PLURAL:$1|επεξεργασίας|επεξεργασιών}}",
        "undeletedrevisions": "{{PLURAL:$1|τροποποίηση|τροποποιήσεις}} αποκαταστάθηκαν",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}} και $2 {{PLURAL:$2|αρχείο|αρχεία}} επαναφέρθηκαν",
        "undeletedfiles": "$1 {{PLURAL:$1|αρχείο|αρχεία}} επαναφέρθηκαν",
-       "cannotundelete": "Î\97 Î±Î½Î±Î¯Ï\81εÏ\83η Î´Î¹Î±Î³Ï\81αÏ\86ήÏ\82 Î±Ï\80έÏ\84Ï\85Ï\87ε: $1",
+       "cannotundelete": "Î\9cεÏ\81ικέÏ\82 Î® Ï\8cλεÏ\82 Î¿Î¹  Î±Î½Î±Î¹Ï\81έÏ\83ειÏ\82 Î´Î¹Î±Î³Ï\81αÏ\86ήÏ\82 Î±Ï\80έÏ\84Ï\85Ï\87αν: $1",
        "undeletedpage": "'''Η $1 έχει επαναφερθεί'''\n\nΣυμβουλευτείτε το [[Special:Log/delete|αρχείο καταγραφής διαγραφών]] για ένα μητρώο των πρόσφατων διαγραφών και επαναφορών.",
        "undelete-header": "Δείτε [[Special:Log/delete|το αρχείο καταγραφής διαγραφών]] για πρόσφατα διαγεγραμμένες σελίδες.",
        "undelete-search-title": "Αναζήτηση διαγεγραμμένων σελίδων",
        "sp-contributions-newbies-sub": "Για νέους λογαριασμούς",
        "sp-contributions-newbies-title": "Συνεισφορές χρηστών για νέους λογαριασμούς",
        "sp-contributions-blocklog": "αρχείο καταγραφών φραγών",
-       "sp-contributions-suppresslog": "διαγεγραμμένες συνεισφορές χρήστη",
+       "sp-contributions-suppresslog": "διαγεγραμμένες συνεισφορές {{GENDER:$1|χρήστη|χρήστριας}}",
        "sp-contributions-deleted": "διαγεγραμμένες συνεισφορές χρήστη",
        "sp-contributions-uploads": "ανεβάσματα αρχείων",
        "sp-contributions-logs": "καταγραφές",
        "watchlistedit-raw-done": "Η λίστα παρακολούθησής σας ενημερώθηκε.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} προστέθηκαν:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} αφαιρέθηκαν:",
-       "watchlistedit-clear-title": "Î\95κκαθαÏ\81ιÏ\83μένη Î»Î¯Ï\83Ï\84α παρακολούθησης",
+       "watchlistedit-clear-title": "Î\95κκαθάÏ\81ιÏ\83η Î»Î¯Ï\83Ï\84αÏ\82 παρακολούθησης",
        "watchlistedit-clear-legend": "Εκκαθάριση λίστας παρακολούθησης",
        "watchlistedit-clear-explain": "Όλοι οι τίτλοι θα αφαιρεθούν από τη λίστα παρακολούθησής σας",
        "watchlistedit-clear-titles": "Τίτλοι:",
        "version-libraries-license": "Άδεια χρήσης",
        "version-libraries-description": "Περιγραφή",
        "version-libraries-authors": "Δημιουργοί",
-       "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα ή αναγνωριστικό αναθεώρησης",
+       "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα, αναγνωριστικό αναθεώρησης ή αρχείο καταγραφής ID",
        "redirect-submit": "Μετάβαση",
        "redirect-lookup": "Αναζήτηση:",
        "redirect-value": "Τιμή:",
        "tags-delete-submit": "Μη αναστρέψιμη διαγραφή αυτής της ετικέτας",
        "tags-delete-not-found": "Η ετικέτα «$1» δεν υπάρχει.",
        "tags-delete-too-many-uses": "Η ετικέτα «$1» εφαρμόζεται σε πάνω από {{PLURAL:$2|μία αναθεώρηση|$2 αναθεωρήσεις}}, που σημαίνει ότι δεν μπορεί να διαγραφεί.",
-       "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε με επιτυχία, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
+       "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
        "tags-delete-no-permission": "Δεν έχετε άδεια να διαχειριστείτε ετικέτες αλλαγής.",
        "tags-activate-title": "Ενεργοποίηση ετικέτας",
        "tags-activate-question": "Πρόκειται να ενεργοποιήσετε την ετικέτα «$1».",
        "tags-edit-reason": "Αιτία:",
        "tags-edit-revision-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτή την αναθεώρηση|$1 αναθεωρήσεις}}",
        "tags-edit-logentry-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτήν την καταχώρηση|$1 καταχωρήσεις}} του αρχείου καταγραφής",
-       "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν με επιτυχία.",
+       "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν.",
        "tags-edit-failure": "Οι αλλαγές δεν ήταν δυνατόν να εφαρμοστούν:\n$1",
        "tags-edit-nooldid-title": "Μη έγκυρη αναθεώρηση προορισμού",
        "tags-edit-none-selected": "Παρακαλώ επιλέξτε τουλάχιστον μία ετικέτα για να προσθέσετε ή να αφαιρέσετε.",
index 892ac2c..a8dd103 100644 (file)
@@ -28,7 +28,7 @@
        "tog-enotifminoredits": "Email me also for minor edits of pages and files",
        "tog-enotifrevealaddr": "Reveal my email address in notification emails",
        "tog-shownumberswatching": "Show the number of watching users",
-       "tog-oldsig": "Existing signature:",
+       "tog-oldsig": "Your existing signature:",
        "tog-fancysig": "Treat signature as wikitext (without an automatic link)",
        "tog-uselivepreview": "Use live preview",
        "tog-forceeditsummary": "Prompt me when entering a blank edit summary",
@@ -45,7 +45,7 @@
        "tog-showhiddencats": "Show hidden categories",
        "tog-norollbackdiff": "Don't show diff after performing a rollback",
        "tog-useeditwarning": "Warn me when I leave an edit page with unsaved changes",
-       "tog-prefershttps": "Always use a secure connection when logged in",
+       "tog-prefershttps": "Always use a secure connection while logged in",
        "underline-always": "Always",
        "underline-never": "Never",
        "underline-default": "Skin or browser default",
        "category-file-count-limited": "The following {{PLURAL:$1|file is|$1 files are}} in the current category.",
        "listingcontinuesabbrev": "cont.",
        "index-category": "Indexed pages",
-       "noindex-category": "Noindexed pages",
+       "noindex-category": "Non-indexed pages",
        "broken-file-category": "Pages with broken file links",
        "categoryviewer-pagedlinks": "($1) ($2)",
        "category-header-numerals": "$1–$2",
        "newwindow": "(opens in new window)",
        "cancel": "Cancel",
        "moredotdotdot": "More...",
-       "morenotlisted": "This list is not complete.",
+       "morenotlisted": "This list may be incomplete.",
        "mypage": "Page",
        "mytalk": "Talk",
        "anontalk": "Talk",
        "yourpasswordagain": "Retype password:",
        "createacct-yourpasswordagain": "Confirm password",
        "createacct-yourpasswordagain-ph": "Enter password again",
-       "remembermypassword": "Remember my login on this browser (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "userlogin-remembermypassword": "Keep me logged in",
        "userlogin-signwithsecure": "Use secure connection",
+       "cannotlogin-title": "Cannot log in",
+       "cannotlogin-text": "Logging in is not possible.",
        "cannotloginnow-title": "Cannot log in now",
        "cannotloginnow-text": "Logging in is not possible when using $1.",
+       "cannotcreateaccount-title": "Cannot create accounts",
+       "cannotcreateaccount-text": "Direct account creation is not enabled on this wiki.",
        "yourdomainname": "Your domain:",
        "password-change-forbidden": "You cannot change passwords on this wiki.",
        "externaldberror": "There was either an authentication database error or you are not allowed to update your external account.",
        "botpasswords-updated-body": "The bot password for bot name \"$1\" of user \"$2\" was updated.",
        "botpasswords-deleted-title": "Bot password deleted",
        "botpasswords-deleted-body": "The bot password for bot name \"$1\" of user \"$2\" was deleted.",
-       "botpasswords-newpassword": "The new password to log in with <strong>$1</strong> is <strong>$2</strong>. <em>Please record this for future reference.</em>",
+       "botpasswords-newpassword": "The new password to log in with <strong>$1</strong> is <strong>$2</strong>. <em>Please record this for future reference.</em> <br> (For old bots which require the login name to be the same as the eventual username, you can also use <strong>$3</strong> as username and <strong>$4</strong> as password.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider is not available.",
        "botpasswords-restriction-failed": "Bot password restrictions prevent this login.",
        "botpasswords-invalid-name": "The username specified does not contain the bot password separator (\"$1\").",
        "invalid-content-data": "Invalid content data",
        "content-not-allowed-here": "\"$1\" content is not allowed on page [[$2]]",
        "editwarning-warning": "Leaving this page may cause you to lose any changes you have made.\nIf you are logged in, you can disable this warning in the \"{{int:prefs-editing}}\" section of your preferences.",
+       "editpage-invalidcontentmodel-title": "Content model not supported",
+       "editpage-invalidcontentmodel-text": "The content model \"$1\" is not supported.",
        "editpage-notsupportedcontentformat-title": "Content format not supported",
        "editpage-notsupportedcontentformat-text": "The content format $1 is not supported by the content model $2.",
        "content-model-wikitext": "wikitext",
        "file-thumbnail-no": "The filename begins with <strong>$1</strong>.\nIt seems to be an image of reduced size <em>(thumbnail)</em>.\nIf you have this image in full resolution upload this one, otherwise change the filename please.",
        "fileexists-forbidden": "A file with this name already exists, and cannot be overwritten.\nIf you still want to upload your file, please go back and use a new name.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "A file with this name exists already in the shared file repository.\nIf you still want to upload your file, please go back and use a new name.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "The upload is an exact duplicate of the current version of <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "The upload is an exact duplicate of {{PLURAL:$2|an older version|older versions}} of <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "This file is a duplicate of the following {{PLURAL:$1|file|files}}:",
        "file-deleted-duplicate": "A file identical to this file ([[:$1]]) has previously been deleted.\nYou should check that file's deletion history before proceeding to re-upload it.",
        "file-deleted-duplicate-notitle": "A file identical to this file has previously been deleted, and the title has been suppressed.\nYou should ask someone with the ability to view suppressed file data to review the situation before proceeding to re-upload it.",
        "filerevert-submit": "Revert",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> has been reverted to the [$4 version as of $3, $2].",
        "filerevert-badversion": "There is no previous local version of this file with the provided timestamp.",
+       "filerevert-identical": "The current version of the file is already identical to the selected one.",
        "filedelete": "Delete $1",
        "filedelete-legend": "Delete file",
        "filedelete-intro": "You are about to delete the file <strong>[[Media:$1|$1]]</strong> along with all of its history.",
        "rollbacklinkcount-morethan": "rollback more than $1 {{PLURAL:$1|edit|edits}}",
        "rollbackfailed": "Rollback failed",
        "rollback-missingparam": "Missing required parameters on request.",
+       "rollback-missingrevision": "Unable to load revision data.",
        "cantrollback": "Cannot revert edit;\nlast contributor is only author of this page.",
        "alreadyrolled": "Cannot rollback last edit of [[:$1]] by [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nsomeone else has edited or rolled back the page already.\n\nThe last edit to the page was by [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "The edit summary was: <em>$1</em>.",
        "tooltip-watchlistedit-raw-submit": "Update watchlist",
        "tooltip-recreate": "Recreate the page even though it has been deleted",
        "tooltip-upload": "Start upload",
-       "tooltip-rollback": "\"Rollback\" reverts edit(s) to this page of the last contributor in one click",
+       "tooltip-rollback": "\"Rollback\" reverts the last contributor's edit(s) to this page in one click",
        "tooltip-undo": "\"Undo\" reverts this edit and opens the edit form in preview mode. It allows adding a reason in the summary.",
        "tooltip-preferences-save": "Save preferences",
        "tooltip-summary": "Enter a short summary",
        "pageinfo-article-id": "Page ID",
        "pageinfo-language": "Page content language",
        "pageinfo-content-model": "Page content model",
+       "pageinfo-content-model-change": "change",
        "pageinfo-robot-policy": "Indexing by robots",
        "pageinfo-robot-index": "Allowed",
        "pageinfo-robot-noindex": "Disallowed",
        "tag-filter": "[[Special:Tags|Tag]] filter:",
        "tag-filter-submit": "Filter",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
+       "tag-mw-contentmodelchange": "content model change",
+       "tag-mw-contentmodelchange-description": "Edits that [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel change the content model] of a page",
        "tags-title": "Tags",
        "tags-intro": "This page lists the tags that the software may mark an edit with, and their meaning.",
        "tags-tag": "Tag name",
        "tags-actions-header": "Actions",
        "tags-active-yes": "Yes",
        "tags-active-no": "No",
-       "tags-source-extension": "Defined by an extension",
+       "tags-source-extension": "Defined by the software",
        "tags-source-manual": "Applied manually by users and bots",
        "tags-source-none": "No longer in use",
        "tags-edit": "edit",
index 13eb863..f0a61b3 100644 (file)
@@ -47,7 +47,8 @@
                        "Robin van der Vliet",
                        "Zciric",
                        "Psychoslave",
-                       "Orikrin1998"
+                       "Orikrin1998",
+                       "Gamliel Fishkin"
                ]
        },
        "tog-underline": "Substrekado de ligiloj:",
@@ -90,7 +91,7 @@
        "tog-ccmeonemails": "Sendi al mi kopiojn de retpoŝtaĵoj, kiujn mi sendis al aliaj uzantoj.",
        "tog-diffonly": "Ne montri paĝan enhavon sub la ŝanĝmontrilo",
        "tog-showhiddencats": "Montri kaŝitajn kategoriojn",
-       "tog-norollbackdiff": "Nemontri diferencon post plenumado de ŝanĝomalfaro",
+       "tog-norollbackdiff": "Ne montri diferencon post plenumado de ŝanĝomalfaro",
        "tog-useeditwarning": "Averti min kiam mi forlasas redaktan paĝon kun nekonservitaj ŝanĝoj",
        "tog-prefershttps": "Ĉiam uzu sekuran konekton ensalutite",
        "underline-always": "Ĉiam",
        "period-am": "atm.",
        "period-pm": "ptm.",
        "pagecategories": "{{PLURAL:$1|Kategorio|Kategorioj}}",
-       "category_header": "Artikoloj en kategorio “$1”",
+       "category_header": "Paĝoj en kategorio “$1”",
        "subcategories": "Subkategorioj",
        "category-media-header": "Dosieroj en kategorio “$1”",
-       "category-empty": "<em>Tiu ĉi kategorio nuntempe enhavas neniun artikolon aŭ plurmedian dosieron.</em>",
+       "category-empty": "<em>Tiu ĉi kategorio nuntempe enhavas neniun paĝon aŭ plurmedian dosieron.</em>",
        "hidden-categories": "{{PLURAL:$1|Kaŝita kategorio|Kaŝitaj kategorioj}}",
        "hidden-category-category": "Kaŝitaj kategorioj",
        "category-subcat-count": "{{PLURAL:$2|Ĉi tiu kategorio havas nur la jenan subkategorion.|Ĉi tiu kategorio havas la {{PLURAL:$1|jenan subkategorion|$1 jenajn subkategoriojn}}, el $2 entute.}}",
        "noindex-category": "Neindeksitaj paĝoj",
        "broken-file-category": "Paĝoj kun rompita ligilo al dosiero",
        "about": "Pri",
-       "article": "Artikolo",
+       "article": "Enhava paĝo",
        "newwindow": "(en nova fenestro)",
        "cancel": "Nuligi",
        "moredotdotdot": "Pli...",
        "tagline": "El {{SITENAME}}",
        "help": "Helpo",
        "search": "Serĉi",
+       "search-ignored-headings": " #<!-- lasu ĉi tiun linion ekzakte kiel ĝi estas --> <pre>\n# Titoloj kiuj estos ignoritaj en serĉado.\n# Ŝanĝoj al ĉi tio efikas tuj kiam la paĝo kun la titolo estas indeksita.\n# Vi povas igi reindeksadon de paĝo, farinte nulan redakton.\n# La sintakso estas jena:\n#   * Io ajn ekde signo \"#\" ĝis la fino de la linio estas komento.\n#   * Ĉiu nemalplena linio estas la ekzakta titolo por ignori usklecon kaj kion ajn.\nReferencoj\nEksteraj ligiloj\nVidu Ankaŭ\n #</pre> <!-- lasu ĉi tiun linion ekzakte kiel ĝi estas -->",
        "searchbutton": "Serĉi",
        "go": "Ek",
        "searcharticle": "Ek",
-       "history": "Historio de versioj",
+       "history": "Historio de paĝo",
        "history_short": "Historio",
        "updatedmarker": "ĝisdatigita de post mia lasta vizito",
        "printableversion": "Presebla versio",
        "protect": "Protekti",
        "protect_change": "ŝanĝi",
        "protectthispage": "Protekti la paĝon",
-       "unprotect": "Ŝanĝi protekadon",
-       "unprotectthispage": "Ŝanĝi protektadon de ĉi tiu paĝo",
+       "unprotect": "Ŝanĝi protekton",
+       "unprotectthispage": "Ŝanĝi protekton de ĉi tiu paĝo",
        "newpage": "Nova paĝo",
        "talkpage": "Diskuti la paĝon",
        "talkpagelinktext": "diskuto",
        "specialpage": "Speciala paĝo",
        "personaltools": "Personaj iloj",
-       "articlepage": "Vidi paĝenhavon",
+       "articlepage": "Vidi enhavan paĝon",
        "talk": "Diskuto",
        "views": "Vidoj",
        "toolbox": "Iloj",
-       "userpage": "Vidi uzulan paĝon",
+       "userpage": "Vidi uzantopaĝon",
        "projectpage": "Rigardi projektopaĝon",
        "imagepage": "Vidi dosieropaĝon",
        "mediawikipage": "Vidi mesaĝopaĝon",
        "jumpto": "Iri al:",
        "jumptonavigation": "navigado",
        "jumptosearch": "serĉi",
-       "view-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun paĝon.\nBonvolu atendi iom antaŭ ol provi atingi ĝin denove.\n\n$1",
-       "generic-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun risurcon.\nBonvolu iom atendi antaŭ vi provos atingi ĝin denove.",
+       "view-pool-error": "Pardonon, la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun paĝon.\nBonvolu atendi iom antaŭ ol provi atingi ĝin denove.\n\n$1",
+       "generic-pool-error": "Pardonon, la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun risurcon.\nBonvolu iom atendi antaŭ vi provos atingi ĝin denove.",
        "pool-timeout": "Tempolimo atingita dum atendo de ŝlosado",
        "pool-queuefull": "Atendovico de servilaro estas plena.",
        "pool-errorunknown": "Nekonata eraro",
        "ok": "Bone",
        "retrievedfrom": "Elŝutita el  \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|Vi havas}} $1 ($2).",
-       "youhavenewmessagesfromusers": "Riceviĝis $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Vi havas}} $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).",
        "youhavenewmessagesmanyusers": "Riceviĝis $1 de multaj uzantoj ($2).",
-       "newmessageslinkplural": "{{PLURAL:$1|nova mesaĝo|999=novaj mesaĝoj}}",
+       "newmessageslinkplural": "{{PLURAL:$1|novan mesaĝon|999=novajn mesaĝojn}}",
        "newmessagesdifflinkplural": "$1 {{PLURAL:$1|ŝanĝo|ŝanĝoj}}",
        "youhavenewmessagesmulti": "Vi havas novajn mesaĝojn ĉe $1",
        "editsection": "redakti",
        "yourpasswordagain": "Retajpu pasvorton",
        "createacct-yourpasswordagain": "Konfirmu pasvorton",
        "createacct-yourpasswordagain-ph": "Retajpu pasvorton",
-       "remembermypassword": "Memori mian ensalutadon ĉe ĉi tiu komputilo (daŭrante maksimume $1 {{PLURAL:$1|tagon|tagojn}})",
        "userlogin-remembermypassword": "Memori mian ensaluton",
        "userlogin-signwithsecure": "Uzu sekurigitan konekton",
+       "cannotlogin-title": "Ne eblas ensaluti",
+       "cannotlogin-text": "Ensaluto estas neebla.",
        "cannotloginnow-title": "Nuntempe ne eblas ensaluti",
        "cannotloginnow-text": "Ne eblas ensaluti dum uzado de $1.",
+       "cannotcreateaccount-title": "Ne eblas krei konton",
+       "cannotcreateaccount-text": "Senpera kreo de uzantokonto ne estas enŝaltita en ĉi tiu vikio.",
        "yourdomainname": "Via domajno",
        "password-change-forbidden": "Ve ne povas ŝanĝi pasvortojn en ĉi tiu vikio.",
        "externaldberror": "Aŭ estis datenbaza eraro rilate al ekstera aŭtentikigado, aŭ vi ne rajtas ĝisdatigi vian eksteran konton.",
        "watchthis": "Atenti ĉi tiun paĝon",
        "savearticle": "Konservi paĝon",
        "savechanges": "Konservi ŝanĝojn",
-       "publishpage": "Publikigi paĝon",
+       "publishpage": "Eldoni paĝon",
        "publishchanges": "Eldoni ŝanĝojn",
        "preview": "Antaŭrigardo",
        "showpreview": "Antaŭrigardo",
        "content-json-empty-object": "Malplena objeto",
        "content-json-empty-array": "Malplena tabelo",
        "deprecated-self-close-category": "Paĝoj kun nevalida memferma HTML‑etikedo",
+       "deprecated-self-close-category-desc": "La paĝo enhavas malvalidajn memfermajn HTML-markojn, tiajn kiel <code>&lt;b/></code> aŭ <code>&lt;span/></code>. Ilia konduto baldaŭ estos ŝanĝita por esti kongrua kun la specifiko de HTML5, tial ilia uzo en vikia teksto estas malrekomendata.",
        "duplicate-args-warning": "'''Averto:''' [[:$1]] vokas al [[:$2]] kun pli ol unu valoro por la parametro \"$3\". Nur la lasta liverita valoro estos uzata.",
        "duplicate-args-category": "Paĝoj kun pluroblaj argumentoj en ŝablonvokoj",
        "duplicate-args-category-desc": "La paĝo enhavas uzon de ŝablono kun pluroble uzitaj argumentoj, kiel ekzemple <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> aŭ <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "grant-group-high-volume": "Efektivigi ampleksegajn agojn",
        "grant-group-customization": "Personecigoj kaj preferoj",
        "grant-group-administration": "Efektivigi administrajn agojn",
+       "grant-group-private-information": "Aliru privatan datumon pri vi",
        "grant-group-other": "Diversaj aktivecoj",
        "grant-blockusers": "Bloki kaj malbloki uzantojn",
        "grant-createaccount": "Krei kontojn",
        "grant-highvolume": "Ampleksega redaktado",
        "grant-oversight": "Kaŝi uzantojn kaj forigi reviziaĵojn",
        "grant-patrol": "Patroli ŝanĝojn al pâgoj",
+       "grant-privateinfo": "Aliro al privataj informoj",
        "grant-protect": "Protekti kaj malprotekti paĝojn",
        "grant-rollback": "Malfari ŝanĝojn de paĝoj",
        "grant-sendemail": "Retpoŝti al aliaj uzantoj",
        "action-applychangetags": "aldoni etikedojn al viaj propraj ŝanĝoj",
        "action-changetags": "aldoni kaj forigi arbitrajn etikedojn ĉe unuopaj revizioj kaj protokoleroj",
        "action-deletechangetags": "Forigi etikedojn de la datenbazo.",
+       "action-purge": "malplenigi servilan kaŝmemoron",
        "nchanges": "$1 {{PLURAL:$1|ŝanĝo|ŝanĝoj}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ekde lasta vizito}}",
        "enhancedrc-history": "historio",
        "rcshowhidemine": "$1 miajn redaktojn",
        "rcshowhidemine-show": "Montri",
        "rcshowhidemine-hide": "Kaŝi",
-       "rcshowhidecategorization": "$1 paĝa enkategoriigo",
+       "rcshowhidecategorization": "$1 kategoriigon de paĝoj",
        "rcshowhidecategorization-show": "Montri",
        "rcshowhidecategorization-hide": "Kaŝi",
        "rclinks": "Montri $1 lastajn ŝanĝojn dum la $2 lastaj tagoj.<br />$3",
        "file-thumbnail-no": "La dosiernomo komencas kun <strong>$1</strong>.\nĜi ŝajnas kiel bildo de malgrandigita grandeco ''(thumbnail)''.\nSe vi havas ĉi tiun bildon en plena distingivo, alŝutu ĉi tiun, alikaze bonvolu ŝanĝi la dosieran nomon.",
        "fileexists-forbidden": "Dosiero kun ĉi tiu nomo jam ekzistas kaj ne povas anstataŭigi ĝin.\nSe vi ankoraŭ volas alŝuti vian dosieron, bonvolu reprovi kun nova nomo.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Dosiero kun ĉi tia nomo jam ekzistas en la komuna dosierujo.\nSe vi ankoraŭ volas alŝuti vian dosieron, bonvolu retroigi kaj uzi novan nomon.[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "La alŝutaĵo estas preciza kopio de la nuna versio de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "La alŝutaĵo estas preciza kopio de {{PLURAL:$2|malnova versio|malnovaj versioj}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ĉi tiu dosiero estas duplikato de la {{PLURAL:$1|jena dosiero|jenaj dosieroj}}:",
        "file-deleted-duplicate": "Duplikata dosiero de ĉi tiu dosiero ([[:$1]]) estis antaŭe forigita. Vi legu la forigan historion de tiu dosiero antaŭ provi realŝuti ĝin.",
        "file-deleted-duplicate-notitle": "Dosiero identa al ĉi tiu dosiero estis forigita antaŭ nelonge kaj la titolo estis subpremita.\nVi demandu iun, kiu havas la eblecon, rigardi la subpremitajn dosierajn datojn, por kontroli la situacion antaŭ rea alŝutado.",
        "upload-http-error": "HTTP-eraro okazis: $1",
        "upload-copy-upload-invalid-domain": "Kopio-alŝutoj ne disponiĝas el ĉi tiu domajno.",
        "upload-foreign-cant-upload": "Tiu vikio ne estas agorita por alŝuti alŝutitan dosieron al la petita fora dosierdeponejo.",
-       "upload-foreign-cant-load-config": "La ŝarĝado de agordo pri dosieran alŝuton malsukcesis por la fora dosiera deponejo.",
+       "upload-foreign-cant-load-config": "Malsukcesis ŝargi la agordon por dosier-alŝutoj al ekstera dosier-deponejo.",
        "upload-dialog-disabled": "Alŝutoj de dosiero per ĉi tiun dialogon estas malfunkciigita sur ĉi tiu vikio.",
        "upload-dialog-title": "Alŝuti dosieron",
        "upload-dialog-button-cancel": "Nuligi",
        "notargettext": "Vi ne precizigis, kiun paĝon aŭ uzanton priumi.",
        "nopagetitle": "Nenia cela paĝo",
        "nopagetext": "La cela paĝo kiun vi enigis ne ekzistas.",
-       "pager-newer-n": "{{PLURAL:$1|pli nova 1|pli novaj $1}}",
-       "pager-older-n": "{{PLURAL:$1|pli malnova 1|pli malnovaj $1}}",
+       "pager-newer-n": "{{PLURAL:$1|pli novan 1|pli novajn $1}}",
+       "pager-older-n": "{{PLURAL:$1|pli malnovan 1|pli malnovajn $1}}",
        "suppress": "Forigu",
        "querypage-disabled": "Tiu ĉi speciala paĝo estas malfunkciigita pro rendimentaj kialoj.",
        "apihelp": "Helpo pri API",
        "watchnologin": "Ne ensalutinta",
        "addwatch": "Aldoniĝi al atentaro",
        "addedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis aldonitaj al via [[Special:Watchlist|atentaro]].",
+       "addedwatchtext-talk": "«[[:$1]]» kaj asociita kun ĝi paĝo estas aldonitaj al via [[Special:Watchlist|atentaro]].",
        "addedwatchtext-short": "La paĝo \"$1\" estis aldonita al via atento-listo.",
        "removewatch": "Forigi el atentaro",
        "removedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis forigita el via [[Special:Watchlist|atentaro]].",
+       "removedwatchtext-talk": "«[[:$1]]» kaj asociita kin ĝi paĝo estas forigitaj el via [[Special:Watchlist|atentaro]].",
        "removedwatchtext-short": "La paĝo \"$1\" estis forigita el via atento-listo.",
        "watch": "Atenti",
        "watchthispage": "Priatenti paĝon",
        "rollbacklinkcount-morethan": "nuligi pli ol $1 {{PLURAL:$1|redakton|redaktojn}}",
        "rollbackfailed": "Malfaro malsukcesis",
        "rollback-missingparam": "Mankas neprajn parametrojn de peto.",
+       "rollback-missingrevision": "Ne eblas ŝarĝi datumojn pri revizioj.",
        "cantrollback": "Ne povas restarigi antaŭan redakton; la redaktinto lasta estas la sola aŭtoro de la paĝo.",
        "alreadyrolled": "Ne povas restarigi la lastan redakton de [[:$1]] de la [[User:$2|$2]] ([[User talk:$2|diskuto]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\npro tio, ke oni intertempe redaktis aŭ restarigis la paĝon.\nLa lasta redaktinto estis [[User:$3|$3]] ([[User talk:$3|diskuto]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "La resumo de la redakto estis: <em>$1</em>.",
        "undeletehistorynoadmin": "Ĉi tiu artikolo estis forigita. La kaŭzo por la forigo estas montrata en la malsupra resumo, kune kun detaloj pri la uzantoj, kiuj redaktis ĉi tiun paĝon antaŭ la forigo. La aktuala teksto de ĉi tiuj forigitaj versioj estas atingebla nur de administrantoj.",
        "undelete-revision": "Forigita versio de $1 (ekde $4, $5) fare de $3:",
        "undeleterevision-missing": "Malvalida aŭ malaperita revizio.\nVi verŝajne havas malbonan ligilon, aŭ la revizio eble estis restarigita aŭ forigita de la arkivo.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Unu versio|$1 versioj}} estas nerestarigeblaj, ĉar {{PLURAL:$1|ĝ|il}}ia <code>rev_id</code> jam estas uzata.",
        "undelete-nodiff": "Neniu antaŭa versio troviĝis.",
        "undeletebtn": "Restarigi",
        "undeletelink": "vidi/restarigi",
        "sp-contributions-newbies-sub": "Kontribuoj de novaj uzantoj. Forigitaj paĝoj ne estas montritaj.",
        "sp-contributions-newbies-title": "Kontribuoj de novaj uzantoj",
        "sp-contributions-blocklog": "protokolo de forbaroj",
-       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de uzanto",
-       "sp-contributions-deleted": "forigitaj kontribuoj de uzanto",
+       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de {{GENDER:$1|uzanto}}",
+       "sp-contributions-deleted": "forigitaj kontribuoj de {{GENDER:$1|uzanto}}",
        "sp-contributions-uploads": "alŝutoj",
        "sp-contributions-logs": "protokoloj",
        "sp-contributions-talk": "diskuto",
        "exportcuronly": "Entenas nur la aktualan version, ne la malnovajn.",
        "exportnohistory": "----\n'''Notu:''' Eksportado de la plena historio de paĝoj per ĉi paĝo estis malebligita pro funkciigaj kialoj.",
        "exportlistauthors": "Inkluzivi plenan liston de kontribuantoj por ĉiu paĝo.",
-       "export-submit": "Eksporti",
+       "export-submit": "Elporti",
        "export-addcattext": "Aldoni paĝojn el kategorio:",
        "export-addcat": "Aldoni",
        "export-addnstext": "Aldoni paĝojn de nomspaco:",
        "import-interwiki-sourcepage": "Fonta paĝo:",
        "import-interwiki-history": "Kopiu ĉiujn historiajn versiojn por ĉi tiu pago.",
        "import-interwiki-templates": "Inkluzivi ĉiujn ŝablonojn",
-       "import-interwiki-submit": "Importi",
+       "import-interwiki-submit": "Enporti",
        "import-mapping-default": "Importi al defaŭltaj lokoj",
        "import-mapping-namespace": "Importi en nomspacon:",
        "import-mapping-subpage": "Importi kiel subpaĝojn de la jena paĝo:",
        "pageinfo-article-id": "Paĝa identigo",
        "pageinfo-language": "Lingvo de paĝa enhavo",
        "pageinfo-content-model": "Modelo de paĝoenhavo",
+       "pageinfo-content-model-change": "ŝanĝi",
        "pageinfo-robot-policy": "Indeksado per robotoj",
        "pageinfo-robot-index": "Permesata",
        "pageinfo-robot-noindex": "Malpermesata",
        "linkaccounts-submit": "Ligi kontojn",
        "unlinkaccounts": "Malligi kontojn",
        "unlinkaccounts-success": "La konto estis malligita.",
-       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?"
+       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?",
+       "userjsispublic": "Bonvolu noti: subpaĝoj en JavaScript ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj.",
+       "usercssispublic": "Bonvolu noti: subpaĝoj en CSS ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj."
 }
index 7099422..74b31c0 100644 (file)
        "tog-enotifminoredits": "Notificarme también por correo electrónico los cambios menores de las páginas y archivos",
        "tog-enotifrevealaddr": "Revelar mi dirección de correo electrónico en los correos de notificación",
        "tog-shownumberswatching": "Mostrar el número de usuarios que la vigilan",
-       "tog-oldsig": "Firma actual:",
+       "tog-oldsig": "Tu firma actual:",
        "tog-fancysig": "Tratar la firma como wikitexto (sin un enlace automático)",
        "tog-uselivepreview": "Usar previsualización dinámica",
        "tog-forceeditsummary": "Avisarme cuando deje en blanco el resumen de la edición",
        "newwindow": "(se abre en una ventana nueva)",
        "cancel": "Cancelar",
        "moredotdotdot": "Más...",
-       "morenotlisted": "Esta lista no está completa.",
+       "morenotlisted": "Esta lista puede estar incompleta.",
        "mypage": "Página",
        "mytalk": "Discusión",
        "anontalk": "Discusión",
        "yourpasswordagain": "Confirma la contraseña:",
        "createacct-yourpasswordagain": "Confirma la contraseña",
        "createacct-yourpasswordagain-ph": "Repite la contraseña",
-       "remembermypassword": "Mantenerme conectado en este navegador (hasta $1 {{PLURAL:$1|día|días}})",
        "userlogin-remembermypassword": "Mantener mi sesión iniciada",
        "userlogin-signwithsecure": "Usar conexión segura",
+       "cannotlogin-title": "No se puede iniciar sesión",
        "cannotloginnow-title": "No se puede iniciar sesión ahora",
        "cannotloginnow-text": "No se puede iniciar sesión cuando se usa $1.",
+       "cannotcreateaccount-title": "No se pueden crear cuentas",
+       "cannotcreateaccount-text": "La creación directa de cuentas no está activada en este wiki.",
        "yourdomainname": "Tu dominio:",
        "password-change-forbidden": "No puedes cambiar las contraseñas en este wiki.",
        "externaldberror": "Hubo un error de autenticación en la base de datos, o bien no tienes autorización para actualizar tu cuenta externa.",
        "accmailtext": "Se ha enviado a $2 una contraseña generada aleatoriamente para [[User talk:$1|$1]]. Una vez iniciada la sesión, se puede cambiar en la página [[Special:ChangePassword|destinada para ello]].",
        "newarticle": "(Nuevo)",
        "newarticletext": "Has seguido un enlace a una página que aún no existe.\nPara crear esta página, escribe en el cuadro que aparece a continuación. Para más información, consulta la [$1 página de ayuda].\nSi llegaste aquí por error, vuelve a la página anterior.",
-       "anontalkpagetext": "----\n<em>Esta es la página de discusión de un usuario anónimo que aún no ha creado una cuenta, o no la usa.</em>\nPor lo tanto, tenemos que usar su dirección IP para identificarlo.\nPuede que varios usuarios compartan una misma dirección IP.\nSi eres un usuario anónimo y crees que se han dirigido a ti con comentarios improcedentes, [[Special:CreateAccount|crea una cuenta]] o [[Special:UserLogin|inicia sessión]] para evitar confusiones futuras con otros usuarios anónimos.",
+       "anontalkpagetext": "----\n<em>Esta es la página de discusión de un usuario anónimo que aún no ha creado una cuenta, o no la usa.</em>\nPor lo tanto, tenemos que usar su dirección IP para identificarlo.\nPuede que varios usuarios compartan una misma dirección IP.\nSi eres un usuario anónimo y crees que se han dirigido a ti con comentarios improcedentes, [[Special:CreateAccount|crea una cuenta]] o [[Special:UserLogin|inicia sesión]] para evitar confusiones futuras con otros usuarios anónimos.",
        "noarticletext": "En este momento no hay texto en esta página.\nPuedes [[Special:Search/{{PAGENAME}}|buscar el título de esta página]] en otras páginas,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} crear esta página]</span>.",
        "noarticletext-nopermission": "Actualmente no hay texto en esta página.\nPuedes [[Special:Search/{{PAGENAME}}|buscar este título de página]] en otras páginas, o <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados]</span>, pero no tienes permiso para crear esta página.",
        "missing-revision": "La revisión n.º $1 de la página «{{FULLPAGENAME}}» no existe.\n\nEsto suele ocurrir cuando se sigue un enlace de historial obsoleto que apunta a una página ya borrada.\nPuedes encontrar detalles en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
        "mergehistory": "Fusionar historiales",
        "mergehistory-header": "Esta página te permite fusionar revisiones del historial de una página origen con los de otra más reciente.\nAsegúrate de que los cambios mantendrán la continuidad histórica de la página.",
        "mergehistory-box": "Fusionar los historiales de dos páginas:",
-       "mergehistory-from": "Página origen:",
+       "mergehistory-from": "Página de origen:",
        "mergehistory-into": "Página destino:",
        "mergehistory-list": "Historial de ediciones fusionable",
        "mergehistory-merge": "Las siguientes revisiones de [[:$1]] pueden fusionarse con las de [[:$2]].\nUsa la columna de casillas para fusionar sólo las revisiones creadas en y antes de la fecha especificada.\nTen en cuenta que si cambias de página, se borrará la selección actual de esta columna.",
        "action-import": "importar páginas desde otro wiki",
        "action-importupload": "importar páginas mediante la carga de un archivo",
        "action-patrol": "marcar ediciones de otros como verificadas",
-       "action-autopatrol": "tener tus ediciones marcadas como verificadas",
+       "action-autopatrol": "marcar como verificadas tus propias ediciones",
        "action-unwatchedpages": "ver la lista de páginas no vigiladas",
        "action-mergehistory": "fusionar el historial de esta página",
        "action-userrights": "modificar todos los permisos de usuario",
        "filerevert-submit": "Revertir",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> ha sido revertido a la [$4 versión del $2 a las $3].",
        "filerevert-badversion": "No existe versión local previa de este archivo con esa marca de tiempo.",
+       "filerevert-identical": "La versión actual del archivo ya es idéntica a la seleccionada.",
        "filedelete": "Borrar $1",
        "filedelete-legend": "Borrar archivo",
        "filedelete-intro": "Estás por borrar el archivo <strong>[[Media:$1|$1]]</strong> así como todo su historial.",
        "usercreated": "{{GENDER:$3|Registrado|Registrada}} el $1 a las $2",
        "newpages": "Páginas nuevas",
        "newpages-submit": "Mostrar",
-       "newpages-username": "Nombre de usuario",
+       "newpages-username": "Nombre de usuario:",
        "ancientpages": "Páginas más antiguas",
        "move": "Trasladar",
        "movethispage": "Trasladar esta página",
        "querypage-disabled": "Esta página especial está deshabilitada por motivos de rendimiento.",
        "apihelp": "Ayuda de la API",
        "apihelp-no-such-module": "No se encontró el módulo \"$1\".",
-       "apisandbox": "Zona de pruebas API",
+       "apisandbox": "Zona de pruebas de la API",
        "apisandbox-jsonly": "Se requiere JavaScript para utilizar la zona de pruebas de API.",
        "apisandbox-api-disabled": "La API está desactivada en este sitio.",
        "apisandbox-intro": "Usa esta página para experimentar con la <strong>API de servicio web de MediaWiki</strong>.\nPara más detalles sobre el uso de la API, visita [[mw:API:Main page|su documentación]]. Ejemplo: [https://www.mediawiki.org/wiki/API#A_simple_example obtener el contenido de una Página principal]. Selecciona una acción para ver más ejemplos.\n\nObserva que, aunque sea una página de pruebas, las acciones que realices en esta página pueden modificar el wiki.",
        "rollbacklinkcount-morethan": "revertir más de $1 {{PLURAL:$1|edición|ediciones}}",
        "rollbackfailed": "No se pudo revertir",
        "rollback-missingparam": "Faltan parámetros requeridos en la solicitud.",
+       "rollback-missingrevision": "No se pueden cargar datos de la revisión.",
        "cantrollback": "No se puede revertir la edición;\nel último colaborador es el único autor de esta página.",
        "alreadyrolled": "No se puede revertir la última edición de [[:$1]] hecha por [[User:$2|$2]] ([[User talk:$2|discusión]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguien más ya ha editado o revertido esa página.\n\nLa última edición fue hecha por [[User:$3|$3]] ([[User talk:$3|discusión]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resumen de la edición fue: <em>$1</em>.",
        "sp-contributions-newbies-sub": "Para cuentas nuevas",
        "sp-contributions-newbies-title": "Contribuciones de usuarios nuevos",
        "sp-contributions-blocklog": "registro de bloqueos",
-       "sp-contributions-suppresslog": "contribuciones de {{GENDER:$1|usuario|usuaria}} suprimidas",
-       "sp-contributions-deleted": "contribuciones de {{GENDER:$1|usuario|usuaria}} borradas",
+       "sp-contributions-suppresslog": "contribuciones {{GENDER:$1|del usuario|de la usuaria}} suprimidas",
+       "sp-contributions-deleted": "contribuciones {{GENDER:$1|del usuario|de la usuaria}} borradas",
        "sp-contributions-uploads": "subidas",
        "sp-contributions-logs": "registros",
        "sp-contributions-talk": "discusión",
        "pageinfo-article-id": "Identificador de la página",
        "pageinfo-language": "Idioma de la página",
        "pageinfo-content-model": "Modelo de contenido de la página",
+       "pageinfo-content-model-change": "cambiar",
        "pageinfo-robot-policy": "Indización por robots",
        "pageinfo-robot-index": "Permitido",
        "pageinfo-robot-noindex": "No permitido",
        "logentry-delete-revision": "$1 {{GENDER:$2|modificó}} la visibilidad de {{PLURAL:$5|una revisión |$5 revisiones}} en la página  $3: $4",
        "logentry-delete-event-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de eventos del registro en $3",
        "logentry-delete-revision-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de las revisiones en la página $3",
-       "logentry-suppress-delete": "$1 {{GENDER:$2|borró}} la página $3",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|suprimió}} la página $3",
        "logentry-suppress-event": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|un evento|$5 eventos}} del registro en $3: $4",
        "logentry-suppress-revision": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|una edición|$5 ediciones}} en la página $3: $4",
        "logentry-suppress-event-legacy": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de los eventos del registro en $3",
        "unlinkaccounts": "Desvincular cuentas",
        "unlinkaccounts-success": "Se ha desvinculado la cuenta.",
        "authenticationdatachange-ignored": "El cambio den los datos de autentificacion no fue realizado. ¿Tal vez, no se configuró un proveedor?",
-       "userjsispublic": "Recuerda: Las subpáginas JavaScript no deberían contener datos confidenciales, pues otros usuarios los pueden ver.",
-       "usercssispublic": "Recuerda: Las subpáginas CSS no deberían contener datos confidenciales, pues otros usuarios los pueden ver."
+       "userjsispublic": "Recuerda: las subpáginas JavaScript no deberían contener datos confidenciales, pues otros usuarios los pueden ver.",
+       "usercssispublic": "Recuerda: las subpáginas CSS no deberían contener datos confidenciales, pues otros usuarios los pueden ver."
 }
index 3b4e14f..d544a4e 100644 (file)
@@ -46,6 +46,7 @@
        "tog-watchdefault": "Lisa jälgimisloendisse minu muudetud leheküljed ja failid",
        "tog-watchmoves": "Lisa jälgimisloendisse minu teisaldatud leheküljed ja failid",
        "tog-watchdeletion": "Lisa jälgimisloendisse minu kustutatud leheküljed ja failid",
+       "tog-watchuploads": "Lisa jälgimisloendisse uued failid, mille olen üles laadinud",
        "tog-watchrollback": "Lisa jälgimisloendisse leheküljed, kus olen muudatuse tühistanud",
        "tog-minordefault": "Märgi kõik parandused vaikimisi pisiparandusteks",
        "tog-previewontop": "Näita eelvaadet toimetamiskasti ees",
        "yourpasswordagain": "Sisesta parool uuesti:",
        "createacct-yourpasswordagain": "Parooli kinnitus",
        "createacct-yourpasswordagain-ph": "Sisesta uuesti parool",
-       "remembermypassword": "Jäta parool meelde (kuni $1 {{PLURAL:$1|päevaks|päevaks}})",
        "userlogin-remembermypassword": "Jää sisseloginuks",
        "userlogin-signwithsecure": "Kasuta turvalist ühendust",
        "yourdomainname": "Sinu domeen:",
        "minoredit": "See on pisiparandus",
        "watchthis": "Jälgi seda lehekülge",
        "savearticle": "Salvesta",
+       "savechanges": "Salvesta",
        "publishpage": "Avalda lehekülg",
        "publishchanges": "Avalda muudatused",
        "preview": "Eelvaade",
        "undeletedrevisions": "$1 {{PLURAL:$1|redaktsioon|redaktsiooni}} taastatud",
        "undeletedrevisions-files": "{{PLURAL:$1|1 redaktsioon|$1 redaktsiooni}} ja {{PLURAL:$2|1 fail|$2 faili}} taastatud",
        "undeletedfiles": "{{PLURAL:$1|1 fail|$1 faili}} taastatud",
-       "cannotundelete": "Taastamine ebaõnnestus:\n$1",
+       "cannotundelete": "Taastamine ebaõnnestus osaliselt või täielikult:\n$1",
        "undeletedpage": "'''$1 on taastatud'''\n\n[[Special:Log/delete|Kustutamise logist]] võib leida loendi viimastest kustutamistest ja taastamistest.",
        "undelete-header": "Hiljuti kustutatud leheküljed leiad [[Special:Log/delete|kustutamislogist]].",
        "undelete-search-title": "Kustutatud lehekülgede otsimine",
        "sp-contributions-newbies-sub": "Uute kontode kaastöö",
        "sp-contributions-newbies-title": "Uute kasutajate kaastöö",
        "sp-contributions-blocklog": "blokeerimised",
-       "sp-contributions-suppresslog": "varjatud kaastöö",
-       "sp-contributions-deleted": "kustutatud kaastöö",
+       "sp-contributions-suppresslog": "{{GENDER:$1|varjatud}} kaastöö",
+       "sp-contributions-deleted": "{{GENDER:$1|kustutatud}} kaastöö",
        "sp-contributions-uploads": "üleslaadimised",
        "sp-contributions-logs": "logid",
        "sp-contributions-talk": "arutelu",
index a79df94..523b2ef 100644 (file)
        "mypreferencesprotected": "Ez daukazu eskumenik zure hobespenak aldatzeko.",
        "ns-specialprotected": "Ezin dira {{ns:special}} izen-tarteko orrialdeak editatu.",
        "titleprotected": "[[User:$1|$1]]ek izenburu hau sortzea ekidin zuen.\nEmandako arrazoia <em>$2</em> izan zen.",
-       "filereadonlyerror": "Ezin izan da \"$1\" fitxategia aldatu, \"$2\" fitxategi bilduma irakrutzeko-bakarrik moduan dagoelako.\n\nBlokeoa ezarri zuen administratzaileak honako arrazoia eman zuen: \"$3\".",
+       "filereadonlyerror": "Ezin izan da \"$1\" fitxategia aldatu, \"$2\" fitxategi bilduma irakrutzeko-bakarrik moduan dagoelako.\n\nBlokeoa ezarri zuen sistema administratzaileak honako arrazoia eman zuen: \"$3\".",
        "invalidtitle-knownnamespace": "Izenburua gaizki dago \"$2\" izen eremuan eta \"$3\" testuan",
        "invalidtitle-unknownnamespace": "Izenburua gaizki dago \"$1\" izen eremuan ezezagunean eta \"$2\" testuan",
        "exception-nologin": "Saioa hasi gabe",
        "yourpasswordagain": "Pasahitza berriz",
        "createacct-yourpasswordagain": "Pasahitza berridatzi",
        "createacct-yourpasswordagain-ph": "Sartu pasahitza berriro ere",
-       "remembermypassword": "Nire saioa ordenagailu honetan gogoratu ({{PLURAL:$1|egun baterako|$1 egunetarako }} gehienez)",
        "userlogin-remembermypassword": "Manten nazazu barruan",
        "userlogin-signwithsecure": "Erabili konexio ziurra",
        "yourdomainname": "Zure domeinua",
        "createacct-another-realname-tip": "Benetako izena hautazkoa da.\nEmatea erabakitzen baduzu hori erabiliko da lanaren atribuzioa egiterako garaian.",
        "pt-login": "Saioa hasi",
        "pt-login-button": "Saioa hasi",
+       "pt-login-continue-button": "Konexioa jarraitu",
        "pt-createaccount": "Sortu kontua",
        "pt-userlogout": "Saioa itxi",
        "php-mail-error-unknown": "PHPren mail() funtzioan arazo ezezagun bat egon da.",
        "resetpass_submit": "Pasahitza definitu eta saioa hasi",
        "changepassword-success": "Zure pasahitza aldatu da!",
        "changepassword-throttled": "Saioa hasteko saiakera gehiegi egin berri dituzu.\nBerriro saiatu aurretik $1 itxoin, mesedez.",
+       "botpasswords": "Bot pasahitzak",
+       "botpasswords-disabled": "Bot pasahitzak desgaituak daude.",
        "botpasswords-label-appid": "Bot izena:",
        "botpasswords-label-create": "Sortu",
        "botpasswords-label-update": "Eguneratu",
        "passwordreset-email": "E-mail helbidea:",
        "passwordreset-emailtitle": "{{SITENAME}}-rako kontuaren xehetasunak",
        "passwordreset-emailelement": "Erabiltzaile izena: \n$1\n\nBehin-behineko pasahitza: \n$2",
-       "passwordreset-emailsentemail": "Hau zure konturako erregistratuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
+       "passwordreset-emailsentemail": "Hau zure kontuarekin lotuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
        "changeemail": "Aldatu edo kendu e-mail helbidea",
        "changeemail-header": "Bete ezazu inprimaki hau, zure helbide elektronikoa aldatzeko. Zure kontuari helbide elektronikorik elkartuta ez izatea nahi baduzu, utz ezazu hutsik helbide elektroniko berria, inprimakia bidaltzen duzunean.",
        "changeemail-no-info": "Orrialde honetara zuzenean sartzeko izena eman behar duzu.",
        "accmailtext": "[[User talk:$1|$1]]-entzako ausaz sortutako pasahitza $2-(r)a bidali da.\n\n''[[Special:ChangePassword|pasahitz aldaketa]]'' orrialdean alda daiteke, behin barruan sartuta.",
        "newarticle": "(Berria)",
        "newarticletext": "Orrialde hau ez da existitzen oraindik. Orrialde sortu nahi baduzu, beheko koadroan idazten hasi zaitezke (ikus [$1 laguntza orrialdea] informazio gehiagorako). Hona nahi gabe etorri bazara, nabigatzaileko '''atzera''' botoian klik egin.",
-       "anontalkpagetext": "----''Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.''",
+       "anontalkpagetext": "<em>Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.</em>\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.",
        "noarticletext": "Oraindik ez dago testurik orri honetan.\nEdukiz hornitzeko, aukera hauek dituzu: beste orri batzuetan [[Special:Search/{{PAGENAME}}|orri izenburu hau bilatzea]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} lotutako logak bilatzea],\nedo [{{fullurl:{{FULLPAGENAME}}|action=edit}} orri hau sortzea]</span>.",
        "noarticletext-nopermission": "Une honetan ez dago testurik orrialde honetan.\nBeste orrialdeetan [[Special:Search/{{PAGENAME}}|izenburu hau bilatu dezakezu]],\nedo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} erlazionatutako erregistroak bilatu]</span>, baina ez duzu orrialde hau sortzeko baimenik.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" lankidea ez dago erregistatuta. Mesedez, konprobatu orri hau editatu/sortu nahi duzun.",
        "userpage-userdoesnotexist-view": "\"$1\" erabiltzaile-kontua ez dago erregistraturik.",
        "blocked-notice-logextract": "Erabiltzaile hau blokeatuta dago une honetan.\nAzken blokeoaren erregistroa ageri da behean, erreferentzia gisa:",
-       "clearyourcache": "'''Oharra:''' Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* '''Firefox / Safari:''' ''Shift'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-Shift-R'' edo ''Crtl-F5'' sakatu (''⌘-R''' Mac batean)\n* '''Google Chrome:''' ''Ctrl-Shift-R'' sakatu (''⌘-Shift-R'' Mac batean)\n* '''Internet Explorer:''' ''Ctrl'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-F5'' sakatu\n* '''Opera''' erabiltzaileek ''Tresnak → Hobespenak'' atalera joan eta katxea garbitzeko aukera hautatu",
+       "clearyourcache": "<strong>Oharra:</strong> Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* <strong>Firefox / Safari:</strong> <em>Shift</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-Shift-R</em> edo <em>Crtl-F5</em>  sakatu (<em>⌘-R</em> Mac batean)\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R </em>  sakatu (<em>⌘-Shift-R</em> Mac batean)\n* <strong>Internet Explorer:</strong> <em>Ctrl</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-F5</em> sakatu\n* <strong>Opera</strong> erabiltzaileek <em>Tresnak → Hobespenak</em> atalera joan eta katxea garbitzeko aukera hautatu",
        "usercssyoucanpreview": "'''Laguntza:''' Zure CSS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
        "userjsyoucanpreview": "'''Laguntza:''' Zure JS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
        "usercsspreview": "'''Ez ahaztu zure CSS kodea aurreikusten zabiltzala.'''\n'''Oraindik gorde gabe dago!'''",
        "previewnote": "'''Gogoratu hau aurrikuspen bat dela.'''\nZure aldaketak ez dira oraindik gorde!",
        "continue-editing": "Edizio-eremura joan",
        "previewconflict": "Aurreikuspenak aldaketen koadroan idatzitako testua erakusten du, gorde ondoren agertuko den bezala.",
-       "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu saioa amaitu eta berriz hasten.'''",
+       "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu [[Special:UserLogout|saioa amaitu]] eta berriz hasten.'''",
        "session_fail_preview_html": "'''Sentitzen dugu! Ezin izan dugu zure aldaketa burutu, saio datu galera bat medio.'''\n\n''Wiki honek HTML kodea onartzen duenez, aurreikuspena ezgaituta dago JavaScript erasoak saihestu asmoz.''\n\n'''Aldaketa saiakera hau zuzena baldin bada, saiatu berriro mesedez. Arazoak jarraitzen badu, saiatu saioa itxi eta berriz hasten.'''",
        "token_suffix_mismatch": "'''Zure aldaketa ezeztatua izan da zure bezeroak puntuazio-karaktereak itxuragabetu dituelako.\nAldaketa ezeztatua izan da testuaren galtzea galarazteko.\nHau batzuetan gertatzen da buggyan oinarritutako web proxy zerbitzua erabiltzean.'''",
        "edit_form_incomplete": "'''Aldaketa formularioaren atal batzuk ez dira iritsi zerbitzarira; bi aldiz ziurtatu zure aldaketak osorik daudela eta berriro saiatu.'''",
        "preferences": "Hobespenak",
        "mypreferences": "Hobespenak",
        "prefs-edits": "Aldaketa kopurua:",
-       "prefsnologintext2": "Mesedez $1 zure hobespenak aldatzeko.",
+       "prefsnologintext2": "Mesedez saioa hasi zure hobespenak aldatzeko.",
        "prefs-skin": "Itxura",
        "skin-preview": "Aurrebista",
        "datedefault": "Hobespenik ez",
        "right-deletedtext": "Ikusi ezabatutako testua eta ezabatutako berrikuspenen arteko aldaketak",
        "right-browsearchive": "Ezabatutako orrialdeak bilatu",
        "right-undelete": "Ezabatutako orrialde bat itzularazi",
-       "right-suppressrevision": "Administratzaileentzat izkutatutako berrikuspenak berrikusi edo berrezarri",
+       "right-suppressrevision": "Edozein erabiltzaileren berrikuspenak ikusi, ezkutatu ala ikustarazi",
        "right-suppressionlog": "Log pribatuak ikusi",
        "right-block": "Blokeatu beste erabiltzaile batzuk, edita ez dezaten",
        "right-blockemail": "Erabiltzaile bati blokeatu mezu elektronikoak bidaltzeko aukera",
        "uploadstash": "Gordailu bat igo",
        "uploadstash-clear": "Kodetutako fitxategiak izkutatu",
        "uploadstash-nofiles": "Ez duzu kodetutako fitxategirik.",
-       "uploadstash-errclear": "Fitxategiak ezabatzeak ez du arrakastarik izan.",
+       "uploadstash-errclear": "Fitxategiak ezabatzeak akatsa eman du.",
        "uploadstash-refresh": "Fitxategien zerrenda eguneratu",
        "img-auth-accessdenied": "Sarbide ukatua",
        "img-auth-nopathinfo": "PATH_INFO falta da.\nZure zerbitzaria ez dago informazio hau pasatzeko konfiguratuta.\nCGI-oinarriduna izan daiteke, img_auth onartzen ez duena.\nIkusi https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "nolicense": "Hautatu gabe",
        "licenses-edit": "Aldatu lizentzien aukerak",
        "license-nopreview": "(Aurreikuspenik ez)",
-       "upload_source_url": " (baliozko URL publikoa)",
-       "upload_source_file": " (zure ordenagailuko fitxategi bat)",
+       "upload_source_url": "(zuk aukeratutako fitxategi baliozko URL publikorako)",
+       "upload_source_file": "(zure ordenagailuko fitxategi bat)",
        "listfiles-delete": "ezabatu",
        "listfiles-summary": "Orri berezi honek igotako fitxategi guztiak erakusten ditu.",
        "listfiles_search_for": "Irudiaren izenagatik bilatu:",
        "undeletedrevisions": "{{PLURAL:$1|Berrikuspen 1 leheneratu da|$1 berrikuspen leheneratu dira}}",
        "undeletedrevisions-files": "{{PLURAL:$1|berrikuspen|berrikuspen}} eta {{PLURAL:$2|fitxategi|fitxategi}} leheneratu dira",
        "undeletedfiles": "{{PLURAL:$1|fitxategi|fitxategi}} leheneratu dira",
-       "cannotundelete": "Ezabatutako birgaitzean akatsa: $1",
+       "cannotundelete": "Ezabatutako birgaitze betean edo hainbatetan akatsa: $1",
        "undeletedpage": "'''«$1» leheneratu da'''\n\nAzken ezabatze eta leheneratzeak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
        "undelete-header": "Berriki ezabatutako orriak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
        "undelete-search-title": "Ezabatutako orrialdeak bilatu",
        "sp-contributions-newbies-sub": "Hasiberrientzako",
        "sp-contributions-newbies-title": "Lankideen ekarpenak lankide berrietn",
        "sp-contributions-blocklog": "Blokeaketa erregistroa",
-       "sp-contributions-suppresslog": "lankide-ekarpen ezabatuak",
+       "sp-contributions-suppresslog": "{{GENDER:$1|(r)en}} lankide-ekarpen ezabatuak",
        "sp-contributions-deleted": "lankide-ekarpen ezabatuak",
        "sp-contributions-uploads": "igoerak",
        "sp-contributions-logs": "erregistroak",
        "tooltip-feed-rss": "Orrialde honen RSS jarioa",
        "tooltip-feed-atom": "Orrialde honen atom jarioa",
        "tooltip-t-contributions": "{{GENDER:$1|Lankide honen}} ekarpen zerrenda ikusi",
-       "tooltip-t-emailuser": "Lankide honi e-posta mezua bidali",
+       "tooltip-t-emailuser": "{{GENDER:$1|Lankide honi}} e-posta mezua bidali",
        "tooltip-t-info": "Orrialde honi buruzko informazio gehiago",
        "tooltip-t-upload": "Irudiak edo media fitxategiak igo",
        "tooltip-t-specialpages": "Orri berezi guztien zerrenda",
index 56e8cbe..302bc58 100644 (file)
@@ -11,7 +11,8 @@
                        "Henares",
                        "MarcoAurelio",
                        "Macofe",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Crucifunked"
                ]
        },
        "tog-underline": "Surrayal atihus:",
        "oct": "Otu",
        "nov": "Nov",
        "dec": "Dic",
+       "january-date": "$1 eneru",
+       "february-date": "$1 de hebreru",
+       "march-date": "$1 de marçu",
+       "april-date": "$1 e abril",
+       "may-date": "$1 e mayu",
+       "june-date": "$1 e húniu",
+       "july-date": "$1 e húliu",
+       "august-date": "$1 e agostu",
+       "september-date": "$1 e setiembri",
+       "october-date": "$1 e outubri",
+       "november-date": "$1 e noviembri",
+       "december-date": "$1 e diziembri",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoria|Categorias}}",
        "category_header": "Artículus ena categoria \"$1\"",
        "subcategories": "Sucategorias",
        "category-file-count": "{{PLURAL:$2|Esta categoria solu contiini el siguienti archivu.|{{PLURAL:$1|El siguienti archivu está|Los siguientis $1 archivus están}} nesta categoria, dun total de $2.}}",
        "category-file-count-limited": "{{PLURAL:$1|El siguienti archivu está|Los siguientis $1 archivus están}} nesta categoria.",
        "listingcontinuesabbrev": "acont.",
+       "broken-file-category": "Páhinas con atihus esgalçaos a archivus",
        "about": "Al tentu",
        "article": "Artículu",
        "newwindow": "(s'abrirá nuna nueva ventana)",
        "cancel": "Cancelal",
        "moredotdotdot": "Mas...",
-       "mypage": "La mi páhina",
+       "morenotlisted": "Esta lista nu está completa",
+       "mypage": "Páhina",
        "mytalk": "La mi caraba",
-       "anontalk": "Caraba pa esta IP",
+       "anontalk": "La mi caraba",
        "navigation": "Güiquipeandu",
        "and": "&#32;i",
        "qbfind": "Alcuentral",
        "actions": "Acionis",
        "namespaces": "Espáciu nombris",
        "variants": "Variantis",
+       "navigation-heading": "Menú de navegación",
        "errorpagetitle": "Marru",
        "returnto": "Gorvel a $1.",
        "tagline": "Dendi {{SITENAME}}",
        "printableversion": "Velsión pa imprental",
        "permalink": "Atiju remanenti",
        "print": "Imprental",
+       "view": "Guipal",
+       "view-foreign": "Vel en $1",
        "edit": "Eital",
+       "edit-local": "Eital descrición local",
        "create": "Crial",
+       "create-local": "Azeñil descrición local",
        "editthispage": "Eital esta páhina",
        "create-this-page": "Crial esta páhina",
        "delete": "Esborral",
        "deletethispage": "Esborral esta páhina",
+       "undeletethispage": "Arrecuperal esta páhina",
        "undelete_short": "Arrecuperal {{PLURAL:$1|una eición|$1 eicionis}}",
+       "viewdeleted_short": "Guipal {{PLURAL:$1|una eición esborrá|$1 eicionis esborrás}}",
        "protect": "Protegel",
        "protect_change": "escambial",
        "protectthispage": "Protegel esta página",
-       "unprotect": "esprotegel",
-       "unprotectthispage": "Esprotegel esta página",
+       "unprotect": "Escambial proteción",
+       "unprotectthispage": "Escambial la proteción esta página",
        "newpage": "Páhina nueva",
        "talkpage": "Palral sobri esta páhina",
        "talkpagelinktext": "Caraba",
        "otherlanguages": "En otras palras",
        "redirectedfrom": "(Rederihiu dendi $1)",
        "redirectpagesub": "Rederihil páhina",
+       "redirectto": "Redirihi a:",
        "lastmodifiedat": "Los úrtimus chambus desta páhina huerun a las $2 el dia $1.",
        "viewcount": "Esta páhina á siu visoreá {{PLURAL:$1|una vezi|$1 vezis}}.",
        "protectedpage": "Página protegia",
        "jumptosearch": "landeal",
        "aboutsite": "Al tentu {{SITENAME}}",
        "aboutpage": "Project:Enjolmación",
-       "copyright": "Continiu disponibri bahu $1.",
+       "copyright": "El continiu está disponibri bahu $1 a nu sel que se diga lo contrariu.",
        "copyrightpage": "{{ns:project}}:Copyright",
        "currentevents": "La trohi las notícias",
        "currentevents-url": "Project:La trohi las notícias",
        "disclaimers": "Avissu legal",
        "disclaimerpage": "Project:Arrayu heneral de responsabiliá",
        "edithelp": "Ayua d'eición",
+       "helppage-top-gethelp": "Ayua",
        "mainpage": "Página prencipal",
        "mainpage-description": "Páhina prencipal",
        "policy-url": "Project:Pulítica",
        "ok": "Dalcuerdu",
        "retrievedfrom": "Arrecuperau dendi \"$1\"",
        "youhavenewmessages": "Tiinis $1 ($2).",
+       "youhavenewmessagesmanyusers": "Tienis $1 de muchus usuárius ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|un mensahi nuevu|999=mensahis nuevus}}",
        "youhavenewmessagesmulti": "Tiinis nuevus mensahis en $1",
        "editsection": "eital",
        "editold": "eital",
        "yourname": "Nombri d'usuáriu:",
        "yourpassword": "Consínia:",
        "yourpasswordagain": "Escrebi e nuevu la consínia:",
-       "remembermypassword": "Recordal la mi cuenta nesti ordinaol (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "yourdomainname": "El tu domiñu:",
        "externaldberror": "Marru d'autentificación esterna e la basi e datus, u bien nu t'alcuentras autorizau p'atualizal la tu cuenta esterna.",
        "login": "Entral",
        "createaccount-title": "Criaeru e cuentas de {{SITENAME}}",
        "createaccount-text": "Alguien á criau una cuenta pa $2 en {{SITENAME}} ($4). La consínia pa \"$2\" es \"$3\".\nEberias entral ena tu cuenta i chambal la tu consínia.\n\nSi s'á criau la cuenta ebiu a angún marru, inora esti mensahi.",
        "loginlanguagelabel": "Palra: $1",
+       "pt-login": "Acedel",
+       "pt-createaccount": "Crial cuenta",
        "changepassword": "Chambal consínia",
        "resetpass_announce": "As entrau ena tu cuenta con una consínia temporal. Pol favol, escrebi una nueva consínia aquí:",
        "resetpass_text": "<!-- Aquí s´escrebi el testu -->",
        "undo-failure": "Nu es posibri eshazel la eición ebiu a que otru usuáriu á realizau una eición entelmeya.",
        "undo-norev": "La eición nu pué sel eshecha ebiu a que nu dessisti, u hue esborrá",
        "undo-summary": "Eshazel revisión $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Caraba]])",
-       "cantcreateaccounttitle": "Nu es posibri crial la cuenta",
        "cantcreateaccount-text": "La criación de cuentas pol parti e la IP ('''$1''') á siu pará pol el usuáriu [[User:$3|$3]].\n\nLa razón dá pol $3 es ''$2''",
        "viewpagelogs": "Vel los rustrihus d´esta páhina",
        "nohistory": "Nu ai dengún estorial d´eicionis pa esta páhina.",
        "action-browsearchive": "landeal páginas esborrás",
        "action-undelete": "arrecuperal esta página",
        "nchanges": "$1 {{PLURAL:$1|chambu|chambus}}",
+       "enhancedrc-history": "Estorial",
        "recentchanges": "Úrtimus chambus",
        "recentchanges-legend": "Ocionis enos úrtimus chambus",
        "recentchanges-summary": "Sigui los úrtimus chambus d´esti güiqui nesta páhina.",
        "rcnotefrom": "Embahu se muestran los chambus hechus dendi el '''$2''' (hata el '''$1''').",
        "rclistfrom": "Muestral los chambus hechus dendi el $3 $2",
        "rcshowhideminor": "$1 eicionis chiqueninas",
+       "rcshowhideminor-hide": "Açonchal",
        "rcshowhidebots": "$1 bots",
+       "rcshowhidebots-show": "Muestral",
        "rcshowhideliu": "$1 usuárius rustrius",
+       "rcshowhideliu-hide": "Açonchal",
        "rcshowhideanons": "$1 usuárius anónimus",
+       "rcshowhideanons-hide": "Açonchal",
        "rcshowhidepatr": "$1 eicionis patrullás",
        "rcshowhidemine": "$1 las mis eicionis",
+       "rcshowhidemine-hide": "Açonchal",
        "rclinks": "Muestral los $1 úrtimus chambus enus $2 úrtimus dias<br />$3",
        "diff": "def",
        "hist": "estor",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|usuáriu está|usuárius están}} vehilandu]",
        "rc_categories": "Arrayal a categorias (separás pol \"|\")",
        "rc_categories_any": "Cualisquiá",
+       "rc-change-size-new": "$1{{PLURAL:$1|byte|bytes}} dempués el chambu",
        "newsectionsummary": "/* $1 */ seción nueva",
        "rc-enhanced-expand": "muestral detallis (es mestel JavaScript)",
        "rc-enhanced-hide": "Açonchal detallis",
        "tooltip-upload": "Prencipial a empuntal",
        "tooltip-rollback": "\"Reveltil\" esborra las eicionis hechas a esta página pol úrtimu usuáriu con un click",
        "tooltip-undo": "\"Esjadel\" revierti ésta eición i abri el mó eición en mó previsoreal.\nÉstu premiti añiil una radón al estorial.",
+       "tooltip-summary": "Escribi un brevi resumen",
        "anonymous": "{{PLURAL:$1|Ussuáriu anónimu|Ussuárius anónimus}} en {{SITENAME}}",
        "siteuser": "{{SITENAME}} usuáriu $1",
        "lastmodifiedatby": "Esta páhina se chambó pol úrtima vezi a las $2, el dia $1 pol $3.",
        "spambot_username": "MediaWiki limpia-spam",
        "spam_reverting": "Revirtiendu a la úrtima velsión que nu contenga atihus a $1",
        "spam_blanking": "Tolas revisionis tienin atihus a $1, branqueandu",
+       "simpleantispam-label": "Compreba anti-spam.\n<strong>nu</strong> rellene estu!",
        "markaspatrolleddiff": "Aseñalal cumu patrullau",
        "markaspatrolledtext": "Aseñalal esti artículu cumu patrullau",
        "markedaspatrolled": "Aseñalal cumu patrullau",
index cfc4e4d..dbe5d53 100644 (file)
        "yourpasswordagain": "تکرار گذرواژه:",
        "createacct-yourpasswordagain": "گذرواژه را دوباره وارد کنید",
        "createacct-yourpasswordagain-ph": "گذرواژه را وارد کنید برای بار دوم",
-       "remembermypassword": "گذرواژه را (تا حداکثر $1 {{PLURAL:$1|روز|روز}}) در این رایانه به خاطر بسپار",
        "userlogin-remembermypassword": "من را واردشده نگه‌دار",
        "userlogin-signwithsecure": "از ورود امن استفاده کنید",
        "cannotloginnow-title": "الان امکان وررود به سامانه نیست",
        "minoredit": "این ویرایش، جزئی است",
        "watchthis": "پی‌گیری این صفحه",
        "savearticle": "صفحه ذخیره شود",
-       "savechanges": "ذخیرهٔ تغییرات",
-       "publishpage": "ذخÛ\8cرÙ\87 صفحه",
-       "publishchanges": "ذخÛ\8cرÙ\87 تغییرات",
+       "savechanges": "ذخیره کردن تغییرات",
+       "publishpage": "اÙ\86تشار صفحه",
+       "publishchanges": "اÙ\86تشار تغییرات",
        "preview": "پیش‌نمایش",
        "showpreview": "پیش‌نمایش",
        "showdiff": "نمایش تغییرات",
        "right-block": "قطع دسترسی ویرایشی دیگر کاربران",
        "right-blockemail": "قطع دسترسی دیگر کاربران برای ارسال ایمیل",
        "right-hideuser": "قطع دسترسی کاربر و پنهان کردن آن از دید عموم",
-       "right-ipblock-exempt": "تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
+       "right-ipblock-exempt": "تأثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
        "right-unblockself": "بازکردن دسترسی خود",
        "right-protect": "تغییر میزان محافظت صفحات و ویرایش صفحات محافظت‌شده آبشاری",
        "right-editprotected": "ویرایش صفحه‌های محافظت‌شده به عنوان «{{int:protect-level-sysop}}»",
        "grant-group-high-volume": "انجام فعالیت‌های حجم بالا",
        "grant-group-customization": "سفارشی‌سازی و تنظیمات",
        "grant-group-administration": "انجام اقدامات مدیریتی",
+       "grant-group-private-information": "دسترسی به اطلاعات محرمانهٔ خودتان",
        "grant-group-other": "فعالیت‌های متفرقه",
        "grant-blockusers": "بستن و باز کردن کاربرها",
        "grant-createaccount": "ایجاد حساب‌های کاربری",
        "grant-highvolume": "ویرایش با حجم بالا",
        "grant-oversight": "پنهان کردن ویرایش‌ها",
        "grant-patrol": "تغییرات گشت صفحات",
+       "grant-privateinfo": "دسترسی به اطلاعات محرمانه",
        "grant-protect": "حفاظت و عدم حفاظت صفحات",
        "grant-rollback": "واگردانی  تغییرات صفحات",
        "grant-sendemail": "ارسال ایمیل به دیگر کاربران",
        "action-applychangetags": "اعمال برچسب بر روی تغییرات شما",
        "action-changetags": "افزودن یا حذف برچسب قراردادی بر روی نسخه یا سیاهه ورودی‌ها",
        "action-deletechangetags": "حذف برچسب‌ها از پایگاه داده",
+       "action-purge": "خالی‌کردن میانگیر این صفحه",
        "nchanges": "$1 تغییر",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|از آخرین بازدید}}",
        "enhancedrc-history": "تاریخچه",
        "uploadstash-errclear": "پاک‌کردن پرونده‌ها ناموفق بود.",
        "uploadstash-refresh": "تازه کردن فهرست پرونده‌ها",
        "uploadstash-thumbnail": "نمایش بندانگشتی",
+       "uploadstash-exception": "ناتوان از ذخیره کردن بارگذاری در نهانگاه ($1): ''$2''.",
        "invalid-chunk-offset": "جابجایی نامعتبر قطعه",
        "img-auth-accessdenied": "منع دسترسی",
        "img-auth-nopathinfo": "PATH_INFO موجود نیست.\nسرور شما برای ردکردن این مقدار تنظیم نشده‌است.\nممکن است مبتنی بر سی‌جی‌آی باشد و از img_auth پشتیبانی نکند.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization را ببینید.",
        "allinnamespace": "همهٔ صفحات (فضای نام $1)",
        "allpagessubmit": "برو",
        "allpagesprefix": "نمایش صفحه‌های دارای پیشوند:",
-       "allpagesbadtitle": "عÙ\86Ù\88اÙ\86 ØµÙ\81Ø­Ù\87Ù\94 Ø¯Ø§Ø¯Ù\87â\80\8cشدÙ\87 Ù\86اÙ\85عتبر Ø§Ø³Øª Û\8cا Ø§Û\8cÙ\86Ú©Ù\87 Ø¯Ø§Ø±Ø§Û\8c Ù¾Û\8cØ´Ù\88Ù\86دÛ\8c Ø¨Û\8cÙ\86â\80\8cزباÙ\86Û\8c Û\8cا Ø¨Û\8cÙ\86â\80\8cÙ\88Û\8cÚ©Û\8câ\80\8cاÛ\8c Ø§Ø³Øª. Ù\85Ù\85Ú©Ù\86 Ø§Ø³Øª Ù\86Ù\88Û\8cسÙ\87â\80\8cÙ\87اÛ\8cÛ\8c Ø¨Ø¯Ø§Ø±Ø¯ Ú©Ù\87 Ù\86Ù\85Û\8câ\80\8cتÙ\88اÙ\86 Ø§Ø² Ø¢Ù\86Ù\87ا در عنوان صفحات استفاده کرد.",
+       "allpagesbadtitle": "عÙ\86Ù\88اÙ\86 ØµÙ\81Ø­Ù\87Ù\94 Ø¯Ø§Ø¯Ù\87â\80\8cشدÙ\87 Ù\86اÙ\85عتبر Ø§Ø³Øª Û\8cا Ø§Û\8cÙ\86Ú©Ù\87 Ø¯Ø§Ø±Ø§Û\8c Ù¾Û\8cØ´Ù\88Ù\86دÛ\8c Ø¨Û\8cÙ\86â\80\8cزباÙ\86Û\8c Û\8cا Ø¨Û\8cÙ\86â\80\8cÙ\88Û\8cÚ©Û\8câ\80\8cاÛ\8c Ø§Ø³Øª. Ù\85Ù\85Ú©Ù\86 Ø§Ø³Øª Ù\86Ù\88Û\8cسÙ\87â\80\8cÙ\87اÛ\8cÛ\8c Ø¯Ø§Ø´ØªÙ\87 Û\8cاشد Ú©Ù\87 Ø§Ø² Ø¢Ù\86Ù\87ا Ù\86Ù\85Û\8câ\80\8cتÙ\88اÙ\86 در عنوان صفحات استفاده کرد.",
        "allpages-bad-ns": "{{SITENAME}} دارای فضای نام «$1» نیست.",
        "allpages-hide-redirects": "پنهان‌کردن تغییرمسیرها",
        "cachedspecial-viewing-cached-ttl": "شما در حال مشاهدهٔ نسخه‌ای از این صفحه که در میانگیر قرار دارد هستید که ممکن است برای $1 قبل باشد.",
        "watchnologin": "به سامانه وارد نشده‌اید",
        "addwatch": "افزودن به فهرست پی‌گیری",
        "addedwatchtext": "«[[:$1]]» و صفحهٔ بحث آن به [[Special:Watchlist|فهرست پی‌گیری‌های]] شما اضافه شد.",
+       "addedwatchtext-talk": "''[[:$1]]'' و صفحهٔ مرتبط به آن به [[Special:Watchlist|فهرست پی‌گیری]] شما افزوده شدند.",
        "addedwatchtext-short": "صفحه \" $1 \" به فهرست پیگیریهای خود اضافه شده است.",
        "removewatch": "حذف از فهرست پی‌گیری",
        "removedwatchtext": "صفحهٔ «[[:$1]]» و صفحهٔ بحث آن از [[Special:Watchlist|فهرست پی‌گیری‌های شما]] برداشته شد.",
+       "removedwatchtext-talk": "''[[:$1]]'' و صفحهٔ مرتبط به آن از [[Special:Watchlist|فهرست پی‌گیری]] شما حذف شدند.",
        "removedwatchtext-short": "صفحهٔ \"$1\" از فهرست پیگیری‌های شما حذف شده‌است.",
        "watch": "پی‌گیری",
        "watchthispage": "پی‌گیری این صفحه",
        "rollbacklinkcount-morethan": "واگردانی بیش از $1 ویرایش",
        "rollbackfailed": "واگردانی نشد",
        "rollback-missingparam": "فقدان پارامترهای ضروری در درخواست",
+       "rollback-missingrevision": "ناتوان از بارگیری اطلاعات نسخه.",
        "cantrollback": "نمی‌توان ویرایش را واگرداند؛\nآخرین مشارکت‌کننده تنها مؤلف این مقاله است.",
        "alreadyrolled": "واگردانی آخرین ویرایش [[:$1]] توسط [[User:$2|$2]] ([[User talk:$2|بحث]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ممکن نیست؛\nپیش از این شخص دیگری مقاله را ویرایش یا واگردانی کرده‌است.\n\nآخرین ویرایش توسط [[User:$3|$3]] ([[User talk:$3|بحث]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) انجام شده‌است.",
        "editcomment": "خلاصهٔ ویرایش این بود:  <em>«$1»</em>.",
        "undeletehistorynoadmin": "این مقاله حذف شده‌است.\nدلیل حذف این مقاله به همراه مشخصات کاربرانی که قبل از حذف این صفحه را ویرایش کرده‌اند، در خلاصهٔ زیر آمده‌است.\nمتن واقعی این ویرایش‌های حذف شده فقط در دسترس مدیران است.",
        "undelete-revision": "نسخهٔ حذف شدهٔ $1 (به تاریخ $4 ساعت $5) توسط $3:",
        "undeleterevision-missing": "نسخه نامعتبر یا مفقود است.\nممکن است پیوندتان نادرست باشد یا اینکه نسخه از بایگانی حذف یا بازیابی شده باشد .",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|یک نسخه|$1 نسخه}} احیا نشد، چون <code>rev_id</code> آن {{PLURAL:$1|نسخه|نسخه‌ها}} از پیش مورد استفاده بود.",
        "undelete-nodiff": "نسخهٔ قدیمی‌تری یافت نشد.",
        "undeletebtn": "احیا",
        "undeletelink": "نمایش/احیا",
        "undeletedrevisions": "$1 نسخه احیا {{PLURAL:$1|شد}}",
        "undeletedrevisions-files": "$1 نسخه و $2 پرونده احیا {{PLURAL:$1|شد|شدند}}.",
        "undeletedfiles": "$1 پرونده احیا {{PLURAL:$1|شد|شدند}}.",
-       "cannotundelete": "احیا ناموفق بود:\n$1",
+       "cannotundelete": "تÙ\85اÙ\85 Û\8cا Ø¨Ø®Ø´Û\8c Ø§Ø² Ø§Ø­Û\8cا Ù\86اÙ\85Ù\88Ù\81Ù\82 Ø¨Ù\88د:\n$1",
        "undeletedpage": "'''$1 احیا شد'''\n\nبرای دیدن سیاههٔ حذف‌ها و احیاهای اخیر به  [[Special:Log/delete|سیاههٔ حذف]] رجوع کنید.",
        "undelete-header": "برای دیدن صفحه‌های حذف‌شدهٔ اخیر [[Special:Log/delete|سیاههٔ حذف]] را ببینید.",
        "undelete-search-title": "جستجوی صفحه‌های حذف‌شده",
        "sp-contributions-newbies-sub": "برای تازه‌کاران",
        "sp-contributions-newbies-title": "مشارکت‌های کاربری برای حساب‌های تازه‌کار",
        "sp-contributions-blocklog": "سیاههٔ بسته‌شدن‌ها",
-       "sp-contributions-suppresslog": "مشارکت‌های فرونشانی‌شده",
-       "sp-contributions-deleted": "مشارکت‌های حذف‌شدهٔ کاربر",
+       "sp-contributions-suppresslog": "مشارکت‌های فرونشانی‌شده {{GENDER:$1|کاربر}}",
+       "sp-contributions-deleted": "مشارکت‌های حذف‌شدهٔ {{GENDER:$1|کاربر}}",
        "sp-contributions-uploads": "بارگذاری‌ها",
        "sp-contributions-logs": "سیاهه‌ها",
        "sp-contributions-talk": "بحث",
        "allmessagesdefault": "متن پیش‌فرض پیغام",
        "allmessagescurrent": "متن کنونی پیغام",
        "allmessagestext": "این فهرستی از پیغام‌های سامانه‌ای موجود در فضای نام مدیاویکی است.\nچنانچه مایل به مشارکت در محلی‌سازی مدیاویکی هستید لطفاً [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation محلی‌سازی مدیاویکی] و [https://translatewiki.net translatewiki.net] را ببینید.",
-       "allmessagesnotsupportedDB": "این صفحه نمی‌تواند استفاده شود به این دلیل که <bdi>'''$wgUseDatabaseMessages'''</bdi> غیرفعال شده‌است.",
+       "allmessagesnotsupportedDB": "این صفحه نمی‌تواند استفاده شود به این دلیل که <strong>‎$wgUseDatabaseMessages</strong> غیرفعال شده است.",
        "allmessages-filter-legend": "پالایه",
        "allmessages-filter": "پالودن بر اساس وضعیت شخصی‌سازی:",
        "allmessages-filter-unmodified": "تغییر نیافته",
        "linkaccounts-submit": "پیوند حساب کاربری",
        "unlinkaccounts": "حذف پیوند حساب کاربری",
        "unlinkaccounts-success": "پیوند کاربری بدون پیوند شد.",
-       "authenticationdatachange-ignored": "به تغيير اطلاعات احراز هويت پرداخته نشد. آیا ممکن است که هيچ مهيا کننده‌ای برای اين کار تنظيم نشده باشد؟"
+       "authenticationdatachange-ignored": "به تغيير اطلاعات احراز هويت پرداخته نشد. آیا ممکن است که هيچ مهيا کننده‌ای برای اين کار تنظيم نشده باشد؟",
+       "userjsispublic": "لطفاً توجه کنید: زیرصفحه‌های جاوااسکریپت نباید حاوی اطلاعات محرمانه باشند چون توسط دیگران قابل مشاهده هستند.",
+       "usercssispublic": "لطفاً توجه کنید: زیرصفحه‌های سی‌اس‌اس نباید حاوی اطلاعات محرمانه باشند چون توسط دیگران قابل مشاهده هستند."
 }
index 74de0d5..e417ee4 100644 (file)
@@ -51,7 +51,8 @@
                        "Mikahama",
                        "01miki10",
                        "Matma Rex",
-                       "BiscuitMan"
+                       "BiscuitMan",
+                       "Alluk."
                ]
        },
        "tog-underline": "Linkkien alleviivaus:",
        "yourpasswordagain": "Salasana uudelleen:",
        "createacct-yourpasswordagain": "Vahvista salasana",
        "createacct-yourpasswordagain-ph": "Kirjoita salasana uudelleen",
-       "remembermypassword": "Muista kirjautumiseni tässä selaimessa (enintään $1 {{PLURAL:$1|päivä|päivää}})",
        "userlogin-remembermypassword": "Pidä minut kirjautuneena",
        "userlogin-signwithsecure": "Käytä salattua yhteyttä",
+       "cannotlogin-title": "Kirjautuminen ei onnistu",
+       "cannotlogin-text": "Kirjautuminen ei ole mahdollista.",
        "cannotloginnow-title": "Nyt ei voi kirjautua sisään",
        "cannotloginnow-text": "Kirjautuminen sisään ei ole mahdollista käytettäessä $1.",
+       "cannotcreateaccount-title": "Tunnuksia ei voida luoda",
+       "cannotcreateaccount-text": "Suora tunnuksen luominen ei ole käytössä tässä wikissä.",
        "yourdomainname": "Verkkonimi:",
        "password-change-forbidden": "Et voi muuttaa salasanoja tässä wikissä.",
        "externaldberror": "Tapahtui virhe ulkoisen autentikointitietokannan käytössä tai sinulla ei ole lupaa päivittää tunnustasi.",
        "userlogin-createanother": "Luo toinen käyttäjätunnus",
        "createacct-emailrequired": "Sähköpostiosoite",
        "createacct-emailoptional": "Sähköpostiosoite (vapaaehtoinen)",
-       "createacct-email-ph": "Anna sähköpostiosoitteesi",
+       "createacct-email-ph": "Kirjoita sähköpostiosoitteesi",
        "createacct-another-email-ph": "Lisää sähköpostiosoite",
        "createaccountmail": "Käytä satunnaista väliaikaissalasanaa ja lähetä se alla olevaan sähköpostiosoitteeseen",
        "createaccountmail-help": "Voidaan käyttää luomaan tunnus toiselle käyttäjälle ilman salasanan tietämistä.",
        "passwordtoopopular": "Tavanomaisen kaltaisia salasanoja ei saa käyttää. Valitse parempi ja yksilöllisempi salasana.",
        "password-name-match": "Salasanasi täytyy olla eri kuin käyttäjätunnuksesi.",
        "password-login-forbidden": "Tämän käyttäjänimen ja salasanan käyttö on estetty.",
-       "mailmypassword": "Uudista salasana",
+       "mailmypassword": "Hanki uusi salasana",
        "passwordremindertitle": "Uusi väliaikainen salasana {{GRAMMAR:elative|{{SITENAME}}}}",
        "passwordremindertext": "Joku IP-osoitteesta $1 pyysi {{GRAMMAR:partitive|{{SITENAME}}}} ($4) lähettämään uuden salasanan. Väliaikainen salasana käyttäjälle $2 on nyt $3. Kirjaudu sisään ja vaihda salasana. Väliaikainen salasana vanhenee {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\n\nJos joku muu on tehnyt tämän pyynnön, tai jos olet muistanut salasanasi ja et halua vaihtaa sitä, voit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.",
        "noemail": "Käyttäjälle $1 ei ole määritelty sähköpostiosoitetta.",
        "botpasswords-label-update": "Päivitä",
        "botpasswords-label-cancel": "Peru",
        "botpasswords-label-delete": "Poista",
-       "botpasswords-label-resetpassword": "Uudista salasana",
+       "botpasswords-label-resetpassword": "Hanki uusi salasana",
        "botpasswords-label-grants": "Valittavissa olevat toimintaoikeudet:",
        "botpasswords-label-restrictions": "Käyttörajoitukset:",
        "botpasswords-label-grants-column": "Myönnetään",
        "resetpass-expired": "Salasanasi on vanhentunut. Valitse uusi salasana, jotta pääset kirjautumaan sisään.",
        "resetpass-expired-soft": "Salasanasi on vanhentunut ja se pitää uudistaa. Valitse uusi salasana nyt tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit uudistaa salasanan myöhemmin.",
        "resetpass-validity-soft": "Salasanasi ei ole kelvollinen: $1\n\nValitse nyt uusi salasana tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit vaihtaa sen myöhemmin.",
-       "passwordreset": "Salasanan uudistus",
+       "passwordreset": "Salasanan palauttaminen",
        "passwordreset-text-one": "Täytä tämä lomake uudistaaksesi salasanasi.",
-       "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi kentistä, jotta saat väliaikaisen salasanan sähköpostitse.}}",
+       "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi seuraavista kentistä, jolloin saat väliaikaisen salasanan sähköpostitse.}}",
        "passwordreset-disabled": "Salasanojen uudistaminen ei ole mahdollista tässä wikissä.",
        "passwordreset-emaildisabled": "Sähköpostitoiminnot on poistettu käytöstä tässä wikissä.",
        "passwordreset-username": "Käyttäjätunnus:",
        "tags-actions-header": "Toiminnot",
        "tags-active-yes": "Kyllä",
        "tags-active-no": "Ei",
-       "tags-source-extension": "Laajennuksen määrittelemä",
+       "tags-source-extension": "Ohjelmiston määrittelemä",
        "tags-source-manual": "Käyttäjien ja bottien käsin asettama",
        "tags-source-none": "Ei enää käytössä",
        "tags-edit": "muokkaa",
index 0f1f6d8..21f2e7c 100644 (file)
                        "Trial",
                        "Matma Rex",
                        "Dcausse",
-                       "Lucas"
+                       "Lucas",
+                       "Mabroukb",
+                       "Pymouss"
                ]
        },
        "tog-underline": "Soulignement des liens :",
-       "tog-hideminor": "Masquer les modifications mineures dans les changements récents",
+       "tog-hideminor": "Masquer les modifications mineures dans les modifications récentes",
        "tog-hidepatrolled": "Masquer les modifications relues dans les modifications récentes",
        "tog-newpageshidepatrolled": "Masquer les pages relues dans la liste des nouvelles pages",
        "tog-hidecategorization": "Masquer la catégorisation des pages",
        "yourpasswordagain": "Confirmez le mot de passe :",
        "createacct-yourpasswordagain": "Confirmez le mot de passe",
        "createacct-yourpasswordagain-ph": "Entrez à nouveau le mot de passe",
-       "remembermypassword": "Mémoriser mes données de connection avec ce navigateur (durant au maximum $1 jour{{PLURAL:$1||s}})",
        "userlogin-remembermypassword": "Garder ma session active",
        "userlogin-signwithsecure": "Utiliser une connexion sécurisée",
+       "cannotlogin-title": "Impossible de se connecter",
+       "cannotlogin-text": "La connexion n’est pas possible.",
        "cannotloginnow-title": "Impossible de se connecter maintenant",
        "cannotloginnow-text": "La connexion n’est pas possible en utilisant $1.",
+       "cannotcreateaccount-title": "Création de comptes impossible",
+       "cannotcreateaccount-text": "La création directe de comptes utilisateurs n’est pas activée sur ce wiki.",
        "yourdomainname": "Votre domaine :",
        "password-change-forbidden": "Vous ne pouvez pas modifier les mots de passe sur ce wiki.",
        "externaldberror": "Soit une erreur s’est produite sur la base de données d’authentification, soit vous n’êtes pas autorisé à mettre à jour votre compte externe.",
        "botpasswords-updated-body": "Le mot de passe pour le robot « $1 » de l'utilisateur « $2 » a été mis à jour.",
        "botpasswords-deleted-title": "Mot de passe de robots supprimé",
        "botpasswords-deleted-body": "Le mot de passe pour le robot « $1 » de l'utilisateur « $2 » a été supprimé.",
-       "botpasswords-newpassword": "Le nouveau mot de passe pour se connecter avec <strong>$1</strong> est <strong>$2</strong>. <em>Veuillez l’enregistrer pour y faire référence ultérieurement.</em>",
+       "botpasswords-newpassword": "Le nouveau mot de passe pour se connecter à <strong>$1</strong> est <strong>$2</strong>. <em>Veuillez l’enregistrer pour y faire référence ultérieurement.</em><br> (Pour les anciens robots qui nécessitent que le nom fourni à la connexion soit le même que le nom d'utilisateur éventuel, vous pouvez aussi utiliser  <strong>$3</strong> comme nom d'utilisateur et <strong>$4</strong> comme mot de passe).",
        "botpasswords-no-provider": "BotPasswordsSessionProvider n’est pas disponible.",
        "botpasswords-restriction-failed": "Les restrictions de mot de passe de robots empêchent cette connexion.",
        "botpasswords-invalid-name": "Le nom d’utilisateur spécifié ne contient pas de séparateur de mot de passe de robots (« $1 »).",
        "previewerrortext": "Une erreur s’est produite lors de la tentative de prévisualisation de vos modifications.",
        "blockedtitle": "L’utilisateur est bloqué.",
        "blockedtext": "'''Votre compte utilisateur ou votre adresse IP a été bloqué.'''\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : ''$2''.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
-       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:''$2''\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
+       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:<em>$2:</em>\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
        "blockednoreason": "aucune raison donnée",
        "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
        "confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].",
        "missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique obsolète vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "userpage-userdoesnotexist": "Le compte utilisateur « <nowiki>$1</nowiki> » n’est pas enregistré. Veuillez vérifier que vous voulez créer cette page.",
        "userpage-userdoesnotexist-view": "Le compte utilisateur « $1 » n'est pas enregistré.",
-       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d’information :",
+       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
        "clearyourcache": "<strong>Note :</strong> après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* <strong>Firefox / Safari :</strong> maintenez la touche <em>Maj</em> (<em>Shift</em>) en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> sur un Mac) \n* <strong>Google Chrome :</strong> appuyez sur <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> sur un Mac) \n* <strong>Internet Explorer :</strong> maintenez la touche <em>Ctrl</em> en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> \n* <strong>Opera :</strong> allez dans <em>Menu → Settings</em> (<em>Opera → Préférences</em> sur un Mac) et ensuite à <em>Confidentialité & sécurité → Effacer les données d’exploration → Images et fichiers en cache</em>.",
        "usercssyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l’enregistrer.",
        "userjsyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l’enregistrer.",
        "explainconflict": "Cette page a été changée après que vous ayez commencé à la modifier.\nLa zone de modification supérieure contient le texte tel qu’il est actuellement enregistré dans la base de données.\nVos modifications apparaissent dans la zone de modification inférieure.\nVous allez devoir fusionner vos modifications dans le texte existant.\n<strong>Seul</strong> le texte de la zone supérieure sera sauvegardé si vous cliquez sur « {{int:savearticle}} ».",
        "yourtext": "Votre texte",
        "storedversion": "La version enregistrée",
-       "nonunicodebrowser": "<strong>Attention : votre navigateur ne supporte pas l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII  sous forme hexadécimale dans la boîte de modification.",
+       "nonunicodebrowser": "<strong>Attention : votre navigateur ne prend pas en charge l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII sous forme hexadécimale dans la boîte de modification.",
        "editingold": "<strong>Attention : vous êtes en train de modifier une ancienne version de cette page.</strong>\nSi vous la publiez, toutes les modifications effectuées depuis cette version seront perdues.",
        "yourdiff": "Différences",
        "copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). \nSi vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br /> \nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public ou d’une ressource libre similaire. \n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
        "invalid-content-data": "Données du contenu non valides",
        "content-not-allowed-here": "Le contenu « $1 » n’est pas autorisé sur la page [[$2]]",
        "editwarning-warning": "Quitter cette page vous fera perdre toutes les modifications que vous avez faites.\nSi vous êtes connecté{{GENDER:||e}}, vous pouvez désactiver cet avertissement dans la section « {{int:prefs-editing}} » de vos préférences.",
+       "editpage-invalidcontentmodel-title": "Modèle de contenu non pris en charge",
+       "editpage-invalidcontentmodel-text": "Le modèle de contenu \"$1\" n'est pas pris en charge.",
        "editpage-notsupportedcontentformat-title": "Format de contenu non pris en charge",
        "editpage-notsupportedcontentformat-text": "Le format de contenu $1 n'est pas pris en charge par le modèle de contenu $2 .",
        "content-model-wikitext": "wikitexte",
        "last": "diff",
        "page_first": "première",
        "page_last": "dernière",
-       "histlegend": "Diff de sélection: cochez les boîtes radio des révisions à comparer et appuyez sur entrée ou sur le bouton en bas.<br />\nLégende: <strong>({{int:cur}})</strong> = différence avec la dernière révision, <strong>({{int:last}})</strong> = différence avec la précédente révision, <strong>{{int:minoreditletter}}</strong> = modification mineure.",
+       "histlegend": "Diff de sélection : cochez les boîtes radio des versions à comparer et appuyez sur entrée ou sur le bouton en bas.<br />\nLégende: <strong>({{int:cur}})</strong> = différence avec la dernière version, <strong>({{int:last}})</strong> = différence avec la précédente version, <strong>{{int:minoreditletter}}</strong> = modification mineure.",
        "history-fieldset-title": "Naviguer dans l’historique",
        "history-show-deleted": "Supprimés seulement",
        "histfirst": "les plus anciennes",
        "history-feed-description": "Historique des versions pour cette page sur le wiki",
        "history-feed-item-nocomment": "$1 le $2",
        "history-feed-empty": "La page demandée n'existe pas.\nElle a peut-être été effacée ou renommée.\nEssayez de [[Special:Search|rechercher sur le wiki]] pour trouver de nouvelles pages en rapport avec le sujet.",
-       "history-edit-tags": "Modifier les balises des révisions sélectionnées",
+       "history-edit-tags": "Modifier les balises des versions sélectionnées",
        "rev-deleted-comment": "(résumé de modification retiré)",
        "rev-deleted-user": "(nom d'utilisateur retiré)",
        "rev-deleted-event": "(détails de l’historique retirés)",
        "prefs-emailconfirm-label": "Confirmation du courriel :",
        "youremail": "Courriel :",
        "username": "{{GENDER:$1|Nom d'utilisateur|Nom d'utilisatrice}} :",
-       "prefs-memberingroups": "{{GENDER:$2|Membre}} {{PLURAL:$1|du groupe|des groupes}} :",
+       "prefs-memberingroups": "{{GENDER:$2|Membre}} {{PLURAL:$1|du groupe|des groupes}}:",
        "prefs-registration": "Date d'inscription :",
        "yourrealname": "Nom réel :",
        "yourlanguage": "Langue :",
        "large-file": "Les fichiers importés ne devraient pas dépasser $1 ; \nce fichier fait $2.",
        "largefileserver": "La taille de ce fichier est supérieure au maximum autorisé par le serveur.",
        "emptyfile": "Le fichier que vous voulez importer semble vide.\nCeci peut être dû à une erreur dans le nom du fichier.\nVeuillez vérifier que vous désirez vraiment importer ce fichier.",
-       "windows-nonascii-filename": "Ce wiki ne supporte pas les noms de fichiers avec des caractères spéciaux.",
+       "windows-nonascii-filename": "Ce wiki ne prend pas en charge les noms de fichiers avec des caractères spéciaux.",
        "fileexists": "Un fichier existe déjà sous ce nom.\nMerci de vérifier <strong>[[:$1]]</strong> si vous n'êtes pas certain{{GENDER:||e|}} de vouloir le remplacer.\n[[$1|thumb]]",
        "filepageexists": "La page de description pour ce fichier a déjà été créée ici <strong>[[:$1]]</strong>, mais aucun fichier n'existe actuellement sous ce nom.\nLe résumé que vous allez spécifier n'apparaîtra pas sur la page de description.\nPour que ce soit le cas, vous devrez modifier manuellement la page. \n[[$1|thumb]]",
        "fileexists-extension": "Un fichier existe avec un nom proche : [[$2|thumb]]\n* Nom du fichier à importer : <strong>[[:$1]]</strong>\n* Nom du fichier existant : <strong>[[:$2]]</strong>\nPeut-être voulez-vous utiliser un nom plus explicite ?",
        "file-thumbnail-no": "Le nom du fichier commence par <strong>$1</strong>.\nIl est possible qu'il s'agisse d'une version réduite <em>(vignette)</em>.\nSi vous disposez du fichier en haute résolution, importez-le, sinon veuillez modifier son nom.",
        "fileexists-forbidden": "Un fichier avec ce nom existe déjà et ne peut pas être écrasé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. \n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un fichier portant ce nom existe déjà dans le dépôt de fichiers partagé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. \n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Le fichier téléchargé est une copie exacte de la version actuelle de <strong>[[:$1]]</strong>",
+       "fileexists-duplicate-version": "Le fichier téléversé est une copie exacte {{PLURAL:$2|d'une version précédente|de versions précédentes}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ce fichier est un doublon {{PLURAL:$1|du fichier suivant|des fichiers suivants}} :",
        "file-deleted-duplicate": "Un fichier identique à celui-ci ([[:$1]]) a déjà été supprimé. \nVous devriez vérifier le journal des suppressions de ce fichier avant de l'importer à nouveau.",
        "file-deleted-duplicate-notitle": "Un fichier identique à ce fichier a déjà été supprimé ainsi que le titre. \nVous devriez demander à quelqu'un la possibilité de vérifier le journal de ce fichier supprimé afin d'examiner la situation  avant de l'importer à nouveau.",
        "upload-form-label-not-own-work-local-generic-local": "Vous pouvez aussi essayer [[Special:Upload|la page de téléchargement par défaut]].",
        "upload-form-label-own-work-message-generic-foreign": "Je comprends que je téléverse ce fichier vers un dépôt partagé. Je confirme agir en accord avec les conditions d’utilisation et les règles relatives aux licences de celui-ci.",
        "upload-form-label-not-own-work-message-generic-foreign": "Si vous n’êtes pas en mesure de téléverser ce fichier de façon conforme aux règles de ce dépôt partagé, veuillez fermer cette boîte de dialogue et essayer une autre méthode.",
-       "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si leur règles de site autorisent le téléversement du fichier.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si leurs règles autorisent le téléversement du fichier.",
        "backend-fail-stream": "Impossible de lire le fichier \"$1\".",
        "backend-fail-backup": "Impossible de sauvegarder le fichier \"$1\".",
        "backend-fail-notexists": "Le fichier $1 n’existe pas.",
        "zip-file-open-error": "Une erreur s'est produite lors de l'ouverture du fichier ZIP pour contrôle.",
        "zip-wrong-format": "Le fichier spécifié n'est pas une archive ZIP.",
        "zip-bad": "Le fichier est une archive ZIP corrompue ou illisible.\nIl ne peut pas être correctement vérifié pour la sécurité.",
-       "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non supportées par MediaWiki. \nSa sécurité ne peut pas être correctement vérifiée.",
+       "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non prises en charge par MediaWiki.\nSa sécurité ne peut pas être correctement vérifiée.",
        "uploadstash": "Cache d’import",
        "uploadstash-summary": "Cette page donne accès aux fichiers qui sont importés (ou en cours d’importation), mais ne sont pas encore publiés dans le wiki. Ces fichiers ne sont pas encore visibles, sauf pour l’utilisateur qui les a importés.",
        "uploadstash-clear": "Effacer les fichiers en cache d'import",
        "uploadstash-exception": "Impossible de stocker le téléchargement dans la réserve ($1) : « $2 ».",
        "invalid-chunk-offset": "Offset de segment non valide",
        "img-auth-accessdenied": "Accès refusé",
-       "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n'est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne supporte pas img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
+       "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n’est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne prend pas en charge img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Le chemin demandé n'est pas le répertoire d'import configuré.",
        "img-auth-badtitle": "Impossible de construire un titre valide à partir de « $1 ».",
        "img-auth-nologinnWL": "Vous n'êtes pas connecté et « $1 » n'est pas dans la liste blanche.",
        "filerevert-submit": "Rétablir",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> a été rétabli à [$4 la version du $2 à $3].",
        "filerevert-badversion": "Il n'y a pas localement de version antérieure du fichier qui porte la date indiquée.",
+       "filerevert-identical": "La version actuelle du fichier est déjà identique à celle sélectionnée.",
        "filedelete": "Supprimer $1",
        "filedelete-legend": "Supprimer le fichier",
        "filedelete-intro": "Vous êtes sur le point de supprimer <strong>[[Media:$1|$1]]</strong> ainsi que tout son historique.",
        "rollbacklinkcount-morethan": "révoquer plus de $1 {{PLURAL:$1|modification|modifications}}",
        "rollbackfailed": "La révocation a échoué",
        "rollback-missingparam": "Paramètres nécessaires à la demande manquants.",
+       "rollback-missingrevision": "Impossible de charger les données de la version.",
        "cantrollback": "Impossible de révoquer la modification ;\nle dernier contributeur est le seul auteur de cette page.",
        "alreadyrolled": "Impossible de révoquer la dernière modification de la page « [[:$1]] » effectuée par [[User:$2|$2]] ([[User talk:$2|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ;\nquelqu'un d'autre a déjà modifié ou révoqué la page.\n\nLa dernière modification de la page a été effectuée par [[User:$3|$3]] ([[User talk:$3|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le résumé de la modification était : <em>$1</em>.",
        "viewdeletedpage": "Voir les pages supprimées",
        "undeletepagetext": "{{PLURAL:$1|La page suivante a été supprimée et se trouve|Les pages suivantes ont été supprimées et se trouvent}} dans la base de données archive, d’où {{PLURAL:$1|elle peut|elles peuvent}} encore être restaurée{{PLURAL:$1||s}}.\nL’archive peut être nettoyée périodiquement.",
        "undelete-fieldset-title": "Restaurer les versions",
-       "undeleteextrahelp": "Pour restaurer l’historique complet de cette page, laissez toutes les cases décochées et cliquez sur '''''Restaurer'''''.\nPour effectuer une restauration partielle, cochez les cases correspondant aux versions à rétablir, puis cliquez sur '''''Restaurer'''''.",
+       "undeleteextrahelp": "Pour restaurer l’historique complet de cette page, laissez toutes les cases décochées et cliquez sur <strong><em>{{int:undeletebtn}}</em></strong>.\nPour effectuer une restauration partielle, cochez les cases correspondant aux versions à rétablir, puis cliquez sur <strong><em>{{int:undeletebtn}}</em></strong>.",
        "undeleterevisions": "$1 {{PLURAL:$1|révision supprimée|révisions supprimées}}",
-       "undeletehistory": "Si vous restaurez la page, toutes les versions seront replacées dans l’historique.\nSi une nouvelle page avec le même nom a été créée depuis la suppression, les versions restaurées apparaîtront dans l’historique antérieur et la version courante ne sera pas automatiquement remplacée.",
+       "undeletehistory": "Si vous restaurez la page, toutes les versions seront replacées dans l’historique.\nSi une nouvelle page avec le même nom a été créée depuis la suppression, les versions restaurées s’inséreront dans l’historique antérieur.",
        "undeleterevdel": "La restauration ne sera pas effectuée si, au final, la version la plus récente de la page ou du fichier reste partiellement supprimée.\nDans de tels cas, vous devez décocher ou démasquer les versions effacées les plus récentes (en tête de liste).",
        "undeletehistorynoadmin": "Cette page a été supprimée.\nLe motif de la suppression est indiqué dans le résumé ci-dessous, avec les détails des utilisateurs qui ont modifié la page avant sa suppression.\nLe contenu effectif de ces versions supprimées n’est accessible qu’aux administrateurs.",
        "undelete-revision": "Version supprimée de $1 (version du $4 à $5) par $3 :",
        "undeletedrevisions": "$1 {{PLURAL:$1|version restaurée|versions restaurées}}",
        "undeletedrevisions-files": "$1 version{{PLURAL:$1||s}} et $2 fichier{{PLURAL:$2||s}} restauré{{PLURAL:$2||s}}",
        "undeletedfiles": "$1 {{PLURAL:$1|fichier restauré|fichiers restaurés}}",
-       "cannotundelete": "Certaines ou toutes les restitutions ont échoué:\n$1",
-       "undeletedpage": "'''La page $1 a été restaurée.'''\n\nConsultez le [[Special:Log/delete|journal des suppressions]] pour obtenir la liste des récentes suppressions et restaurations.",
+       "cannotundelete": "Certaines ou toutes les restaurations ont échoué :\n$1",
+       "undeletedpage": "<strong>La page $1 a été restaurée.</strong>\n\nConsultez le [[Special:Log/delete|journal des suppressions]] pour obtenir la liste des récentes suppressions et restaurations.",
        "undelete-header": "Consultez le [[Special:Log/delete|journal des suppressions]] pour lister les pages récemment supprimées.",
        "undelete-search-title": "Rechercher les pages supprimées",
-       "undelete-search-box": "Rechercher des pages supprimées",
+       "undelete-search-box": "Rechercher les pages supprimées",
        "undelete-search-prefix": "Montrer les pages commençant par :",
        "undelete-search-submit": "Rechercher",
        "undelete-no-results": "Aucune page correspondante n’a été trouvée dans les archives de suppression.",
        "sp-contributions-logs": "journaux",
        "sp-contributions-talk": "discuter",
        "sp-contributions-userrights": "gérer les droits",
-       "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. La dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
-       "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
+       "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. \nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
+       "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
        "sp-contributions-search": "Rechercher les contributions",
        "sp-contributions-username": "Adresse IP ou nom d'utilisateur :",
        "sp-contributions-toponly": "Ne montrer que les contributions qui sont les dernières des articles",
        "whatlinkshere": "Pages liées",
        "whatlinkshere-title": "Pages qui pointent vers « $1 »",
        "whatlinkshere-page": "Page :",
-       "linkshere": "Les pages ci-dessous contiennent un lien vers '''[[:$1]]''' :",
-       "nolinkshere": "Aucune page ne contient de lien vers '''[[:$1]]'''.",
-       "nolinkshere-ns": "Aucune page ne contient de lien vers '''[[:$1]]''' dans l'espace de noms choisi.",
+       "linkshere": "Les pages ci-dessous contiennent un lien vers <strong>[[:$1]]</strong> :",
+       "nolinkshere": "Aucune page ne contient de lien vers <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Aucune page ne contient de lien vers <strong>[[:$1]]</strong> dans l'espace de noms choisi.",
        "isredirect": "page de redirection",
        "istemplate": "inclusion",
        "isimage": "lien vers le fichier",
        "ipb-hardblock": "Empêcher les utilisateurs connectés de modifier en utilisant cette adresse IP",
        "ipbcreateaccount": "Empêcher la création de compte",
        "ipbemailban": "Empêcher l'utilisateur d'envoyer des courriels",
-       "ipbenableautoblock": "Bloquer automatiquement la dernière adresse IP utilisée par l'utilisateur et toutes ses IPs ultérieures qu'il pourrait essayer",
-       "ipbsubmit": "Bloquer",
+       "ipbenableautoblock": "Bloquer automatiquement la dernière adresse IP utilisée par cet utilisateur et toutes ses IPs ultérieures qu'il pourrait essayer",
+       "ipbsubmit": "Bloquer cet utilisateur",
        "ipbother": "Autre durée :",
        "ipboptions": "2 heures:2 hours,1 jour:1 day,3 jours:3 days,1 semaine:1 week,2 semaines:2 weeks,1 mois:1 month,3 mois:3 months,6 mois:6 months,1 an:1 year,indéfiniment:infinite",
        "ipbhidename": "Masquer le nom d'utilisateur des modifications et des listes",
        "ipbwatchuser": "Suivre les pages utilisateur et de discussion de cet utilisateur",
        "ipb-disableusertalk": "Empêcher l'utilisateur de modifier sa page de discussion pendant le blocage",
-       "ipb-change-block": "Modifier les paramètres de blocage",
+       "ipb-change-block": "Bloquer à nouveau l'utilisateur avec ces paramètres",
        "ipb-confirm": "Confirmer le blocage",
        "badipaddress": "Adresse IP incorrecte",
        "blockipsuccesssub": "Blocage réussi",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] a été bloqué{{GENDER:$1||e}}.<br />\nConsultez la [[Special:BlockList|liste des blocages]] pour revoir les blocages.",
        "ipb-blockingself": "Vous êtes sur le point de bloquer votre propre compte ! Êtes-vous certain{{GENDER:||e}} de vouloir faire cela ?",
-       "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprime le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
+       "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprimera le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
        "ipb-confirmaction": "Si vous êtes sûr{{GENDER:||e}} de vraiment vouloir le faire, veuillez cocher le champ « {{int:ipb-confirm}} » en bas.",
        "ipb-edit-dropdown": "Modifier les motifs de blocage par défaut",
        "ipb-unblock-addr": "Débloquer $1",
        "ipb-blocklist": "Voir les blocages existants",
        "ipb-blocklist-contribs": "Contributions pour {{GENDER:$1|$1}}",
        "ipb-blocklist-duration-left": "$1 restant",
-       "unblockip": "Débloquer un utilisateur ou une adresse IP",
-       "unblockiptext": "Utilisez le formulaire ci-dessous pour redonner les droits d’écriture à une adresse IP ou un nom d’utilisateur.",
+       "unblockip": "Débloquer un utilisateur",
+       "unblockiptext": "Utilisez le formulaire ci-dessous pour redonner les droits d’écriture à une adresse IP ou un nom d’utilisateur qui a été bloqué auparavant.",
        "ipusubmit": "Supprimer ce blocage",
        "unblocked": "[[User:$1|$1]] a été débloqué{{GENDER:$1||e}}",
        "unblocked-range": "Le compte $1 a été débloqué",
        "blocklist-userblocks": "Masquer les blocages de comptes",
        "blocklist-tempblocks": "Masquer les blocages temporaires",
        "blocklist-addressblocks": "Masquer les blocages d’adresses IP uniques",
-       "blocklist-rangeblocks": "Masquer les blocs de portée",
+       "blocklist-rangeblocks": "Masquer les blocages sur intervalles",
        "blocklist-timestamp": "Date et heure",
        "blocklist-target": "Cible",
        "blocklist-expiry": "Date d’expiration",
        "noautoblockblock": "blocage automatique désactivé",
        "createaccountblock": "création de compte bloquée",
        "emailblock": "courriel bloqué",
-       "blocklist-nousertalk": "ne peut modifier sa propre page de discussion",
+       "blocklist-nousertalk": "ne peut pas modifier sa propre page de discussion",
        "ipblocklist-empty": "La liste des adresses IP bloquées est actuellement vide.",
        "ipblocklist-no-results": "L'adresse IP ou l'utilisateur demandé n'est pas bloqué.",
        "blocklink": "bloquer",
        "unblocklink": "débloquer",
        "change-blocklink": "modifier le blocage",
        "contribslink": "contributions",
-       "emaillink": "Envoyer un courriel",
+       "emaillink": "envoyer un courriel",
        "autoblocker": "Vous avez été bloqué automatiquement parce que votre adresse IP a été récemment utilisée par « [[User:$1|$1]] ».\nLe motif fourni pour le blocage de $1 est « $2 »",
        "blocklogpage": "Journal des blocages",
-       "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. Le journal des blocages est disponible ci-dessous :",
-       "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. Le journal des masquages est disponible ci-dessous :",
+       "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. \nLe journal des blocages est affiché ci-dessous pour référence :",
+       "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. \nLe journal des masquages est affiché ci-dessous pour référence :",
        "blocklogentry": "a bloqué [[$1]] ; expiration : $2 $3",
        "reblock-logentry": "a modifié les paramètres du blocage de [[$1]] avec une expiration au $2 $3",
        "blocklogtext": "Ceci est le journal des actions de blocage et déblocage d’utilisateurs.\nLes adresses IP automatiquement bloquées ne sont pas listées.\nConsultez la [[Special:BlockList|liste des blocages]] pour voir les bannissements et blocages effectivement en cours.",
        "ipb_cant_unblock": "Erreur : identifiant de blocage $1 non trouvé.\nIl est possible qu'un déblocage ait déjà été effectué.",
        "ipb_blocked_as_range": "Erreur : l'adresse IP $1 n'est pas bloquée directement et ne peut donc pas être débloquée.\nElle fait cependant partie de la plage $2 qui, elle, peut être débloquée.",
        "ip_range_invalid": "Plage d’adresses IP incorrecte.",
-       "ip_range_toolarge": "Les blocages de plages plus grandes que /$1 ne sont pas autorisées.",
+       "ip_range_toolarge": "Les plages de blocage plus grandes que /$1 ne sont pas autorisées.",
        "proxyblocker": "Bloqueur de mandataires",
        "proxyblockreason": "Votre adresse IP a été bloquée car il s'agit d'un mandataire ouvert.\nVeuillez contacter votre fournisseur d'accès Internet ou votre support technique et l'informer de ce sérieux problème de sécurité.",
        "sorbsreason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.",
        "sorbs_create_account_reason": "Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.\nVous ne pouvez pas créer un compte.",
        "xffblockreason": "Une adresse IP dans l'en-tête X-Forwarded-For, soit la vôtre ou celle d'un serveur proxy que vous utilisez, a été bloquée. La raison du blocage initial est : $1",
-       "cant-see-hidden-user": "L’utilisateur que vous tentez de bloquer a déjà été bloqué et masqué. N’ayant pas le droit ''hideuser'', vous ne pouvez pas voir ou modifier le blocage de cet utilisateur.",
+       "cant-see-hidden-user": "L’utilisateur que vous tentez de bloquer a déjà été bloqué et masqué. \nN’ayant pas le droit de masquer des utilisateurs, vous ne pouvez pas voir ou modifier le blocage de cet utilisateur.",
        "ipbblocked": "Vous ne pouvez pas bloquer ou débloquer d'autres utilisateurs, parce que vous êtes vous-même bloqué{{GENDER:||e}}.",
        "ipbnounblockself": "Vous n'êtes pas autorisé{{GENDER:||e}} à vous débloquer vous-même",
        "lockdb": "Verrouiller la base de données",
        "unlockdbsuccesssub": "Verrouillage de la base de données supprimé",
        "lockdbsuccesstext": "La base de données a été verrouillée.<br />\nN'oubliez pas de la [[Special:UnlockDB|déverrouiller]] lorsque vous aurez terminé votre opération de maintenance.",
        "unlockdbsuccesstext": "La base de données a été déverrouillée.",
-       "lockfilenotwritable": "Le fichier de verrouillage de la base de données n'est pas inscriptible.\nPour bloquer ou débloquer la base de données, il doit être accessible par le serveur web.",
+       "lockfilenotwritable": "Le fichier de verrouillage de la base de données n'est pas inscriptible.\nPour bloquer ou débloquer la base de données, il doit être accessible en écriture par le serveur web.",
        "databaselocked": "La base de données est déjà verrouillée.",
        "databasenotlocked": "La base de données n'est pas verrouillée.",
-       "lockedbyandtime": "(par $1 le $2 à $3)",
+       "lockedbyandtime": "(par {{GENDER:$1|$1}} le $2 à $3)",
        "move-page": "Renommer $1",
        "move-page-legend": "Renommer une page",
        "movepagetext": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom. L’ancien titre deviendra une page de redirection vers le nouveau titre. \nVous pouvez mettre à jour automatiquement les redirections qui pointent vers le titre original. \nSi vous choisissez de ne pas le faire, assurez-vous de vérifier toute [[Special:DoubleRedirects|double redirection]] ou [[Special:BrokenRedirects|redirection cassée]]. Vous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</strong> renommée s’il existe déjà une page portant le nouveau titre, sauf si cette dernière est une simple redirection avec un historique de modifications vierge. \nCela signifie que vous pouvez de nouveau renommer une page vers sa position d’origine si vous avez fait une erreur et que vous ne pouvez pas écraser une page existante.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’avoir compris les conséquences de votre démarche avant de continuer.",
        "movepagetext-noredirectfixer": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom.\nL’ancien titre deviendra une page de redirection vers le nouveau titre.\nVérifiez bien les [[Special:DoubleRedirects|doubles redirections]] ou les [[Special:BrokenRedirects|redirections cassées]].\nVous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</strong> déplacée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière a un historique de modifications vierge et est soit vide, soit une simple redirection. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné, et il est impossible d’écraser une page existante.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
        "movepagetalktext": "Si vous cochez cette case, la page de discussion associée sera automatiquement renommée, à moins qu’une page de discussion non vide existe déjà sous ce nouveau nom.\n\nDans ce cas, vous devrez renommer ou fusionner cette page de discussion manuellement si vous le désirez.",
-       "moveuserpage-warning": "'''Attention :''' Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur '''ne''' sera '''pas''' renommé.",
+       "moveuserpage-warning": "<strong>Attention :</strong> Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur <em>ne</em> sera <em>pas</em> renommé.",
        "movecategorypage-warning": "<strong>Avertissement :</strong> Vous êtes sur le point de renommer une page de catégorie. Veuillez noter que seule la catégorie sera renommée et <em>qu’aucune</em> des pages de l’ancienne catégorie ne sera transférée dans la nouvelle.",
-       "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré et d'ancienneté suffisante.",
+       "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré.",
        "movenotallowed": "Vous n'avez pas la permission de renommer les pages.",
        "movenotallowedfile": "Vous n'avez pas la permission de renommer les fichiers.",
-       "cant-move-user-page": "Vous n’avez pas la permission de renommer les pages principales d’utilisateurs.",
-       "cant-move-to-user-page": "Vous n’avez pas la permission de renommer une page vers une page utilisateur (à l’exception d’une sous-page).",
-       "cant-move-category-page": "Vous n'avez pas la permis de renommer les pages de catégorie.",
+       "cant-move-user-page": "Vous n’avez pas la permission de renommer les pages principales des utilisateurs (sauf les sous-pages).",
+       "cant-move-to-user-page": "Vous n’avez pas la permission de renommer une page vers une page utilisateur (mais vous pouvez le faire vers une sous-page utilisateur).",
+       "cant-move-category-page": "Vous n'avez pas la permission de renommer les pages de catégorie.",
        "cant-move-to-category-page": "Vous n'avez pas la permission de renommer une page vers une page de catégorie.",
        "newtitle": "Nouveau titre :",
        "move-watch": "Suivre les pages originale et nouvelle",
        "movepagebtn": "Renommer la page",
        "pagemovedsub": "Renommage réussi",
-       "movepage-moved": "'''« $1 » a été renommée en « $2 »'''",
+       "movepage-moved": "<strong>« $1 » a été renommée en « $2 »</strong>",
        "movepage-moved-redirect": "Une redirection depuis l'ancien nom a été créée.",
        "movepage-moved-noredirect": "La création d'une redirection depuis l'ancien nom a été annulée.",
        "articleexists": "Il existe déjà une page portant ce titre, ou le titre que vous avez choisi n'est pas correct.\nVeuillez en choisir un autre.",
        "cantmove-titleprotected": "Vous ne pouvez pas déplacer une page vers cet emplacement car la création de page avec ce nouveau titre a été protégée.",
        "movetalk": "Renommer aussi la page de discussion associée",
-       "move-subpages": "Renommer les sous-pages (jusqu'à $1 {{PLURAL:$1|page|pages}})",
-       "move-talk-subpages": "Renommer les sous-pages de la page de discussion (jusqu'à $1 pages)",
+       "move-subpages": "Renommer les sous-pages (maximum $1)",
+       "move-talk-subpages": "Renommer les sous-pages de la page de discussion (maximum $1)",
        "movepage-page-exists": "La page $1 existe déjà et ne peut pas être écrasée automatiquement.",
        "movepage-page-moved": "La page $1 a été renommée en $2.",
        "movepage-page-unmoved": "La page $1 n'a pas pu être renommée en $2.",
        "selfmove": "Les titres d'origine et de destination sont les mêmes ;\nimpossible de renommer une page sur elle-même.",
        "immobile-source-namespace": "Vous ne pouvez pas renommer les pages dans l'espace de noms « $1 »",
        "immobile-target-namespace": "Vous ne pouvez pas renommer des pages vers l’espace de noms « $1 ».",
-       "immobile-target-namespace-iw": "Les destinations interwikis ne sont pas une cible valide pour les déplacements.",
+       "immobile-target-namespace-iw": "Un lien interwiki n’est pas une cible valide pour un renommage de page.",
        "immobile-source-page": "Cette page n'est pas renommable.",
        "immobile-target-page": "Il n'est pas possible de renommer la page vers ce titre.",
        "bad-target-model": "La destination souhaitée utilise un autre modèle de contenu. Impossible de convertir de $1 vers $2.",
        "imageinvalidfilename": "Le nom du fichier cible est incorrect",
        "fix-double-redirects": "Mettre à jour les redirections pointant vers le titre original",
        "move-leave-redirect": "Laisser une redirection vers le nouveau titre",
-       "protectedpagemovewarning": "'''Attention :''' Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
-       "semiprotectedpagemovewarning": "'''Note :''' Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
+       "protectedpagemovewarning": "<strong>Attention :</strong> Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
+       "semiprotectedpagemovewarning": "<strong>Note :</strong> Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
        "move-over-sharedrepo": "[[:$1]] existe déjà sur un dépôt partagé. Renommer ce fichier rendra le fichier sur le dépôt partagé inaccessible.",
        "file-exists-sharedrepo": "Le nom choisi est déjà utilisé par un fichier sur un dépôt partagé.\nChoisissez un autre nom.",
        "export": "Exporter des pages",
-       "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages ; le résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
+       "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages.\nLe résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
        "exportall": "Exporter toutes les pages",
        "exportcuronly": "Exporter uniquement la version courante, sans l’historique complet",
        "exportnohistory": "----\n'''Note :''' l’exportation de l’historique complet des pages à l’aide de ce formulaire a été désactivée pour des raisons de performance.",
        "export-submit": "Exporter",
        "export-addcattext": "Ajouter les pages de la catégorie :",
        "export-addcat": "Ajouter",
-       "export-addnstext": "Ajouter des pages dans l'espace de noms :",
+       "export-addnstext": "Ajouter des pages de l'espace de noms :",
        "export-addns": "Ajouter",
        "export-download": "Enregistrer dans un fichier",
        "export-templates": "Inclure les modèles",
-       "export-pagelinks": "Inclure les pages liées à une profondeur de :",
+       "export-pagelinks": "Inclure les pages liées jusqu'à une profondeur de :",
        "export-manual": "Ajouter des pages manuellement :",
        "allmessages": "Messages système",
-       "allmessagesname": "Nom du message",
+       "allmessagesname": "Nom",
        "allmessagesdefault": "Message par défaut",
        "allmessagescurrent": "Message actuel",
        "allmessagestext": "Ceci est la liste des messages système disponibles dans l’espace de noms MediaWiki.\nVeuillez visiter la [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation Régionalisation de MediaWiki] et [https://translatewiki.net/ translatewiki.net] si vous désirez contribuer à la régionalisation générique de MediaWiki.",
-       "allmessagesnotsupportedDB": "Cette page '''{{ns:special}}:Allmessages''' n'est pas utilisable car '''$wgUseDatabaseMessages''' a été désactivé.",
+       "allmessagesnotsupportedDB": "Cette page n’est pas utilisable car <strong>$wgUseDatabaseMessages</strong> a été désactivé.",
        "allmessages-filter-legend": "Filtrer",
        "allmessages-filter": "Filtrer par état de modification :",
        "allmessages-filter-unmodified": "Non modifié",
        "thumbnail_invalid_params": "Paramètres de la miniature incorrects",
        "thumbnail_toobigimagearea": "Fichier avec des dimensions supérieures à $1",
        "thumbnail_dest_directory": "Impossible de créer le répertoire de destination",
-       "thumbnail_image-type": "Type d'image non supporté",
+       "thumbnail_image-type": "Type d’image non pris en charge",
        "thumbnail_gd-library": "Configuration incomplète de la bibliothèque GD : fonction $1 introuvable",
        "thumbnail_image-missing": "Le fichier suivant est introuvable : $1",
        "thumbnail_image-failure-limit": "Il y a eu récemment trop de tentatives échouées ($1 ou plus) pour restituer cette vignette. Veuillez réessayer ultérieurement.",
        "import-mapping-subpage": "Importer comme sous-pages de la page suivante :",
        "import-upload-filename": "Nom du fichier :",
        "import-comment": "Commentaire :",
-       "importtext": "Veuillez exporter le fichier depuis le wiki d'origine en utilisant son [[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
+       "importtext": "Veuillez exporter le fichier depuis le wiki d’origine en utilisant l’[[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
        "importstart": "Importation des pages…",
        "import-revision-count": "$1 version{{PLURAL:$1||s}}",
        "importnopages": "Aucune page à importer.",
        "importuploaderrortemp": "L'import du fichier a échoué.\nUn dossier temporaire est manquant.",
        "import-parse-failure": "Échec lors de l'analyse du XML à importer",
        "import-noarticle": "Aucune page à importer !",
-       "import-nonewrevisions": "Aucune révision importée (toutes étaient déjà présentes, ou ignorées du fait d’erreurs).",
+       "import-nonewrevisions": "Aucune révision importée (toutes étaient soit déjà présentes, soit ignorées du fait d’erreurs).",
        "xml-error-string": "$1 à la ligne $2, colonne $3 (octet $4) : $5",
        "import-upload": "Import de données XML",
        "import-token-mismatch": "Perte des données de session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayez</strong>.\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "import-error-special": "La page « $1 » n’a pas été importée parce qu’elle appartient à un espace de noms spécial qui n’autorise aucune page.",
        "import-error-invalid": "Page « $1 » n’a pas été importée parce que le nom sous lequel elle aurait été importée n’est pas valide sur ce wiki.",
        "import-error-unserialize": "La révision $2 de la page « $1 » ne peut pas être désérialisée. La révision est indiquée comme utilisant le modèle de contenu $3 sérialisé en $4.",
-       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stocké sur « $1 » sur ce wiki, car ce modèle n’est pas supporté sur cette page.",
+       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stockée sur « $1 » sur ce wiki, car ce modèle n’est pas pris en charge sur cette page.",
        "import-options-wrong": "{{PLURAL:$2|Mauvaise option|Mauvaises options}} : <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "La page racine fournie est un titre non valide.",
        "import-rootpage-nosubpage": "L'espace de noms « $1 » de la page racine n'autorise pas les sous-pages.",
        "javascripttest-pagetext-unknownaction": "Action « $1 » inconnue.",
        "javascripttest-qunit-intro": "Voir [$1 la documentation de test] sur mediawiki.org.",
        "tooltip-pt-userpage": "{{GENDER:|Votre}} page utilisateur",
-       "tooltip-pt-anonuserpage": "La page utilisateur de l'IP avec laquelle vous contribuez",
+       "tooltip-pt-anonuserpage": "La page utilisateur avec l'adresse IP de laquelle vous contribuez",
        "tooltip-pt-mytalk": "{{GENDER:|Votre}} page de discussion",
        "tooltip-pt-anontalk": "La page de discussion pour les contributions depuis cette adresse IP",
        "tooltip-pt-preferences": "{{GENDER:|Vos}} préférences",
-       "tooltip-pt-watchlist": "La liste des pages dont vous suivez les modifications",
+       "tooltip-pt-watchlist": "Une liste des pages dont vous suivez les modifications",
        "tooltip-pt-mycontris": "La liste de {{GENDER:|vos}} contributions",
        "tooltip-pt-anoncontribs": "Une liste des modifications effectuées depuis cette adresse IP",
        "tooltip-pt-login": "Il est recommandé de vous identifier ; ce n'est cependant pas obligatoire.",
        "tooltip-n-recentchanges": "Liste des modifications récentes sur le wiki",
        "tooltip-n-randompage": "Afficher une page au hasard",
        "tooltip-n-help": "Aide",
-       "tooltip-t-whatlinkshere": "Liste des pages liées à celle-ci",
-       "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages liées à celle-ci",
+       "tooltip-t-whatlinkshere": "Liste des pages liées qui pointent sur celle-ci",
+       "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages appelées par celle-ci",
        "tooltip-feed-rss": "Flux RSS pour cette page",
        "tooltip-feed-atom": "Flux Atom pour cette page",
        "tooltip-t-contributions": "Voir la liste des contributions de {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
        "tooltip-ca-nstab-template": "Voir le modèle",
        "tooltip-ca-nstab-help": "Voir la page d'aide",
        "tooltip-ca-nstab-category": "Voir la page de la catégorie",
-       "tooltip-minoredit": "Marquer mes modifications comme mineures",
+       "tooltip-minoredit": "Marquer ceci comme modification mineure",
        "tooltip-save": "Enregistrer vos modifications",
        "tooltip-publish": "Publier vos modifications",
        "tooltip-preview": "Merci de prévisualiser vos modifications avant de les publier",
-       "tooltip-diff": "Affiche les modifications que vous avez apportées au texte",
-       "tooltip-compareselectedversions": "Afficher les différences entre deux versions de cette page",
+       "tooltip-diff": "Afficher les modifications que vous avez apportées au texte",
+       "tooltip-compareselectedversions": "Afficher les différences entre les deux versions selectionnées de cette page",
        "tooltip-watch": "Ajouter cette page à votre liste de suivi",
        "tooltip-watchlistedit-normal-submit": "Enlever les titres",
        "tooltip-watchlistedit-raw-submit": "Mise à jour de la liste de suivi",
        "tooltip-recreate": "Recréer la page même si celle-ci a été effacée",
        "tooltip-upload": "Démarrer l'import",
-       "tooltip-rollback": "« Révoquer » annule en un clic la ou les modification(s) de cette page par son dernier contributeur.",
+       "tooltip-rollback": "« Révoquer » annule en un clic la ou les modification(s) de cette page réalisées par son dernier contributeur",
        "tooltip-undo": "« Annuler » rétablit la modification précédente et ouvre la fenêtre de modification en mode prévisualisation. Il est possible d’ajouter une raison dans le résumé.",
        "tooltip-preferences-save": "Sauvegarder les préférences",
        "tooltip-summary": "Entrez un bref résumé",
        "pageinfo-article-id": "Numéro de la page",
        "pageinfo-language": "Langue du contenu de la page",
        "pageinfo-content-model": "Modèle de contenu de la page",
+       "pageinfo-content-model-change": "modifier",
        "pageinfo-robot-policy": "Indexation par robots",
        "pageinfo-robot-index": "Autorisée",
        "pageinfo-robot-noindex": "Interdite",
        "pageinfo-magic-words": "{{PLURAL:$1|Mot magique|Mots magiques}} ($1)",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Catégorie cachée|Catégories cachées}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Modèle inclu|Modèles inclus}} ($1)",
-       "pageinfo-transclusions": "{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} elle est incluse ($1)",
+       "pageinfo-transclusions": "{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} cette page est incluse ($1)",
        "pageinfo-toolboxlink": "Information sur la page",
        "pageinfo-redirectsto": "Rediriger vers",
        "pageinfo-redirectsto-info": "info",
-       "pageinfo-contentpage": "Compté comme page de contenu",
+       "pageinfo-contentpage": "Comptée comme page de contenu",
        "pageinfo-contentpage-yes": "Oui",
-       "pageinfo-protect-cascading": "Les protections sont déduites d'ici",
+       "pageinfo-protect-cascading": "Les protections sont déduites à partir d'ici",
        "pageinfo-protect-cascading-yes": "Oui",
        "pageinfo-protect-cascading-from": "Les protections sont déduites depuis",
        "pageinfo-category-info": "Informations sur la catégorie",
        "pageinfo-category-files": "Nombre de fichiers",
        "markaspatrolleddiff": "Marquer comme relue",
        "markaspatrolledtext": "Marquer cette page comme relue",
-       "markaspatrolledtext-file": "Marquer cette version de fichier comme patrouillée",
+       "markaspatrolledtext-file": "Marquer cette version de fichier comme relue",
        "markedaspatrolled": "Marquée comme relue",
        "markedaspatrolledtext": "La version sélectionnée de [[:$1]] a été marquée comme relue.",
        "rcpatroldisabled": "La fonction de relecture des modifications récentes n'est pas activée.",
        "filedelete-archive-read-only": "Le dossier d'archivage « $1 » n'est pas modifiable par le serveur.",
        "previousdiff": "← Modification précédente",
        "nextdiff": "Modification suivante →",
-       "mediawarning": "'''Attention :''' ce type de fichier peut contenir du code malveillant.\nSi vous l'exécutez, votre système peut être compromis.",
-       "imagemaxsize": "Taille maximale des images :<br />''(pour les pages de description de fichier)''",
+       "mediawarning": "<strong>Attention :</strong> ce type de fichier peut contenir du code malveillant.\nSi vous l'exécutez, votre système peut être compromis.",
+       "imagemaxsize": "Taille maximale des images :<br /><em>(pour les pages de description de fichier)</em>",
        "thumbsize": "Taille de la miniature :",
        "widthheight": "$1&nbsp;×&nbsp;$2",
        "widthheightpage": "$1 × $2, $3 page{{PLURAL:$3||s}}",
        "file-info-size-pages": "$1 × $2 pixels, taille de fichier : $3, type MIME : $4, $5 page{{PLURAL:$5||s}}",
        "file-nohires": "Pas de plus haute résolution disponible.",
        "svg-long-desc": "Fichier SVG, résolution de $1 × $2 pixels, taille : $3",
-       "svg-long-desc-animated": "Fichier SVG animé, taille $1 × $2 pixels, taille du fichier : $3",
+       "svg-long-desc-animated": "Fichier SVG animé, résolution $1 × $2 pixels, taille du fichier : $3",
        "svg-long-error": "Fichier SVG non valide : $1",
        "show-big-image": "Fichier d'origine",
        "show-big-image-preview": "Taille de cet aperçu : $1.",
        "file-info-png-looped": "en boucle",
        "file-info-png-repeat": "joué $1 {{PLURAL:$1|fois}}",
        "file-info-png-frames": "$1 {{PLURAL:$1|image|images}}",
-       "file-no-thumb-animation": "'''Remarque : En raison de limitations techniques, les vignettes de ce fichier ne seront pas animées.'''",
-       "file-no-thumb-animation-gif": "'''Remarque : En raison de limitations techniques, les vignettes d'images GIF en haute résolution telles que celle-ci ne seront pas animées.'''",
+       "file-no-thumb-animation": "<strong>Remarque : En raison de limitations techniques, les vignettes de ce fichier ne seront pas animées.</strong>",
+       "file-no-thumb-animation-gif": "<strong>Remarque : En raison de limitations techniques, les vignettes d'images GIF en haute résolution telles que celle-ci ne seront pas animées.</strong>",
        "newimages": "Galerie des nouveaux fichiers",
-       "imagelisttext": "Voici une liste de '''$1''' fichier{{PLURAL:$1||s}} classée $2.",
+       "imagelisttext": "Voici une liste de <strong>$1</strong> {{PLURAL:$1|fichier classé|fichiers classés}} $2.",
        "newimages-summary": "Cette page spéciale affiche les derniers fichiers importés.",
-       "newimages-legend": "Nom du fichier",
+       "newimages-legend": "Filtre",
        "newimages-label": "Nom du fichier (ou une partie de celui-ci) :",
-       "newimages-showbots": "Afficher les imports par des robots",
+       "newimages-showbots": "Afficher les imports faits par des robots",
        "newimages-hidepatrolled": "Masquer les téléchargements patrouillés",
        "noimages": "Aucune image à afficher.",
        "ilsubmit": "Rechercher",
        "saturday-at": "Samedi à $1",
        "sunday-at": "Dimanche à $1",
        "yesterday-at": "Hier à $1",
-       "bad_image_list": "Le format est le suivant :\n\nSeules les listes d’énumération (commençant par *) sont prises en compte. Le premier lien d’une ligne doit être celui d’une mauvaise image.\nLes autres liens sur la même ligne sont considérés comme des exceptions, par exemple des pages sur lesquelles l’image peut apparaître.",
+       "bad_image_list": "Le format est le suivant :\n\nSeules les listes d’énumération (commençant par *) sont prises en compte. \nLe premier lien d’une ligne doit être celui d’une mauvaise image.\nLes autres liens sur la même ligne sont considérés comme des exceptions, par exemple des pages sur lesquelles l’image peut apparaître.",
        "variantname-ku-arab": "ku-arab",
        "variantname-ku-latn": "ku-latn",
        "variantname-tg-cyrl": "tg-cyrl",
        "variantname-shi-tfng": "shi-tfng",
        "variantname-shi-latn": "shi-latn",
        "metadata": "Métadonnées",
-       "metadata-help": "Ce fichier contient des informations supplémentaires, probablement ajoutées par l'appareil photo numérique ou le numériseur utilisé pour le créer. Si le fichier a été modifié depuis son état original, certains détails peuvent ne pas refléter entièrement l'image modifiée.",
+       "metadata-help": "Ce fichier contient des informations supplémentaires, probablement ajoutées par l'appareil photo numérique ou le numériseur utilisé pour le créer. \nSi le fichier a été modifié depuis son état original, certains détails peuvent ne pas refléter entièrement l'image modifiée.",
        "metadata-expand": "Afficher les informations détaillées",
        "metadata-collapse": "Masquer les informations détaillées",
        "metadata-fields": "Les champs de métadonnées d'image listés dans ce message seront inclus dans la page de description de l'image quand la table de métadonnées sera réduite. Les autres champs seront cachés par défaut.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-yresolution": "Résolution verticale",
        "exif-stripoffsets": "Emplacement des données de l'image",
        "exif-rowsperstrip": "Nombre de lignes par bande",
-       "exif-stripbytecounts": "Taille en octets par bande",
+       "exif-stripbytecounts": "Taille en octets par bande compressée",
        "exif-jpeginterchangeformat": "Position du SOI JPEG",
        "exif-jpeginterchangeformatlength": "Taille en octets des données JPEG",
        "exif-whitepoint": "Chromaticité du point blanc",
        "exif-primarychromaticities": "Chromaticité des primaires",
        "exif-ycbcrcoefficients": "Coefficients YCbCr",
-       "exif-referenceblackwhite": "Valeurs de référence noir et blanc",
-       "exif-datetime": "Date de modification",
-       "exif-imagedescription": "Description de l'image",
-       "exif-make": "Fabricant de l'appareil",
-       "exif-model": "Modèle de l'appareil",
+       "exif-referenceblackwhite": "Valeurs des couples noir et blanc de référence",
+       "exif-datetime": "Date de modification du fichier",
+       "exif-imagedescription": "Titre de l'image",
+       "exif-make": "Fabricant de l'appareil photo",
+       "exif-model": "Modèle de l'appareil photo",
        "exif-software": "Logiciel utilisé",
        "exif-artist": "Auteur",
        "exif-copyright": "Détenteur du droit d'auteur",
        "exif-exifversion": "Version EXIF",
-       "exif-flashpixversion": "Version FlashPix",
+       "exif-flashpixversion": "Version FlashPix supportée",
        "exif-colorspace": "Espace colorimétrique",
        "exif-componentsconfiguration": "Signification de chaque composante",
        "exif-compressedbitsperpixel": "Mode de compression de l'image",
        "tag-filter": "Filtrer les [[Special:Tags|balises]] :",
        "tag-filter-submit": "Filtrer",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Balise|Balises}}]] : $2)",
+       "tag-mw-contentmodelchange": "modification du modèle de contenu",
+       "tag-mw-contentmodelchange-description": "Modifications qui [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel changent le modèle de contenu] d'une page",
        "tags-title": "Balises",
        "tags-intro": "Cette page liste les balises que le logiciel peut utiliser pour marquer une modification et la signification de chacune.",
        "tags-tag": "Nom de la balise",
        "tags-actions-header": "Actions",
        "tags-active-yes": "Oui",
        "tags-active-no": "Non",
-       "tags-source-extension": "Définie par une extension",
+       "tags-source-extension": "Défini par le logiciel",
        "tags-source-manual": "Appliquée manuellement par les utilisateurs et les bots",
        "tags-source-none": "Obsolète",
        "tags-edit": "modifier",
        "tags-edit-logentry-submit": "Appliquer les modifications à {{PLURAL:$1|cette entrée de journal|$1 entrées de journal}}",
        "tags-edit-success": "Les modifications ont été appliquées.",
        "tags-edit-failure": "Les modifications n’ont pas pu être appliquées :\n$1",
-       "tags-edit-nooldid-title": "Révision cible non valide",
-       "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de révision cible sur laquelle exécuter cette fonction, soit la révision spécifiée n’existe pas.",
+       "tags-edit-nooldid-title": "Version cible non valide",
+       "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de version cible sur laquelle exécuter cette fonction, soit la version spécifiée n’existe pas.",
        "tags-edit-none-selected": "Veuillez sélectionner au moins une balise à ajouter ou enlever.",
        "comparepages": "Comparer des pages",
        "compare-page1": "Page 1",
        "htmlform-title-not-exists": "$1 n’existe pas",
        "htmlform-user-not-exists": "<strong>$1</strong> n’existe pas.",
        "htmlform-user-not-valid": "<strong>$1</strong> n’est pas un nom d’utilisateur valide.",
-       "sqlite-has-fts": "$1 avec recherche en texte intégral supportée",
-       "sqlite-no-fts": "$1 sans recherche en texte intégral supportée",
+       "sqlite-has-fts": "$1 avec recherche en texte intégral prise en charge",
+       "sqlite-no-fts": "$1 sans recherche en texte intégral prise en charge",
        "logentry-delete-delete": "$1 {{GENDER:$2|a supprimé}} la page $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restauré}} la page $3",
        "logentry-delete-event": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'un événement du journal|de $5 événements du journal}} sur $3: $4",
index 7e69455..2765143 100644 (file)
        "minoredit": "Mionathrú é seo",
        "watchthis": "Déan faire ar an lch seo",
        "savearticle": "Sábháil an lch",
+       "publishpage": "Foilsigh leathanach",
        "preview": "Réamhamharc",
        "showpreview": "Taispeáin réamhamharc",
        "showdiff": "Taispeáin athruithe",
        "recreate-moveddeleted-warn": "'''Rabhadh: Tá tú ag athchruthú leathanaigh a scriosadh cheana.'''\n\nAn bhfuil tú cinnte go bhfuil sé oiriúnach an leathanach seo a cur in eagar?\nCuirtear an loga scriosta ar fáil anseo mar áis:",
        "log-fulllog": "Féach ar an logchomhad iomlán",
        "undo-summary": "Cealaíodh athrú $1 le [[Special:Contributions/$2|$2]] ([[User talk:$2|plé]])",
-       "cantcreateaccounttitle": "Ní féidir cuntas a chruthú",
        "viewpagelogs": "Féach ar logaí faoin leathanach seo",
        "nohistory": "Níl aon stáir athraithe ag an leathanach seo.",
        "currentrev": "Leagan reatha",
        "tooltip-ca-nstab-category": "Féach ar an leathanach catagóire",
        "tooltip-minoredit": "Déan mionathrú den athrú seo",
        "tooltip-save": "Sábháil do chuid athruithe",
+       "tooltip-publish": "Foilsigh do chuid athruithe",
        "tooltip-preview": "Réamhamharc ar do chuid athruithe; úsáid an gné seo roimh a shábhálaíonn tú!",
        "tooltip-diff": "Taispeáin na difríochtaí áirithe a rinne tú don téacs",
        "tooltip-compareselectedversions": "Féach na difríochtaí idir an dhá leagain roghnaithe den leathanach seo.",
        "exif-gpsspeed-n": "Muirmhílte",
        "exif-gpsdirection-t": "Fíorthreo",
        "exif-gpsdirection-m": "Treo maighnéadach",
+       "exif-dc-publisher": "Foilsitheoir",
        "namespacesall": "iad uile",
        "monthsall": "gach mí",
        "confirmemail": "Deimhnigh do ríomhsheoladh",
index 3a86779..d28d4eb 100644 (file)
@@ -51,7 +51,7 @@
        "tog-enotifminoredits": "Enviádeme tamén unha mensaxe de correo electrónico cando se produzan edicións pequenas nas páxinas ou nos ficheiros",
        "tog-enotifrevealaddr": "Revelar o meu enderezo de correo electrónico nos correos de notificación",
        "tog-shownumberswatching": "Mostrar o número de usuarios que están a vixiar",
-       "tog-oldsig": "Sinatura actual:",
+       "tog-oldsig": "A súa sinatura actual:",
        "tog-fancysig": "Tratar a sinatura como se fose texto wiki (sen ligazón automática)",
        "tog-uselivepreview": "Usar a vista previa en tempo real",
        "tog-forceeditsummary": "Avisádeme cando o campo resumo estea baleiro",
@@ -68,7 +68,7 @@
        "tog-showhiddencats": "Mostrar as categorías ocultas",
        "tog-norollbackdiff": "Omitir as diferenzas despois de levar a cabo unha reversión de edicións",
        "tog-useeditwarning": "Avisádeme cando deixe unha páxina de edición cos cambios sen gardar",
-       "tog-prefershttps": "Utilizar unha conexión segura sempre que acceda ao sistema",
+       "tog-prefershttps": "Utilizar sempre unha conexión segura mentres acceda ao sistema",
        "underline-always": "Sempre",
        "underline-never": "Nunca",
        "underline-default": "Opción predeterminada da aparencia ou do navegador",
        "newwindow": "(abre unha ventá nova)",
        "cancel": "Cancelar",
        "moredotdotdot": "Máis...",
-       "morenotlisted": "Esta lista non está completa.",
+       "morenotlisted": "Esta lista pode estar incompleta.",
        "mypage": "Páxina",
        "mytalk": "Conversa",
        "anontalk": "Conversa",
        "yourpasswordagain": "Insira o contrasinal outra vez:",
        "createacct-yourpasswordagain": "Confirme o contrasinal",
        "createacct-yourpasswordagain-ph": "Insira o contrasinal outra vez",
-       "remembermypassword": "Lembrar o meu contrasinal neste ordenador (ata $1 {{PLURAL:$1|día|días}})",
        "userlogin-remembermypassword": "Manter a miña conexión",
        "userlogin-signwithsecure": "Utilizar a conexión segura",
+       "cannotlogin-title": "Non se pode acceder ó sistema",
+       "cannotlogin-text": "O acceso ó sistema non está dispoñible.",
        "cannotloginnow-title": "Non se pode iniciar a sesión agora mesmo",
        "cannotloginnow-text": "Non é posible iniciar a sesión cando se usa $1.",
+       "cannotcreateaccount-title": "Non se poden crear contas de usuario",
+       "cannotcreateaccount-text": "A creación directa de contas de usuario non está habilitada nesta wiki.",
        "yourdomainname": "O seu dominio:",
        "password-change-forbidden": "Non pode mudar os contrasinais neste wiki.",
        "externaldberror": "Ou ben se produciu un erro da base de datos na autenticación externa ou ben non se lle permite actualizar a súa conta externa.",
        "botpasswords-updated-body": "O contrasinal de bot do bot de nome \"$1\" do usuario \"$2\" foi actualizado.",
        "botpasswords-deleted-title": "Contrasinal de bot borrado",
        "botpasswords-deleted-body": "O contrasinal de bot do bot de nome \"$1\" do usuario \"$2\" foi borrado.",
-       "botpasswords-newpassword": "O novo contrasinal para acceder con strong>$1</strong> é <strong>$2</strong>. <em>Por favor, rexistra isto para referencia futura.</em>",
+       "botpasswords-newpassword": "O novo contrasinal para acceder con <strong>$1</strong> é <strong>$2</strong>. <em>Por favor, rexistre isto para referencias futuras.</em><br />(Para bots vellos que requiren que o nome de acceso sexa o mesmo que o nome de usuario eventual, pode usar tamén <strong>$3</strong> como nome de usuario e <strong>$4</strong>  como contrasinal.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider non está dispoñible.",
        "botpasswords-restriction-failed": "Restricións de contrasinal de bots evitaron esta conexión.",
        "botpasswords-invalid-name": "O nome de usuario especificado non contén o separador de contrasinal de bot (\"$1\").",
        "invalid-content-data": "Datos de contido inválidos",
        "content-not-allowed-here": "O contido \"$1\" non está permitido na páxina \"[[$2]]\"",
        "editwarning-warning": "Deixar esta páxina pode causar a perda de calquera cambio feito.\nSe accedeu ao sistema, pode desactivar esta mensaxe de advertencia na sección \"{{int:prefs-editing}}\" das súas preferencias.",
+       "editpage-invalidcontentmodel-title": "Modelo de contido non válido",
+       "editpage-invalidcontentmodel-text": "O modelo de contido \"$1\" non é válido.",
        "editpage-notsupportedcontentformat-title": "Formato de contido non admitido",
        "editpage-notsupportedcontentformat-text": "O formato de contido $1 non é compatible co modelo de contido $2.",
        "content-model-wikitext": "texto wiki",
        "file-thumbnail-no": "O nome do ficheiro comeza por <strong>$1</strong>.\nParece tratarse dunha imaxe de tamaño reducido ''(miniatura)''.\nSe dispón dunha versión desta imaxe de maior resolución cárguea; se non, múdelle o nome ao ficheiro.",
        "fileexists-forbidden": "Xa existe un ficheiro co mesmo nome e este non pode ser sobrescrito.\nSe aínda quere cargar o seu ficheiro, por favor, retroceda e use un novo nome. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Xa existe un ficheiro con este nome no repositorio de ficheiros compartidos.\nSe aínda quere cargar o seu ficheiro, volva atrás e use outro nome.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "O ficheiro cargado é un duplicado exacto da versión actual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "O ficheiro cargado é un duplicado exacto {{PLURAL:$2|dunha versión vella|de varias versións vellas}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Este ficheiro é un duplicado {{PLURAL:$1|do seguinte|dos seguintes}}:",
        "file-deleted-duplicate": "Un ficheiro idéntico a este (\"[[:$1]]\") foi borrado previamente. Debería comprobar o historial de borrados do ficheiro antes de proceder a cargalo de novo.",
        "file-deleted-duplicate-notitle": "Un ficheiro idéntico a este foi borrado con anterioridade e o título foi suprimido.\nDebería contactar con alguén capaz de ver os datos de ficheiros borrados para que revise esta situación antes de subilo de novo.",
        "filerevert-submit": "Reverter",
        "filerevert-success": "Reverteuse \"'''[[Media:$1|$1]]'''\" á [$4 versión do $2 ás $3].",
        "filerevert-badversion": "Non existe unha versión local anterior deste ficheiro coa data e hora indicadas.",
+       "filerevert-identical": "A versión actual do ficheiro é igual á seleccionada.",
        "filedelete": "Borrar \"$1\"",
        "filedelete-legend": "Eliminar un ficheiro",
        "filedelete-intro": "Está a piques de eliminar o ficheiro \"'''[[Media:$1|$1]]'''\" xunto con todo o seu historial.",
        "rollbacklinkcount-morethan": "reverter máis de $1 {{PLURAL:$1|edición|edicións}}",
        "rollbackfailed": "Houbo un erro ao reverter as edicións",
        "rollback-missingparam": "Faltan parámetros obrigatorios na solicitude.",
+       "rollback-missingrevision": "Non se poden cargar datos da revisión.",
        "cantrollback": "Non se pode desfacer a edición; o último colaborador é o único autor desta páxina.",
        "alreadyrolled": "Non se pode desfacer a edición en \"[[:$1]]\" feita por [[User:$2|$2]] ([[User talk:$2|conversa]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); alguén máis editou ou desfixo os cambios desta páxina.\n\nA última edición fíxoa [[User:$3|$3]] ([[User talk:$3|conversa]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "O resumo de edición foi: <em>$1</em>.",
        "pageinfo-article-id": "ID da páxina",
        "pageinfo-language": "Lingua do contido da páxina",
        "pageinfo-content-model": "Modelo do contido da páxina",
+       "pageinfo-content-model-change": "cambiar",
        "pageinfo-robot-policy": "Indexación por robots",
        "pageinfo-robot-index": "Permitida",
        "pageinfo-robot-noindex": "Non permitida",
        "tag-filter": "Filtrar as [[Special:Tags|etiquetas]]:",
        "tag-filter-submit": "Filtro",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiqueta|Etiquetas}}]]: $2)",
+       "tag-mw-contentmodelchange": "cambio de modelo de contido",
+       "tag-mw-contentmodelchange-description": "Edicións que [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambian o modelo de contido] dunha páxina",
        "tags-title": "Etiquetas",
        "tags-intro": "Esta páxina lista as etiquetas coas que o software pode marcar unha edición, e mailos seus significados.",
        "tags-tag": "Nome da etiqueta",
        "tags-actions-header": "Accións",
        "tags-active-yes": "Si",
        "tags-active-no": "Non",
-       "tags-source-extension": "Definida por unha extensión",
+       "tags-source-extension": "Definida polo software",
        "tags-source-manual": "Aplicado manualmente por usuarios e bots",
        "tags-source-none": "Xa non está en uso",
        "tags-edit": "editar",
index 80cf567..7e0e0fd 100644 (file)
        "yourpasswordagain": "ગુપ્ત સંજ્ઞા (પાસવર્ડ) ફરી લખો:",
        "createacct-yourpasswordagain": "પાસવર્ડની ખાતરી કરો",
        "createacct-yourpasswordagain-ph": "પાસવર્ડ ફરીથી દાખલ કરો",
-       "remembermypassword": "આ કોમ્યૂટર પર મારી લૉગ ઇન વિગતો ધ્યાનમાં રાખો (વધુમાં વધુ $1 {{PLURAL:$1|દિવસ|દિવસ}} માટે)",
        "userlogin-remembermypassword": "મને પ્રવેશિત રાખો",
        "userlogin-signwithsecure": "સલામત જોડાણ વાપરો",
        "yourdomainname": "તમારૂં ડોમેઇન:",
        "minoredit": "આ એક નાનો સુધારો છે",
        "watchthis": "આ પાનાને ધ્યાનમાં રાખો",
        "savearticle": "પાનું સાચવો",
+       "savechanges": "પરિવર્તન સાચવો",
        "publishpage": "પાનું પ્રકાશિત કરો",
        "publishchanges": "ફેરફારો પ્રકાશિત કરો",
        "preview": "પૂર્વાવલોકન",
        "alllogstext": "{{SITENAME}} ના લોગનો સંયુક્ત વર્ણન.\nતમે લોગનો પ્રકાર,સભ્ય નામ અથવા અસરગ્રસ્ત પાના આદિ પસંદ કરી તમારી યાદિ ટૂંકાવી શકો.",
        "logempty": "લોગમાં આને મળતી કોઇ વસ્તુ નથી",
        "log-title-wildcard": "આ શબ્દો દ્વારા શરૂ થનાર શીર્ષકો શોધો",
-       "showhideselectedlogentries": "પસàª\82દàª\97à«\80નà«\80 àª²à«\8bàª\97 àª¨à«\8bàª\82ધણà«\80àª\93 àª¬àª¤àª¾àªµà«\8b/àª\9bà«\82પાવો",
+       "showhideselectedlogentries": "પસàª\82દàª\97à«\80નà«\80 àª²à«\8bàª\97 àª¨à«\8bàª\82ધણà«\80àª\93 àª¬àª¤àª¾àªµà«\8b/àª\9bà«\81પાવો",
        "allpages": "બધા પાના",
        "nextpage": "આગળનું પાનું ($1)",
        "prevpage": "પાછળનું પાનું ($1)",
        "revertmove": "પૂર્વવત",
        "delete_and_move_text": "== પાનું દૂર કરવાની જરૂર છે  ==\nલક્ષ્ય પાનું  \"[[:$1]]\" પહેલેથી અસ્તિત્વમાં છે.\nશું તમે આને હટાવીને સ્થળાંતર કરવાનો માર્ગ મોકળો કરવા માંગો છો?",
        "delete_and_move_confirm": "હા, આ પાનું હટાવો",
-       "delete_and_move_reason": "હટાવવાનું કામ આગળ વધાવવા ભૂંસી દેવાયુ \"[[$1]]\"",
+       "delete_and_move_reason": "\"[[$1]]\"થી ખસેડીને માહિતી અહિં લાવવા માટે ભૂંસી દેવાયું.",
        "selfmove": "સ્રોત ને લક્ષ્ય શીર્ષકો સમાન છે;\nપાના ને તેવા જ નામ ધરાવતા પાના પર પુનઃ સ્થાપન નહીં કરી શકાય.",
        "immobile-source-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
        "immobile-target-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
        "svg-long-desc": "SVG ફાઇલ, માત્ર $1 × $2 પીક્સલ, ફાઇલનું કદ: $3",
        "svg-long-error": "અયોગ્ય SVG ફાઇલ: $1",
        "show-big-image": "મૂળભુત ફાઇલ",
-       "show-big-image-preview": "àª\86 àª®àª¹àª¾àªµàª°àª¾ àª¦à«\8dરશà«\8dયનà«\81àª\82 àª®àª¾àªª: $1.",
+       "show-big-image-preview": "àª\86 àª\9dલàª\95àª\9aિતà«\8dરનà«\81àª\82 àª\95દ: $1.",
        "show-big-image-other": "અન્ય {{PLURAL:$2|આવર્તન|આવર્તનો}}: $1.",
        "show-big-image-size": "$1 × $2 પિક્સેલ",
        "file-info-gif-looped": "આવર્તન  (લુપ)",
        "seconds-ago": "$1 {{PLURAL:$1|સેકંડ|સેકંડો}} ago",
        "monday-at": "$1 પર સોમવાર",
        "tuesday-at": "$1 પર મંગળવાર",
-       "wednesday-at": "$1 પર બુધવાર",
+       "wednesday-at": "બુધવારે $1 વાગ્યે",
        "thursday-at": "$1 પર ગુરુવાર",
        "friday-at": "$1 પર શુક્રવાર",
        "saturday-at": "$1 પર શનિવાર",
        "htmlform-select-badoption": "તમે બતાવેલ વિકલ્પ અવૈધ છે",
        "htmlform-int-invalid": "તમે લખેલ વિકલ્પ અંક નથી",
        "htmlform-float-invalid": "તમે લખેલ વિકલ્પ અંક નથી",
-       "htmlform-int-toolow": "$1 આ કિંમત આ ન્યૂનતમ કિંમત છે",
+       "htmlform-int-toolow": "મુલ્ય જે તમે દર્શાવ્યું છે તે ન્યૂનતમ મુલ્ય $1 કરતાં ઓછું છે.",
        "htmlform-int-toohigh": "તમે પુરી પાડેલ માહિતી મહત્તમ $1થી વધુ છે",
        "htmlform-required": "આ કિઁમત જોઈએ છે",
        "htmlform-submit": "જમા કરો",
index b77a1ce..e76347f 100644 (file)
@@ -63,7 +63,7 @@
        "tog-enotifminoredits": "לשלוח אליי דוא\"ל גם על עריכות משניות בדפים וקבצים",
        "tog-enotifrevealaddr": "חשיפת כתובת הדוא\"ל שלי בהתראות דוא\"ל",
        "tog-shownumberswatching": "הצגת מספר המשתמשים העוקבים",
-       "tog-oldsig": "החתימה הנוכחית:",
+       "tog-oldsig": "החתימה הנוכחית שלך:",
        "tog-fancysig": "התייחסות לחתימה כקוד ויקי (ללא קישור אוטומטי)",
        "tog-uselivepreview": "שימוש בתצוגה מקדימה מהירה",
        "tog-forceeditsummary": "הצגת אזהרה בעת הכנסת תקציר עריכה ריק",
        "newwindow": "(נפתח בחלון חדש)",
        "cancel": "ביטול",
        "moredotdotdot": "עוד...",
-       "morenotlisted": "רשימה זו אינה מלאה.",
+       "morenotlisted": "×\99×\99ת×\9b×\9f ×©×¨×©×\99×\9e×\94 ×\96×\95 ×\90×\99× ×\94 ×\9e×\9c×\90×\94.",
        "mypage": "דף משתמש",
        "mytalk": "שיחה",
        "anontalk": "שיחה",
        "yourpasswordagain": "חזרה על הסיסמה:",
        "createacct-yourpasswordagain": "אימות הסיסמה",
        "createacct-yourpasswordagain-ph": "יש להקליד את הסיסמה שנית",
-       "remembermypassword": "שמירת הכניסה שלי בדפדפן הזה ({{PLURAL:$1|ליום אחד|ליומיים|ל־$1 ימים}} לכל היותר)",
        "userlogin-remembermypassword": "לזכור שנכנסתי",
        "userlogin-signwithsecure": "שימוש בחיבור מאובטח",
+       "cannotlogin-title": "לא ניתן להיכנס לחשבון",
+       "cannotlogin-text": "הכניסה לחשבון אינה אפשרית.",
        "cannotloginnow-title": "לא ניתן להיכנס עכשיו",
        "cannotloginnow-text": "הכניסה אינה אפשרית בעת שימוש ב{{GRAMMAR:תחילית|$1}}.",
+       "cannotcreateaccount-title": "לא ניתן ליצור חשבונות",
+       "cannotcreateaccount-text": "יצירת חשבונות באופן ישיר אינה מותרת באתר זה.",
        "yourdomainname": "המתחם שלך:",
        "password-change-forbidden": "אין באפשרותך לשנות סיסמאות באתר זה.",
        "externaldberror": "אירעה שגיאת אימות בבסיס הנתונים, או שאינך מורשה לעדכן את החשבון החיצוני שלך.",
        "botpasswords-updated-body": "ססמת הבוט עבור בוט בשם \"$1\" של המשתמש \"$2\" עודכנה.",
        "botpasswords-deleted-title": "ססמת הבוט נמחקה",
        "botpasswords-deleted-body": "ססמת הבוט עבור בוט בשם \"$1\" של המשתמש \"$2\" נמחקה.",
-       "botpasswords-newpassword": "×\94סס×\9e×\94 ×\94×\97×\93ש×\94 ×\9c×\9b× ×\99ס×\94 <strong>$1</strong> ×\94×\99×\90 <strong>$2</strong>. <em>× ×\90 ×\9cש×\9e×\95ר ×\9e×\99×\93×¢ ×\96×\94 ×\9cצ×\95ר×\9a ×¢×\99×\95×\9f ×¢×ª×\99×\93×\99.</em>",
+       "botpasswords-newpassword": "×\94ס×\99ס×\9e×\94 ×\94×\97×\93ש×\94 ×\9c×\9b× ×\99ס×\94 ×\9c×\97ש×\91×\95×\9f <strong>$1</strong> ×\94×\99×\90 <strong>$2</strong>. <em>× ×\90 ×\9cש×\9e×\95ר ×\9e×\99×\93×¢ ×\96×\94 ×\9cצ×\95ר×\9a ×¢×\99×\95×\9f ×¢×ª×\99×\93×\99.</em> <br> (×¢×\91×\95ר ×\91×\95×\98×\99×\9d ×\99שנ×\99×\9d ×©×\93×\95רש×\99×\9d ×©×©×\9d ×\94×\9eשת×\9eש ×\91×\9b× ×\99ס×\94 ×\9c×\97ש×\91×\95×\9f ×\99×\94×\99×\94 ×\96×\94×\94 ×\9cש×\9d ×\94×\9eשת×\9eש ×©×\90×\99ת×\95 ×\94×\9d ×\99פע×\9c×\95, × ×\99ת×\9f ×\9c×\94שת×\9eש ×\92×\9d ×\91ש×\9d ×\94×\9eשת×\9eש <strong>$3</strong> ×¢×\9d ×\94ס×\99ס×\9e×\94 <strong>$4</strong>.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider אינו זמין.",
        "botpasswords-restriction-failed": "כניסה זו נמנעה בשל הגבלות על ססמאות בוט.",
        "botpasswords-invalid-name": "שם המשתמש שניתן אינו מכיל את תו הפרדת ססמאות הבוט (\"$1\").",
        "invalid-content-data": "מידע שגוי על התוכן",
        "content-not-allowed-here": "תוכן מסוג \"$1\" אינו מותר בדף [[$2]]",
        "editwarning-warning": "עזיבת הדף הזה עלולה לגרום לך לאבד את כל השינויים שביצעת.\nאם יש לך חשבון באתר, באפשרותך לבטל את האזהרה הזאת בחלק \"{{int:prefs-editing}}\" שבהעדפות שלך.",
+       "editpage-invalidcontentmodel-title": "מודל התוכן אינו נתמך",
+       "editpage-invalidcontentmodel-text": "מודל התוכן \"$1\" אינו נתמך.",
        "editpage-notsupportedcontentformat-title": "סוג התוכן אינו נתמך",
        "editpage-notsupportedcontentformat-text": "תוכן מסוג $1 אינו נתמך על־ידי מודל התוכן $2.",
        "content-model-wikitext": "קוד ויקי",
        "file-thumbnail-no": "שם הקובץ מתחיל ב־<strong>$1</strong>.\nנראה שזוהי תמונה מוקטנת (ממוזערת).\nאם התמונה בגודל מלא מצויה ברשותך, יש להעלות אותה ולא את התמונה הממוזערת; אחרת, יש לשנות את שם הקובץ.",
        "fileexists-forbidden": "קובץ בשם זה כבר קיים, ואינכם יכולים להחליף אותו.\nאם אתם עדיין מעוניינים להעלות קובץ זה, אנא חזרו לדף הקודם והעלו את הקובץ תחת שם חדש.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "קובץ בשם זה כבר קיים כקובץ משותף.\nאם אתם עדיין מעוניינים להעלות קובץ זה, אנא חזרו לדף הקודם והעלו את הקובץ תחת שם חדש.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "הקובץ שהועלה הוא העתק מדויק של הגרסה הנוכחית של <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "הקובץ שהועלה הוא העתק מדויק של {{PLURAL:$2|גרסה קודמת|גרסאות קודמות}} של <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "קובץ זה זהה {{PLURAL:$1|לקובץ הבא|לקבצים הבאים}}:",
        "file-deleted-duplicate": "קובץ זהה לקובץ זה ([[:$1]]) נמחק בעבר.\nיש לבדוק את היסטוריית המחיקה של הקובץ לפני העלאתו מחדש.",
        "file-deleted-duplicate-notitle": "קובץ זהה לקובץ זה נמחק בעבר, והכותרת שלו הועלמה.\nיש לבקש ממשתמש שיכול לראות נתונים על קבצים שהועלמו לבדוק את המצב לפני העלאת הקובץ מחדש.",
        "sharedupload-desc-edit": "זהו קובץ מתוך $1 וניתן להשתמש בו גם במיזמים אחרים.\nניתן לערוך את התקציר שלו ב[$2 דף תיאור הקובץ] שם.",
        "sharedupload-desc-create": "זהו קובץ מתוך $1 וניתן להשתמש בו גם במיזמים אחרים.\nניתן לערוך את התקציר שלו ב[$2 דף תיאור הקובץ] שם.",
        "filepage-nofile": "לא קיים קובץ בשם זה.",
-       "filepage-nofile-link": "לא קיים קובץ בשם זה, אך באפשרותך [$1 להעלות אחד].",
+       "filepage-nofile-link": "לא קיים קובץ בשם זה, אך באפשרותך [$1 להעלותו].",
        "uploadnewversion-linktext": "העלאת גרסה חדשה של קובץ זה",
        "shared-repo-from": "מתוך $1",
        "shared-repo": "מקום איחסון משותף",
        "filerevert-submit": "שחזור",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> שוחזר ל[$4 גרסה מ־$3, $2].",
        "filerevert-badversion": "אין גרסה מקומית קודמת של הקובץ שהועלתה בתאריך המבוקש.",
+       "filerevert-identical": "הגרסה הנוכחית של הקובץ כבר זהה לגרסה שנבחרה.",
        "filedelete": "מחיקת $1",
        "filedelete-legend": "מחיקת קובץ",
        "filedelete-intro": "אתם עומדים למחוק את הקובץ <strong>[[Media:$1|$1]]</strong> יחד עם כל היסטוריית הגרסאות שלו.",
        "emailccsubject": "העתק של הודעתך למשתמש $1: $2",
        "emailsent": "הדואר נשלח",
        "emailsenttext": "הודעת הדואר האלקטרוני שלך נשלחה.",
-       "emailuserfooter": "$1 {{GENDER:$1|שלח|שלחה}} את הדוא\"ל הזה ל{{GRAMMAR:תחילית|$2}} באמצעות התכונה \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
+       "emailuserfooter": "$1 {{GENDER:$1|שלח|שלחה}} את הדוא\"ל הזה ל{{GENDER:$2|משתמש|משתמשת}} $2 באמצעות התכונה \"{{int:emailuser}}\" באתר {{SITENAME}}.",
        "usermessage-summary": "השארת הודעת מערכת.",
        "usermessage-editor": "שולח הודעות המערכת",
        "watchlist": "רשימת המעקב",
        "rollbacklinkcount-morethan": "שחזור יותר מ{{PLURAL:$1|עריכה אחת|־$1 עריכות}}",
        "rollbackfailed": "השחזור נכשל",
        "rollback-missingparam": "חסרים פרמטרים נדרשים להגשת הבקשה.",
+       "rollback-missingrevision": "לא ניתן לטעון את המידע על הגרסה.",
        "cantrollback": "לא ניתן לשחזר את העריכה;\nהתורם האחרון הוא היחיד שכתב בדף זה.",
        "alreadyrolled": "לא ניתן לשחזר את העריכה של [[User:$2|$2]] ([[User talk:$2|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) בדף [[:$1]]; הדף כבר נערך או שוחזר.\n\nהעריכה האחרונה הייתה של [[User:$3|$3]] ([[User talk:$3|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "תקציר העריכה היה: <em>$1</em>.",
        "rollback-success": "שוחזר מעריכות של $1 לעריכה האחרונה של $2",
        "rollback-success-notify": "שוחזר מעריכות של $1 לעריכה האחרונה של $2. [$3 הצגת שינויים]",
        "sessionfailure-title": "בעיה בחיבור",
-       "sessionfailure": "נראה שיש בעיה בחיבורכם לאתר;\nפעולתכם בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבכם.\nאנא חזרו לדף הקודם, העלו אותו מחדש ונסו שוב.",
+       "sessionfailure": "נראה שיש בעיה בחיבור שלך לאתר;\nפעולה זו בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבך.\nנא לחזור לדף הקודם, לטעון אותו מחדש ולנסות שוב.",
        "changecontentmodel": "שינוי מודל התוכן של דף",
        "changecontentmodel-legend": "שינוי מודל התוכן",
        "changecontentmodel-title-label": "שם הדף",
        "pageinfo-article-id": "מזהה הדף",
        "pageinfo-language": "שפת התוכן של הדף",
        "pageinfo-content-model": "מודל התוכן של הדף",
+       "pageinfo-content-model-change": "שינוי",
        "pageinfo-robot-policy": "איסוף על־ידי רובוטים של מנועי חיפוש",
        "pageinfo-robot-index": "מותר",
        "pageinfo-robot-noindex": "אסור",
        "tag-filter": "מסנן [[Special:Tags|תגיות]]:",
        "tag-filter-submit": "סינון",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|תגית|תגיות}}]]: $2)",
+       "tag-mw-contentmodelchange": "שינוי מודל התוכן",
+       "tag-mw-contentmodelchange-description": "עריכות ש[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel משנות את מודל התוכן] של דף",
        "tags-title": "תגיות",
        "tags-intro": "דף זה מכיל רשימה של תגיות שהתוכנה יכולה לסמן איתן עריכה, ומשמעויותיהן.",
        "tags-tag": "שם התגית",
        "tags-actions-header": "פעולות",
        "tags-active-yes": "כן",
        "tags-active-no": "לא",
-       "tags-source-extension": "×\94×\95×\92×\93ר ×¢×\9cÖ¾×\99×\93×\99 ×\94ר×\97×\91ה",
+       "tags-source-extension": "×\94×\95×\92×\93ר ×¢×\9cÖ¾×\99×\93×\99 ×\94ת×\95×\9b× ה",
        "tags-source-manual": "מוחל באופן ידני על־ידי משתמשים ובוטים",
        "tags-source-none": "אינו בשימוש כעת",
        "tags-edit": "עריכה",
index 2dff172..cb933de 100644 (file)
        "yourpasswordagain": "कूटशब्द दुबारा लिखें:",
        "createacct-yourpasswordagain": "कूटशब्द की पुष्टि करें",
        "createacct-yourpasswordagain-ph": "कूटशब्द पुनः लिखें",
-       "remembermypassword": "इस ब्राउज़र पर मेरा लॉगिन याद रखें (अधिकतम $1 {{PLURAL:$1|दिन|दिनों}} के लिए)",
        "userlogin-remembermypassword": "मुझे लॉग्ड इन रखें",
        "userlogin-signwithsecure": "सुरक्षित कनेक्शन का प्रयोग करें",
        "cannotloginnow-title": "अभी प्रवेश नहीं हो रहा है",
        "passwordreset-emailelement": "सदस्यनाम: \n$1\n\nअस्थायी कूटशब्द: \n$2",
        "passwordreset-emailsentemail": "यदि आपका यह ईमेल आपके खाते के साथ जोड़ा गया है तो पासवर्ड बदलने का ईमेल इसमें भेज दिया गया है।",
        "passwordreset-emailsentusername": "यदि कोई ईमेल इस खाते से जुड़ी है तो पासवर्ड आपके ईमेल में भेज दिया जाएगा।",
-       "passwordreset-emailsent-capture": "नीचे दिखाया गया कूटशब्द रीसेट ई-मेल भेज दिया गया है।",
-       "passwordreset-emailerror-capture": "नीचे दृष्टित कूटशब्द रीसेट ई-मेल उत्पन्न किया गया था, परंतु उसे {{GENDER:$2|सदस्य}} को भेजना असफल रहा।\nत्रुटि: $1",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|सदस्य}} को ईमेल भेजना विफल : $1 {{PLURAL:$3|सदस्य नाम और पासवर्ड|सदस्य नाम और पासवर्ड की सूची}} नीचे दिया गया है।",
        "passwordreset-invalideamil": "अवैध ईमेल पता",
        "changeemail": "ई-मेल पता परिवर्तित करें",
        "changeemail-header": "अपना ईमेल पता परिवर्तन हेतु इसे पूरा करें। यदि आप अपना वर्तमान ईमेल पता हटाना चाहते हैं, तो इसे खाली छोड़ दें और इसे भेजें।",
-       "changeemail-passwordrequired": "आपको इस परिवर्तन हेतु पासवर्ड (कूटशब्द) डालना होगा।",
        "changeemail-no-info": "इस पृष्ठ का सीधे प्रयोग करने के लिए आपको लॉग इन करना होगा।",
        "changeemail-oldemail": "वर्तमान ई-मेल पता:",
        "changeemail-newemail": "नया ई-मेल पता:",
        "minoredit": "यह एक छोटा बदलाव है",
        "watchthis": "इस पृष्ठ को ध्यानसूची में डालें",
        "savearticle": "पृष्ठ सहेजें",
+       "savechanges": "बदलाव सहेजें",
        "publishpage": "पृष्ठ प्रकाशित करें",
+       "publishchanges": "परिवर्तन प्रकाशित करें",
        "preview": "झलक",
        "showpreview": "झलक दिखाएँ",
        "showdiff": "बदलाव दिखाएँ",
        "undo-nochange": "ऐसा लगता है कि इस सम्पादन को पहले ही पूर्ववत कर दिया गया है।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|वार्ता]]) द्वारा किए बदलाव $1 को पूर्ववत किया",
        "undo-summary-username-hidden": "छुपाए गए सदस्य द्वारा किये बदलाव $1 को पूर्ववत किया",
-       "cantcreateaccounttitle": "खाता खोल नहीं सकते",
        "cantcreateaccount-text": "इस आइ॰पी पते ('''$1''') को खाता निर्मित करने से [[User:$3|$3]] ने प्रतिबंधित किया है।\n\nइसके लिये $3 ने ''$2'' कारण दिया है।",
        "cantcreateaccount-range-text": "<strong>$1</strong> की श्रेणी में आने वाले आई॰पी पतों से, जिसमें आपका आई॰पी पता (<strong>$4</strong>) शामिल है, नए खातों की रचना [[User:$3|$3]] द्वारा अवरोधित की गयी है। \n\n$3 द्वारा दिया गया कारण है: \"$2\"",
        "viewpagelogs": "इस पृष्ठ का लॉग देखें",
        "mw-widgets-dateinput-placeholder-day": "DD-MM-YYYY",
        "mw-widgets-titleinput-description-new-page": "पृष्ठ अभी मौजूद नहीं है",
        "mw-widgets-titleinput-description-redirect": "$1 को अनुप्रेषित",
-       "api-error-blacklisted": "कृपया कोई दूसरा विवरणात्मक शीर्षक चुनें।",
        "sessionmanager-tie": "एक साथ कई अनुरोध को नहीं मिला सकता: $1",
        "sessionprovider-generic": "$1 सत्र",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "कुकी-आधारित सत्र",
index d241144..d8780e1 100644 (file)
        "yourpasswordagain": "Jelszavad ismét:",
        "createacct-yourpasswordagain": "Új jelszó megerősítése",
        "createacct-yourpasswordagain-ph": "Írd be a jelszót újra",
-       "remembermypassword": "Emlékezzen rám ezen a számítógépen (legfeljebb $1 napig)",
        "userlogin-remembermypassword": "Maradjak bejelentkezve",
        "userlogin-signwithsecure": "Biztonságos kapcsolat használata",
+       "cannotlogin-title": "A bejelentkezés nem lehetséges.",
+       "cannotlogin-text": "A bejelentkezés nem lehetséges.",
        "cannotloginnow-title": "Nem lehet most bejelentkezni",
        "cannotloginnow-text": "A bejelentkezés nem lehetséges $1 használatakor.",
+       "cannotcreateaccount-title": "Felhasználói fiók létrehozása sikertelen",
+       "cannotcreateaccount-text": "Közvetlen fiók létrehozása nem engedélyezett ezen a wikin.",
        "yourdomainname": "A domainneved:",
        "password-change-forbidden": "Nem módosíthatod a jelszót ezen a wikin.",
        "externaldberror": "Hiba történt a külső adatbázis hitelesítése közben, vagy nem vagy jogosult a külső fiókod frissítésére.",
        "grant-highvolume": "nagy mennyiségű szerkesztés",
        "grant-oversight": "felhasználók és lapváltozatok elrejtése",
        "grant-patrol": "szerkesztések ellenőrzése",
+       "grant-privateinfo": "Hozzáférés a személyes információkhoz",
        "grant-protect": "lapok védelme és védelem feloldása",
        "grant-rollback": "szerkesztések gyors visszaállítása",
        "grant-sendemail": "e-mail küldése más felhasználóknak",
        "upload-too-many-redirects": "Az URL túl sokszor volt átirányítva",
        "upload-http-error": "HTTP-hiba történt: $1",
        "upload-copy-upload-invalid-domain": "Másolás nem engedélyezett ebből a tartományból.",
+       "upload-dialog-disabled": "Fájl feltöltés ezzel a párbeszéddel tiltott ezen a wikin.",
        "upload-dialog-title": "Fájl feltöltése",
        "upload-dialog-button-cancel": "Mégse",
        "upload-dialog-button-done": "Kész",
        "pageinfo-article-id": "Lapazonosító",
        "pageinfo-language": "Laptartalom nyelve",
        "pageinfo-content-model": "Oldal tartalom modell",
+       "pageinfo-content-model-change": "módosítás",
        "pageinfo-robot-policy": "Indexelés robottal",
        "pageinfo-robot-index": "Engedélyezett",
        "pageinfo-robot-noindex": "Nem engedélyezett",
        "tags-deactivate": "deaktiválás",
        "tags-hitcount": "{{PLURAL:$1|Egy|$1}} változtatás",
        "tags-manage-no-permission": "Nincs engedélyed a változtatások címkéinek kezeléséhez.",
+       "tags-manage-blocked": "Nem tudja kezelni a változás kategóriát, amíg blokkolt.",
        "tags-create-heading": "Új címke létrehozása",
        "tags-create-explanation": "Alapértelmezés szerint az újonnan létrehozott címkék felhasználók és botok által manuálisan hozzáadhatók lesznek.",
        "tags-create-tag-name": "Címke neve:",
        "tags-delete-not-found": "A(z) „$1” címke nem létezik.",
        "tags-delete-too-many-uses": "A(z) „$1” címke több mint $2 lapváltoztatásban szerepel, ezáltal nem törölhető.",
        "tags-delete-warnings-after-delete": "A(z) „$1” címke sikeresen törölve lett, de a következő {{PLURAL:$2|figyelmeztetést|figyelmeztetéseket}} találtam:",
+       "tags-delete-no-permission": "Nincs engedélye a változás címkéinek törléséhez.",
        "tags-activate-title": "Címke aktiválása",
        "tags-activate-question": "Éppen a(z) „$1” címke aktiválására készülsz.",
        "tags-activate-reason": "Indoklás:",
        "log-action-filter-move": "Átnevezés típusa:",
        "log-action-filter-patrol": "Járőrözés típusa:",
        "log-action-filter-protect": "Lapvédelem típusa:",
+       "log-action-filter-upload": "Feltöltés típusa:",
        "log-action-filter-all": "Mind",
        "log-action-filter-block-block": "Blokk",
        "log-action-filter-block-reblock": "Blokk módosítása",
        "log-action-filter-delete-delete": "Laptörlés",
        "log-action-filter-delete-restore": "Visszaállítás",
        "log-action-filter-delete-event": "Naplótörlés",
+       "log-action-filter-managetags-create": "Címke létrehozása",
+       "log-action-filter-managetags-delete": "Címke törlése",
+       "log-action-filter-managetags-activate": "Tag aktiválása",
+       "log-action-filter-newusers-create": "Létrehozás által anonim felhasználó által",
+       "log-action-filter-newusers-create2": "Létrehozás regisztrált felhasználó által",
        "log-action-filter-newusers-autocreate": "Automatikus létrehozás",
+       "log-action-filter-newusers-byemail": "Létrehozás jelszóval, e-mail által küldve",
        "log-action-filter-protect-protect": "Lapvédelem",
        "log-action-filter-protect-unprotect": "Védelem feloldása",
+       "log-action-filter-rights-rights": "Kézi módosítás",
        "log-action-filter-upload-upload": "Új feltöltés",
+       "authmanager-create-disabled": "Új fiók létrehozása tiltva.",
+       "authmanager-create-from-login": "A fiókja létrehozásához, kérjük, töltse ki az alábbi mezőket.",
+       "authmanager-create-not-in-progress": "Fiók létrehozása nincs folyamatban, vagy a folyamat adatai elvesztek. Kérjük, indítsa újra az elejétől.",
+       "authmanager-authplugin-setpass-failed-title": "Jelszó megváltoztatása nem sikerült",
+       "authmanager-authplugin-setpass-failed-message": "A hitelesítés beépülője megtagadta a jelszó módosítását.",
+       "authmanager-authplugin-create-fail": "A hitelesítés beépülője megtagadta a fiók létrehozását.",
+       "authmanager-authplugin-setpass-denied": "A hitelesítés beépülője nem teszi lehetővé a jelszó megváltoztatását.",
+       "authmanager-authplugin-setpass-bad-domain": "Érvénytelen domain.",
+       "authmanager-autocreate-noperm": "Az automatikus fióklétrehozás nem engedélyezett.",
+       "authmanager-autocreate-exception": "A fiókok automatikus létrehozását átmenetileg letiltottuk a korábbi hibák miatt.",
+       "authmanager-userdoesnotexist": "A(z) „$1” felhasználó nincs regisztrálva.",
+       "authmanager-retype-help": "Jelszó még egyszer a megerősítéshez.",
+       "authmanager-email-label": "E-mail",
+       "authmanager-email-help": "E-mail-cím",
+       "authmanager-realname-label": "Valódi név",
+       "authmanager-realname-help": "A felhasználó valódi neve",
+       "authmanager-provider-password": "Jelszó alapú hitelesítés",
+       "authmanager-provider-password-domain": "Jelszó - domain-alapú hitelesítés",
+       "authmanager-provider-temporarypassword": "Ideiglenes jelszó",
        "authprovider-resetpass-skip-label": "Kihagy",
        "cannotauth-not-allowed-title": "Engedély megtagadva",
-       "credentialsform-account": "Fiók neve:"
+       "credentialsform-account": "Fiók neve:",
+       "cannotlink-no-provider-title": "Nincsenek csatolható fiókok",
+       "cannotlink-no-provider": "Nincsenek csatolható fiókok.",
+       "linkaccounts": "A fiókok csatolása",
+       "linkaccounts-success-text": "A fiók csatolva."
 }
index a94a60a..20f83e5 100644 (file)
        "yourpasswordagain": "Կրկնեք գաղտնաբառը",
        "createacct-yourpasswordagain": "Հաստատեք գաղտնաբառը",
        "createacct-yourpasswordagain-ph": "Կրկին մուտքագրեք գաղտնաբառը",
-       "remembermypassword": "Հիշել իմ մուտքը այս դիտարկչում ($1 {{PLURAL:$1|օրից}} ոչ ավել ժամկետով)",
        "userlogin-remembermypassword": "Մուտք գործած մնալ",
        "userlogin-signwithsecure": "Օգտագործել անվտանգ միացում",
        "cannotloginnow-title": "Այժմ դուրս գալ անհնար է",
        "passwordreset-emailtitle": "{{SITENAME}} հաշվի մանրամասները",
        "passwordreset-emailelement": "Մասնակցային անունը՝ \n$1\n\nԺամանակավոր գաղտնաբառը՝ \n$2",
        "passwordreset-emailsentemail": "Ուղարկվեց հիշեցնող էլ․ նամակ։",
-       "passwordreset-emailsent-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։",
-       "passwordreset-emailerror-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։ Սակայն մասնակցին ուղարկելը չհաջողվեց․",
        "changeemail": "Փոխել էլ. հասցեն",
        "changeemail-header": "Փոխել հաշվի էլ․ հասցեն",
        "changeemail-oldemail": "Ներկա էլ․ հասցե․",
        "minoredit": "Սա չնչին խմբագրում է",
        "watchthis": "Հսկել այս էջը",
        "savearticle": "Հիշել էջը",
+       "savechanges": "Պահպանել փոփոխությունները",
+       "publishpage": "Հիշել փոփոխությունները",
+       "publishchanges": "Հիշել փոփոխությունները",
        "preview": "Նախադիտում",
        "showpreview": "Նախադիտել",
        "showdiff": "Կատարված փոփոխությունները",
        "undo-success": "Խմբագրումը կարող է հետ շրջվել։ Ստուգեք տարբերակների համեմատությունը ստորև, որպեսզի համոզվեք, որ դա է ձեզ հետաքրքրող փոփոխությունը և մատնահարեք «Հիշել էջը»՝ գործողությունն ավարտելու համար։",
        "undo-failure": "Խմբագրումը չի կարող հետ շրջվել միջանկյալ խմբագրումների ընդհարման պատճառով։",
        "undo-summary": "Հետ է շրջվում $1 խմբագրումը, որի հեղինակն է՝ [[Special:Contributions/$2|$2]] ([[User talk:$2|քննարկում]]) {{GENDER:$2|մասնակիցը|մասնակցուհին}}",
-       "cantcreateaccounttitle": "Չհաջողվեց ստեղծել մասնակցային հաշիվ",
        "cantcreateaccount-text": "Այս IP-հասցեից ('''$1''') մասնակցային հաշվի ստեղծումը արգելափակվել է [[User:$3|$3]] մասնակցի կողմից։\n\n$3 մասնակիցը տվել է հետևյալ պատճառը. ''$2''",
        "viewpagelogs": "Դիտել այս էջի տեղեկամատյանները",
        "nohistory": "Այս էջը չունի խմբագրումների պատմություն։",
index bcb0af0..fa83ac9 100644 (file)
        "yourpasswordagain": "Repete contrasigno:",
        "createacct-yourpasswordagain": "Confirma contrasigno",
        "createacct-yourpasswordagain-ph": "Repete le contrasigno",
-       "remembermypassword": "Memorar mi contrasigno in iste navigator (pro un maximo de $1 {{PLURAL:$1|die|dies}})",
        "userlogin-remembermypassword": "Mantener mi session aperte",
        "userlogin-signwithsecure": "Usar un connexion secur",
+       "cannotlogin-title": "Impossibile aperir session",
+       "cannotlogin-text": "Non es possibile aperir un session.",
        "cannotloginnow-title": "Impossibile aperir session ora",
        "cannotloginnow-text": "Non es possibile aperir un session usante $1.",
+       "cannotcreateaccount-title": "Impossibile crear contos",
+       "cannotcreateaccount-text": "Le creation directe de contos non es activate in iste wiki.",
        "yourdomainname": "Tu dominio:",
        "password-change-forbidden": "Non es possibile cambiar le contrasigno in iste wiki.",
        "externaldberror": "O il occurreva un error in le base de datos de authentication, o tu non ha le autorisation de actualisar tu conto externe.",
        "botpasswords-updated-body": "Le contrasigno pro le robot \"$1\" del usator \"$2\" ha essite actualisate.",
        "botpasswords-deleted-title": "Contrasigno de robot delite",
        "botpasswords-deleted-body": "Le contrasigno pro le robot \"$1\" del usator \"$2\" ha essite delite.",
-       "botpasswords-newpassword": "Le nove contrasigno pro aperir session con <strong>$1</strong> es <strong>$2</strong>. <em>Per favor, conserva isto pro uso futur.</em>",
+       "botpasswords-newpassword": "Le nove contrasigno pro aperir session con <strong>$1</strong> es <strong>$2</strong>. <em>Per favor, conserva isto pro uso futur.</em> <br> (Pro vetule robots que require que le nomine usate pro aperir session sia le mesme que le nomine de usator final, tu pote etiam usar <strong>$3</strong> como nomine de usator <strong>$4</strong> como contrasigno.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider non es disponibile.",
        "botpasswords-restriction-failed": "Session impedite per restrictiones de contrasigno de robot.",
        "botpasswords-invalid-name": "Iste nomine de usator non contine le separator pro contrasigno de robot (\"$1\").",
        "invalid-content-data": "Datos de contento invalide",
        "content-not-allowed-here": "Le contento \"$1\" non es permittite in le pagina [[$2]]",
        "editwarning-warning": "Quitar iste pagina pote causar le perdita de omne modificationes que tu ha facite.\nSi tu ha aperite un session, tu pote disactivar iste aviso in le section \"{{int:prefs-editing}}\" de tu preferentias.",
+       "editpage-invalidcontentmodel-title": "Modello de contento non supportate",
+       "editpage-invalidcontentmodel-text": "Le modello de contento \"$1\" non es supportate.",
        "editpage-notsupportedcontentformat-title": "Formato de contento non supportate",
        "editpage-notsupportedcontentformat-text": "Le formato de contento $1 non es supportate per le modello de contento $2.",
        "content-model-wikitext": "wikitexto",
        "grant-group-high-volume": "Exequer actiones in massa",
        "grant-group-customization": "Personalisation e perferentias",
        "grant-group-administration": "Exequer actiones administrative",
+       "grant-group-private-information": "Acceder a tu datos private",
        "grant-group-other": "Activitates diverse",
        "grant-blockusers": "Blocar e disblocar usatores",
        "grant-createaccount": "Crear contos",
        "grant-highvolume": "Modification in massa",
        "grant-oversight": "Celar usatores e supprimer versiones",
        "grant-patrol": "Patruliar cambiamentos a paginas",
+       "grant-privateinfo": "Acceder a information private",
        "grant-protect": "Proteger e disproteger paginas",
        "grant-rollback": "Revocar cambiamentos a paginas",
        "grant-sendemail": "Inviar e-mail a altere usatores",
        "file-thumbnail-no": "Le nomine del file comencia con <strong>$1</strong>.\nIllo pare esser un imagine a grandor reducite ''(miniatura)''.\nSi tu possede iste imagine in plen resolution, incarga lo, alteremente cambia le nomine del file per favor.",
        "fileexists-forbidden": "Un file con iste nomine existe ja, e non pote esser superscribite.\nSi tu vole ancora incargar iste file, per favor retorna e usa un nove nomine. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un file con iste nomine existe ja in le repositorio de files commun.\nSi tu vole totevia incargar iste file, per favor retorna e usa un nove nomine. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Le file incargate es un copia exacte del version actual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Le file incargate es un copia exacte de {{PLURAL:$2|un version|versiones}} precedente de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Iste file es un duplicato del sequente {{PLURAL:$1|file|files}}:",
        "file-deleted-duplicate": "Un file identic a iste file ([[:$1]]) esseva ja delite anteriormente. Tu deberea verificar le registro de deletiones concernente iste file ante de re-incargar lo.",
        "file-deleted-duplicate-notitle": "Un file identic a iste file ha essite delite anteriormente, e le titulo ha essite supprimite. Tu deberea demandar a un persona con le privilegio de vider datos de files supprimite a examinar le situation ante de incargar lo de novo.",
        "filerevert-submit": "Reverter",
        "filerevert-success": "'''[[Media:$1|$1]]''' ha essite revertite al [$4 version del $3 a $2].",
        "filerevert-badversion": "Non existe un version local anterior de iste file con le data e hora providite.",
+       "filerevert-identical": "Le version actual del file es jam identic al file seligite.",
        "filedelete": "Deler $1",
        "filedelete-legend": "Deler file",
        "filedelete-intro": "Tu es super le puncto de deler le file '''[[Media:$1|$1]]''' con tote su historia.",
        "rollbacklinkcount-morethan": "revocar plus de $1 {{PLURAL:$1|modification|modificationes}}",
        "rollbackfailed": "Revocation fallite",
        "rollback-missingparam": "Manca parametros obligatori in le requesta.",
+       "rollback-missingrevision": "Impossibile cargar le datos del version.",
        "cantrollback": "Impossibile revocar le modification;\nle ultime contributor es le sol autor de iste pagina.",
        "alreadyrolled": "Non pote revocar le ultime modification de [[:$1]] per [[User:$2|$2]] ([[User talk:$2|discussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nun altere persona ha ja modificate o revocate le pagina.\n\nLe ultime modification esseva facite per [[User:$3|$3]] ([[User talk:$3|discussion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le summario del modification esseva: <em>$1</em>.",
        "pageinfo-article-id": "ID del pagina",
        "pageinfo-language": "Lingua del contento del pagina",
        "pageinfo-content-model": "Modello de contento de pagina",
+       "pageinfo-content-model-change": "cambiar",
        "pageinfo-robot-policy": "Indexation per robots",
        "pageinfo-robot-index": "Permittite",
        "pageinfo-robot-noindex": "Non permittite",
        "tag-filter": "Filtro de [[Special:Tags|etiquettas]]:",
        "tag-filter-submit": "Filtrar",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiquetta|Etiquettas}}]]: $2)",
+       "tag-mw-contentmodelchange": "cambiamento de modello de contento",
+       "tag-mw-contentmodelchange-description": "Modificationes que [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambia le modello de contento] de un pagina",
        "tags-title": "Etiquettas",
        "tags-intro": "Iste pagina lista le etiquettas con le quales le software pote marcar un modification, e lor significato.",
        "tags-tag": "Nomine del etiquetta",
        "tags-actions-header": "Actiones",
        "tags-active-yes": "Si",
        "tags-active-no": "No",
-       "tags-source-extension": "Definite per un extension",
+       "tags-source-extension": "Definite per le software",
        "tags-source-manual": "Applicate manualmente per usatores e robots",
        "tags-source-none": "Non plus in uso",
        "tags-edit": "modificar",
        "linkaccounts-submit": "Ligar contos",
        "unlinkaccounts": "Disligar contos",
        "unlinkaccounts-success": "Le conto ha essite disligate.",
-       "authenticationdatachange-ignored": "Le cambiamento del datos de authentication non ha succedite. Pote esser que nulle fornitor ha essite configurate?"
+       "authenticationdatachange-ignored": "Le cambiamento del datos de authentication non ha succedite. Pote esser que nulle fornitor ha essite configurate?",
+       "userjsispublic": "Nota ben: Subpaginas JavaScript non debe continer datos confidential perque altere usatores pote vider los.",
+       "usercssispublic": "Nota ben: Subpaginas CSS non debe continer datos confidential perque altere usatores pote vider los."
 }
index bc271bd..2d8d4ef 100644 (file)
        "yourpasswordagain": "Ulangi kata sandi:",
        "createacct-yourpasswordagain": "Konfirmasi kata sandi",
        "createacct-yourpasswordagain-ph": "Masukkan lagi kata sandi",
-       "remembermypassword": "Ingat kata sandi saya di komputer ini (selama $1 {{PLURAL:$1|hari|hari}})",
        "userlogin-remembermypassword": "Biarkan saya tetap masuk",
        "userlogin-signwithsecure": "Gunakan server aman",
        "cannotloginnow-title": "Tidak dapat masuk log saat ini",
        "minoredit": "Ini adalah suntingan kecil.",
        "watchthis": "Pantau halaman ini",
        "savearticle": "Simpan halaman",
+       "savechanges": "Simpan perubahan",
        "publishpage": "Terbitkan halaman",
        "preview": "Pratayang",
        "showpreview": "Lihat pratayang",
index 0cca8aa..be841af 100644 (file)
        "undelete_short": "Isubli ti pannakaikkat {{PLURAL:$1|ti maysa a naurnos|dagiti $1 a naurnos}}",
        "viewdeleted_short": "Kitaen {{PLURAL:$1|ti maysa a naikkat a naurnos|dagiti $1 a naikkat a naurnos}}",
        "protect": "Salakniban",
-       "protect_change": "sukatan",
+       "protect_change": "baliwan",
        "protectthispage": "Salakniban daytoy a panid",
        "unprotect": "Sukatan ti salaknib",
        "unprotectthispage": "Sukatan ti salaknib daytoy a panid",
        "yourpasswordagain": "Imakinilya manen ti kontrasenias:",
        "createacct-yourpasswordagain": "Pasingkedan ti kontrasenias",
        "createacct-yourpasswordagain-ph": "Ikabil manen ti kontrasenias",
-       "remembermypassword": "Laglagipem ti iseserrekko iti daytoy a pagbasabasa (para iti kapaut iti $1 {{PLURAL:$1|nga aldaw|nga al-aldaw}})",
        "userlogin-remembermypassword": "Taginayonennak nga iserrek",
        "userlogin-signwithsecure": "Usaren ti natalged a koneksion",
+       "cannotlogin-title": "Saan a makastrek",
+       "cannotlogin-text": "Saan a mabalin ti panagserrek.",
        "cannotloginnow-title": "Saan a mabalin itan iti sumrek",
        "cannotloginnow-text": "Saan a mabalin ti sumrek no agus-usar iti $1.",
+       "cannotcreateaccount-title": "Saan a makapartuat kadagiti pakabilangan",
+       "cannotcreateaccount-text": "Saan a napakabaelan ti dagus a panagpartuat iti pakabilangan iti daytoy a wiki.",
        "yourdomainname": "Ti bukodmo a dominio:",
        "password-change-forbidden": "Saanmo a mabaliwan dagiti kontrasenias iti daytoy a wiki.",
        "externaldberror": "Mabalin nga adda biddut iti pannakapasingked ti database wenno saanka a mapalubosan a mangpabaro ti akinruar a pakabilangam.",
        "botpasswords-updated-body": "Napabaro ti kontrasenias ti bot para iti nagan ti bot iti \"$1\" ni agar-aramat \"$2\".",
        "botpasswords-deleted-title": "Naikkat ti kontrasenias ti bot",
        "botpasswords-deleted-body": "Naikkat ti kontrasenias ti bot para iti nagan ti bot iti \"$1\" ni agar-aramat \"$2\".",
-       "botpasswords-newpassword": "Ti baro a kontrasenias iti panagserrek iti <strong>$1</strong> ket <strong>$2</strong>. <em>Pangngaasi nga irekord daytoy para iti masakbayan a reperensia.</em>",
+       "botpasswords-newpassword": "Ti baro a kontrasenias iti panagserrek iti <strong>$1</strong> ket <strong>$2</strong>. <em>Pangngaasi nga irekord daytoy para iti masakbayan a reperensia.</em> <br> (Para kadagiti daan a bot a makasapul iti nagan iti panagserrek a kapada koma ti kanungpalan a nagan ti agar-aramat, mabalinmo pay ti agusar ti <strong>$3</strong> a kas nagan ti <strong>$4</strong> a kas kontrasenias.)",
        "botpasswords-no-provider": "Saan a magun-od ti BotPasswordsSessionProvider.",
        "botpasswords-restriction-failed": "Ti panangigawid ti kontrasenias ti bot ket nangpawil iti daytoy a panagserrek.",
        "botpasswords-invalid-name": "Ti naibaga a nagan ti agar-aramat ket saan nga aglaon iti panangisina ti kontrasenias ti bot (\"$1\").",
        "publishchanges": "Ipablaak dagiti binaliwan",
        "preview": "Ipadas",
        "showpreview": "Ipakita ti ipadas",
-       "showdiff": "Ipakita dagiti sinukatan",
+       "showdiff": "Ipakita dagiti binaliwan",
        "blankarticle": "<strong>Ballaag:</strong> Ti panid a parpatuatem ket blanko.\nNo pindutem manen ti \"{{int:savearticle}}\", ti panid ket mapartuatto nga awan ti ania man a linaon.",
        "anoneditwarning": "<strong>Ballaag:</strong> Saanka a nakastrek. Ti IP a pagtaengan ket publikonto a makita nga agaramidka iti ania man a panagurnos. No <strong>[$1 sumrekka]</strong> wenno <strong>[$2 agpartuatka iti pakabilangan]</strong>, dagiti inurnosmo ket maitunosto iti naganmo nga agar-aramat, ken dagiti dadduma pay a pagimbagan.",
        "anonpreviewwarning": "<em>Saanka a nakastrek. Ti panagidulin ket agirehistro ti IP a pagtaengam kadagitoy a pakasaritaan ti panagurnos iti daytoy a panid.</em>",
        "invalid-content-data": "Imbalido a datos ti linaon",
        "content-not-allowed-here": "Ti \"$1\" a linaon ket saan a maipalubos iti panid ti [[$2]]",
        "editwarning-warning": "Ti ipapanaw iti daytoy a panid ket makapataud ti pannakapukaw kadagiti ania man a binalbaliwam.\nNo nakastrekka, mabalinmo nga ibaldado daytoy a ballaag iti \"{{int:prefs-editing}}\" a seksion kadagiti kakaykayatam.",
+       "editpage-invalidcontentmodel-title": "Saan a masuportaran ti modelo ti linaon",
+       "editpage-invalidcontentmodel-text": "Saan a nasuportaran ti modelo ti linaon ti \"$1\".",
        "editpage-notsupportedcontentformat-title": "Ti pormat ti linaon ket saan a nasuportaran",
        "editpage-notsupportedcontentformat-text": "Ti pormat ti linaon ti $1 ket saan a nasuportaran babaen ti modelo ti linaon ti $2.",
        "content-model-wikitext": "wikitext",
        "rightslogtext": "Daytoy ket listaan dagiti sinukatan a karbengan ti agar-aramat.",
        "action-read": "agbasa iti datoy a panid",
        "action-edit": "agurnos iti daytoy a panid",
-       "action-createpage": "partuaten daytoy a panid",
+       "action-createpage": "agpartuat iti daytoy a panid",
        "action-createtalk": "partuaten daytoy a pagtungtungan a panid",
        "action-createaccount": "agpartuat iti pakabilangan daytoy nga agar-aramat",
        "action-autocreateaccount": "automatiko a partuaten daytoy nga akinruar a pakabilangan ti agar-aramat",
        "file-thumbnail-no": "Ti nagan ti papeles ket mangrugi iti <strong>$1</strong>.\nKasla ti ladawan a napabassit ti kadakkel <em>(thumbnail)</em>.\nNo addaanka iti daytoy a ladawan iti napno a resolusion ikargam dayta, no saan pangngaasi a sukatam ti nagan ti papeles.",
        "fileexists-forbidden": "Daytoy a nagan ti papeles ket addan, ken saan a mabalin a masuratan manen.\nNo kayatmo pay latta nga ikarga ti papelesmo, pangngaasi nga agsublika ken agusar iti baro a nagan.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Ti papeles iti daytoy a nagan ket addan iti pagbingayan a repositorio ti papeles.\nNo kayatmo pay latta nga ikarga ti papeles, pangngaasi nga agsublika ken agusar iti baro a nagan.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Ti karga ket maysa nga eksakto a duplikado ti agdama a bersion ti <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Ti karga ket maysa nga eksakto a duplikado {{PLURAL:$2|ti maysa a daan a bersion|dagiti daan a bersion}} ti <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Daytoy a papeles ket duplikado kadagiti sumaganad a {{PLURAL:$1|papeles|pappapeles}}:",
        "file-deleted-duplicate": "Ti papeles a kapadpada ti papeles a ([[:$1]]) ket dati a naikkat.\nKitaem koma ti pakasaritaan a pannakaikkat ti papeles sakbay a mangirugika a mangikarga manen.",
        "file-deleted-duplicate-notitle": "Ti papales a kapada iti daytoy a papeles ket dati a naikkat, ken nalapdan ti titulo.\nNasken nga agdamagka iti sabali nga addaan iti abilidad a mangrepaso ti nalapdan a datos ti papeles tapno marepaso ti kasasaad sakbay a mapan nga agikarga manen iti daytoy.",
        "filerevert-submit": "Isubli",
        "filerevert-success": "Ti <strong>[[Media:$1|$1]]</strong> ket naipasubli iti [$4 bersion manipud idi $3, $2].",
        "filerevert-badversion": "Awan ti dati a lokal a bersion iti daytoy a papeles a naited ti dayta nga oras ken petsa.",
+       "filerevert-identical": "Ti agdama a bersion ti papeles ket kapadan iti maysa a napili.",
        "filedelete": "Ikkaten ti $1",
        "filedelete-legend": "Ikkaten ti papeles",
        "filedelete-intro": "Mangrugrugika nga agikkat ti <strong>[[Media:$1|$1]]</strong> ken mairaman amin a pakasaritaanna.",
        "rollbacklinkcount-morethan": "agisubli ti ad-adu ngem $1 {{PLURAL:$1|nga inurnos|nga inur-urnos}}",
        "rollbackfailed": "Napaay ti panangisubli",
        "rollback-missingparam": "Awan dagiti nasken a parametro iti kiddaw.",
+       "rollback-missingrevision": "Saan a maikarga ti datos ti rebision.",
        "cantrollback": "Saan a maisubli ti panagurnos;\nti naudi a nakaaramid ket iti laeng nagsurat iti daytoy a panid.",
        "alreadyrolled": "Saan a maipasubli ti kinaudi a panagurnos iti [[:$1]] babaen ni [[User:$2|$2]] ([[User talk:$2|tungtungan]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nadda sabali a naurnos wenno nagipasubli ti panid.\n\nTi kinaudi a panagurnos ti daytoy a panid ket babaen ni [[User:$3|$3]] ([[User talk:$3|tungtungan]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Ti pakabuklan idi ti panagurnos ket: <em>$1</em>.",
        "tooltip-ca-nstab-template": "Kitaen ti plantilia",
        "tooltip-ca-nstab-help": "Kitaen ti panid ti tulong",
        "tooltip-ca-nstab-category": "Kitaen ti panid ti kategoria",
-       "tooltip-minoredit": "Markaan daytoy a kas bassit a panag-urnos",
-       "tooltip-save": "Idulin dagiti sinukatam",
+       "tooltip-minoredit": "Markaan daytoy a kas bassit a panagurnos",
+       "tooltip-save": "Idulin dagiti binaliwam",
        "tooltip-publish": "Ipablaak dagiti binaliwam",
-       "tooltip-preview": "Ipadas dagiti sinukatam, pangngaasi nga usarem daytoy sakbay nga idulin ti panid!",
-       "tooltip-diff": "Ipakita no ania dagiti sinukatan nga inaramidmo iti testo",
+       "tooltip-preview": "Ipadas dagiti binaliwam. Pangngaasi nga usaren daytoy sakbay nga idulin ti panid.",
+       "tooltip-diff": "Ipakita no ania dagiti binaliwan nga inaramidmo iti teksto",
        "tooltip-compareselectedversions": "Kitaen ti naggidiatan dagiti dua a napili a bersion iti daytoy a panid.",
        "tooltip-watch": "Inayon daytoy a panid iti listaan ti bambantayam",
        "tooltip-watchlistedit-normal-submit": "Ikkaten dagiti titulo",
        "pageinfo-article-id": "ID ti panid",
        "pageinfo-language": "Pagsasao ti naglaon a panid",
        "pageinfo-content-model": "Modelo ti linaon ti panid",
+       "pageinfo-content-model-change": "baliwan",
        "pageinfo-robot-policy": "Panagpasurot babaen dagiti robot",
        "pageinfo-robot-index": "Maipalubos",
        "pageinfo-robot-noindex": "Saan a maipalubos",
        "confirmemail_body_set": "Addaan, baka sika met laeng, manipud ti IP a pagtaengan ti $1,\nket nangikabil ti esurat a pagtaengan ti pakabilangan ti \"$2\" iti daytoy a pagtaengan idiay {{SITENAME}}\n\nTapno mapasingkedan daytoy a pakabilangan ket agpayso a kukuam ken \npakabaelan dagiti esurat a langa idiay {{SITENAME}}, lukatam daytoy a silpo idiay pabasabasam:\n\n$3\n\nNo daytoy a pakabilangan ket *saanmo* a kukua, surutem daytoy a silpo\ntapno ukasen ti panagpasingked ti esurat a pagtaengan:\n\n$5\n\nDaytoy a panagpasingked ti kodigo ket agpaso intono $4.",
        "confirmemail_invalidated": "Naukas ti pammasingked ti esurat a pagtaengam",
        "invalidateemail": "Ukasen ti pammasingked ti esurat",
+       "notificationemail_subject_changed": "Nabaliwanen ti nairehistro nga adres ti esurat ti {{SITENAME}}",
+       "notificationemail_subject_removed": "Naikkaten ti nairehistro nga adres ti esurat ti {{SITENAME}}",
+       "notificationemail_body_changed": "Adda maysa a tao, mabalin a sika, manipud iti adres ti IP ti $1,\nket nangbaliw ti adres ti esurat ti pakabilangan ti \"$2\" iti \"$3\" iti {{SITENAME}}.\n\nNo saan a sika daytoy, dagus a kontaken ti administrador ti sitio.",
+       "notificationemail_body_removed": "Adda maysa a tao, mabalin a sika, manipud iti adres ti IP ti $1,\nket nangikkat ti adres ti esurat ti pakabilangan ti \"$2\" iti {{SITENAME}}.\n\nNo saan a sika daytoy, dagus a kontaken ti administrador ti sitio.",
        "scarytranscludedisabled": "[Nabaldado ti Interwiki panagiraman]",
        "scarytranscludefailed": "[Napaay ti panagala ti plantilia para iti $1]",
        "scarytranscludefailed-httpstatus": "[Napaay ti panagala ti plantilia para iti $1: HTTP $2]",
        "tag-filter": "Sagat ti [[Special:Tags|etiketa]]:",
        "tag-filter-submit": "Sagat",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiketa|Et-etiketa}}]]: $2)",
+       "tag-mw-contentmodelchange": "panagbaliw ti modelo ti linaon",
+       "tag-mw-contentmodelchange-description": "Dagiti panagurnos a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel mangbaliw ti modelo ti linaon] ti panid",
        "tags-title": "Dagiti etiketa",
        "tags-intro": "Daytoy a panid ket ilistana dagiti etiketa a mablin nga usaren ti sopwer a mangmarka iti inurnos, ken dagiti kaibuksilanda.",
        "tags-tag": "Nagan ti etiketa",
        "tags-actions-header": "Dagiti aramid",
        "tags-active-yes": "Wen",
        "tags-active-no": "Saan",
-       "tags-source-extension": "Naipalawag babaen ti maysa a pagpaatiddog",
+       "tags-source-extension": "Naipalawag babaen ti sopwer",
        "tags-source-manual": "Manual a naipakat babaen dagiti agar-aramat ken dagiti bot",
        "tags-source-none": "Saan a maus-usar",
        "tags-edit": "urnosen",
        "feedback-useragent": "Ahente ti agar-aramat:",
        "searchsuggest-search": "Biruken",
        "searchsuggest-containing": "naglaon ti...",
+       "api-error-autoblocked": "Automatiko a naserraan ti IP nga adresmo, gapu ta inusar babaen ti naseraan nga agar-aramat.",
        "api-error-badaccess-groups": "Saanka mapalubosan nga agikarga kadagiti papeles iti daytoy a wiki.",
        "api-error-badtoken": "Akin-uneg a biddut: Dakes a tandaan.",
        "api-error-blocked": "Naserraankan manipud iti panagurnos.",
        "linkaccounts-submit": "Isilpo dagiti pakabilangan",
        "unlinkaccounts": "Ikkaten ti silpo dagiti pakabilangan",
        "unlinkaccounts-success": "Ti pakabilangan ket naikkat iti pannakaisilpo.",
-       "authenticationdatachange-ignored": "Saan a natengngel ti panagbaliw ti datos ti pammasingked. Mabalin nga awan ti nakompigura a mangited?"
+       "authenticationdatachange-ignored": "Saan a natengngel ti panagbaliw ti datos ti pammasingked. Mabalin nga awan ti nakompigura a mangited?",
+       "userjsispublic": "Pangngaasi a laglagipen: Dagiti subpanid ti JavaScript ket nasken a saan nga aglaon iti datos a nailemed gapu ta makita dagitoy babaen dagiti sabali nga agar-aramat.",
+       "usercssispublic": "Pangngaasi a laglagipen: Dagiti subpanid ti CSS ket nasken a saan nga aglaon iti datos a nailemed gapu ta makita dagitoy babaen dagiti sabali nga agar-aramat."
 }
index d781023..41aa59c 100644 (file)
@@ -12,7 +12,8 @@
                        "Wyvernoid",
                        "לערי ריינהארט",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Robin van der Vliet"
                ]
        },
        "tog-underline": "Sub-strekizez ligili:",
        "yourname": "Vua uzantonomo:",
        "yourpassword": "Pasovorto:",
        "yourpasswordagain": "Riskribez la pasovorto:",
-       "remembermypassword": "Memorez mea pasovorto en ca komputoro (maximo: $1 {{PLURAL:$1|dio|dii}})",
        "yourdomainname": "Vua domano:",
        "login": "Enirar",
        "nav-login-createaccount": "Enirar",
        "minoredit": "Ico esas mikra redaktajo",
        "watchthis": "Surveyar ica pagino",
        "savearticle": "Registragar pagino",
+       "publishpage": "Publikigar pagino",
+       "publishchanges": "Publikigar chanji",
        "preview": "Previdar",
        "showpreview": "Previdar",
        "showdiff": "Montrez chanji",
index a8fbc04..fd1d4c4 100644 (file)
@@ -42,6 +42,7 @@
        "tog-watchdefault": "Bæta síðum og skrám sem ég breyti á vaktlistann minn",
        "tog-watchmoves": "Bæta á vaktlistann minn síðum og skrám sem ég færi",
        "tog-watchdeletion": "Bæta síðum og skrám sem ég eyði á vaktlistann minn",
+       "tog-watchuploads": "Bæta nýjum skrám sem ég hleð inn við á vaktlistann minn",
        "tog-watchrollback": "Bæta síðum þar sem ég hef tekið aftur breytingu á vaktlistann minn",
        "tog-minordefault": "Merkja sjálfgefið allar breytingar sem minniháttar",
        "tog-previewontop": "Sýna forskoðun á undan breytingareitnum",
        "yourpasswordagain": "Endurrita lykilorð:",
        "createacct-yourpasswordagain": "Staðfestu lykilorðið",
        "createacct-yourpasswordagain-ph": "Sláðu inn lykilorðið aftur",
-       "remembermypassword": "Muna innskráninguna mína í þessum vafra (í allt að $1 {{PLURAL:$1|dag|daga}})",
        "userlogin-remembermypassword": "Muna innskráningu mína",
        "userlogin-signwithsecure": "Nota örugga tengingu",
        "cannotloginnow-title": "Get ekki skráð inn núna",
        "minoredit": "Þetta er minniháttar breyting",
        "watchthis": "Vakta þessa síðu",
        "savearticle": "Vista síðu",
+       "savechanges": "Vista breytingar",
        "publishpage": "Gefa út síðu",
        "publishchanges": "Gefa út breytingar",
        "preview": "Forskoða",
index b807488..a752071 100644 (file)
@@ -99,7 +99,9 @@
                        "Urielejh",
                        "Matma Rex",
                        "Matteocng",
-                       "Einreiher"
+                       "Einreiher",
+                       "Anto",
+                       "Saracrovetto"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "tog-enotifminoredits": "Inviami una email anche per le modifiche minori di pagine e file",
        "tog-enotifrevealaddr": "Mostra il mio indirizzo nelle e-mail di notifica",
        "tog-shownumberswatching": "Mostra il numero di utenti che hanno la pagina in osservazione",
-       "tog-oldsig": "Firma attuale:",
+       "tog-oldsig": "La tua firma attuale:",
        "tog-fancysig": "Gestisci la firma come wikitesto (senza collegamento automatico)",
        "tog-uselivepreview": "Abilita la funzione ''Live preview'' (anteprima in diretta)",
        "tog-forceeditsummary": "Chiedi conferma se il campo oggetto è vuoto",
        "newwindow": "(si apre in una nuova finestra)",
        "cancel": "Annulla",
        "moredotdotdot": "Altro...",
-       "morenotlisted": "Questo elenco non è completo.",
+       "morenotlisted": "Questo elenco potrebbe essere incompleto.",
        "mypage": "Pagina",
        "mytalk": "discussioni",
        "anontalk": "discussioni",
        "yourpasswordagain": "Ripeti la password:",
        "createacct-yourpasswordagain": "Conferma password",
        "createacct-yourpasswordagain-ph": "Inserisci nuovamente la password",
-       "remembermypassword": "Ricorda la password su questo browser (per un massimo di $1 {{PLURAL:$1|giorno|giorni}})",
        "userlogin-remembermypassword": "Mantienimi collegato",
        "userlogin-signwithsecure": "Usa una connessione sicura",
+       "cannotlogin-text": "Accesso non è possibile.",
        "cannotloginnow-title": "Impossibile accedere ora",
        "cannotloginnow-text": "L'accesso non è possibile quando si sta usando $1.",
+       "cannotcreateaccount-title": "Impossibile creare l'utenza",
+       "cannotcreateaccount-text": "La creazione diretta dell'utenza non è attivata su questo wiki.",
        "yourdomainname": "Specificare il dominio",
        "password-change-forbidden": "Non è possibile modificare le password su questo wiki.",
        "externaldberror": "Si è verificato un errore con il server di autenticazione esterno, oppure non si dispone delle autorizzazioni necessarie per aggiornare il proprio accesso esterno.",
        "botpasswords-updated-body": "La password per il bot di nome \"$1\" dell'utente \"$2\" è stata aggiornata.",
        "botpasswords-deleted-title": "Password bot cancellata",
        "botpasswords-deleted-body": "La password per il bot di nome \"$1\" dell'utente \"$2\" è stata cancellata.",
-       "botpasswords-newpassword": "La nuova password per accedere con <strong>$1</strong> è <strong>$2</strong>. <em>Registratelo per consultazioni future.</em>",
+       "botpasswords-newpassword": "La nuova password per accedere con <strong>$1</strong> è <strong>$2</strong>. <em>Registratelo per consultazioni future.</em> <br> (Per i vecchi bot che richiedono che il nome per accedere sia lo stesso del nome utente, puoi utilizzare <strong>$3</strong> come nome utente e <strong>$4</strong> come password.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider non è disponibile.",
        "botpasswords-restriction-failed": "Le restrizioni di password bot impediscono questo accesso.",
        "botpasswords-invalid-name": "Il nome utente indicato non contiene il separatore per password bot (\"$1\").",
        "invalid-content-data": "Dati contenuti non validi",
        "content-not-allowed-here": "Contenuto in \"$1\" non consentito nella pagina [[$2]]",
        "editwarning-warning": "Lasciare questa pagina potrebbe causare la perdita di tutte le modifiche fatte.\nSe hai effettuato l'accesso, puoi disattivare questo avviso nella sezione \"{{int:prefs-editing}}\" delle tue preferenze.",
+       "editpage-invalidcontentmodel-title": "Modello di contenuto non supportato",
+       "editpage-invalidcontentmodel-text": "Il modello di contenuto \"$1\" non è supportato.",
        "editpage-notsupportedcontentformat-title": "Formato contenuto non supportato",
        "editpage-notsupportedcontentformat-text": "Il formato del contenuto $1 non è supportato dal modello di contenuto $2.",
        "content-model-wikitext": "wikitesto",
        "file-thumbnail-no": "Il nome del file inizia con <strong>$1</strong>; sembra quindi essere una miniatura ''(thumbnail)''.\nSe si dispone dell'immagine nella risoluzione originale, si prega di caricarla. In caso contrario, si prega di cambiare il nome del file.",
        "fileexists-forbidden": "Un file con questo nome esiste già e non può essere sovrascritto. Tornare indietro e modificare il nome con il quale caricare il file. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un file con questo nome esiste già nell'archivio di risorse multimediali condivise. Se si desidera ancora caricare il file, tornare indietro e modificare il nome con il quale caricare il file. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Il file caricato è un duplicato esatto dell'attuale versione di <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Il file caricato è un duplicato esatto di {{PLURAL:$2|una versione precedente|versioni precedenti}} di <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Questo file è un duplicato {{PLURAL:$1|del seguente|dei seguenti}} file:",
        "file-deleted-duplicate": "Un file identico a questo ([[:$1]]) è stato cancellato in passato. Verificare la cronologia delle cancellazioni prima di caricarlo di nuovo.",
        "file-deleted-duplicate-notitle": "Un file identico a questo è stato cancellato in passato, ed il titolo è stato soppresso. Chiedi a qualcuno che ha la possibilità di vedere i file soppressi di esaminare la situazione prima di procedere nuovamente al caricamento.",
        "filerevert-submit": "Ripristina",
        "filerevert-success": "'''Il file [[Media:$1|$1]]''' è stato ripristinato alla [$4 versione del $2, $3].",
        "filerevert-badversion": "Non esistono versioni locali precedenti del file con il timestamp richiesto.",
+       "filerevert-identical": "La versione attuale del file è già identica a quella selezionata.",
        "filedelete": "Cancella $1",
        "filedelete-legend": "Cancella il file",
        "filedelete-intro": "Stai per cancellare il file '''[[Media:$1|$1]]''' con tutta la sua cronologia.",
        "rollbacklinkcount-morethan": "rollback di più di {{PLURAL:$1|una modifica|$1 modifiche}}",
        "rollbackfailed": "Rollback fallito",
        "rollback-missingparam": "Parametri obbligatori mancanti nella richiesta.",
+       "rollback-missingrevision": "Impossibile caricare i dati della versione.",
        "cantrollback": "Impossibile annullare le modifiche; l'utente che le ha effettuate è l'unico ad aver contribuito alla pagina.",
        "alreadyrolled": "Non è possibile annullare le modifiche apportate alla pagina [[:$1]] da parte di [[User:$2|$2]] ([[User talk:$2|discussione]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); un altro utente ha già modificato la pagina oppure ha effettuato il rollback.\n\nLa modifica più recente alla pagina è stata apportata da [[User:$3|$3]] ([[User talk:$3|discussione]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "L'oggetto della modifica era: <em>$1</em>.",
        "tooltip-ca-undelete": "Ripristina la pagina com'era prima della cancellazione",
        "tooltip-ca-move": "Sposta questa pagina (cambia titolo)",
        "tooltip-ca-watch": "Aggiungi questa pagina alla tua lista degli osservati speciali",
-       "tooltip-ca-unwatch": "Elimina questa pagina dalla tua lista degli osservati speciali",
+       "tooltip-ca-unwatch": "Rimuovi questa pagina dalla tua lista degli osservati speciali",
        "tooltip-search": "Cerca all'interno di {{SITENAME}}",
        "tooltip-search-go": "Vai a una pagina con il titolo indicato, se esiste",
        "tooltip-search-fulltext": "Cerca il testo indicato nelle pagine",
        "pageinfo-article-id": "ID della pagina",
        "pageinfo-language": "Lingua del contenuto della pagina",
        "pageinfo-content-model": "Modello del contenuto della pagina",
+       "pageinfo-content-model-change": "cambia",
        "pageinfo-robot-policy": "Indicizzazione per i robot",
        "pageinfo-robot-index": "Consentito",
        "pageinfo-robot-noindex": "Non consentito",
        "confirm-watch-button": "OK",
        "confirm-watch-top": "Aggiungi questa pagina alla tua lista degli osservati speciali?",
        "confirm-unwatch-button": "OK",
-       "confirm-unwatch-top": "Elimina questa pagina dalla tua lista degli osservati speciali?",
+       "confirm-unwatch-top": "Rimuovere questa pagina dalla tua lista degli osservati speciali?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Ripristinare le modifiche di questa pagina?",
        "percent": "$1&#160;%",
        "tag-filter": "Filtra per [[Special:Tags|etichetta]]:",
        "tag-filter-submit": "Filtra",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etichetta|Etichette}}]]: $2)",
+       "tag-mw-contentmodelchange": "modifica modello contenuti",
+       "tag-mw-contentmodelchange-description": "Modifiche che [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambiano il modello di contenuti] di una pagina",
        "tags-title": "Etichette",
        "tags-intro": "Questa pagina elenca le etichette che il software potrebbe associare a una modifica e il loro significato.",
        "tags-tag": "Nome dell'etichetta",
        "tags-actions-header": "Azioni",
        "tags-active-yes": "Sì",
        "tags-active-no": "No",
-       "tags-source-extension": "Definito da un'estensione",
+       "tags-source-extension": "Definito dal software",
        "tags-source-manual": "Applicato manualmente da utenti e bot",
        "tags-source-none": "Non più in uso",
        "tags-edit": "modifica",
index 321c5d4..5f451b9 100644 (file)
@@ -71,7 +71,8 @@
                        "Kana Higashikawa",
                        "Shield-9",
                        "Waiesu",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "組曲師"
                ]
        },
        "tog-underline": "リンクの下線:",
        "yourpasswordagain": "パスワード再入力:",
        "createacct-yourpasswordagain": "パスワード再入力",
        "createacct-yourpasswordagain-ph": "パスワードを再入力",
-       "remembermypassword": "このブラウザーにログイン情報を保存 (最長 $1 {{PLURAL:$1|日|日間}})",
        "userlogin-remembermypassword": "ログイン状態を保持",
        "userlogin-signwithsecure": "安全な接続の使用",
        "cannotloginnow-title": "今はログインできません",
        "feedback-terms": "私のユーザーエージェント情報には、使用ブラウザやオペレーティングシステムのバージョンの情報が含まれており、その情報は私が提供するフィードバックとあわせて公開されることを理解しました。",
        "feedback-termsofuse": "利用規約に従い、フィードバックを提供することに同意します。",
        "feedback-thanks": "ありがとうございます。フィードバックを「[$2 $1]」のページに投稿しました。",
-       "feedback-thanks-title": "ã\81\82ã\82\8aã\81\8cã\81¨ã\81\86ã\81\94ã\81\96ã\81\84ã\81¾ã\81\99!",
+       "feedback-thanks-title": "ã\81\8aé¡\98ã\81\84ã\81\97ã\81¾ã\81\99ï¼\81",
        "feedback-useragent": "ユーザーエージェント:",
        "searchsuggest-search": "検索",
        "searchsuggest-containing": "この語句を全文検索",
        "log-action-filter-block-block": "ブロック",
        "log-action-filter-block-reblock": "ブロック変更",
        "log-action-filter-block-unblock": "ブロック解除",
+       "log-action-filter-contentmodel-change": "コンテンツモデルの変更",
        "log-action-filter-delete-delete": "ページの削除",
        "log-action-filter-delete-restore": "ページの復帰",
        "log-action-filter-delete-event": "記録の削除",
index 52b1e42..0fa8f88 100644 (file)
@@ -35,7 +35,7 @@
        "tog-watchmoves": "Wuwuh kaca lan barkas lih-lihanku nyang pawawanganku",
        "tog-watchdeletion": "Wuwuh kaca lan barkas busakanku nyang pawawanganku",
        "tog-watchuploads": "Wuwuh barkas anyar unggahanku nyang pawawanganku",
-       "tog-watchrollback": "Wuwuh kaca sing tak wurungaké nyang pawawanganku",
+       "tog-watchrollback": "Wuwuh kaca sing takpulihaké nyang pawawanganku",
        "tog-minordefault": "Tengeri kabèh besutan minangka besutan cilik sacara baku",
        "tog-previewontop": "Deleng pratuduh sadurungé mbesut kothak",
        "tog-previewonfirst": "Delelng pratuduh nalika mbesut pisanan",
@@ -85,7 +85,7 @@
        "fri": "Jum",
        "sat": "Set",
        "january": "Januari",
-       "february": "bruari",
+       "february": "bruari",
        "march": "Maret",
        "april": "April",
        "may_long": "Mèi",
        "august": "Agustus",
        "september": "Sèptèmber",
        "october": "Oktober",
-       "november": "Nopèmber",
+       "november": "Novèmber",
        "december": "Dhésèmber",
        "january-gen": "Januari",
-       "february-gen": "bruari",
+       "february-gen": "bruari",
        "march-gen": "Maret",
        "april-gen": "April",
        "may-gen": "Mèi",
        "august-gen": "Agustus",
        "september-gen": "Sèptèmber",
        "october-gen": "Oktober",
-       "november-gen": "Nopèmber",
+       "november-gen": "Novèmber",
        "december-gen": "Dhésèmber",
        "jan": "Jan",
-       "feb": "Pèb",
+       "feb": "Fèb",
        "mar": "Mar",
        "apr": "Apr",
        "may": "Mèi",
        "nov": "Nop",
        "dec": "Dhé",
        "january-date": "Januari $1",
-       "february-date": "Pèbruari $1",
+       "february-date": "Fèbruari $1",
        "march-date": "Maret $1",
        "april-date": "April $1",
        "may-date": "$1 Mèi",
        "august-date": "Agustus $1",
        "september-date": "$1 Sèptèmber",
        "october-date": "Oktober $1",
-       "november-date": "$1 Nopèmber",
+       "november-date": "$1 Novèmber",
        "december-date": "$1 Dhésèmber",
        "period-am": "Isuk-Awan",
        "period-pm": "Soré-Wengi",
        "subcategories": "Anak kategori",
        "category-media-header": "Médhia sajeroning kategori \"$1\"",
        "category-empty": "<em>Kategori iki lagi ora ngandhut artikel utawa médhia.</em>",
-       "hidden-categories": "{{PLURAL:$1|Kategori kadhelikan}}",
-       "hidden-category-category": "Kategori kadhelikan",
+       "hidden-categories": "{{PLURAL:$1|Kategori ndhelik}}",
+       "hidden-category-category": "Kategori ndhelik",
        "category-subcat-count": "{{PLURAL:$2|Kategori iki mung ngandhut saanak kategori ngisor iki.|Kategori iki ngandhut {{PLURAL:$1|anak kategori|$1 anak kategori}} ngisor iki saka gunggung $2 anak kategori.}}",
        "category-subcat-count-limited": "Kategori iki duwé {{PLURAL:$1|anak kategori|$1 anak kategori}} kaya ngisor iki.",
        "category-article-count": "{{PLURAL:$2|Kategori iki mung ngandhut kaca ngisor iki.|{{PLURAL:$1|Kaca|$1 kaca}} ngisor iki ana ing kategori iki saka gunggung $2 kaca.}}",
-       "category-article-count-limited": "Kategori iki ngandhut {{PLURAL:$1|kaca|$1 kaca-kaca}} sing kapacak ing ngisor iki.",
+       "category-article-count-limited": "Kategori iki ngandhut {{PLURAL:$1|kaca|$1 kaca}} sing kapacak ing ngisor iki.",
        "category-file-count": "{{PLURAL:$2|Kategori iki mung isi barkas iki.|{{PLURAL:$1|Barkas|$1 barkas}} iki ana sajeroning kategori iki saka $2 gunggungé.}}",
-       "category-file-count-limited": "Kategori iki ndarbèni {{PLURAL:$1|berkas|$1 berkas-berkas}} sing kapacak ing ngisor iki.",
+       "category-file-count-limited": "Kategori iki duwé {{PLURAL:$1|barkas|$1 barkas}} sing kapacak ing ngisor iki.",
        "listingcontinuesabbrev": "samb.",
-       "index-category": "Kaca kaindhèksan",
-       "noindex-category": "Kaca ora kaindhèksan",
+       "index-category": "Kaca kaindhèks",
+       "noindex-category": "Kaca ora kaindhèks",
        "broken-file-category": "Kaca mawa pranala barkas rusak",
        "about": "Bab",
        "article": "Kaca isi",
        "newwindow": "(buka mawa jendhéla anyar)",
-       "cancel": "Wurungaké",
+       "cancel": "Wurung",
        "moredotdotdot": "Liyané...",
-       "morenotlisted": "Pratélan iki ora jangkep.",
+       "morenotlisted": "Pratélan iki ora wutuh.",
        "mypage": "Kaca",
        "mytalk": "Parembugan",
        "anontalk": "Parembugan",
-       "navigation": "Napigasi",
+       "navigation": "Navigasi",
        "and": "&#32;lan",
        "qbfind": "Golèk",
        "qbbrowse": "Luru",
        "qbmyoptions": "Kaca-kacaku",
        "faq": "Pitakon Kerep",
        "faqpage": "Project:Pitakon Kerep",
-       "actions": "Tumindak",
+       "actions": "Lelabuhan",
        "namespaces": "Jagat aran",
-       "variants": "Parian",
-       "navigation-heading": "Menu napigasi",
+       "variants": "Varian",
+       "navigation-heading": "Menu navigasi",
        "errorpagetitle": "Cacad",
        "returnto": "Bali nyang $1.",
        "tagline": "Saka {{SITENAME}}",
        "searchbutton": "Golèk",
        "go": "Menyang",
        "searcharticle": "Menyang",
-       "history": "Babading kaca",
+       "history": "Sujarah kaca",
        "history_short": "Sujarah",
-       "updatedmarker": "wis inganyaran kawit tekaku sing pungkasan",
-       "printableversion": "Cara cithakan",
+       "updatedmarker": "wis dianyari kawit tekaku mréné pungkasan",
+       "printableversion": "Vèrsi cithak",
        "permalink": "Pranala permanèn",
        "print": "Cithak",
        "view": "Deleng",
        "view-foreign": "Deleng nyang $1",
        "edit": "Besut",
-       "edit-local": "Besut panyandra enggon-enggonan",
+       "edit-local": "Besut andharan enggon-enggonan",
        "create": "Gawé",
        "create-local": "Tambah panyadra enggon-enggonan",
        "editthispage": "Besut kaca iki",
        "create-this-page": "Gawé kaca iki",
        "delete": "Busak",
        "deletethispage": "Busak kaca iki",
-       "undeletethispage": "Wurungaké pambusaking kaca iki",
-       "undelete_short": "Batal busak {{PLURAL:$1|sabesutan|$1 besutan}}",
-       "viewdeleted_short": "Deleng {{PLURAL:$1|sabesutan sing kabusak|$1 besutan sing kabusak}}",
+       "undeletethispage": "Wurung busak kaca iki",
+       "undelete_short": "Wurung busak {{PLURAL:$1|sabesutan|$1 besutan}}",
+       "viewdeleted_short": "Deleng {{PLURAL:$1|sabesutan kabusak|$1 besutan kabusak}}",
        "protect": "Reksa",
        "protect_change": "owah",
        "protectthispage": "Reksa kaca iki",
        "viewcount": "Kaca iki wis diaksès ping {{PLURAL:$1|siji|$1}}.",
        "protectedpage": "Kaca kareksa",
        "jumpto": "Jujug:",
-       "jumptonavigation": "napigasi",
+       "jumptonavigation": "navigasi",
        "jumptosearch": "golèk",
        "view-pool-error": "Nyuwun ngapuro, peladèn lagi sibuk wektu iki.\nKakèhan panganggo sing nyoba mbukak kaca iki.\nEntèni sedhéla sadurungé nyoba ngaksès kaca iki manèh .\n\n$1",
        "generic-pool-error": "Nyuwun pangapura, paladèn saiki nembé arungan.\nKakèhan panganggo sing péngin ndeleng sumber iki.\nEntèna sadhéla sadurungé sampéyan nekani sumber iki manèh.",
        "viewyourtext": "Sampéyan bisa ndeleng lan nyalin sumbering <strong>besutaning sampéyan</strong> ing kaca iki.",
        "protectedinterface": "Kaca iki isiné tèks antarmuka sing dienggo software lan wis dikunci kanggo menghindari kasalahan.",
        "editinginterface": "'''Pènget:''' Panjenengan nyunting kaca sing dianggo nyedyakaké tèks antarmuka kanggo piranti alus.\nPangowahan kaca iki bakal awèh pangaruh marang tampilan antarmuka panganggo kanggoné panganggo liya.\nKanggo terjemahan, mangga nganggo [https://translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], proyèk lokalisasi MediaWiki.",
-       "translateinterface": "Kanggo nambah utawa ngowah pertalan kanggo kabèh wiki, mangga anggoa [https://translatewiki.net/ translatewiki.net] minangka proyèk palokaling MediaWiki.",
+       "translateinterface": "Saperlu nambah utawa ngowah pertalan tumrap kabèh wiki, mangga anggoa [https://translatewiki.net/ translatewiki.net] minangka proyèk panglokaling MediaWiki.",
        "cascadeprotected": "Kaca iki wis direksa saka panyuntingan amerga disertakaké ing {{PLURAL:$1|kaca|kaca-kaca}} ngisor iki sing wis direksa mawa opsi \"runtun\" diaktifaké:\n$2",
        "namespaceprotected": "Panjenengan ora kagungan idin kanggo nyunting kaca ing bilik nama '''$1'''.",
        "customcssprotected": "Sampéyan ora dililakaké nyunting kaca CSS iki amarga kaisi pangaturan pribadi saka panganggo liya.",
        "yourpasswordagain": "Tik manèh tembung wadiné:",
        "createacct-yourpasswordagain": "Netepaké tembung wadi",
        "createacct-yourpasswordagain-ph": "Lebokaké manèh tembung wadiné",
-       "remembermypassword": "Émut tembung sandi kula (salebeting $1 {{PLURAL:$1|dinten|dinten}})",
        "userlogin-remembermypassword": "Gawé amrih aku panggah kalebu",
        "userlogin-signwithsecure": "Nganggo koneksi aman",
        "cannotloginnow-title": "Ora bisa mlebu saiki",
        "cannotchangeemail": "Alamat layang èlèktronik akun ora bisa diganti nèng wiki iki.",
        "emaildisabled": "Situs iki ora bisa ngirim layang èlèktronik.",
        "accountcreated": "Akun wis kagawé",
-       "accountcreatedtext": "Akun panganggo kanggo  [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|wicara]]) wis digawé.",
+       "accountcreatedtext": "Akun panganggo [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|rembug]]) wis digawé.",
        "createaccount-title": "Gawé rékening kanggo {{SITENAME}}",
        "createaccount-text": "Ana wong sing nggawé sawijining akun utawa rékening kanggo alamat e-mail panjenengan ing {{SITENAME}} ($4) mawa jeneng \"$2\" lan tembung sandi \"$3\". Panjenengan disaranaké kanggo mlebu log lan ngganti tembung sandi panjenengan saiki.\n\nPanjenengan bisa nglirwakaké pesen iki yèn akun utawa rékening iki digawé déné sawijining kaluputan.",
        "login-throttled": "Panjenengan wis kakèhan njajal mlebu log.\nTulung nunggu dhisik $1 sadurungé njajal manèh.",
        "botpasswords-label-appid": "Jeneng bot:",
        "botpasswords-label-create": "Gawé",
        "botpasswords-label-update": "Anyari",
-       "botpasswords-label-cancel": "Batal",
+       "botpasswords-label-cancel": "Wurung",
        "botpasswords-label-delete": "Busak",
        "botpasswords-label-resetpassword": "Balèni gawé tembung wadi",
        "resetpass_forbidden": "Tembung wadi ora bisa diganti",
        "minoredit": "Iki besutan cilik",
        "watchthis": "Awasi kaca iki",
        "savearticle": "Simpen kaca",
+       "savechanges": "Simpen owahan",
        "publishpage": "Babar kaca",
        "publishchanges": "Babar owahan",
        "preview": "Pratuduh",
        "missingcommentheader": "'''Pangéling:''' Sampéyan durung nyadhiyakaké judhul/jejer kanggo tanggepan iki.\nYèn Sampéyan klik \"{{int:savearticle}}\" manèh, suntingan Sampéyan bakal kasimpen tanpa kuwi.",
        "summary-preview": "Pratuduh tingkesan:",
        "subject-preview": "Prawuryaning jejer:",
+       "previewerrortext": "Cacad dumadi nalika njajal mratuduh owahanmu.",
        "blockedtitle": "Panganggo kapalangan",
        "blockedtext": "<b>Asma panganggo utawa alamat IP panjenengan diblokir.</b>\n\nBlokir iki sing nglakoni $1.\nAlesané <i>$2</i>.\n\n* Diblokir wiwit: $8\n* Kadaluwarsa pemblokiran ing: $6\n* Sing arep diblokir: $7\n\nPanjenengan bisa ngubungi $1 utawa [[{{MediaWiki:Grouppage-sysop}}|pangurus liyané]] kanggo ngomongaké prakara iki.\n\nPanjenengan ora bisa nggunakaké fitur 'Kirim layang é-mail panganggo iki' kejaba panjenengan wis nglebokaké alamat é-mail sing sah ing [[Special:Preferences|prèferènsi]] panjenengan.\n\nAlamat IP panjenengan iku $3, lan ID pamblokiran iku #$5.\nTulung kabèh informasi ing ndhuwur iki disertakaké ing saben pitakon panjenengan.",
        "autoblockedtext": "Alamat IP panjenangan wis diblokir minangka otomatis amerga dienggo déning panganggo liyané. Pamblokiran dilakoni déning $1 mawa alesan:\n\n:''$2''\n\n* Diblokir wiwit: $8\n* Blokir kadaluwarsa ing: $6\n* Sing dikarepaké diblokir: $7\n\nPanjenengan bisa ngubungi $1 utawa [[{{MediaWiki:Grouppage-sysop}}|pangurus liyané]] kanggo ngomongaké perkara iki.\n\nPanjenengan ora bisa nganggo fitur \"kirim e-mail panganggo iki\" kejaba panjenengan wis nglebokaké alamat e-mail sing sah ing [[Special:Preferences|prèferènsi]] panjenengan lan panjenengan wis diblokir kanggo nggunakaké.\n\nID pamblokiran panjenengan iku #$5 lan alamat IP panjenengan iku $3. Tulung sertakna informasi ing dhuwur kabèh iki saben ngajokaké pitakonan panjenengan. Matur nuwun.",
        "userjsyoucanpreview": "'''Tips:''' Gunakna tombol \"{{int:showpreview}}\" kanggo ngetès JavaScript anyar panjenengan sadurungé disimpen.",
        "usercsspreview": "'''Pèngeten yèn panjenengan namung mirsani pratilik CSS panjenengan.''''\n'''Pratilik iku durung kasimpen!'''",
        "userjspreview": "'''Pèngeten yèn sing panjenengan pirsani namung pratilik JavaScript panjenengan, lan menawa pratilik iku dèrèng kasimpen!'''",
-       "sitecsspreview": "'''Èling yèn Sampéyan mung ndelok pratayang CSS iki.'''\n'''Iki durung disimpen!'''",
-       "sitejspreview": "'''Èling yèn Sampéyan mung ndelok pratayang kodhé JavaScript iki.'''\n'''Iki durung disimpen!'''",
+       "sitecsspreview": "<strong>Élinga yèn Sampéyan mung mratuduh CSS iki.\nIki durung kasimpen!</strong>",
+       "sitejspreview": "<strong>Élinga yèn Sampéyan mung mratuduh kodhé JavaScript iki.\nIki durung kasimpen!</strong>",
        "userinvalidcssjstitle": "'''Pènget:''' Kulit \"$1\" ora ditemokaké. Muga dipèngeti yèn kaca .css lan .js nggunakaké huruf cilik, conto {{ns:user}}:Foo/vector.css lan dudu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Kaanyaran)",
        "note": "<strong>Cathetan:</strong>",
-       "previewnote": "'''Èling yèn Sampéyan mung ndelok pratayang.'''\nOwahan Sampéyan durung kasimpen!",
+       "previewnote": "<strong>Élinga yèn iki mung pratuduh.</strong>\nOwahanmu durung kasimpen!",
        "continue-editing": "Menyang pambesutan",
        "previewconflict": "Pratilik iki nuduhaké tèks ing bagian dhuwur kothak suntingan tèks kayadéné bakal katon yèn panjenengan bakal simpen.",
        "session_fail_preview": "'''Nuwun sèwu, suntingan panjenengan ora bisa diolah amarga dhata sèsi kabusak.\nCoba kirim dhata manèh. Yèn tetep ora bisa, coba log metua lan mlebu log manèh.''''''Amerga wiki iki marengaké panggunan kodhe HTML mentah, mula pratilik didhelikaké minangka pancegahan marang serangan JavaScript.'''\n'''Menawa iki sawijining usaha panyuntingan sing sah, mangga dicoba manèh.\nYèn isih tetep ora kasil, cobanen metu log lan mlebu manèh.'''",
        "page_last": "pungkasan",
        "histlegend": "Kanggo mbandhingaké: tandhani kothak radhio révisi-révisi sing arep dibandhingaké lan pencèt ''Enter'' utawa tombol sing ana ing ngisor.<br />\nLegéndha: <strong>({{int:cur}})</strong> = béda karo révisi pungkasan, <strong>({{int:last}})</strong> = béda karo révisi sadurungé, <strong>{{int:minoreditletter}}</strong> = besutan cilik.",
        "history-fieldset-title": "Luru sujarah",
-       "history-show-deleted": "Namung sing dibusak",
-       "histfirst": "suwé dhéwé",
+       "history-show-deleted": "Mligi sing dibusak",
+       "histfirst": "lawas dhéwé",
        "histlast": "anyar dhéwé",
        "historysize": "($1 {{PLURAL:$1|bét|bét}})",
        "historyempty": "(suwung)",
        "rev-suppressed-diff-view": "Sawiji benahan saka prabédan iki wis '''dibrèdèl'''.\nSampéyan isih bisa ndelok prabédan iki; rincian bisa ditemokaké nèng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log pambrèdèlan].",
        "rev-delundel": "Owah kasatmatan",
        "rev-showdeleted": "tuduhaké",
-       "revisiondelete": "Busak/batal busak revisi",
-       "revdelete-nooldid-title": "Target revisi ora ditemokaké",
+       "revisiondelete": "Busak/wurung busak révisi",
+       "revdelete-nooldid-title": "Rèvisi tujuan ora sah",
        "revdelete-nooldid-text": "Panjenengan durung mènèhi target revisi kanggo nglakoni fungsi iki.",
        "revdelete-no-file": "Berkas sing dituju ora ana.",
        "revdelete-show-file-confirm": "Apa panjenengan yakin arep mirsani révisi sing wis kabusak saka berkas \"<nowiki>$1</nowiki>\" ing $2, jam $3?",
        "revdelete-show-file-submit": "Iya",
        "logdelete-selected": "{{PLURAL:$1|Log kapilih|Log kapilih}} kanggo:",
+       "revdelete-text-others": "Administrator liya isih bisa ngaksès isian ndhelik lan mulihaké iku saka pambusakan, kajaba rereksan tambahan disetèl.",
        "revdelete-confirm": "Mangga pesthèkaké yèn Sampéyan pancèn kudu nglakoni iki, yèn Sampéyan ngerti akibaté, lan yèn Sampéyan ngakoni iki cocok karo [[{{MediaWiki:Policy-url}}|kawicakan]].",
        "revdelete-suppress-text": "Pandhelikan révisi '''mung''' bisa dipigunakaké kanggo kasus ing ngisor:\n* Informasi sing kagolong pitnah\n* Informasi pribadi sing kurang pantes\n*: ''alamat omah lan nomer telepon, nomer kartu idhèntitas, lsp..''",
        "revdelete-legend": "Atur watesan:",
        "mergehistory-fail": "Ora bisa nggabung sajarah, coba dipriksa manèh kacané lan paramèter wektuné.",
        "mergehistory-no-source": "Kaca sumber $1 ora ana.",
        "mergehistory-no-destination": "Kaca paran $1 ora ana.",
-       "mergehistory-invalid-source": "Irah-irahan kaca sumber kudu irah-irahan utawa judhul sing bener.",
-       "mergehistory-invalid-destination": "Irah-irahan kaca tujuan kudu irah-irahan utawa judhul sing bener.",
+       "mergehistory-invalid-source": "Kaca sumber kudu asesirah sing sah.",
+       "mergehistory-invalid-destination": "Kaca tujuan kudu asesirah sing sah.",
        "mergehistory-autocomment": "Nggabung [[:$1]] menyang [[:$2]]",
        "mergehistory-comment": "Nggabung [[:$1]] menyang [[:$2]]: $3",
        "mergehistory-same-destination": "Jeneng kaca sumber lan tujuan ora kena padha",
        "lineno": "Larik $1:",
        "compareselectedversions": "Bandhingna vèrsi kapilih",
        "showhideselectedversions": "Tampilaké/dhelikaké révisi kapilih",
-       "editundo": "wurungaké",
+       "editundo": "wurung",
        "diff-empty": "(Ora ana bedane)",
        "diff-multi-sameuser": "({{PLURAL:$1|Saowahan madya|$1 owahan madya}} déning panganggo sing padha ora dituduhaké)",
        "diff-multi-manyusers": "({{PLURAL:$1Siji rèvisi sedhengan|$1 rèvisi sedhengan}} déning luwih saka $2 {{PLURAL:$2|panganggo|panganggo}} ora dituduhaké)",
        "difference-missing-revision": "{{PLURAL:$2|Sak pambenahan|$2 pambenahan}} saka prabédan iki ($1) {{PLURAL:$2|ora ditemokaké|ora ditemokaké}}.\n\nIki biasané kasebab pranala prabedan sing wis ora kanggo saka kaca isi wis dibusak.\nRinciané bisa ditemokaké nèng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
        "searchresults": "Kasiling golèk",
        "searchresults-title": "Kasiling golèk \"$1\"",
-       "titlematches": "Irah-irahan artikel sing cocog",
+       "titlematches": "Sesirah kaca cocog",
        "textmatches": "Tèks artikel sing cocog",
        "notextmatches": "Ora ana tèks kaca sing cocog",
        "prevn": "{{PLURAL:$1|$1}} sadurungé",
        "searchmenu-exists": "'''Ana kaca kanthi jeneng \"[[$1]]\" ing wiki iki'''",
        "searchmenu-new": "<strong>Gawéa kaca \"[[:$1]]\" nyang wiki iki!</strong> {{PLURAL:$2|0=|Uga delenga kaca sing katemu sarana panggolèking sampéyan.|Uga delenga kasiling panggolèk.}}",
        "searchprofile-articles": "Kaca isi",
-       "searchprofile-images": "Sarwamadya",
+       "searchprofile-images": "Multimédhia",
        "searchprofile-everything": "Samubarang",
        "searchprofile-advanced": "Lungidan",
        "searchprofile-articles-tooltip": "Golèkan ing $1",
        "yourvariant": "Werna basa isi:",
        "prefs-help-variant": "Varian utawa ortograpi sing Sampéyan pilih kanggo nampilaké kaca kontèn saka wiki iki.",
        "yournick": "Asma sesinglon/samaran (kagem tapak asta):",
-       "prefs-help-signature": "Komentar ing kaca wicara kudu ditapak astani nganggo \"<nowiki>~~~~</nowiki>\" sing bakal dikonvèrsi dadi tapak asta panjenengan lan tanggal wektu.",
+       "prefs-help-signature": "Tanggapan ing kaca parembugan kudu ditandhatangani mawa \"<nowiki>~~~~</nowiki>\", sing bakal salin dadi tandha tangan lan cap wektumu.",
        "badsig": "Tapak astanipun klèntu; cèk rambu HTML.",
        "badsiglength": "Tapak asta panjenengan kedawan.\nAja luwih saka {{PLURAL:$1|karakter|karakter}}.",
        "yourgender": "Kepiyé sampéyan medhar priangganing sampéyan?",
        "prefs-timeoffset": "Format wektu",
        "prefs-advancedediting": "Pilihan sabanjuré",
        "prefs-editor": "Wong besut",
-       "prefs-preview": "Pratayang",
+       "prefs-preview": "Pratuduh",
        "prefs-advancedrc": "Opsi lanjutan",
        "prefs-advancedrendering": "Opsi lanjutan",
        "prefs-advancedsearchoptions": "Opsi lanjutan",
        "right-writeapi": "Nganggo API tulis",
        "right-delete": "Busak kaca-kaca",
        "right-bigdelete": "Busak kaca-kaca mawa sajarah panyuntingan sing gedhé",
-       "right-deletelogentry": "Busak lan batalaké mbusak isi log spésipik",
+       "right-deletelogentry": "Busak lan wurung busak èntri log tartamtu",
        "right-deleterevision": "Busak lan batal busak révisi tartamtu kaca-kaca",
        "right-deletedhistory": "Ndeleng sajarah èntri-èntri kabusak, tanpa bisa ndeleng apa sing dibusak",
        "right-deletedtext": "Delok tèks kabusak lan panggantèn antara rèpisi kabusak",
        "right-browsearchive": "Golèk kaca-kaca sing wis dibusak",
-       "right-undelete": "Batal busak sawijining kaca",
+       "right-undelete": "Wurung busak kaca",
        "right-suppressrevision": "Ndeleng lan mbalèkaké révisi-révisi sing didelikaké saka para opsis",
        "right-suppressionlog": "Ndeleng log-log pribadi",
        "right-block": "Blokir panganggo-panganggo liya saka panyuntingan",
        "right-markbotedits": "Tandhani besutan kawurungan minangka besutan bot",
        "right-noratelimit": "Ora dipengaruhi déning wates cacahing suntingan.",
        "right-import": "Impor kaca-kaca saka wiki liya",
-       "right-importupload": "Impor kaca-kaca saka sawijining pangunggahan berkas",
+       "right-importupload": "Impor kaca saka unggahan barkas",
        "right-patrol": "Tandhanana suntingan minangka wis dipatroli",
        "right-autopatrol": "Gawé supaya suntingan-suntingan ditandhani minangka wis dipatroli",
        "right-patrolmarks": "Ndeleng tandha-tandha patroli owah-owahan anyar",
        "grant-createaccount": "Gawé akun",
        "grant-createeditmovepage": "Gawé, besut, lan lih kaca",
        "grant-delete": "Busak kaca, owahan, lan isian cathetan",
+       "grant-editinterface": "Besut jagad aran MediaWiki lan CSS/JavaScript panganggo",
+       "grant-editmycssjs": "Besut CSS/JavaScript panganggomu",
+       "grant-editmyoptions": "Besut préferènsi panganggomu",
        "newuserlogpage": "Log naraguna anyar",
        "newuserlogpagetext": "Ing ngisor iki kapacak log pandaftaran panganggo anyar.",
-       "rightslog": "Log pangowahan hak aksès",
+       "rightslog": "Log hak panganggo",
        "rightslogtext": "Ing ngisor iki kapacak log pangowahan marang hak-hak panganggo.",
-       "action-read": "maca kaca iki",
+       "action-read": "waca kaca iki",
        "action-edit": "besut kaca iki",
-       "action-createpage": "nggawé kaca-kaca",
-       "action-createtalk": "gawé kaca wicara anyar",
+       "action-createpage": "gawé kaca iki",
+       "action-createtalk": "gawé kaca parembugan iki",
        "action-createaccount": "gawé akun panganggo iki",
        "action-minoredit": "tandhani iki minangka besutan cilik",
-       "action-move": "alihna kaca iki",
+       "action-move": "alih kaca iki",
        "action-move-subpages": "mindahaké kaca iki, lan kabèh anak-kacané",
        "action-move-rootuserpages": "ngalih kaca panganggo oyod",
-       "action-movefile": "lih barkas iki",
-       "action-upload": "ngunggahaké berkas iki",
+       "action-move-categorypages": "alih kaca kategori",
+       "action-movefile": "alih barkas iki",
+       "action-upload": "unggah barkas iki",
        "action-reupload": "nindhih berkas sing wis ana",
        "action-reupload-shared": "nindhih berkas sing wis ana ing papan panyimpanan berkas sing dianggo bebarengan",
        "action-upload_by_url": "unggahna berkas iki saka sawijining alamat URL",
        "action-deleterevision": "busak revisi iki",
        "action-deletedhistory": "pirsani sajarah kaca sing wis dibusak iki",
        "action-browsearchive": "nggolèki kaca-kaca sing wis dibusak",
-       "action-undelete": "mbatalaké pambusakan kaca iki",
+       "action-undelete": "wurung busak kaca iki",
        "action-suppressrevision": "ninjo lan mbalèkaké revisi sing didhelikaké iki",
        "action-suppressionlog": "mirsani log pribadi iki",
        "action-block": "malang panganggo iki mbesut",
        "uploadnologin": "Durung mlebu log",
        "uploadnologintext": "Sampéyan kudu $1 supaya bisa ngunggah berkas.",
        "upload_directory_missing": "Direktori pamunggahan ($1) ora ditemokaké lan ora bisa digawé déning server wèb.",
-       "upload_directory_read_only": "Dirèktori pangunggahan ($1) ora bisa ditulis déning server wèb.",
+       "upload_directory_read_only": "Dhirèktori pangunggahan ($1) ora bisa ditulis déning paladèn jaringan.",
        "uploaderror": "Kaluputan pangunggahan berkas",
        "upload-recreate-warning": "'''Pèngetan: Berkas mawa jeneng kuwi wis dibusak utawa disingkiraké.'''\n\nLog pambusakan lan panyingkiran saka kaca iki sumadhiya nèng kéné:",
        "uploadtext": "Anggé formulir ing ngandhap punika kanggé nginggahaké gambar.\nKanggé mirsani utawi madosi gambar ingkang sampun dipununggah sakdèrèngipun pigunakaken [[Special:FileList|dhaftar berkas sing wis diunggah]], gambar ingkang dipununggah ulang ugi kadhaftar ing [[Special:Log/upload|log pangunggahan]], pambusakan ing [[Special:Log/delete|Log pambusakan]].\n\nKanggé nyertakaken gambar ing satunggiling kaca, pigunakaken pranala salah setunggal saking format ing ngandhap punika:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Berkas.jpg]]</nowiki></code>''' kanggé migunakaken versi pepak gambar\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Berkas.png|200px|thumb|left|tèks alt]]</nowiki></code>''' kanggé migunakaken gambar wiyaripun 200 piksel ing kothak ing sisih kiwa kanthi 'tèks alt' minangka panjelasan\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Berkas.ogg]]</nowiki></code>''' kanggé nggandhèng langsung dhumateng gambar tanpi nampilaké gambar",
        "upload-permitted": "Jenis berkas sing diidinaké: $1.",
        "upload-preferred": "Jenis berkas sing disaranaké: $1.",
        "upload-prohibited": "Jenis berkas sing dilarang: $1.",
-       "uploadlogpage": "Log pangunggahan",
+       "uploadlogpage": "Log unggah",
        "uploadlogpagetext": "Ing ngisor iki kapacak log pangunggahan berkas sing anyar dhéwé.\nMangga mirsani [[Special:NewFiles|galeri berkas-berkas anyar]] kanggo pratélan visual.",
        "filename": "Jeneng barkas",
        "filedesc": "Tingkesan",
        "img-auth-accessdenied": "Aksès ditulak",
        "img-auth-nopathinfo": "Kélangan PATH_INFO.\nSasana Sampéyan durung disetèl kanggo ngliwati inpormasi iki.\nMungkin amarga abasis-CGI lan ora bisa nyengkuyung img_auth.\nDelok https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Alur sing dijaluk dudu dirèktori unggah kakonpigurasi.",
-       "img-auth-badtitle": "Ora bisa mbangun judhul sah saka \"$1\".",
+       "img-auth-badtitle": "Ora bisa ngyasa sesirah sing sah saka \"$1\".",
        "img-auth-nologinnWL": "Sampéyan durung mlebu log lan \"$1\" ora nèng daptar putih.",
        "img-auth-nofile": "Berkas \"$1\" ora ana.",
        "img-auth-isdir": "Sampéyan lagi njajal ngaksès dirèktori \"$1\".\nNamung aksès berkas sing dililakaké.",
        "license": "Jenis lisènsi:",
        "license-header": "Pamalilah",
        "nolicense": "Durung ana sing dipilih",
-       "license-nopreview": "(Pratayang ora sumedya)",
+       "license-nopreview": "(Pratuduh ora sumadhiya)",
        "upload_source_url": " (sawijining URL absah sing bisa diaksès publik)",
        "upload_source_file": " (sawijining berkas ing komputeré panjenengan)",
        "listfiles-summary": "Kaca mirunggan iki nuduhaké kabèh barkas sing kaunggah.",
        "statistics-header-hooks": "Statistik liya",
        "statistics-articles": "Kaca-kaca isi",
        "statistics-pages": "Gunggung kaca",
-       "statistics-pages-desc": "Kabèh kaca ing wiki iki, klebu kaca wicara, pangalihan, lan liya-liyané.",
+       "statistics-pages-desc": "Kabèh kaca ing wiki iki, kalebu kaca parembugan, alihan, lsp.",
        "statistics-files": "Berkas sing diunggahaké",
        "statistics-edits": "Gunggung suntingan wiwit {{SITENAME}} diwiwiti",
        "statistics-edits-average": "Rata-rata suntingan saben kaca",
        "unusedimages": "Berkas sing ora dienggo",
        "wantedcategories": "Kategori sing diperlokaké",
        "wantedpages": "Kaca sing dipèngini",
-       "wantedpages-badtitle": "Judhul ora valid ing sèt asil: $1",
+       "wantedpages-badtitle": "Sesirah ora sah ing omboyakan kasil: $1",
        "wantedfiles": "Berkas sing diperlokaké",
        "wantedfiletext-cat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>. Lan, kaca sing nyartakaké berkas sing ora ana bakal kadaptar nèng [[:$1]].",
        "wantedfiletext-nocat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>.",
        "protectedpages-page": "Kaca",
        "protectedpages-expiry": "Kadaluwarsa",
        "protectedpages-reason": "Alesan",
-       "protectedtitles": "Irah-irahan sing direksa",
-       "protectedtitlesempty": "Ora ana irah-irahan utawa judhul sing direksa karo paramèter-paramèter iki.",
+       "protectedtitles": "Sesirah direksa",
+       "protectedtitlesempty": "Ora ana sesirah sing saiki kareksa mawa paramèter iki.",
        "listusers": "Daftar panganggo",
        "listusers-editsonly": "Tampilaké mung panganggo sing nduwèni kontribusi",
        "listusers-creationsort": "Urut miturut tanggal digawé",
        "usercreated": "{{GENDER:$3|Digawé}} $1 wanci $2",
        "newpages": "Kaca anyar",
        "newpages-username": "Asma panganggo:",
-       "ancientpages": "Kaca-kaca langkung sepuh",
+       "ancientpages": "Kaca paling lawas",
        "move": "Pindhahen",
        "movethispage": "Lih kaca iki",
        "unusedimagestext": "Berkas-berkas sing kapacak iki ana nanging ora dienggo ing kaca apa waé.\nTulung digatèkaké yèn situs wèb liyané mbok-menawa bisa nyambung ing sawijining berkas sacara langsung mawa URL langsung, lan berkas-berkas kaya mengkéné iku mbok-menawa ana ing daftar iki senadyan ora dienggo aktif manèh.",
        "nopagetitle": "Kaca tujuan ora ditemokaké",
        "nopagetext": "Kaca sing panjenengan tuju ora ditemokaké.",
        "pager-newer-n": "{{PLURAL:$1|1 luwih anyar|$1 luwih anyar}}",
-       "pager-older-n": "{{PLURAL:$1|1 sing luwih lawas|$1 sing luwih lawas}}",
+       "pager-older-n": "{{PLURAL:$1|1 luwih lawas|$1 luwih lawas}}",
        "suppress": "Dhelikaké",
        "querypage-disabled": "Kaca kusus iki dipatèni kanggo alesan kinerja.",
        "apisandbox": "Kothak wedhi API",
        "all-logs-page": "Kabèh log publik",
        "alllogstext": "Gabungan tampilam kabèh log sing ana ing {{SITENAME}}.\nPanjenengan bisa mbatesi tampilan kanthi milih jinis log, jeneng panganggo (sènsitif aksara gedhé/cilik), utawa kaca sing magepokan (uga sènsitif aksara gedhé/cilik).",
        "logempty": "Ora ditemokaké èntri log sing pas.",
-       "log-title-wildcard": "Golèk irah-irahan utawa judhul sing diawali mawa tèks kasebut",
+       "log-title-wildcard": "Golèk sesirah sing diwiwiti tulisan iki",
        "showhideselectedlogentries": "Tuduhalé/dhelikaké èntri log kapilih",
        "allpages": "Kabèh kaca",
        "nextpage": "Kaca sabanjuré ($1)",
        "categories": "Kategori",
        "categoriespagetext": "{{PLURAL:$1|kategori ing ngisor iki ngandhut|kategori ing ngisor iki ngandhut}} kaca utawa media.\n[[Special:UnusedCategories|Kategori sing ora dianggo]] ora ditampilaké ing kéné.\nDeleng uga [[Special:WantedCategories|kategori sing diperlokaké]].",
        "categoriesfrom": "Tampilaké kategori-kategori diwiwiti saka:",
-       "deletedcontributions": "Sumbanganing panganggo sing dibusak",
+       "deletedcontributions": "Sumbangan panganggo sing dibusak",
        "deletedcontributions-title": "Sumbanganing panganggo sing dibusak",
        "sp-deletedcontributions-contribs": "sumbangan",
        "linksearch": "Golèkan pranala njaba",
        "linksearch-error": "''Wildcards'' namung bisa dienggo ing bagéyan awal saka jeneng host.",
        "listusersfrom": "Tuduhna panganggo sing diawali karo:",
        "listusers-submit": "Tuduhna",
-       "listusers-noresult": "Panganggo ora ditemokaké.",
+       "listusers-noresult": "Naraguna ora ana.",
        "listusers-blocked": "(diblokir)",
        "activeusers": "Dhaptar panganggo aktif",
        "activeusers-intro": "Iki daptar panganggo sing katon lakuné ing $1 {{PLURAL:$1|dina|dina}} kapungkur.",
        "activeusers-from": "Tampilna panganggo wiwit saka:",
        "activeusers-hidebots": "Delikna bot",
        "activeusers-hidesysops": "Delikna pangurus",
-       "activeusers-noresult": "Panganggo ora ditemokaké.",
+       "activeusers-noresult": "Naraguna ora ana.",
        "listgrouprights": "Hak-hak grup panganggo",
        "listgrouprights-summary": "Ing ngisor iki kapacak dhaftar grup panganggo sing didéfinisi ing wiki iki, kanthi hak-hak aksès gandhèngané.\nInformasi tambahan perkara hak-hak individual bisa ditemokaké ing [[{{MediaWiki:Listgrouprights-helppage}}|kéné]].",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Hak sing diidinaké</span>\n* <span class=\"listgrouprights-revoked\">Hak sing dijabel</span>",
        "delete-toobig": "Kaca iki ndarbèni sajarah panyuntingan sing dawa, yaiku ngluwihi $1 {{PLURAL:$1|revision|révisi}}.\nPambusakan kaca sing kaya mangkono mau wis ora diparengaké kanggo menggak anané karusakan ing {{SITENAME}}.",
        "delete-warning-toobig": "Kaca iki duwé sajarah panyuntingan sing dawa, luwih saka $1 {{PLURAL:$1|révisi|révisi}}.\nMbusak kaca iki bisa ngrusak operasi basis data ing {{SITENAME}};\nkudu ngati-ati.",
        "deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
-       "rollback": "Wurungaké besutan",
+       "rollback": "Pulihaké besutan",
        "rollbacklink": "balèkaké",
        "rollbacklinkcount": "balèkaké $1 {{PLURAL:$1|besutan|besutan}}",
        "rollbacklinkcount-morethan": "balèkaké luwih saka $1 {{PLURAL:$1|suntingan|suntingan}}",
        "rollbackfailed": "Pambalèkan gagal dilakoni",
        "cantrollback": "Ora bisa mbalèkaké suntingan; panganggo pungkasan iku siji-sijiné penulis artikel iki.",
-       "alreadyrolled": "Ora bisa mbalèkaké suntingan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); wong liya wis nyunting utawa mbalèkaké kaca artikel iku.\n\nSuntingan pungkasan dilakoni déning [[User:$3|$3]] ([[User talk:$3|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
+       "alreadyrolled": "Ora bisa mulihaké besutan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|rembug]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); ana wong liya sing wis mbesut utawa mulihaké kaca iki.\n\nBesutan pungkasan kaca iku garapané [[User:$3|$3]] ([[User talk:$3|rembug]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Ringkesan suntingan yaiku: <em>$1</em>.",
        "revertpage": "Besutan sing dibalèkaké [[Special:Contributions/$2|$2]] ([[User talk:$2|rembugan]]) bab owahan pungkasan déning [[User:$1|$1]]",
        "revertpage-nouser": "Suntingan déning panganggo sing didhelikake, dibalèkaké nèng benahan pungkasan déning [[User:$1|$1]]",
        "restriction-level-autoconfirmed": "pangreksan sémi",
        "restriction-level-all": "kabèh tingkatan",
        "undelete": "Kembalikan halaman yang telah dihapus",
-       "undeletepage": "Lihat dan kembalikan halaman yang telah dihapus",
+       "undeletepage": "Deleng lan pulihaké kaca kabusak",
        "undeletepagetitle": "'''Ing ngisor iki kapacak daftar révisi sing dibusak saka [[:$1]]'''.",
        "viewdeletedpage": "Deleng kaca sing wis dibusak",
        "undeletepagetext": "{{PLURAL:$1|kaca iki wis dibusak nanging isih|$1 kaca iki wis dibusak nanging isih}} ana ing arsip lan bisa dibalèkaké.\nArsip bisa diresiki sakala-kala.",
        "undelete-fieldset-title": "Mulihaké rèvisi",
-       "undeleteextrahelp": "Kanggo mbalèkaké kabèh sajarah kaca, kothongaké kabèh kothak-cèk lan klik '''''{{int:undeletebtn}}'''''.\nKanggo nglakoni pambalèkan pinilih, conthèngen kothak-cèk  sing magepokan karo révisi sing dipéngini lan klik '''''{{int:undeletebtn}}'''''.\nMencèt tombol '''''Reset''''' bakal ngosongaké isi komentar lan kabèh kothak-cèk.",
+       "undeleteextrahelp": "Saperlu mulihaké kabèh surajah kaca, jaraké kothak cèk kosong banjur klik <strong><em>{{int:undeletebtn}}</em></strong>.\nSaperlu ngayahi réstorasi sèlèktif, cèk kothak sing magepokan karo révisi sing arep dipulihaké, banjur klik <strong><em>{{int:undeletebtn}}</em></strong>.",
        "undeleterevisions": "$1 {{PLURAL:$1|révisi|révisi}} diarsipaké",
        "undeletehistory": "Yèn panjenengan mbalèkaké kaca, kabèh révisi bakal dibalèkaké jroning sajarah.\nYèn sawijining kaca anyar kanthi jeneng sing padha wis digawé wiwit nalika pambusakan, révisi sing wis dibalèkaké bakal katon jroning sajarah sadurungé.",
        "undeleterevdel": "Pambatalan pambusakan ora bakal dilakokaké yèn bab iku bakal ngakibataké révisi pungkasan kaca dadi sabagéyan kabusak.\nIng kasus kaya mengkono, panjenengan kudu ngilangaké cèk utawa mbusak pandelikan révisi kabusak sing anyar dhéwé.",
        "undelete-search-prefix": "Tuduhna kaca sing diwiwiti karo:",
        "undelete-search-submit": "Golèk",
        "undelete-no-results": "Ora ditemokaké kaca sing cocog ing arsip pambusakan.",
-       "undelete-filename-mismatch": "Ora bisa mbatalaké pambusakan révisi berkas mawa tandha wektu $1: jeneng berkas ora padha",
+       "undelete-filename-mismatch": "Ora bisa mulihaké révisi barkas mawa tandha wektu $1: Jeneng barkas ora padha",
        "undelete-bad-store-key": "Ora bisa mbatalaké pambusakan révisi berkas mawa tandha wektu $1: berkas ilang sadurungé dibusak.",
        "undelete-cleanup-error": "Ana kaluputan nalika mbusak arsip berkas \"$1\" sing ora dienggo.",
-       "undelete-missing-filearchive": "Ora bisa mbalèkaké arsip bekas mawa ID $1 amerga ora ana ing basis data.\nBerkas iku mbok-menawa wis dibusak.",
+       "undelete-missing-filearchive": "Ora bisa mulihaké arsip barkas ID $1 amarga ora ana ing basis data.\nBarkas iku bokmenawa wis dibusak.",
        "undelete-error": "Kasalahan mbalèkaké kaca",
        "undelete-error-short": "Kaluputan olèhé mbatalaké pambusakan: $1",
        "undelete-error-long": "Ana kaluputan nalika mbatalaké pambusakan berkas:\n\n$1",
        "tooltip-namespace_association": "Centhang kothak iki kanggo nglebokaké uga bilik jeneng gumenan utawa subyèk sing kakait karo bilik jeneng kapilih",
        "blanknamespace": "(Pokok)",
        "contributions": "Sumbangan {{GENDER:$1|panganggo}}",
-       "contributions-title": "Sumbanganing panganggo $1",
+       "contributions-title": "Sumbangan panganggo $1",
        "mycontris": "Sumbangan",
        "anoncontribs": "Sumbangan",
        "contribsub2": "Kanggo {{GENDER:$3|$1}} ($2)",
        "sp-contributions-deleted": "sumbanganing panganggo sing dibusak",
        "sp-contributions-uploads": "unggahan",
        "sp-contributions-logs": "log",
-       "sp-contributions-talk": "wicara",
+       "sp-contributions-talk": "rembug",
        "sp-contributions-userrights": "pengaturan hak panganggo",
        "sp-contributions-blocked-notice": "Panganggo iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
        "sp-contributions-blocked-notice-anon": "Alamat IP iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
        "sp-contributions-search": "Golèk sumbangan",
        "sp-contributions-username": "Alamat IP utawa jeneng panganggo:",
-       "sp-contributions-toponly": "Tuduhaké was suntingan saka benahan pungkasan",
+       "sp-contributions-toponly": "Tuduhaké besutan mligi rèvisi anyar",
+       "sp-contributions-newonly": "Tuduhaké besutan mligi kaca gawéan",
        "sp-contributions-submit": "Golèk",
        "whatlinkshere": "Sing nggayut mréné",
        "whatlinkshere-title": "Kaca mawa pranala nggayut \"$1\"",
        "infiniteblock": "salawasé",
        "expiringblock": "kadaluwarsa ing $1, $2",
        "anononlyblock": "namung anon",
-       "noautoblockblock": "pamblokiran otomatis dipatèni",
+       "noautoblockblock": "blokir otomatis dipatèni",
        "createaccountblock": "ndamelipun akun dipunblokir",
        "emailblock": "layang e-mail diblokir",
        "blocklist-nousertalk": "ora kena mbesut kaca guneman dhéwé",
-       "ipblocklist-empty": "Daftar pamblokiran kosong.",
+       "ipblocklist-empty": "Pratélan blokir kosong.",
        "ipblocklist-no-results": "alamat IP utawa panganggo sing disuwun ora diblokir.",
        "blocklink": "palang",
        "unblocklink": "jabel blokir",
        "contribslink": "sumbangan",
        "emaillink": "kirim layang èlèktronik",
        "autoblocker": "Panjenengan otomatis dipun-blok amargi nganggé alamat protokol internet (IP) ingkang sami kaliyan \"[[User:$1|$1]]\". Alesanipun $1 dipun blok inggih punika \"'''$2'''\"",
-       "blocklogpage": "Log pamblokiran",
+       "blocklogpage": "Log blokir",
        "blocklog-showlog": "Panganggo iki wis tau diblokir sakdurungé.\nLog blokiran sumadhiya nèng ngisor kanggo rujukan:",
        "blocklog-showsuppresslog": "Panganggo iki wis tau diblokir lan didhelikaké sakdurungé.\nLog brèdèlan sumadhiya nèng ngisor kanggo rujukan:",
        "blocklogentry": "mblokir [[$1]] kanthi wektu kadaluwarsa $2 $3",
        "delete_and_move_text": "Kaca jujugan \"[[:$1]]\" wis ana.\nApa sampéyan kersa mbusak iku supaya kacané bisa dilih?",
        "delete_and_move_confirm": "Ya, busak kaca iku.",
        "delete_and_move_reason": "Dibusak kanggo jaga-jaga ananing pamindhahan saka \"[[$1]]\"",
-       "selfmove": "Pangalihan kaca ora bisa dilakoni amerga irah-irahan utawa judhul sumber lan tujuané padha.",
+       "selfmove": "Sesirah sumber lan tujuan padha;\nora bisa ngalih nyang tujuan sing padha.",
        "immobile-source-namespace": "Ora bisa mindhahaké kaca jroning bilik jeneng \"$1\"",
        "immobile-target-namespace": "Ora bisa mindhahaké kaca menyang bilik jeneng \"$1\"",
        "immobile-target-namespace-iw": "Pranala interwiki dudu target sing sah kanggo pamindhahan kaca.",
        "imagetypemismatch": "Èkstènsi anyar berkas ora cocog karo jenisé",
        "imageinvalidfilename": "Jeneng berkas tujuan ora sah",
        "fix-double-redirects": "Dandani kabèh pangalihan gandha sing tumuju marang irah-irahan asli",
-       "move-leave-redirect": "Gawé pangalihan menyang irah-irahan anyar",
+       "move-leave-redirect": "Ungkur kaca alihan",
        "protectedpagemovewarning": "'''Pènget:''' Kaca iki wis dikunci dadi mung panganggo sing nduwé hak aksès pangurus baé sing bisa mindhahaké.\nCathetan entri pungkasan disadiakaké ing ngisor kanggo referensi:",
        "semiprotectedpagemovewarning": "'''Cathetan:''' Kaca iki wis direksa saéngga mung panganggo kadhaptar sing bisa mindhahaké.\nEntri cathetan pungkasan disadiakake ing ngisor kanggo referensi:",
        "move-over-sharedrepo": "[[:$1]] ana ing panyimpenan barengan. Ngalih barkas mawa sesirah iki bakal ngamblegi barkas barengan iku.",
        "allmessages-language": "Basa:",
        "allmessages-filter-submit": "Tumuju menyang",
        "thumbnail-more": "Gedhèkaké",
-       "filemissing": "Berkas ora ditemokaké",
+       "filemissing": "Barkas ilang",
        "thumbnail_error": "Kaluputan nalika nggawé gambar cilik (''thumbnail''): $1",
        "thumbnail_error_remote": "Peringatan kasalahan saka $1:\n$2",
        "djvu_page_error": "Kaca DjVu ana ing sajabaning ranggèhan (''range'')",
        "import-error-invalid": "Kaca \"$1\" ora diimpor amarga jenengé ora sah.",
        "import-error-unserialize": "Revisi $2 saka kaca \"$1\" ora bisa diurutaké. Revisi iku dilapuraké murih nganggo gagrag isi $3 sing diurutaké minangka $4.",
        "import-options-wrong": "{{PLURAL:$2|Opsi|Opsi}} salah: <nowiki>$1</nowiki>",
-       "import-rootpage-invalid": "Halaman turunan yang diberikan adalah judul yang salah.",
+       "import-rootpage-invalid": "Kaca wod iki sesirahé ora sah.",
        "import-rootpage-nosubpage": "Ruang nama \"$1\" di halaman turunan tidak mengizinkan subhalaman.",
        "importlogpage": "Log impor",
        "importlogpagetext": "Impor administratif kaca-kaca mawa sajarah panyuntingan saka wiki liya.",
        "tooltip-diff": "Tuduhaké owah-owahan endi sing sampéyan gawé tumrap tulisan iki",
        "tooltip-compareselectedversions": "Delengen prabédan antara rong vèrsi kaca iki sing dipilih.",
        "tooltip-watch": "Wuwuh kaca iki nyang pawawanganing sampéyan",
-       "tooltip-watchlistedit-normal-submit": "Singkiraké judhul",
+       "tooltip-watchlistedit-normal-submit": "Busak sesirah",
        "tooltip-watchlistedit-raw-submit": "Anyari daptar pangawasan",
        "tooltip-recreate": "Gawéa kaca iki manèh senadyan tau dibusak",
-       "tooltip-upload": "Miwiti pangunggahan",
+       "tooltip-upload": "Wiwit ngunggah",
        "tooltip-rollback": "Balèkaké besutan-besutan kaca iki déning sing pungkasan nyumbang sarana saklikan.",
-       "tooltip-undo": "Mbalèkaké révisi iki lan mbukak kothak panyuntingan jroning mode pratayang. Wènèhi kasempatan kanggo ngisi alesan ing kothak ringkesan.",
+       "tooltip-undo": "\"Wurung\" mbalèkaké besutan iki lan mbukak blangko besutan sarana modhe pratuduh. Alesan kena diwuwuhaké ing babagan ringkesan.",
        "tooltip-preferences-save": "Simpen préperensi",
        "tooltip-summary": "Isi tingkesan cendhak",
        "anonymous": "{{PLURAL:$1|Panganggo|panganggo}} anon ing {{SITENAME}}.",
        "pageinfo-header-edits": "Sujarah besutan",
        "pageinfo-header-restrictions": "Perlindungan halaman",
        "pageinfo-header-properties": "Properti kaca",
-       "pageinfo-display-title": "Judul tampilan",
+       "pageinfo-display-title": "Sesirah pajangan",
        "pageinfo-default-sort": "Kunci urut baku",
        "pageinfo-length": "Panjang halaman (dalam bita)",
        "pageinfo-article-id": "ID kaca",
        "exif-ycbcrcoefficients": "Koèfisièn matriks transformasi papan werna",
        "exif-referenceblackwhite": "Wiji réferènsi pasangan ireng putih",
        "exif-datetime": "Tanggal lan tabuh owahing barkas",
-       "exif-imagedescription": "Judhul gambar",
+       "exif-imagedescription": "Sesirah gambar",
        "exif-make": "Produsèn kamera",
        "exif-model": "Modhèl kaméra",
        "exif-software": "Piranti alus sing dianggo",
        "exif-provinceorstatedest": "Propinsi utawa nagara bagéyan katampilaké",
        "exif-citydest": "Kutha katampilaké",
        "exif-sublocationdest": "Dhaèrahé kutha katampilaké",
-       "exif-objectname": "Judhul cendhèk",
+       "exif-objectname": "Sesirah cekak",
        "exif-specialinstructions": "Prèntah kusus",
        "exif-headline": "Tajuk",
        "exif-credit": "Krédit/Panyadhiya",
        "table_pager_limit": "Tuduhna $1 entri per kaca",
        "table_pager_limit_label": "Barang per kaca:",
        "table_pager_limit_submit": "Golèk",
-       "table_pager_empty": "Ora ditemokaké",
+       "table_pager_empty": "Ora ana",
        "autosumm-blank": "Ngothongaké kaca",
        "autosumm-replace": "←Ngganti kaca karo '$1'",
        "autoredircomment": "←Ngalihaké menyang [[$1]]",
        "watchlistedit-normal-title": "Besut pawawangan",
        "watchlistedit-normal-legend": "Busak sesirah saka pawawangan",
        "watchlistedit-normal-explain": "Irah-irahan utawa judhul ing daftar pangawasan panjenengan kapacak ing ngisor iki.\nKanggo mbusak sawijining irah-irahan, kliken kothak ing pinggiré, lan banjur kliken \"Busak judhul\".\nPanjenengan uga bisa [[Special:EditWatchlist/raw|nyunting daftar mentah]].",
-       "watchlistedit-normal-submit": "Busak irah-irahan",
+       "watchlistedit-normal-submit": "Busak sesirah",
        "watchlistedit-normal-done": "Irah-irahan {{PLURAL:$1|siji|$1}} wis dibusak saka daftar pangawasan panjenengan:",
        "watchlistedit-raw-title": "Besut pawawangan wantahan",
        "watchlistedit-raw-legend": "Besut pawawangan wantahan",
        "watchlistedit-raw-explain": "Irah-irahan ing daftar pangawasan panjenengan kapacak ing ngisor iki, lan bisa diowahi mawa nambahaké utawa mbusak daftar; sairah-irahan saban barisé.\nYèn wis rampung, anyarana kaca daftar pangawasan iki.\nPanjenengan uga bisa [[Special:EditWatchlist|nganggo éditor standar panjenengan]].",
-       "watchlistedit-raw-titles": "Irah-irahan:",
+       "watchlistedit-raw-titles": "Sesirah:",
        "watchlistedit-raw-submit": "Anyari pawawangan",
        "watchlistedit-raw-done": "Pawawanganing sampéyan wis dianyari.",
-       "watchlistedit-raw-added": "{{PLURAL:$1|1 irah-irahan wis|$1 irah-irahan wis}} ditambahaké:",
-       "watchlistedit-raw-removed": "{{PLURAL:$1|1 irah-irahan wis|$1 irah-irahan wis}} diwetokaké:",
+       "watchlistedit-raw-added": "{{PLURAL:$1|1 sesirah|$1 sesirah}} ditambahaké:",
+       "watchlistedit-raw-removed": "{{PLURAL:$1|1 sesirah|$1 sesirah}} dibusak:",
        "watchlisttools-view": "Tuduhna owah-owahan sing ana gandhèngané",
        "watchlisttools-edit": "Deleng lan besut pawawangan",
        "watchlisttools-raw": "Besut pawawangan wantahan",
        "compare-rev1": "Révisi 1",
        "compare-rev2": "Révisi 2",
        "compare-submit": "Bandingaké",
-       "compare-invalid-title": "Judhul sing Sampéyan awèhaké ora sah.",
-       "compare-title-not-exists": "Judhul sing Sampéyan jaluk ora ana.",
+       "compare-invalid-title": "Sesirah sing kokawèhaké ora sah.",
+       "compare-title-not-exists": "Sesirah sing kokawèhaké ora ana.",
        "compare-revision-not-exists": "Benahan sing Sampéyan jaluk ora ana.",
        "dberr-problems": "Nyuwun ngapura! Situs iki ngalami masalah tèknis.",
        "dberr-again": "Coba nunggu sawetara menit lan unggahna manèh.",
        "logentry-newusers-create2": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1",
        "logentry-newusers-byemail": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1 lan tembung sandhine dikirim lewat layang elektronik",
        "logentry-newusers-autocreate": "Akun $1 {{GENDER:$2|digawé}} otomatis",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|ngilangi}} rereksan saka $3",
        "logentry-rights-rights": "$1 {{GENDER:$2|ngganti}} golongané {{GENDER:$6|$3}} saka $4 dadi $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|ngganti}} golongané $3",
        "logentry-rights-autopromote": "$1 otomatis {{GENDER:$2|dipromosikne}} saka $4 nèng $5",
        "feedback-bugornote": "Yèn Sampéyan siap njelasaké masalah tèhnis kanthi rinci mangga [$1 laporaké bug].\nUtawa, Sampéyan bisa nganggo pormulir gampang ngisor. Tanggepan Sampéyan bakal ditambahaké nèng kaca \"[$3 $2]\", bebarengan karo jeneng panganggo Sampéyan lan pramban sing Sampéyan anggo.",
        "feedback-cancel": "Batal",
        "feedback-close": "Rampung",
+       "feedback-error-title": "Cacad",
        "feedback-error1": "Kasalahan: Asil ora dikenal saka API",
        "feedback-error2": "Cacad: Gagal mbesut",
        "feedback-error3": "Kasalahan: Ora ana tanggepan saka API",
        "limitreport-expensivefunctioncount": "Expensive parser function count",
        "expandtemplates": "Cithakan dikembangaké",
        "expand_templates_intro": "Kaca astaméwa iki njupuk sawetara tèks lan ngembangaké kabèh cithakan sajroning iku sacara rékursif.\nKaca iki uga ngembangaké fungsi parser kaya ta\n<nowiki>{{</nowiki>#language:…}}, lan variabel kaya ta\n<nowiki>{{</nowiki>CURRENTDAY}}&mdash;sajatiné mèh kabèh sing ana ing antara rong tandha kurung akolade.",
-       "expand_templates_title": "Irah-irahan kontèks, kanggo {{FULLPAGENAME}} lan sabanjuré:",
+       "expand_templates_title": "Sesirah kontèks, kanggo {{FULLPAGENAME}}, lsp.:",
        "expand_templates_input": "Tèks sumber:",
        "expand_templates_output": "Pituwas (kasil)",
        "expand_templates_xml_output": "Pituwas XML",
        "expand_templates_remove_nowiki": "Brèdèl tag <nowiki> nèng asilé",
        "expand_templates_generate_xml": "Tuduhna uwit parser XML",
        "expand_templates_generate_rawhtml": "Show raw HTML",
-       "expand_templates_preview": "Pratayang",
+       "expand_templates_preview": "Pratuduh",
        "special-characters-group-latin": "Latin",
        "special-characters-group-latinextended": "Latin pepak",
        "special-characters-group-ipa": "IPA",
index 8ec2e36..e9e2f92 100644 (file)
        "noindex-category": "არ არსებობს ინდექსირებული გვერდები",
        "broken-file-category": "გვერდები ფაილების არასწორი ბმულებით",
        "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "შესახებ",
        "article": "სტატია",
        "newwindow": "(ახალ ფანჯარაში)",
        "tagline": "{{SITENAME}} გვერდიდან",
        "help": "დახმარება",
        "search": "ძიება",
+       "search-ignored-headings": "#<!-- დატოვეთ ეს ხაზი უცვლელად --> <pre>\n# სათაურები, რომლებთაც ძიება დააგნორებს.\n# ცვლილებები აისახება როგორც კი სათაურის მქონე გვერდს ინდექსირება გაუკეთდება.\n# შეგიძლიათ გააკეთოთ გვერდის ძალით ხელახალი ინდექსირება null edit-ის გაკეთებით.\n# სინტაქსი შემდეგია:\n#   * ყველაფერი \"#\" სიმბოლოდან ხაზის ბოლმდე კომენტარია.\n#   * ყველა არა-ცარიელი ხაზი is the exact title to ignore, case and everything.\nწყაროები\nრესურსები ინტერნეტში\nიხილეთ აგრეთვე\n #</pre> <!-- დატოვეთ ეს ხაზი უცვლელად -->",
        "searchbutton": "ძიება",
        "go": "სტატია",
        "searcharticle": "გვერდი",
        "yourpasswordagain": "ხელმეორედ შეიყვანეთ პაროლი",
        "createacct-yourpasswordagain": "დაადასტურეთ პაროლი",
        "createacct-yourpasswordagain-ph": "ხელმეორედ შეიყვანეთ პაროლი",
-       "remembermypassword": "დამიმახსოვრე ამ კომპიუტერზე (მაქსიმუმ $1 {{PLURAL:$1|დღე}})",
        "userlogin-remembermypassword": "დამიმახსოვრე",
        "userlogin-signwithsecure": "უსაფრთხო კავშირის გამოყენება",
        "cannotloginnow-title": "ამჟამად შესვლა შუეძლებელია",
        "passwordreset-emailelement": "მომხმარებლის სახელი: \n$1\n\nდროებითი პაროლი: \n$2",
        "passwordreset-emailsentemail": "თუ ეს მეილი თქვენს ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
        "passwordreset-emailsentusername": "თუ არსებობს მეილი, რომელიც ამ ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
-       "passwordreset-emailsent-capture": "ქვემოთ ნაჩვენები პაროლის თავიდან დასაყენებელი წერილი გაიგზავნა.",
-       "passwordreset-emailerror-capture": "ქვემოთ მოცემულია შექმნილი პაროლის დასაყენებელი წერილი, რომლის გაგზავნაც {{GENDER:$2|მომხმარებელთან}} ვერ მოხერხდა: $1 გამო",
        "passwordreset-emailsent-capture2": "პაროლის გაუქმების შესახებ {{PLURAL:$1|მეილი|მეილები}} გაიგზავნა. {{PLURAL:$1|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|მომხმარებელთან}} მეილის გაგზავნა ვერ მოხერხდა: $1 {{PLURAL:$3|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-nocaller": "გამომძახებელი უნდა იყოს მიწოდებული",
        "passwordreset-nodata": "არც მომხმარებლის სახელი და არც ელ-ფოსტის მისამართი არ იყო მოწოდებული",
        "changeemail": "ელ-ფოსტის მისამართის შეცვლა ან წაშლა",
        "changeemail-header": "შეავსეთ ეს ფორმა მეილის შესაცვლელად. თუ გსურთ თქვენი ანგარიში არ იყოს დაკავშირებული არცერთ მეილთან, ახალი მეილის მისამართის ველი დატოვეთ ცარიელი.",
-       "changeemail-passwordrequired": "ამ ცვლილების დასადასტურებლად დაგჭირდებათ პაროლის შეყვანა.",
        "changeemail-no-info": "თქვენ ავტირიზებული უნდა იყოთ ამ გვერდთან უშუალო წვდომისთვის.",
        "changeemail-oldemail": "ელ-ფოსტის ამჟამინდელი მისამართი:",
        "changeemail-newemail": "ახალი ელ-ფოსტის მისამართი:",
        "content-model-css": "CSS",
        "content-json-empty-object": "ცარიელი ობიექტი",
        "content-json-empty-array": "ცარიელი ტაბლო",
+       "deprecated-self-close-category": "გვერდები, რომლებიც იყენებენ არავალიდურ თვითდახურვად HTML ტეგებს",
        "duplicate-args-warning": "<strong>გაფრთხილება:</strong> [[:$1]] იძახებს [[:$2]]-ის \"$3\" პარამეტრის ერთზე მეტ მნიშვნელობას. აისახება მხოლოდ ბოლოს გამოყენებული მნიშვნელობა.",
        "duplicate-args-category": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას",
        "duplicate-args-category-desc": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას, როგორებიც არის <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ან <code><nowiki>{{foo|bar|1=bar}}</nowiki></code>.",
        "undo-nochange": "როგორც ჩანს, რედაქტირება უკვე გაუქმდა.",
        "undo-summary": "[[Special:Contributions/$2|$2-ის]]([[User talk:$2|განხილვა]]) ცვლილებების გაუქმება (№$1)",
        "undo-summary-username-hidden": "ცვლილების გაუქმება $1, მომხმარებლის მიერ, რომლის სახელი დამალულია",
-       "cantcreateaccounttitle": "ანგარიშის შექმნა ვერ ხერხდება",
        "cantcreateaccount-text": "ამ IP-მისამართიდან აიკრძალა (<b>$1</b>) მომხმარებელ [[User:$3|$3]]-ის მიერ.\n\n$3 -ემ ამგვარი ახსნა : ''$2''",
        "cantcreateaccount-range-text": "{{GENDER:$3|მომხმარებელმა}} [[User:$3|$3]] ანგარიშის ან IP-მისამართის $1 შექმნისთვის {{GENDER:$3|დაადო}} აკრძალვა <strong>$1</strong>, თქვენი IP-მისამართის ჩათვლით ($4).\n\nმითითებულია შემდეგი მიზეზი: $2.",
        "viewpagelogs": "ამ გვერდისთვის სარეგისტრაციო ჟურნალების ჩვენება",
        "right-managechangetags": "[[Special:Tags|ტეგების]] შექმნა და (დე)აქტივაცია",
        "right-applychangetags": "[[Special:Tags|tags]] მიღება თქვენ ცვლილებებთან ერთად",
        "right-changetags": "თვითნებური [[Special:Tags|tags]] დამატება ან წაშლა ცალკეულ ცვლილებებსა და ჟურნალის ჩანაწერებში",
+       "right-deletechangetags": "მონაცემთა ბაზიდან [[Special:Tags|ტეგების]] წაშლა",
        "grant-generic": "\"$1\" უფლებები",
        "grant-group-page-interaction": "კავშირი გვერდებთან",
        "grant-group-file-interaction": "კავშირი მედია-ფაილებთან",
        "grant-highvolume": "დიდი მოცულობით რედაქტირება",
        "grant-oversight": "მომხმარებლებისა და შესწორებების დამალვა",
        "grant-patrol": "გვერდების რედაქტირებების შემოწმება",
+       "grant-privateinfo": "პირად ინფორმაციაზე წვდომა",
        "grant-protect": "გვერდების და დაცვა და დაცვის მოხსნა",
        "grant-rollback": "გვერდების რედაქტირებების სწრაფი გაუქმება",
        "grant-sendemail": "გაგუგზავნე ელექტრონული ფოსტა სხვა მომხმარებლებს",
        "rightslogtext": "მომხმარებელთა უფლებების ცვლილებათა ჟურბალი",
        "action-read": "ამ გვერდის კითხვა",
        "action-edit": "ამ გვერდის რედაქტირება",
-       "action-createpage": "á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
-       "action-createtalk": "á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
+       "action-createpage": "á\83\90á\83\9b á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
+       "action-createtalk": "á\83\90á\83\9b á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
        "action-createaccount": "ამ ანგარიშის შექმნა",
        "action-autocreateaccount": "გარე მომხმარებლის ანგარიშის ავტომატურად შექმნა",
        "action-history": "ამ გვერდის ისტორიის ნახვა",
        "action-managechangetags": "ტეგების შექმნა და (დე)აქტივაცია",
        "action-applychangetags": "ტეგების მიღება თქვენ ცვლილებებთან ერთად",
        "action-changetags": "თავისუფალი ტეგების დამატება და წაშლა ცალკეულ ცვლილებებსა და ჟურნალების ჩანაწერებში",
+       "action-deletechangetags": "მონაცემთა ბაზიდან ტეგების წაშლა",
+       "action-purge": "ამ გვერდის წაშლა",
        "nchanges": "$1 ცვლილება",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ბოლო ვიზიტის შემდეგ}}",
        "enhancedrc-history": "ისტორია",
        "upload-http-error": "მოხდა HTTP შეცდომა: $1",
        "upload-copy-upload-invalid-domain": "ამ დომენში ატვირთვების კოპირება არ არის ხელმისაწვდომი.",
        "upload-foreign-cant-upload": "ეს ვიკი არ არის დაკონფიგურირებული იმისათვის, რომ ატვირთოს ფაილების უცხო ფაილთა საწყობში.",
+       "upload-dialog-disabled": "ფაილის ატვირთვა ამ დიალოგური ფანჯრით გათიშულია ამ ვიკიზე.",
        "upload-dialog-title": "ფაილის ატვირთვა",
        "upload-dialog-button-cancel": "გაუქმება",
        "upload-dialog-button-done": "შესრულდა",
        "uploadstash-badtoken": "მითითებული მოქმედება ვერ შესრულდა. შესაძლოა, რედაქტირების უფლებამოსილების მოქმედების ვადა ამოიწურა. გთხოვთ, სცადეთ თავიდან.",
        "uploadstash-errclear": "ფაილების გასუფთავება ვერ მოხერხდა.",
        "uploadstash-refresh": "ფაილების სიის განახლება",
+       "uploadstash-thumbnail": "მინიატურის ნახვა",
        "invalid-chunk-offset": "არასწორი საწყისი წერტილი",
        "img-auth-accessdenied": "მოქმედება აკრძალულია",
        "img-auth-nopathinfo": "დაკარგულია PATH_INFO.\nთქვენი სერვერი არ არის მომართული ამ ინფორმაციის გადასაცემად.\nშესაძლოა, ის მუშაობს CGI-ის ბაზაზე და არ გააჩნია img_auth მხარდაჭერა.\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization იხილეთ სურათის ავტორიზაცია.]",
        "filerevert-submit": "გაუქმება",
        "filerevert-success": "'''[[Media:$1|$1]]''' დაუბრუნდა ვერსიას [$4  $3, $2]-დან.",
        "filerevert-badversion": "არ არსებობს ფაილის წინა ლოკალური ვერსია მოთხოვნილი  თარიღითა და დროით",
+       "filerevert-identical": "ფაილის ამჟამინდელი ვერსია უკვე არის შერჩეულის იდენტური.",
        "filedelete": "$1 წაშლა",
        "filedelete-legend": "ფაილის წაშლა",
        "filedelete-intro": "თქვენ შლით <strong>[[Media:$1|$1]]-ს</strong> მისი ისტორიით.",
        "trackingcategories-msg": "კატეგორიის მიდევნება",
        "trackingcategories-name": "შეტყობინების სახელი",
        "trackingcategories-desc": "კატეგორიაში ჩართვის კრიტერიუმები",
+       "restricted-displaytitle-ignored": "გვერდები დაიგნორებული სათაურებით",
        "noindex-category-desc": "გვერდი არ არის ინდექსირებული საძიებო სამუშაოებით, რადგან მასზე არის „ჯადოსნური სიტყვა“ <code><nowiki>__NOINDEX__</nowiki></code> და ის იმყოფება სახელთა სივრცეში, სადაც დასაშვებია ეს დროშა.",
        "index-category-desc": "გვერდზე არის „ჯადოსნური სიტყვა“ <code><nowiki>__INDEX__</nowiki></code> (და გვერდი იმყოფება სახელთა სივრცეში, სადაც დაშვებულია ეს დროშა). ამიტომ იგი ინდექსირებულია საძიებო სამუშაოებით იმ შემთხვევებში, როცა ეს ჩვეულებრივ არ ხდება.",
        "post-expand-template-inclusion-category-desc": "გვერდის ზომა უფრო გაიზრდება <code>$wgMaxArticleSize</code> ყველა თარგის ჩვენების შემდეგ, ამიტომ ზოგიერთი მათგანი არ იყო ნაჩვენები მთლიანად.",
        "watchnologin": "რეგისტრაცია ვერ შესრულდა",
        "addwatch": "კონტროლის სიაში დამატება",
        "addedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
+       "addedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
        "addedwatchtext-short": "გვერდი „$1“ დაემატა თქვენი კონტროლის სიას.",
        "removewatch": "კონტროლის სიიდან წაშლა",
        "removedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
+       "removedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
        "removedwatchtext-short": "გვერდი „$1“ წაიშალა თქვენი კონტროლის სიიდან.",
        "watch": "კონტროლი",
        "watchthispage": "ამ გვერდის კონტროლი",
        "rollbacklinkcount": "$1 {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
        "rollbacklinkcount-morethan": "$1-ზე მეტი {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
        "rollbackfailed": "შეცდომა გაუქმებისას",
+       "rollback-missingparam": "აკლია საჭირო პარამეტრები.",
        "cantrollback": "შეუძლებელია უწინდელი რედაქციის აღდგენა; ის, ვინც უკანასკნელი ცვლილებები შეიტანა, ამ სტატიის ერთადერთი ავტორია.",
        "alreadyrolled": "შეუძლებელია ბოლო ცვლილების გაუქმება [[:$1]], გაკეებული [[User:$2|$2]] ([[User talk:$2|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nვიღაცა სხვამ უკვე შეასწორა ან გაააუქმა ეს გვერდი.\n\nბოლო ცვლილებები შეიტანა  [[User:$3|$3]] ([[User talk:$3|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "რედაქტირება განმარტებული იყო როგორც: <em>$1</em>.",
        "changecontentmodel-success-text": "[[:$1]]-ის კონტენტის ტიპი შეიცვალა.",
        "changecontentmodel-cannot-convert": "[[:$1]]-ის შინაარსის $2-ის ტიპზე კონვერტაცია შეუძლებელია.",
        "changecontentmodel-nodirectediting": "$1 შინაარსის მოდელს არ აქვს პირდაპირი რედაქტირების მხარდაჭერა",
+       "changecontentmodel-emptymodels-title": "შინაარსის მოდელები არ არის ხელმისაწვდომი",
+       "changecontentmodel-emptymodels-text": "შინაარსი [[:$1]]-ზე არ კონვერტირდება არცერთ ტიპზე.",
        "log-name-contentmodel": "შინაარსის მოდელის შეცვლის ჟურნალი",
        "log-description-contentmodel": "გვერდის შინაარსის მოდელთან დაკავშირებული მოვლენები",
        "logentry-contentmodel-new": "$1-მ {{GENDER:$2|შექმნა}} გვერდი $3, არა-სტანდარტული მოდელით \"$5\"",
        "undeletehistorynoadmin": "ეს სტატია წაშლილია. წაშლის მიზეზი ნაჩვენებია მოკლე ანოტაციაში ქვემოთ, იმ მომხმარებელთა დეტალებთან ერთად ვინც რედაქტირება გაუკეთა ამ გვერდს წაშლის წინ. იმ წაშლილი ტექსტების აქტუალური ვერსიები მიღწევადია მხოლოდ ადმინისტრატორებისათვის.",
        "undelete-revision": "$1-ის წაშლილი ვერსია ($5, $4-ის მდგომარეობით), შენახული მომხმარებლის $3 მიერ:",
        "undeleterevision-missing": "არასწორი ან არარსებული ვერსია. სავარაუდოდ ქვენ გადახვედით არასწორ ბმულზე, ან იგი წაიშალა არქივიდან.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|ერთი ცვლილება|$1 ცვლილება}} ვერ აღდგა, რადგან {{PLURAL:$1|მისი|მათი}} <code>rev_id</code> უკვე გამოიყენებოდა.",
        "undelete-nodiff": "წინა ცვლილება ვერ ვიპოვეთ.",
        "undeletebtn": "აღდგენა",
        "undeletelink": "ნახვა/აღდგენა",
        "undeletedrevisions": "$1 ვერსია აღდგენილია",
        "undeletedrevisions-files": "$1 ვერსია და $2 ფაილი აღდგენილია",
        "undeletedfiles": "$1 ფაილი აღდგენილია",
-       "cannotundelete": "á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90\n$1",
+       "cannotundelete": "á\83\96á\83\9dá\83\92á\83\98á\83\94á\83 á\83\97á\83\98 á\83\90á\83\9c á\83§á\83\95á\83\94á\83\9aá\83\90 á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90:\n$1",
        "undeletedpage": "'''$1 აღდგენილია'''\n\nუკანასკნელი წაშლილთა და აღდგენის სია შეგიძლიათ ნახოთ [[Special:Log/delete|წაშლილთა სიაში]].",
        "undelete-header": "ბოლოს წაშლილი გვერდების სიის ნახვა შეიძლება [[Special:Log/delete|წაშლათა ჟურნალში]].",
        "undelete-search-title": "წაშლილი გვერდების ძიება",
        "undelete-error-long": "ფაილის აღდგენისას წარმოიშვა შეცდომები\n\n$1",
        "undelete-show-file-confirm": "დარწმუნებული ხართ, რომ გსურთ ფაილ <nowiki>$1</nowiki>-ის წაშლილი ვერსიის ხილვა $2 $3-დან?",
        "undelete-show-file-submit": "ჰო",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "სახელთა სივრცე:",
        "invert": "ყველა, მონიშნულის გარდა",
        "tooltip-invert": "მონიშნეთ ეს უჯრა, რათა დამალოთ გვერდების ცვლილებები არჩეული სახელთა სივრცის ფარგლებში (და მასთან დაკავშირებულ სახელთა სივრცეში, თუ მსგავსი რამ მითითებულია)",
        "sp-contributions-newbies-sub": "ახალბედებისთვის",
        "sp-contributions-newbies-title": "ბოლოს დარეგისტრირებულ მომხმარებელთა წვლილი",
        "sp-contributions-blocklog": "ბლოკირების ისტორია",
-       "sp-contributions-suppresslog": "მომხმარებლის წაშლილი წვლილი",
-       "sp-contributions-deleted": "მომხმარებლის წაშლილი შესწოებები",
+       "sp-contributions-suppresslog": "{{GENDER:$1|მომხმარებლის}} წაშლილი წვლილი",
+       "sp-contributions-deleted": "{{GENDER:$1|მომხმარებლის}} წაშლილი შესწოებები",
        "sp-contributions-uploads": "ატვირთვები",
        "sp-contributions-logs": "ჟურნალები",
        "sp-contributions-talk": "განხილვა",
        "sp-contributions-username": "IP მისამართი ან მომხმარებლის სახელი:",
        "sp-contributions-toponly": "აჩვენე მხოლოდ ბოლო ვერსიები",
        "sp-contributions-newonly": "აჩვენე მხოლოდ ცვლილებები, რომელიც წარმოადგენს გვერდის შექმნილს",
+       "sp-contributions-hideminor": "მცირე რედაქტირებების დამალვა",
        "sp-contributions-submit": "ძიება",
        "whatlinkshere": "ბმული გვერდზე",
        "whatlinkshere-title": "გვერდები, რომლებიც შეიცავენ „$1“-ის ბმულებს",
        "ipb-unblock": "მომხმარებლის სახელზე ან IP მისამართზე ბლოკის მოხსნა",
        "ipb-blocklist": "იხილე არსებული ბლოკირებები",
        "ipb-blocklist-contribs": "მომხმარებელ {{GENDER:$1|$1}} წვლილი",
+       "ipb-blocklist-duration-left": "დარჩა $1",
        "unblockip": "მომხმარებელზე ბლოკის მოხსნა",
        "unblockiptext": "გამოიყენეთ ქვემოთ მოცემული ფორმულარი, რათა  დაბლოკილი IP მისამართი ან მომხმარებლის სახელი აღადგინოთ.",
        "ipusubmit": "ამ ბლოკის მოხსნა",
        "lockdbsuccesstext": "პროექტის მონაცემთა ბაზა დაიბლოკა.<br />\nარ დაგავიწყდეთ [[Special:UnlockDB|ბლოკის მოხსნა]] მონაცემთა ბაზასთან სამუშაოების გატარების შემდეგ.",
        "unlockdbsuccesstext": "მომაცემთა ბაზაზე ბლოკი მოიხსნა.",
        "lockfilenotwritable": "არ გაქვთ უფლება მონაცემთა ბაზის დაცვის ფაილის შესწორების. დასაბლოკად ან ბლოკის მოსახსნელად საჭიროა ფაილი ღია იყოს.",
+       "databaselocked": "მონაცემთა ბაზა უკვე ჩაკეტილია.",
        "databasenotlocked": "მონაცემთა ბაზა არაა ჩაკეტილი.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "$1 — გადატანა",
        "tooltip-ca-nstab-category": "გვერდის კატეგორიის ჩვენება",
        "tooltip-minoredit": "მონიშნე როგორც მცირე რედაქტირება [alt-i]",
        "tooltip-save": "თქვენი ცვლილებების შენახვა",
+       "tooltip-publish": "თქვენი ცვლილებების გამოქვეყნება",
        "tooltip-preview": "წინასწარ გადახედე ცვლილებებს, გთხოვთ გამოიყენოთ ეს შენახვამდე! [alt-p]",
        "tooltip-diff": "ტექსტში შეტანილი ცვლილებების ჩვენება. [alt-v]",
        "tooltip-compareselectedversions": "იხილეთ ამ გვერდის  ორ შერჩეულ ვერსიას შორის განსხვავებები.",
        "confirmemail_body_set": "ვიღაცამ, შესაძლოა თქვენ, IP მისამართით $1,\nპროექტში {{SITENAME}} შეცვალა ელ.ფოსტის მისამართი ანგარიშისათვის \"$2\" ამ მისამართით.\n\nიმის დასადასტურებლად, რომ ეს ანგარიში ნამდვილად თქვენ გეკუთვნით\nდა ელ.ფოსტის შესაძლებლობების  გასააქტიურებლად საიტზე {{SITENAME}}, გახსენით ეს ბმული თქვენს ბრაუზერში:\n\n$3\n\nთუ ეს თქვენ *არ* იყავით, მაშინ ელ.ფოსტის მისამართის დასტურების გასაუქმებლად, გადადით ამ ბმულზე:\n\n$5\n\nწერილის ვადის გასვლის თარიღია $4.",
        "confirmemail_invalidated": "ელ-ფოსტის დადასტურება გაუქმდა",
        "invalidateemail": "ელ-ფოსტის დადასტურების გაუქმება",
+       "notificationemail_subject_changed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები შეიცვალა",
+       "notificationemail_subject_removed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები წაიშალა",
+       "notificationemail_body_changed": "ვიღაცამ, სავარაუდოდ თქვენ, IP მისამართიდან $1,\n{{SITENAME}}ში შეცვალა „$2“ ანგარიშის იმეილის მისამართი „$3“-ზე.\n\nთუ ეს თქვენ არ იყავით, გთხოვთ დაუკავშირდით საიტის ადმინისტრატორს სასწრაფოდ.",
+       "notificationemail_body_removed": "ვიღაცამ, სავარაუდოდ თქვენ, IP მისამართიდან $1,\n{{SITENAME}}ში წაშალა ანგარიშის „$2“ იმეილის მისამართი.\n\nთუ ეს თქვენ არ იყავით, გთხოვთ დაუკავშირდით საიტის ადმინისტრატორს სასწრაფოდ.",
        "scarytranscludedisabled": "[«Interwiki transcluding» გათიშულია]",
        "scarytranscludefailed": "[$1-თან დაკავშირების შეცდომა]",
        "scarytranscludefailed-httpstatus": "[ვერ მოხერხდა თარგის ჩატვირთვა $1-თვის: HTTP $2]",
        "confirm-watch-top": "დავამატო ეს გვერდი თქვენი კონტროლის სიას?",
        "confirm-unwatch-button": "დიახ",
        "confirm-unwatch-top": "მოვხსნა ეს გვერდი თქვენი კონტროლის სიიდან?",
+       "confirm-rollback-button": "კარგი",
+       "confirm-rollback-top": "დავაბრუნოთ რედაქტირებები ამ გვერდზე?",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
        "timezone-local": "ლოკალური",
        "duplicate-defaultsort": "'''ყურადღება.'''სორტირების გასაღებს «$2»-ს გააჭრის წინა გასაღებს «$1»-ს.",
        "duplicate-displaytitle": "<strong>ყურადღება:</strong> დისპლეის სათაური \"$2\" განსაზღვრავს ადრე გაცემულ დისპლეის სათაურს \"$1\".",
+       "restricted-displaytitle": "<strong>Warning:</strong> სათაური „$1“ იგნორირებულ იქნა, რადგან ის არ ემთხვევა გვერდის ნამდვილ სათაურს.",
        "invalid-indicator-name": "<strong>შეცდომა:</strong> გვერდის სტატუსის ინდიკატორი <code>name</code> ატრიბუტი არ უნდა იყოს ცარიელი.",
        "version": "ვერსია",
        "version-extensions": "დაყენებული გაფართოებები",
        "tags-delete-not-found": "აღნიშვნა „$1“ არ არსებობს.",
        "tags-delete-too-many-uses": "ტეგი \"$1\" მიღებულია $2 ვერსიებთან, რაც იმას ნიშნავს, რომ იგი არ შეიძლება იყოს წაშლილი",
        "tags-delete-warnings-after-delete": "ტეგი „$1“ წაიშალა, თუმცა აღმოჩენილია შემდეგი {{PLURAL:$2|შეტყობინება|შეტყობინებები}}:",
+       "tags-delete-no-permission": "თქვენ არ გაქვთ შეცვლილი ტეგების წაშლის უფლება.",
        "tags-activate-title": "ტეგის გააქტიურება",
        "tags-activate-question": "თქვენ ცდილობთ დასათაურების გააქტიურებას „$1“.",
        "tags-activate-reason": "მიზეზი:",
        "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 [კასკადური]",
-       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83£á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98 $3-á\83¡ $4-დან $5-ზე",
+       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98á\83¡ á\83¬á\83\94á\83\95á\83 á\83\9dá\83\91á\83\90 á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡á\83\90á\83\97á\83\95á\83\98á\83¡ {{GENDER:$6|$3}} $4-დან $5-ზე",
        "logentry-rights-rights-legacy": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ჯგუფის წევრობა $3-თვის",
        "logentry-rights-autopromote": "მომხმარებელი $1 ავტომატურად იქნა {{GENDER:$2|გადაყვანილი}} $4–დან $5–ში",
        "logentry-upload-upload": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3",
        "feedback-useragent": "მომხმარებლის აგენტი:",
        "searchsuggest-search": "ძიება",
        "searchsuggest-containing": "შეიცავს...",
+       "api-error-autoblocked": "თქვენი IP მისამართი ავტომატურად დაიბლოკა, რადგან ის გამოიყენა დაბლოკილმა მომხმარებელმა.",
        "api-error-badaccess-groups": "თქვენ არ გაქვთ ამ ვიკიში ფაილების ატვირთვის უფლება.",
        "api-error-badtoken": "შიდა შეცდომა: ცუდი ტოკენი.",
+       "api-error-blocked": "თქვენთვის რედაქტირება დაბლოკილია.",
        "api-error-copyuploaddisabled": "ამ სერვერზე URL-მისამართის საშუალებით ატვირთვა გამორთულია.",
        "api-error-duplicate": "საიტზე უკვე {{PLURAL:$1|არსებობს სხვა ფაილი|არსებობს სხვა ფაილები}} ანალოგიური შინაარსით.",
        "api-error-duplicate-archive": "საიტზე ადრე {{PLURAL:$1|უკვე იყო ფაილი}} ანალოგიური შინაარსით, მაგრამ {{PLURAL:$1|ის წაიშალა|ისინი წაიშალა}}.",
        "api-error-unknownerror": "უცნობი შეცდომა: „$1“.",
        "api-error-uploaddisabled": "ატვირთვის მექანიზმი ამ ვიკიზე გამორთულია",
        "api-error-verification-error": "ეს ფაილი ან რაიმე შეცდომას შეიცავს, ან არ აქვს სახელის გაფართოება.",
+       "api-error-was-deleted": "ფაილი ამ სახელწოდებით ადრე აიტვირთა და შემდეგ წაიშალა.",
        "duration-seconds": "$1 {{PLURAL:$1|წამი|წამი}}",
        "duration-minutes": "$1 {{PLURAL:$1|წუთი|წუთი}}",
        "duration-hours": "$1 {{PLURAL:$1|საათი|საათი}}",
        "sessionprovider-generic": "$1 სესიები",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-სთან დაკავშირებული სესიები",
        "sessionprovider-nocookies": "შესაძლოა ქუქები გათიშულია. გთხოვთ ჩართეთ და სცადეთ განმეორებით.",
-       "randomrootpage": "შემთხვევითი ძირეული გვერდი"
+       "randomrootpage": "შემთხვევითი ძირეული გვერდი",
+       "log-action-filter-block": "ბლოკირების ტიპი:",
+       "log-action-filter-contentmodel": "შეინაარსის მოდელის მოდერაციის ტიპი:",
+       "log-action-filter-delete": "წაშლის ტიპი:",
+       "log-action-filter-import": "იმპორტის ტიპი:",
+       "log-action-filter-managetags": "ტეგის ცვლილების ტიპი:",
+       "log-action-filter-move": "გადატანის ტიპი:",
+       "log-action-filter-newusers": "ანგარიშის შექმნის ტიპი:",
+       "log-action-filter-patrol": "შემოწმების ტიპი:",
+       "log-action-filter-protect": "დაცვის ტიპი:",
+       "log-action-filter-rights": "უფლების შეცვლის ტიპი:",
+       "log-action-filter-suppress": "დამალვის ტიპი:",
+       "log-action-filter-upload": "ატვირთვის ტიპი:",
+       "log-action-filter-all": "ყველა",
+       "log-action-filter-block-block": "დაბლოკვა",
+       "log-action-filter-block-reblock": "ბლოკირების შეცვლა",
+       "log-action-filter-block-unblock": "განბლოკვა",
+       "log-action-filter-contentmodel-change": "შინაარსის მოდელის შეცვლა",
+       "log-action-filter-contentmodel-new": "გვერდის შექმნა არასტანდარტული შინაარსის მოდელით",
+       "log-action-filter-delete-delete": "გვერდის წაშლა",
+       "log-action-filter-delete-restore": "გვერდის აღდგენა",
+       "log-action-filter-delete-event": "ჯურნალის ჩანაწერის წაშლა",
+       "log-action-filter-delete-revision": "ვერსიის წაშლა",
+       "log-action-filter-import-interwiki": "Transwiki-ს იმპორტი",
+       "log-action-filter-import-upload": "XML ატვირთვიდან იმპორტი",
+       "log-action-filter-managetags-create": "ტეგის შექმნა",
+       "log-action-filter-managetags-delete": "ტეგის წაშლა",
+       "log-action-filter-managetags-activate": "ტეგის აქტივაცია",
+       "log-action-filter-managetags-deactivate": "ტეგის დეაქტივაცია",
+       "log-action-filter-move-move": "გადატანა გადამისამართებების გადაწერის გარეშე",
+       "log-action-filter-move-move_redir": "გადატანა გადამისამართებების გადაწერით",
+       "log-action-filter-newusers-create": "შექმნა ანონიმური მომხმარებლის მიერ",
+       "log-action-filter-newusers-create2": "დარეგისტრირებული მომხმარებლის შექმნა",
+       "log-action-filter-newusers-autocreate": "ავტომატური შექმნა",
+       "log-action-filter-newusers-byemail": "პაროლით შექმნა, რომელიც გამოიგზავნა იმეილით",
+       "log-action-filter-patrol-patrol": "ხელით შემოწმება",
+       "log-action-filter-patrol-autopatrol": "ავტომატური შემოწმება",
+       "log-action-filter-protect-protect": "დაცვა",
+       "log-action-filter-protect-modify": "დაცვის შეცვლა",
+       "log-action-filter-protect-unprotect": "დაცვის მოხსნა",
+       "log-action-filter-protect-move_prot": "დაცვა გადატანისაგან",
+       "log-action-filter-rights-rights": "ხელით შეცვლა",
+       "log-action-filter-rights-autopromote": "ავტომატური შეცვლა",
+       "log-action-filter-suppress-event": "ჟურნალის დამალვა",
+       "log-action-filter-suppress-revision": "ცვლილების დამალვა",
+       "log-action-filter-suppress-delete": "გვერდის დამალვა",
+       "log-action-filter-suppress-block": "მომხმარებლის დამალვა ბლოკით",
+       "log-action-filter-suppress-reblock": "მომხმარებლის დამალვა ხელახლა ბლოკირებით",
+       "log-action-filter-upload-upload": "ახალი ატვირთვა",
+       "log-action-filter-upload-overwrite": "ხელახლა ატვირთვა",
+       "authmanager-authn-not-in-progress": "აუტენტიფიკაცია არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-authn-no-primary": "მოწოდებული მონაცემები ვერ იქნა აუტენტიფიცირებული.",
+       "authmanager-authn-no-local-user": "მოწოდებული მონაცემები არ უკავშირდება არცერთ მომხმარებელს ამ ვიკიზე.",
+       "authmanager-create-disabled": "ანგარიშის შექმნა გათიშულია.",
+       "authmanager-create-from-login": "ანგარიშის შესაქმნელად, გთხოვთ შეავსეთ ქვემოთ მოცემული ველები.",
+       "authmanager-create-not-in-progress": "ანგარიშის შექმა არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-create-no-primary": "მოწოდებული მონაცემები არ გამოდგა ანგარიშის შესაქმნელად.",
+       "authmanager-link-no-primary": "მოწოდებული მონაცემები არ გამოდგა ანგარიშის დასაკავშირებლად.",
+       "authmanager-link-not-in-progress": "ანგარიშის დაკავშირება არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-authplugin-setpass-failed-title": "პაროლის ცვლილება ვერ განხორციელდა",
+       "authmanager-authplugin-setpass-failed-message": "აუთენთიფიკაციის პლაგინმა უარყო პაროლის ცვლილება.",
+       "authmanager-authplugin-create-fail": "აუთენთიფიკაციის პლაგინმა უარყო ანგარიშის შექმნა.",
+       "authmanager-authplugin-setpass-denied": "აუთენთიფიკაციის პლაგინი არ იძლევა პაროლების გამოცვლის უფლებას.",
+       "authmanager-authplugin-setpass-bad-domain": "არასწორი დომეინი.",
+       "authmanager-autocreate-noperm": "ავტომატური ანგარიშის შექმნა არ არის ნებადართული.",
+       "authmanager-autocreate-exception": "ავტომატური ანგარიშის შექმნა დროებით გათიშულია ადრინდელი ხარვეზების გამო.",
+       "authmanager-userdoesnotexist": "მომხმარებლის ანგარიში „$1“ არ არის რეგისტრირებული",
+       "authmanager-username-help": "მომხმარებლის სახელი აუთენთიფიკაციისთვის.",
+       "authmanager-password-help": "პაროლი აუთენთიფიკაციისთვის.",
+       "authmanager-domain-help": "დომეინი გარე აუთენთიფიკაციისთვის.",
+       "authmanager-retype-help": "დასადასტურებლად კვლავ შეიყვანეთ პაროლი.",
+       "authmanager-email-label": "ელ. ფოსტა",
+       "authmanager-email-help": "ელ. ფოსტის მისამართი",
+       "authmanager-realname-label": "ნამდვილი სახელი",
+       "authmanager-realname-help": "მომხმარებლის ნამდვილი სახელი",
+       "authmanager-provider-password": "პაროლზე დაფუძნებული აუთენთიფიკაცია",
+       "authmanager-provider-password-domain": "პაროლზე და დომეინზე დაფუძნებული აუთენთიფიკაცია",
+       "authmanager-provider-temporarypassword": "დროებითი პაროლი",
+       "authprovider-confirmlink-request-label": "ანგარიშები, რომლებიც დაკავშირებული უნდა იყოს",
+       "authprovider-confirmlink-success-line": "$1: დაუკავშირდა წარმატებით.",
+       "authprovider-confirmlink-failed": "ანგარიშის დაკავშირება არ შესრულდა სრულად: $1",
+       "authprovider-confirmlink-ok-help": "გააგრძელე დაკავშირების ჩავარდნის შეტყობინებების ჩვენების შემდეგ.",
+       "authprovider-resetpass-skip-label": "გამოტოვება",
+       "authprovider-resetpass-skip-help": "გამოტოვეთ პაროლის შეცვლის პროცესი.",
+       "authform-nosession-login": "ავტორიზაციამ წარმატებით ჩაიარა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+       "authform-nosession-signup": "ანგარიში შეიქმნა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+       "authform-newtoken": "არ არის ტოკენი. $1",
+       "authform-notoken": "არ არის ტოკენი",
+       "authform-wrongtoken": "არასწორი ტოკენი",
+       "specialpage-securitylevel-not-allowed-title": "არ არის ნებადართული",
+       "specialpage-securitylevel-not-allowed": "უკაცრავად, თქვე არ შეგიძლიათ ამ გვერდის გამოყენება, რადგან თქვენი იდენთიფიკაცია არ არის დადასტურებული.",
+       "authpage-cannot-login": "შეუძლებელია ავტორიზაციის დაწყება.",
+       "authpage-cannot-login-continue": "შეუძლებელია ავტორიზაციის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "authpage-cannot-create": "შეუძლებელია ანგარიშის შექმნის დაწყება.",
+       "authpage-cannot-create-continue": "შეუძლებელია ანგარიშის შექმნის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "authpage-cannot-link": "შეუძლებელია ანგარიშის დაკავშირების დაწყება.",
+       "authpage-cannot-link-continue": "შეუძლებელია ანგარიშის დაკავშირების გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "cannotauth-not-allowed-title": "ნებართვა უარყოფილია",
+       "cannotauth-not-allowed": "თქვენ არ გაქვთ ამ გვერდის გამოყენების უფლება",
+       "changecredentials": "მონაცემების შეცვლა",
+       "changecredentials-submit": "მონაცემების შეცვლა",
+       "changecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+       "changecredentials-success": "თქვენი მონაცემები შეიცვალა.",
+       "removecredentials": "მონაცემების წაშლა",
+       "removecredentials-submit": "მონაცემების წაშლა",
+       "removecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+       "removecredentials-success": "თქვენი მონაცემები წაიშალა.",
+       "credentialsform-provider": "მონაცემის ტიპი:",
+       "credentialsform-account": "ანგარიშის სახელი:",
+       "cannotlink-no-provider-title": "არ არის დაკავშირებული ანგარიშები",
+       "cannotlink-no-provider": "არ არის დაკავშირებული ანგარიშები.",
+       "linkaccounts": "ანგარიშების დაკავშირება",
+       "linkaccounts-success-text": "ანგარიში დაკავშირებულია.",
+       "linkaccounts-submit": "ანგარიშების დაკავშირება",
+       "unlinkaccounts": "ანგარიშებისთვის დაკავშირების მოშორება",
+       "unlinkaccounts-success": "ანგარიშს მოეხსნა დაკავშირება.",
+       "userjsispublic": "შენიშვნა: ჯავასკრიპტის ქვეგვერდები არ უნდა შეიცავდეს პირადულ მონაცემებს, რადგან მას სხვა მომხმარებლებიც ხედავენ.",
+       "usercssispublic": "შენიშვნა: CSS ქვეგვერდები არ უნდა შეიცავდეს პირადულ მონაცემებს, რადგან მას სხვა მომხმარებლებიც ხედავენ."
 }
index 8abca6a..43dc0de 100644 (file)
@@ -7,7 +7,8 @@
                        "Mirzali",
                        "아라",
                        "Macofe",
-                       "Kumkumuk"
+                       "Kumkumuk",
+                       "Asmen"
                ]
        },
        "tog-underline": "Bınê gırey de xete bonce:",
        "specialpage": "Pela xısusiye",
        "personaltools": "Hacetê keşi",
        "articlepage": "Pela zerreki bıvêne",
-       "talk": "Hurênais",
+       "talk": "Hurênayis",
        "views": "Asaişi",
        "toolbox": "Qutiya hacetu",
        "userpage": "Pela karberi bıvêne",
        "yourname": "Namê karberi:",
        "yourpassword": "Parola:",
        "yourpasswordagain": "Parola tekrar ke:",
-       "remembermypassword": "Cıkotena mı na komputeri de bia ho viri (seba tewr jêde $1 {{PLURAL:$1|roze|rozu}})",
        "yourdomainname": "Bandıra sıma:",
        "externaldberror": "Cıfeteliyaisê naskerdene de ya xeta esta ya ki tebera vırastena hesabê sıma rê destur çino.",
        "login": "Cı kuye",
        "botpasswords-label-cancel": "Bıtexelne",
        "resetpass_forbidden": "Paroley nêşikinê bıvurniyê",
        "resetpass-submit-loggedin": "Parola bıvurne",
-       "resetpass-submit-cancel": "Bıtexelne",
+       "resetpass-submit-cancel": "Peyd kı",
        "resetpass-temp-password": "Parola vêrdiye:",
        "bold_sample": "Nusto qolınd",
        "bold_tip": "Nusto qolınd",
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "tooltip-minoredit": "Ney jê vurnaiso qıc isaret ke",
        "tooltip-save": "Vurnaisunê ho qeyd ke",
+       "tooltip-publish": "Pele xo bışevekne",
        "tooltip-preview": "Kerem ke, vurnaisunê ho qeyd-kerdene ra ravêr be verqayt bıasne!",
        "tooltip-diff": "Kamci vurnayışi ke to meqale de kerdê, ninan basne.",
        "tooltip-compareselectedversions": "Ferqunê wertê ni dı nımınunê weçinıtu bıvêne.",
index f6df3c3..08b84e8 100644 (file)
        "yourpasswordagain": "Құпия сөзді қайталаңыз:",
        "createacct-yourpasswordagain": "Құпия сөзді құптаңыз",
        "createacct-yourpasswordagain-ph": "Құпия сөзіңізді қайтадан енгізіңіз",
-       "remembermypassword": "Тіркелгімді осы браузерде ұмытпа (ең көбі $1 {{PLURAL:$1|күн|күн}})",
        "userlogin-remembermypassword": "Мені жүйеде сақтап қою",
        "userlogin-signwithsecure": "Қауіпсіз байланысуды қолдану",
        "cannotloginnow-title": "Қазір шығу мүмкін емес",
        "createacct-reason-ph": "Неге басқа тіркегі жасамақшысыз",
        "createacct-submit": "Тіркелгіңізді жасаңыз",
        "createacct-another-submit": "Тіркелгі жасау",
+       "createacct-continue-submit": "Тіркелуді жалғастыру",
        "createacct-benefit-heading": "{{SITENAME}} сіздермен жасалады.",
        "createacct-benefit-body1": "{{PLURAL:$1|өңдеме|өңдеме}}",
        "createacct-benefit-body2": "{{PLURAL:$1|бет|бет}}",
        "mw-widgets-dateinput-placeholder-day": "ЖЖЖЖ-АА-КК",
        "mw-widgets-dateinput-placeholder-month": "ЖЖЖЖ-АА",
        "mw-widgets-titleinput-description-new-page": "бет жоқ екен",
-       "mw-widgets-titleinput-description-redirect": "$1 дегенге бағыттату"
+       "mw-widgets-titleinput-description-redirect": "$1 дегенге бағыттату",
+       "log-action-filter-protect": "Қорғау түрі"
 }
index 688ee15..50d114f 100644 (file)
        "tog-showhiddencats": "숨은 분류 보이기",
        "tog-norollbackdiff": "되돌리기 후 차이를 보지 않기",
        "tog-useeditwarning": "바꾼 내용을 저장하지 않고 편집 페이지를 벗어날 때 내게 경고하기",
-       "tog-prefershttps": "ë¡\9cê·¸ì\9d¸í\95  ë\95\8c 항상 보안 연결 사용",
+       "tog-prefershttps": "ë¡\9cê·¸ì\9d¸í\95\98ë\8a\94 ë\8f\99ì\95\88 항상 보안 연결 사용",
        "underline-always": "항상",
        "underline-never": "항상 치지 않기",
        "underline-default": "스킨 또는 브라우저 기본값",
        "newwindow": "(새 창에서 열림)",
        "cancel": "취소",
        "moredotdotdot": "더 보기...",
-       "morenotlisted": "ì\9d´ ëª©ë¡\9dì\9d\80 ì\99\84ì\84±ë\90\98ì§\80 ì\95\8aì\95\98습니다.",
+       "morenotlisted": "ì\9d´ ëª©ë¡\9dì\9d\80 ì\99\84ì \84í\95\98ì§\80 ì\95\8aì\9d\84 ì\88\98 ì\9e\88습니다.",
        "mypage": "문서",
        "mytalk": "토론",
        "anontalk": "토론",
        "undelete_short": "{{PLURAL:$1|편집 한 개|편집 $1개}} 되살리기",
        "viewdeleted_short": "{{PLURAL:$1|삭제된 편집 한 개|삭제된 편집 $1개}} 보기",
        "protect": "보호",
-       "protect_change": "ë³´í\98¸ ì\88\98ì¤\80 ë°\94꾸기",
+       "protect_change": "바꾸기",
        "protectthispage": "이 문서 보호하기",
        "unprotect": "보호 설정 바꾸기",
        "unprotectthispage": "이 문서의 보호 설정을 바꾸기",
        "yourpasswordagain": "비밀번호 다시 입력:",
        "createacct-yourpasswordagain": "비밀번호 확인",
        "createacct-yourpasswordagain-ph": "비밀번호를 다시 입력하세요",
-       "remembermypassword": "이 브라우저에 로그인 상태 저장하기 (최대 $1일)",
        "userlogin-remembermypassword": "로그인 상태를 유지하기",
        "userlogin-signwithsecure": "보안 연결 사용",
+       "cannotlogin-title": "로그인할 수 없음",
+       "cannotlogin-text": "로그인할 수 없습니다.",
        "cannotloginnow-title": "지금 로그인할 수 없습니다.",
        "cannotloginnow-text": "$1 사용 중에는 로그인이 불가능합니다.",
+       "cannotcreateaccount-title": "계정을 만들 수 없습니다",
+       "cannotcreateaccount-text": "이 위키에서 직접 계정 만들기는 활성화되어 있지 않습니다.",
        "yourdomainname": "도메인 이름:",
        "password-change-forbidden": "이 위키에서 비밀번호를 바꿀 수 없습니다.",
        "externaldberror": "인증 데이터베이스에 오류가 있거나 바깥 계정을 새로 고칠 권한이 없습니다.",
        "invalid-content-data": "잘못된 내용 데이터입니다",
        "content-not-allowed-here": "\"$1\" 내용은 [[$2]] 문서예 허용하지 않습니다",
        "editwarning-warning": "이 페이지에서 벗어나면 저장하지 않은 바뀜이 모두 사라집니다.\n로그인을 했다면, 환경 설정의 \"{{int:prefs-editing}}\"에서 이 경고를 띄우지 않도록 설정할 수 있습니다.",
+       "editpage-invalidcontentmodel-title": "지원하지 않는 콘텐츠 모델",
+       "editpage-invalidcontentmodel-text": "\"$1\" 콘텐츠 모델은 지원되지 않습니다.",
        "editpage-notsupportedcontentformat-title": "지원하지 않는 내용 형식",
        "editpage-notsupportedcontentformat-text": "내용 형식 $1은(는) $2 내용 모델에서 지원하지 않습니다.",
        "content-model-wikitext": "위키텍스트",
        "filerevert-submit": "되돌리기",
        "filerevert-success": "'''[[Media:$1|$1]]''' 파일을 [$4 $2 $3 버전]으로 되돌렸습니다.",
        "filerevert-badversion": "입력된 시간 기록을 가진 파일의 로컬 버전이 없습니다.",
+       "filerevert-identical": "파일의 현재 버전은 선택한 것과 이미 동일합니다.",
        "filedelete": "$1 삭제하기",
        "filedelete-legend": "파일 삭제하기",
        "filedelete-intro": "'''[[Media:$1|$1]]''' 파일과 모든 역사를 삭제합니다.",
        "rollbacklinkcount-morethan": "{{PLURAL:$1|편집}} $1회 이상 되돌리기",
        "rollbackfailed": "되돌리기 실패",
        "rollback-missingparam": "요청에 필요한 변수가 존재하지 않습니다.",
+       "rollback-missingrevision": "판 데이터를 불러올 수 없습니다.",
        "cantrollback": "편집을 되돌릴 수 없습니다.\n문서를 편집한 저자가 한 명뿐입니다.",
        "alreadyrolled": "[[:$1]]에서 [[User:$2|$2]] ([[User talk:$2|토론]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])의 편집을 되돌릴 수 없습니다.\n누군가가 이미 문서를 고치거나 되돌렸습니다.\n\n마지막으로 이 문서를 편집한 사용자는 [[User:$3|$3]] ([[User talk:$3|토론]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])입니다.",
        "editcomment": "편집 요약: <em>$1</em>",
        "pageinfo-article-id": "문서 ID",
        "pageinfo-language": "문서 내용 언어",
        "pageinfo-content-model": "문서 내용 모델",
+       "pageinfo-content-model-change": "변경",
        "pageinfo-robot-policy": "로봇에 의한 색인",
        "pageinfo-robot-index": "허용됨",
        "pageinfo-robot-noindex": "불허됨",
        "tag-filter": "[[Special:Tags|태그]] 필터:",
        "tag-filter-submit": "필터",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|태그}}]]: $2)",
+       "tag-mw-contentmodelchange": "콘텐츠 모델 변경",
        "tags-title": "태그",
        "tags-intro": "이 문서는 소프트웨어에서 편집에 대해 표시하는 태그와 그 의미를 설명하는 목록입니다.",
        "tags-tag": "태그 이름",
        "tags-actions-header": "동작",
        "tags-active-yes": "예",
        "tags-active-no": "아니오",
-       "tags-source-extension": "확장 기능에 의해 정의됨",
+       "tags-source-extension": "소프트웨어에 의해 정의됨",
        "tags-source-manual": "사용자나 봇에 의해 수동으로 적용됨",
        "tags-source-none": "더 이상 사용하지 않음",
        "tags-edit": "편집",
index 1994b0d..5c34a58 100644 (file)
        "yourpasswordagain": "Tesseram adfirmare:",
        "createacct-yourpasswordagain": "Tesseram confirmare",
        "createacct-yourpasswordagain-ph": "Tesseram iterum inscribe",
-       "remembermypassword": "Tesseram meam hoc in navigatro inter conventa memento ({{PLURAL:$1|die|diebus}} $1 tenus)",
        "userlogin-remembermypassword": "Nomen meum retineatur",
        "yourdomainname": "Regnum tuum:",
        "login": "Nomen dare",
        "minoredit": "Haec est recensio minor",
        "watchthis": "Hanc paginam observare",
        "savearticle": "Hanc redactionem servare",
+       "savechanges": "Mutationes divulgare",
        "publishpage": "Hanc paginam divulgare",
        "publishchanges": "Hanc recensionem divulgare",
        "preview": "Praevidere",
index f9bb80c..e04e424 100644 (file)
@@ -45,7 +45,7 @@
        "tog-enotifminoredits": "Schéckt mir och bei klengen Ännerungen op vu mir iwwerwaachte Säiten oder Fichieren eng E-Mail.",
        "tog-enotifrevealaddr": "Meng E-Mail-Adress an de Benoriichtegungsmaile weisen.",
        "tog-shownumberswatching": "D'Zuel vun de Benotzer déi dës Säit iwwerwaache weisen",
-       "tog-oldsig": "Aktuell Ënnerschrëft:",
+       "tog-oldsig": "Är Aktuell Ënnerschrëft:",
        "tog-fancysig": "Ënnerschrëft als Wiki-Text behandelen (Ouni automatesche Link)",
        "tog-uselivepreview": "Live-Preview benotzen",
        "tog-forceeditsummary": "Warnen, wa beim Späicheren de Resumé feelt",
@@ -62,7 +62,7 @@
        "tog-showhiddencats": "Verstoppt Kategorië weisen",
        "tog-norollbackdiff": "Ënnerscheed nom Zrécksetzen net weisen",
        "tog-useeditwarning": "Mech warne wann ech d'Ännerung vun enger Säit verloossen, ouni Ännerunge gespäichert ze hunn",
-       "tog-prefershttps": "Ëmmer eng sécher Verbindung benotze wann ageloggt",
+       "tog-prefershttps": "Benotzt ëmmer eng sécher Verbindung wann ageloggt",
        "underline-always": "Ëmmer",
        "underline-never": "Ni",
        "underline-default": "Skin oder Standard vum Browser",
        "newwindow": "(geet an enger neier Fënster op)",
        "cancel": "Zréck",
        "moredotdotdot": "Méi …",
-       "morenotlisted": "Dës Lëscht ass net komplett.",
+       "morenotlisted": "Dës Lëscht ass eventuell net komplett.",
        "mypage": "Säit",
        "mytalk": "Diskussioun",
        "anontalk": "Diskussioun",
        "yourpasswordagain": "Passwuert nach eemol antippen:",
        "createacct-yourpasswordagain": "Passwuert confirméieren",
        "createacct-yourpasswordagain-ph": "Passwuert nach eng Kéier aginn",
-       "remembermypassword": "Meng Umeldung op dësem Computer (fir maximal $1 {{PLURAL:$1|Dag|Deeg}}) verhalen",
        "userlogin-remembermypassword": "Mech ageloggt halen",
        "userlogin-signwithsecure": "Eng sécher Verbindung benotzen",
+       "cannotlogin-title": "Aloggen ass net méiglech",
+       "cannotlogin-text": "Aloggen ass net méiglech.",
        "cannotloginnow-title": "Aloggen ass elo net méiglech",
        "cannotloginnow-text": "Aloggen ass net méiglech wann dir $1 benotzt.",
+       "cannotcreateaccount-title": "Benotzerkont kënnen net opgemaach ginn",
+       "cannotcreateaccount-text": "D'direkt Uleeë vu Benotzerkonten ass an dëser Wiki net aktivéiert.",
        "yourdomainname": "Ären Domän:",
        "password-change-forbidden": "Dir däerft op dëser Wiki Passwierder net änneren.",
        "externaldberror": "Entweder ass e Feeler bei der externer Authentifizéierung geschitt, oder Dir däerft Ären externe Benotzerkont net aktualiséieren.",
        "botpasswords-updated-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf aktualiséiert.",
        "botpasswords-deleted-title": "Botpasswuert geläscht",
        "botpasswords-deleted-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf geläscht.",
-       "botpasswords-newpassword": "Dat neit Passwuert fir sech mat <strong>$1</strong> anzeloggen ass <strong>$2</strong>.\n<em>Versuergt dat fir sech spéider dorop ze referéieren.</em>",
+       "botpasswords-newpassword": "Dat neit Passwuert fir sech mat <strong>$1</strong> anzeloggen ass <strong>$2</strong>.\n<em>Versuergt dat fir sech spéider dorop ze referéieren.</em><br />(Fir al Botten déi verlaangen datt de Login-Numm d'selwecht ass wéi den spéidere Benotzernumm, kënnt Dir och <strong>$3</strong> als Benotzernumm benotzten a(n) <strong>$4</strong> als Passwuert.)",
        "botpasswords-not-exist": "De Benotzer \"$1\" huet kee Botpasswuert mam Numm \"$2\".",
        "resetpass_forbidden": "Passwierder kënnen net geännert ginn.",
        "resetpass_forbidden-reason": "Passwierder kënnen net geännert ginn: $1",
        "invalid-content-data": "Donnéeë vum Inhalt sinn net valabel",
        "content-not-allowed-here": "\"$1\"-Inhalt ass op der Säit [[$2]] net erlaabt",
        "editwarning-warning": "Wann Dir dës Säit verloosst kann dat dozou féieren datt Dir all Ännerungen, déi Dir gemaach hutt, verléiert.\nWann Dir ageloggt sidd, kënnt Dir dës Warnung an der Sektioun \"{{int:prefs-editing}}\" vun Ären Astellungen ausschalten.",
+       "editpage-invalidcontentmodel-title": "Modell vum Inhalt gëtt net ënnerstëtzt",
        "editpage-notsupportedcontentformat-title": "Format vum Inhalt gëtt net ënnerstëtzt",
        "editpage-notsupportedcontentformat-text": "De Format vum Inhalt $1 gëtt net vum Modell vum Inhalt $2 ënnerstëtzt.",
        "content-model-wikitext": "Wikitext",
        "filerevert-submit": "Zrécksetzen",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> gouf op d'[$4 Versioun vum $2, $3 Auer] zréckgesat.",
        "filerevert-badversion": "Et gëtt keng vireg lokal Versioun vun deem Fichier mat der Zäitinformatioun déi Dir uginn hutt.",
+       "filerevert-identical": "Déi aktuell Versioun vum Fichier ass identesch mat där erausgesichter.",
        "filedelete": "Läsch \"$1\"",
        "filedelete-legend": "Fichier läschen",
        "filedelete-intro": "Dir läscht de Fichier '''[[Media:$1|$1]]''' mat all senge Versiounen (Historique).",
        "rollbacklinkcount-morethan": "méi wéi {{PLURAL:$1|Eng Ännerung|$1 Ännerungen}} zrécksetzen",
        "rollbackfailed": "Zrécksetzen huet net geklappt",
        "rollback-missingparam": "An der Ufro feelen obligatoresch Parameteren.",
+       "rollback-missingrevision": "D'Donnéeë vun der Versioun konnten net geluede ginn.",
        "cantrollback": "Lescht Ännerung kann net zréckgesat ginn. De leschten Auteur ass deen eenzegen Auteur vun dëser Säit.",
        "alreadyrolled": "Déi lescht Ännerung vun der Säit [[:$1]] vum [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); kann net zréckgesat ginn;\neen Aneren huet dat entweder scho gemaach oder nei Ännerungen agedroen.\n\nDéi lescht Ännerung vun der Säit war vum [[User:$3|$3]] ([[User talk:$3|Diskussioun]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "De Resumé vun der Ännerung war: <em>$1</em>.",
        "anoncontribs": "Kontributiounen",
        "contribsub2": "Fir {{GENDER:$3|den $1|d'$1|de Benotzer $1}} ($2)",
        "contributions-userdoesnotexist": "De Benotzerkont \"$1\" ass net registréiert.",
-       "nocontribs": "Et goufe keng Ännerunge fonnt, déi dëse Kritèren entspriechen.",
+       "nocontribs": "Et goufe keng Ännerunge fonnt, déi dëse Critèren entspriechen.",
        "uctop": "(aktuell)",
        "month": "Vum Mount (a virdrun):",
        "year": "Vum Joer (a virdrun):",
        "pageinfo-article-id": "ID (Nummer) vun der Säit",
        "pageinfo-language": "Sprooch vum Inhalt vun der Säit",
        "pageinfo-content-model": "Modell vun enger Säit mat Inhalt",
+       "pageinfo-content-model-change": "änneren",
        "pageinfo-robot-policy": "Indexéierung duerch Botten",
        "pageinfo-robot-index": "Erlaabt",
        "pageinfo-robot-noindex": "Net erlaabt",
        "specialpages-group-changes": "Rezent Ännerungen a Lëschten",
        "specialpages-group-media": "Medie-Rapporten an eropgeluede Fichieren",
        "specialpages-group-users": "Benotzer a Rechter",
-       "specialpages-group-highuse": "Dacks benotzte Säiten",
+       "specialpages-group-highuse": "Dacks benotzt Säiten",
        "specialpages-group-pages": "Lëschte vu Säiten",
        "specialpages-group-pagetools": "Handwierksgeschir fir Säiten",
        "specialpages-group-wiki": "Daten an Handwierksgeschir",
        "tag-filter": "[[Special:Tags|Markéierungs]]-Filter:",
        "tag-filter-submit": "Filter",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
+       "tag-mw-contentmodelchange": "Ännerung vum Modell vum Inhalt",
        "tags-title": "Markéierungen",
        "tags-intro": "Op dëser Säit stinn all déi Taggen, déi vun dëser Software fir Ännerungen unzeweise benotzt ginn, an hir Bedeitung.",
        "tags-tag": "Numm vun der Markéierung",
        "tags-actions-header": "Aktiounen",
        "tags-active-yes": "Jo",
        "tags-active-no": "Neen",
-       "tags-source-extension": "Duerch eng Erweiderung definéiert",
+       "tags-source-extension": "Duerch d'Software definéiert",
        "tags-source-manual": "Manuell vu Benotzer a vu Botten agesat",
        "tags-source-none": "Gëtt net méi gebraucht",
        "tags-edit": "änneren",
        "tags-edit-revision-submit": "Ännerungen op {{PLURAL:$1|dës Versioun|$1 Versiounen}} uwennen",
        "tags-edit-success": "D'Ännerunge goufen applizéiert.",
        "tags-edit-failure": "D'Ännerunge konnten net applizéiert ginn: $1",
+       "tags-edit-nooldid-title": "Net-valabel Zilversioun",
        "tags-edit-none-selected": "Sicht mindestens eng Markéierung eraus déi dir dobäisetzen oder ewechhuele wëllt.",
        "comparepages": "Säite vergläichen",
        "compare-page1": "Säit 1",
        "credentialsform-account": "Numm vum Kont:",
        "cannotlink-no-provider-title": "Et gëtt keng Benotzerkonte fir ze verlinken",
        "linkaccounts": "Benotzerkonte verbannen",
-       "linkaccounts-submit": "Benotzerkonte verbannen"
+       "linkaccounts-submit": "Benotzerkonte verbannen",
+       "userjsispublic": "DEnkt drun: Op JavaScript-Ënnersäite solle keng vertraulech Informatioune stoe well se vun anere Benotzer kënne gesi ginn."
 }
index 8de4353..5194c8b 100644 (file)
@@ -61,7 +61,7 @@
        "underline-always": "Sempre",
        "underline-never": "Mâi",
        "underline-default": "Impostassioin predefinie do navegatô o da skin",
-       "editfont-style": "Stile do carattere de l'aera de modiffica",
+       "editfont-style": "Stile do carattere de l'area de modiffica",
        "editfont-default": "Predefinio do navegatô",
        "editfont-monospace": "Carattere a larghessa fissa",
        "editfont-sansserif": "Carattere sans-serif",
        "tagline": "Da {{SITENAME}}",
        "help": "Agiùtto",
        "search": "Çerca",
+       "search-ignored-headings": " #<!-- lascia questa riga esattamente comm'a l'è --> <pre>\n# Elenco de intestaçioin che saian ignoræ da-a riçerca.\n# E modifiche a questa paggina saian effettive non apen-a a paggina a saiâ indiçizâ.\n# Ti poeu forçâ a re-indiçizzaçion de 'na paggina effettoando una modifica nulla.\n# A scintasci a l'è a seguente:\n#   * Tutto da-o carattere \"#\" a-a fin da riga o l'è un commento\n#   * Tutte e righe non voeue son e intestaçioin esatte da ignorâ, maiuscolo/minuscolo e tutto\nNotte\nVoxe correlæ\nCollegamenti esterni\n #</pre> <!-- lascia questa riga esattamente comm'a l'è -->",
        "searchbutton": "Çerca",
        "go": "Vanni",
        "searcharticle": "Vanni",
        "yourpasswordagain": "Riscrivi a pòula segrétta:",
        "createacct-yourpasswordagain": "Conferma a password",
        "createacct-yourpasswordagain-ph": "Conferma a password un'atra votta",
-       "remembermypassword": "Aregòrda a mæ login in sto navegatô (pe in mascimo de $1 {{PLURAL:$1|giórno|giórni}})",
        "userlogin-remembermypassword": "Mantegnime collegou",
        "userlogin-signwithsecure": "Adoeuvia una conescion segua",
+       "cannotlogin-title": "Imposcibbile intrâ",
        "cannotloginnow-title": "Aoa no se poeu intrâ",
        "cannotloginnow-text": "Quande s'adoeuvia $1 no se poeu intrâ.",
+       "cannotcreateaccount-title": "Imposcibbile creâ di utençe",
        "yourdomainname": "Indirisso do scito:",
        "password-change-forbidden": "No ti peu cangiâ poula segretta in questa wiki.",
        "externaldberror": "Gh'è stæto un aro co-o server de aotenticaçion esterno, oppû no ti g'hæ i aotorizzaçioin pe aggiornâ o to accesso esterno.",
        "botpasswords-updated-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta aggiornâ.",
        "botpasswords-deleted-title": "Password bot scassâ",
        "botpasswords-deleted-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta scassâ.",
-       "botpasswords-newpassword": "A noeuva password pe accede con <strong>$1</strong> a l'è <strong>$2</strong>. <em>Marchitelo pe rifeimento futuo.</em>",
+       "botpasswords-newpassword": "A noeuva password pe accede con <strong>$1</strong> a l'è <strong>$2</strong>. <em>Marchitelo pe rifeimento futuo.</em><br> (Pe-i vegi bot che g'han de besoeugno che o nomme pe accede o segge o mæximo che o nomme utente, ti poeu doeuviâ <strong>$3</strong> comme nomme utente e <strong>$4</strong> comme password.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider o no l'è disponibbile.",
        "botpasswords-restriction-failed": "E restriçioin de password bot impediscian questo accesso.",
        "botpasswords-invalid-name": "O nomme utente indicou o no conten o separatô pe-o password bot (\"$1\").",
        "subject-preview": "Anteprimma do soggetto:",
        "previewerrortext": "Gh'è stæto un errô mentre se çercava de mostrâ l'anteprimma.",
        "blockedtitle": "L'utente o l'é bloccòu",
-       "blockedtext": "''''O to nomme utente ò adresso IP o l'è stæto bloccòu.'''\n\nO blòcco o l'è stæto fæto da $1. A raxon dæta a l'è ''$2''.\n\n* Iniçio do blocco: $8\n* Fin do blocco: $6\n* Utente blocou: $7\n\nL'è poscibbile contattâ $1 o un âtro [[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discûtte inscio blòcco.\nNo ti poeu doeuviâ o comando \"Manda un'e-mail a st'ûtente\" se no ti g'hæ 'n adresso e-mail registròu inte to [[Special:Preferences|preferençe]] e se no t'ê stæto bloccòu ascì.\nO to adresso IP o l'è $3, e o to blòcco ID o l'è #$5.\nPe piaxei, pe domandâ informaçioin, speçifficali tutti doî.",
+       "blockedtext": "''''O to nomme utente ò adresso IP o l'è stæto bloccòu.'''\n\nO blòcco o l'è stæto fæto da $1. A raxon dæta a l'è ''$2''.\n\n* Prinçippio do blocco: $8\n* Fin do blocco: $6\n* Utente blocou: $7\n\nL'è poscibbile contattâ $1 ò un atro [[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discûtte inscio blòcco.\nNo ti poeu doeuviâ o comando \"Manda un'e-mail a st'utente\" se no ti g'hæ 'n adreçço e-mail registròu inte to [[Special:Preferences|preferençe]] e se no t'ê stæto bloccòu ascì.\nO to adreçço IP o l'è $3, e o to blòcco ID o l'è #$5.\nPe piaxei, pe domandâ de informaçioin, speçifficali tutti doî.",
        "autoblockedtext": "O teu addresso IP o l'è stæto bloccòu outomaticamente perché o l'ea za usòu da 'n âtro utente, bloccòu da $1.\nA raxon dæta a l'è stæta:\n\n:''$2''\n\n* Prinsippio do blòcco: $8\n* Fin do blòcco: $6\n\nTi peu contattâ $1 ò un âtro\n[[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discutte o blòcco.\n\nDanni a mente a che no ti pêu ûsâ o comando \"manda 'na e-mail a sto utente\" se non ti g'hæ 'n addresso de posta elettronega registròu in te têu [[Special:Preferences|preferense]] e se ti no t'ê stæto bloccòu ascì.\n\nO to adresso IP o l'è $3, e o to blòcco ID o l'è #$5. Pe piaxei, pe domandâ informaçioin, speçifficali tutti doî.",
        "blockednoreason": "nisciun-a motivaçion dæta",
        "whitelistedittext": "Pe modificâ e paggine l'è necessaio $1.",
        "invalid-content-data": "Dæti contegnui non vallidi",
        "content-not-allowed-here": "Contegnuo in \"$1\" non consentio inta paggina [[$2]]",
        "editwarning-warning": "Lasciâ sta paggina porriæ caosâ a perdia de tutte e modiffiche fæte.\nSe ti t'hê introu, ti peu disattivâ st'aviso inta seçion \"{{int:prefs-editing}}\" de teu preferençe.",
+       "editpage-invalidcontentmodel-title": "Modello de contegnuo non supportou",
+       "editpage-invalidcontentmodel-text": "O modello de contegnuo \"$1\" o no l'è supportou.",
        "editpage-notsupportedcontentformat-title": "Formato contegnuo non supportou",
        "editpage-notsupportedcontentformat-text": "O formato do contegnuo $1 o no l'è supportou da-o modello de contegnuo $2.",
        "content-model-wikitext": "wikitesto",
        "grant-group-high-volume": "Esegue açioin mascive",
        "grant-group-customization": "Personalizzaçion e preferençe",
        "grant-group-administration": "Esegue açioin amministrative",
+       "grant-group-private-information": "O l'accede a-i dæti privæ sciu de ti",
        "grant-group-other": "Attivitæ varrie",
        "grant-blockusers": "Blocca e sblocca utenti",
        "grant-createaccount": "Crea un'utença",
        "grant-highvolume": "Modiffiche mascive",
        "grant-oversight": "Asconde i utenti e soprimme e revixoin",
        "grant-patrol": "Marca e modifiche a-e paggine comme veificæ",
+       "grant-privateinfo": "O l'accede a de informaçioin privæ",
        "grant-protect": "Proteze e sproteze e paggine",
        "grant-rollback": "Rollback de modifiche a-e pagine",
        "grant-sendemail": "Manda di email a di atri utenti",
        "action-applychangetags": "appricâ di etichette a-e to modiffiche",
        "action-changetags": "Azonze e levâ de specifiche etichette sciu scingole verscioin o voxe de registro",
        "action-deletechangetags": "scassâ i etichette da-o database",
+       "action-purge": "aggiornâ questa paggina",
        "nchanges": "$1 {{PLURAL:$1|modiffica|modiffiche}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|da l'urtima vixita}}",
        "enhancedrc-history": "cronologia",
        "file-thumbnail-no": "O nomme do file comença con <strong>$1</strong>; pâ ch'o segge un'inmaggine de dimenscioin redute ''(miniatua)''.\nSe ti dispon-i del'immaggine inta risoluçion originale, carreghila. Sedunque, pe piaxei, cangighe o nomme.",
        "fileexists-forbidden": "Un file con questo nomme o l'existe za e o no poeu ese soviascrito. Se ti voeu caregâ o to file, torna inderê e dagghe un atro nomme. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un file con questo nomme o l'esiste za inte l'archivio de risorse multimediæ condivise. Se ti dexiddei ancon caregâ o file, torna inderê e modifica o nomme co-o quæ caregâ o file. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "O file caregou o l'è un duplicou esatto de l'attoale verscion de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "O file caregou o l'è un duplicou esatto de {{PLURAL:$2|'na verscion precedente|verscioin precedente}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Questo file o l'è un dupricou {{PLURAL:$1|do seguente|di seguenti}} file:",
        "file-deleted-duplicate": "Un file identico a questo ([[:$1]]) o l'è stæto scassou into passou. Verifica a cronologia de scassatue primma de caregâlo torna.",
        "file-deleted-duplicate-notitle": "Un file identico a questo o l'è stæto scassou into passou, e o tittolo o l'è stæto soppresso. Domanda a quarcun ch'o g'ha a poscibilitæ de vedde i file soppresci de esaminâ a scituaçion primma de procede torna a-o caregamento.",
        "filerevert-submit": "Ripristina",
        "filerevert-success": "'''O file [[Media:$1|$1]]''' o l'è stæto ripristinou a-a [$4 verscion do $2, $3].",
        "filerevert-badversion": "No gh'è de verscioin locali precedenti do file co-o timestamp provisto.",
+       "filerevert-identical": "A verscion attoale do file a l'è za identica a quella seleçionâ.",
        "filedelete": "Scassa \"$1\"",
        "filedelete-legend": "Scassa o file",
        "filedelete-intro": "Ti stæ pe scassâ o file '''[[Media:$1|$1]]''' con tutta a so cronologia.",
        "defemailsubject": "Messaggio da {{SITENAME}} da l'utente \"$1\"",
        "usermaildisabled": "e-mail utente disabilitâ",
        "usermaildisabledtext": "No l'è poscibbile inviâ de e-mail a di atri utenti insce questo wiki",
-       "noemailtitle": "Nisciun adreççoo e-mail",
+       "noemailtitle": "Nisciun adreçço e-mail",
        "noemailtext": "Questo utente o no l'ha indicou un adreçço e-mail vallido.",
        "nowikiemailtext": "Questo utente o l'ha scerto de no riçeive messaggi de posta elettronica da-i atri utenti.",
        "emailnotarget": "Nomme utente do destinataio inexistente o non vallido.",
        "emailto": "A:",
        "emailsubject": "Sogetto:",
        "emailmessage": "Messaggio:",
-       "emailsend": "Spèdi",
-       "emailccme": "Mandame unn-a copia do messagio co unn-a lettìa elettronega.",
+       "emailsend": "Spedisci",
+       "emailccme": "Mandime una copia do messaggio a-o mæ adreçço.",
        "emailccsubject": "Coppia do messaggio inviou a $1: $2",
        "emailsent": "E-mail spedïa",
        "emailsenttext": "A teu e-mail a l'è stæta spedïa.",
        "watchnologin": "Accesso non effettuou",
        "addwatch": "Azonzi a-a lista sotta öservaçion",
        "addedwatchtext": "\"[[:$1]]\" e a so paggina de discuscion son stæte azonte a-a proppia [[Special:Watchlist|lista di öservæ]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" e a so paggina associâ son stæte azonte a-a to [[Special:Watchlist|lista di öservæ]].",
        "addedwatchtext-short": "A pagina \"$1\" a l'è stæata azonta a-a proppia lista di öservæ.",
        "removewatch": "Rimoeuvi da-i öservæ speciali",
        "removedwatchtext": "\"[[:$1]]\" e a so paggina de discuscion son stæte rimosse da-a proppia [[Special:Watchlist|lista di öservæ]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" e a so paggina associâ son stæte rimosse da-a to [[Special:Watchlist|lista di öservæ]].",
        "removedwatchtext-short": "A pagina \"$1\" a l'è stæata rimossa da-a proppia lista di öservæ.",
        "watch": "Metti sotta oservaçion",
        "watchthispage": "Vigilâ 'sta paggina",
        "rollbacklinkcount-morethan": "rollback de ciù de {{PLURAL:$1|una modiffica|$1 modiffiche}}",
        "rollbackfailed": "Rollback fallio",
        "rollback-missingparam": "Parammetri obrigatoi mancanti inta recesta.",
+       "rollback-missingrevision": "Imposcibile caregâ i dæti da verscion.",
        "cantrollback": "No se peu tornâ inderê; l'utente ch'o l'ha fæto quelle modiffiche o l'è stæto l'unico contribuente.",
        "alreadyrolled": "No l'è poscibbile annullâ e modiffiche apportæ a-a pagina [[:$1]] da parte de [[User:$2|$2]] ([[User talk:$2|discuscion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); un atro utente o l'ha zà modificou a pagina oppù o l'ha effettuou o rollback.\n\nA modifica ciù reçente a.a paggina a l'è stæta apportâ da [[User:$3|$3]] ([[User talk:$3|discuscion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "L'ogetto da modiffica o l'ea: <em>$1</em>.",
        "undeletehistorynoadmin": "Questa pagina a l'è stæta scassâ.\nO motivo da scassatua o l'è mostrou chì sotta, insemme a-i detaggi de l'utente ch'o l'ha modificou questa pagina primma da scassatua.\nO testo contegnuo inte verscioin scassæ o l'è disponibile solo a-i amministratoî.",
        "undelete-revision": "Verscion scassâ da pagina $1, inseia o $4 a $5 da $3:",
        "undeleterevision-missing": "Verscion errâ o mancante. O collegamento o l'è errou o dunque a verscion a l'è stæta zà ripristinâ ò eliminâ da l'archivvio.",
+       "undeleterevision-duplicate-revid": "No s'è posciuo ripristinâ {{PLURAL:$1|una verscion|$1 verscioin}}, percose {{PLURAL:$1|o so}} <code>rev_id</code> o l'ea za in doeuvia.",
        "undelete-nodiff": "No l'è stæto trovou nisciun-a verscion precedente.",
        "undeletebtn": "Ristorâ",
        "undeletelink": "fanni védde/repìggia",
        "undeletedrevisions": "{{PLURAL:$1|Una verscion recuperâ|$1 verscioin recuperæ}}",
        "undeletedrevisions-files": "{{PLURAL:$1|Una verscion|$1 verscioin}} e $2 file recuperæ",
        "undeletedfiles": "{{PLURAL:$1|Un file recuperou|$1 file recuperæ}}",
-       "cannotundelete": "Ripristino non riuscio:\n$1",
+       "cannotundelete": "Çerti ò tutti i ripristini non riuscii:\n$1",
        "undeletedpage": "'''A pagina $1 a l'è stæta recuperâ'''\n\nConsurta o [[Special:Log/delete|registro de scançellaçioin]] pe vedde e scançellaçioin e i recupperi ciù reçente.",
        "undelete-header": "Consurta o [[Special:Log/delete|registro de scançellaçioin]] pe vedde e scassatue ciù reçente.",
        "undelete-search-title": "Çerca inte pagine scassæ",
        "sp-contributions-newbies-sub": "Pe i nêuvi ûtenti",
        "sp-contributions-newbies-title": "Contribuçioin di noeuvi utenti",
        "sp-contributions-blocklog": "Blòcchi",
-       "sp-contributions-suppresslog": "contributi utente soppresci",
-       "sp-contributions-deleted": "contributi utente scassæ",
+       "sp-contributions-suppresslog": "contributi {{GENDER:$1|utente}} soppresci",
+       "sp-contributions-deleted": "contributi {{GENDER:$1|utente}}  scassæ",
        "sp-contributions-uploads": "caregaménti",
        "sp-contributions-logs": "log",
        "sp-contributions-talk": "Ciæti",
        "export-templates": "Inciodi i template",
        "export-pagelinks": "Includdi paggine colegæ scin a 'na profonditæ de:",
        "export-manual": "Azonzi paggine manoalmente:",
-       "allmessages": "Messaggi do scistemma",
+       "allmessages": "Messaggi do scistema",
        "allmessagesname": "Nomme",
        "allmessagesdefault": "Testo predefinio",
        "allmessagescurrent": "Testo corrente",
        "pageinfo-article-id": "ID da paggina",
        "pageinfo-language": "Lengua do contegnuo da paggina",
        "pageinfo-content-model": "Modello do contegnuo da paggina",
+       "pageinfo-content-model-change": "cangia",
        "pageinfo-robot-policy": "Indiçizzaçion pe-i robot",
        "pageinfo-robot-index": "Consentio",
        "pageinfo-robot-noindex": "Non consentio",
        "tag-filter": "Filtra pe [[Special:Tags|etichetta]]:",
        "tag-filter-submit": "Filtro",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etichetta|Etichette}}]]: $2)",
+       "tag-mw-contentmodelchange": "cangio a-o modello di contegnui",
        "tags-title": "Etichette",
        "tags-intro": "Questa pagina a l'elenca i etichette che o software o poriæ associâ a 'na modiffica e o so scignificou.",
        "tags-tag": "Nomme de l'etichetta",
        "tags-actions-header": "Açioin",
        "tags-active-yes": "Sci",
        "tags-active-no": "No",
-       "tags-source-extension": "Definio da 'n'estenscion",
+       "tags-source-extension": "Definio da-o programma",
        "tags-source-manual": "Appricou manoalmente da utenti e bot",
        "tags-source-none": "No ciù in doeuvia",
        "tags-edit": "cangia",
index 86a2ba4..f3cf09b 100644 (file)
        "yourpasswordagain": "Pakartokite slaptažodį:",
        "createacct-yourpasswordagain": "Patvirtinkite slaptažodį",
        "createacct-yourpasswordagain-ph": "Įveskite slaptažodį dar kartą",
-       "remembermypassword": "Prisiminti prisijungimo duomenis šiame kompiuteryje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})",
        "userlogin-remembermypassword": "Įsiminti mane",
        "userlogin-signwithsecure": "Naudoti saugią jungtį",
+       "cannotlogin-title": "Negalima prisijungti",
+       "cannotlogin-text": "Prisijungti neįmanoma.",
        "cannotloginnow-title": "Dabar negalima prisijungti",
        "cannotloginnow-text": "Prisijungimas negalimas, kai naudojama $1.",
+       "cannotcreateaccount-title": "Negali kurti paskyrų",
+       "cannotcreateaccount-text": "Tiesioginis paskyros kūrimas nėra įgalintas šioje viki.",
        "yourdomainname": "Jūsų domenas:",
        "password-change-forbidden": "Jus negalite keisti slaptažodžių šioje wiki.",
        "externaldberror": "Yra arba išorinė autorizacijos duomenų bazės klaida arba jums neleidžiama atnaujinti jūsų išorinės paskyros.",
        "file-thumbnail-no": "Failo pavadinimas prasideda  <strong>$1</strong>.\nAtrodo, kad yra sumažinto dydžio paveikslėlis ''(miniatiūra)''.\nJei jūs turite šį paveisklėlį pilna raiška, įkelkite šitą, priešingu atveju prašome pakeisti failo pavadinimą.",
        "fileexists-forbidden": "Failas tokiu pačiu vardu jau egzistuoja ir negali būti perrašytas;\nprašome eiti atgal ir įkelti šį failą kitu vardu. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Failas tokiu vardu jau egzistuoja bendrojoje failų saugykloje;\nJei visvien norite įkelti savo failą, prašome eiti atgal ir įkelti šį failą kitu vardu. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Įkėlimas yra <strong>[[:$1]]</strong> dabartinės versijos tikslus dublikatas.",
+       "fileexists-duplicate-version": "Įkėlimas yra <strong>[[:$1]]</strong> {{PLURAL:$2|senesnės versijos|senesnių versijų}} tikslus dublikatas.",
        "file-exists-duplicate": "Šis failas yra {{PLURAL:$1|šio failo|šių failų}} dublikatas:",
        "file-deleted-duplicate": "Failas, identiškas šiam failui ([[:$1]]), seniau buvo ištrintas. Prieš įkeldami jį vėl patikrinkite šio failo ištrynimo istoriją.",
        "file-deleted-duplicate-notitle": "Rinkmena, visiškai atitinkanti šią, anksčiau buvo ištrinta, o jos pavadinimas uždraustas. Jums reiktų paprašyti kieno nors, turinčio galimybę peržiūrėti uždraustą rinkmeną, kad jis išaiškintų padėtį, prieš bandant vėl kelti rinkmeną.",
        "filerevert-submit": "Grąžinti",
        "filerevert-success": "<span class=\"plainlinks\">'''[[Media:$1|$1]]''' buvo sugrąžintas į versiją $4 ($2, $3).</span>",
        "filerevert-badversion": "Nėra jokių ankstesnių vietinių šio failo versijų su pateiktu laiku.",
+       "filerevert-identical": "Dabartinė failo versija jau yra identiška pasirinktajai.",
        "filedelete": "Trinti $1",
        "filedelete-legend": "Trinti rinkmeną",
        "filedelete-intro": "Jūs ketinate ištrinti failą '''[[Media:$1|$1]]''' su visa istorija.",
        "pageinfo-article-id": "Puslapio ID",
        "pageinfo-language": "Puslapio turinio kalba",
        "pageinfo-content-model": "Puslapio turinio modelis",
+       "pageinfo-content-model-change": "keisti",
        "pageinfo-robot-policy": "Robotų indeksavimas",
        "pageinfo-robot-index": "Leidžiama",
        "pageinfo-robot-noindex": "Neleidžiama",
index 89742eb..bc9db01 100644 (file)
@@ -51,7 +51,7 @@
        "tog-enotifminoredits": "Paziņot pa e-pastu arī par maznozīmīgiem labojumiem rakstos un failos",
        "tog-enotifrevealaddr": "Atklāt manu e-pasta adresi paziņojumu vēstulēs",
        "tog-shownumberswatching": "Rādīt uzraudzītāju skaitu",
-       "tog-oldsig": "Pašreizējais paraksts:",
+       "tog-oldsig": "Esošais paraksts:",
        "tog-fancysig": "Vienkāršs paraksts (bez automātiskās saites)",
        "tog-uselivepreview": "Lietot tūlītējo priekšskatījumu",
        "tog-forceeditsummary": "Atgādināt man, ja kopsavilkuma ailīte ir tukša",
        "newwindow": "(atveras jaunā logā)",
        "cancel": "Atcelt",
        "moredotdotdot": "Vairāk...",
-       "morenotlisted": "Šis saraksts nav pilnīgs.",
+       "morenotlisted": "Šis saraksts var nebūt pilnīgs.",
        "mypage": "Lapa",
        "mytalk": "Diskusijas",
        "anontalk": "Diskusijas",
        "yourpasswordagain": "Atkārto paroli",
        "createacct-yourpasswordagain": "Apstipriniet paroli",
        "createacct-yourpasswordagain-ph": "Vēlreiz ievadiet paroli",
-       "remembermypassword": "Atcerēties pēc pārlūka aizvēršanas (spēkā ne vairāk kā $1 {{PLURAL:$1|dienas|diena|dienas}}).",
        "userlogin-remembermypassword": "Atcerēties mani",
        "userlogin-signwithsecure": "Izmantot drošu savienojumu",
        "yourdomainname": "Tavs domēns",
        "prefs-resetpass": "Mainīt paroli",
        "prefs-changeemail": "Mainīt vai noņemt e-pastu",
        "prefs-setemail": "Uzstādīt e-pasta adresi",
-       "prefs-email": "E-pasta uzstādījumi",
+       "prefs-email": "E-pasta iestatījumi",
        "prefs-rendering": "Izskats",
        "saveprefs": "Saglabāt",
        "restoreprefs": "Atjaunot noklusētos uzstādījumus (visās sadaļās)",
        "recreate": "Izveidot no jauna",
        "confirm_purge_button": "Labi",
        "confirm-purge-top": "Iztīrīt šīs lapas kešu (''cache'')?",
+       "confirm-purge-bottom": "Lapas atjaunināšana iztīra kešatmiņu un liek parādīt lapas jaunāko versiju.",
        "confirm-watch-button": "Labi",
        "confirm-watch-top": "Pievienot šo lapu uzraugāmo lapu sarakstam?",
        "confirm-unwatch-button": "Labi",
index e02a91c..d9d8ea0 100644 (file)
        "pagecategories": "{{PLURAL:$1|श्रेणी|श्रेणीसभ}}",
        "category_header": "श्रेणी \"$1\" मे पन्ना सभ",
        "subcategories": "उपश्रेणी",
-       "category-media-header": "शà¥\8dरà¥\87णà¥\80 \"$1\" à¤®à¥\87 à¤®à¥\80डिया",
+       "category-media-header": "शà¥\8dरà¥\87णà¥\80 \"$1\" à¤®à¥\87 à¤®à¤¿डिया",
        "category-empty": "<em>ई श्रेणीमे ई समय कोनो पृष्ठ या मिडिया नै अछि।</em>",
        "hidden-categories": "{{PLURAL:$1|नुकाएल श्रेणी|नुकाएल श्रेणीसभ}}",
        "hidden-category-category": "नुकाएल श्रेणीसभ",
        "broken-file-category": "पन्नासभ जाइमे फाइल लिङ्कसभ टूटल हुअए",
        "about": "क विषयमे",
        "article": "सामग्री लेख",
-       "newwindow": "(नव à¤\96िडà¤\95à¥\80सà¤\81 à¤\96à¥\81à¤\9cà¥\88à¤\9b)",
+       "newwindow": "(नव à¤µà¤¿à¤¨à¥\8dडà¥\8bमà¥\87 à¤\96à¥\81à¤\9cत)",
        "cancel": "रद्द करी",
        "moredotdotdot": "आर...",
        "morenotlisted": "ई पुरा सूची नै छी।",
        "otherlanguages": "अन्य भाषासभमे",
        "redirectedfrom": "($1सँ पुनर्निर्देशित)",
        "redirectpagesub": "पृष्ठ पुनर्निर्देशित करी",
-       "redirectto": "क अनुप्रेषित:",
-       "lastmodifiedat": "à¤\88 à¤ªà¥\83षà¥\8dठà¤\95 à¤ªà¤¹à¤¿à¤¨à¥\81à¤\95ा à¤¬à¤¦à¤²à¤¾à¤µ $1 à¤\95à¥\87 $2 à¤¬à¤\9cà¥\87 à¤­à¤\8fल छल।",
+       "redirectto": "क अनुप्रेषित:",
+       "lastmodifiedat": "à¤\88 à¤ªà¥\83षà¥\8dठà¤\95 à¤ªà¤¹à¤¿à¤¨à¥\81à¤\95ा à¤¬à¤¦à¤²à¤¾à¤µ $1 à¤\95à¥\87 $2 à¤¬à¤\9cà¥\87 à¤­à¥\87ल छल।",
        "viewcount": "ई पृष्ठ {{PLURAL:$1|एक|$1}} बेर देखल गेल छल।",
        "protectedpage": "सुरक्षित पृष्ठ",
        "jumpto": "एतय जाए:",
        "yourpasswordagain": "कुटशब्द फेरसँ टाइप करी:",
        "createacct-yourpasswordagain": "कुटशब्द जाँच करी",
        "createacct-yourpasswordagain-ph": "कुटशब्द पुनः लिखी",
-       "remembermypassword": "ई ब्राउजर पर हमर सम्प्रवेश याद राखी (अधिकतम $1 {{PLURAL:$1|दिन|दिनधरि}}क लेल)",
        "userlogin-remembermypassword": "हमरा सम्प्रवेशित राखी",
        "userlogin-signwithsecure": "सुरक्षित कनेक्शनक प्रयोग करी",
        "cannotloginnow-title": "अखन प्रवेश नै भऽ रहल अछि",
        "gotaccountlink": "सम्प्रवेश",
        "userlogin-resetlink": "अपन सम्प्रवेश विवरण बिसरि गेलौ?",
        "userlogin-resetpassword-link": "अपन कूटशब्द बिसरि गेलौ?",
-       "userlogin-helplink2": "सम्प्रवेशित करवाकलेल मदत",
+       "userlogin-helplink2": "सम्प्रवेशित करवाक लेल मदति",
        "userlogin-loggedin": "अहाँ {{GENDER:$1|$1}}क रूपमे पहिनेसँ सम्प्रवेशित छी।\nकोनो दोसर सदस्यक रुपमे सम्प्रवेशित करवाक लेल देल गेल फारमके प्रयोग करी।",
        "userlogin-reauth": "अहाँ {{GENDER:$1|$1}} छी, एहि लेल अहाँक एक बेर आर खातामे प्रवेश करै पडत।",
        "userlogin-createanother": "दोसर खाता बनाबी",
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण:",
        "createacct-reason-ph": "अहा इगो आर दोसर खाता कियाक बनउने जा रहल छि",
-       "createacct-submit": "à¤\85पन à¤\96ाता à¤¬à¤¨à¤¾à¤\89",
+       "createacct-submit": "à¤\85पन à¤\96ाता à¤¬à¤¨à¤¾à¤¬à¥\80",
        "createacct-another-submit": "खाता बनाबी",
-       "createacct-benefit-heading": "{{SITENAME}} à¤\85हाà¤\81 à¤\9cà¥\8bà¤\95ा à¤²à¥\8bà¤\97सभदà¥\8dवारा à¤¬à¤¨à¤¾à¤\8fल à¤\97à¤\8fल अछि।",
+       "createacct-benefit-heading": "{{SITENAME}} à¤\85हाà¤\81 à¤\9cà¥\8bà¤\95ा à¤²à¥\8bà¤\97सभदà¥\8dवारा à¤¬à¤¨à¤¾à¤\8fल à¤\97à¥\87ल अछि।",
        "createacct-benefit-body1": "$1 {{PLURAL:$1|सम्पादन|सम्पादनसभ}}",
        "createacct-benefit-body2": "{{PLURAL:$1|पन्ना|पन्नासभ}}",
        "createacct-benefit-body3": "सन्निकट {{PLURAL:$1|योगदानकर्ता|योगदानकर्तासभ}}",
        "sectioneditnotsupported-text": "ई पृष्ठ पर अनुभाग सम्पादन समर्थित नै अछि",
        "permissionserrors": "आज्ञा गल्ती",
        "permissionserrorstext": "अहाँके ऐ लेल अनुमति नै अछि, ऐ ले {{PLURAL:$1|कारण|कारणसभ}}:",
-       "permissionserrorstext-withaction": "अहाँके अनुमति नै अछि $2 लेल, ऐ लेल {{PLURAL:$1|कारण|कारणसभ}}सँ:",
+       "permissionserrorstext-withaction": "अहाँक अनुमति नै अछि $2 लेल, एकर लेल {{PLURAL:$1|कारण|कारणसभ}}सँ:",
        "recreate-moveddeleted-warn": "'''चेतौनी''': अहाँ फेरसँ ओ पन्ना बना रहल छी जे पहिने मेटा देल गेल छै।'''\n\nअहाँ विचारू जे की ई सम्पादन केनाइ उचित अछि।\nऐ पन्नाक मेटाएल बला आ हटाएल वृत्तलेख एतए सुविधा लेल देल जा रहल अछि:",
-       "moveddeleted-notice": "ई पन्ना मेटा देल गेल अछि।\nऐ पन्ना लेल मेटाएल आ हटाएल बला वृत्तलेख सन्दर्भ लेल नीचाँ देल गेल अछि।",
+       "moveddeleted-notice": "ई पन्ना मेटाएल गेल अछि।\nई पन्ना लेल मेटाएल आ स्थानान्तरणक लग सन्दर्भ लेल नीचाँ देल गेल अछि।",
        "log-fulllog": "सभटा वृत्तलेख देखी",
        "edit-hook-aborted": "सम्पादन नोकसीसँ खतम भेल।\nई कोनो कारण नै देलक।",
        "edit-gone-missing": "पन्ना अद्यतन नै भऽ सकल।\nलगैए जे ई मेटा देल गेल अछि।",
        "revertmerge": "नै मिज्झर",
        "mergelogpagetext": "नीचाँ एक पन्ना इतिहासक दोसरमे अद्यतन मिश्रणक सूची अछि।",
        "history-title": "\"$1\" क संशोधन इतिहास",
-       "difference-title": "\"$1\" à¤\95à¥\87 à¤\85वतरणसभमà¥\87 à¤\85à¤\82तर",
+       "difference-title": "\"$1\" à¤\95à¥\87 à¤\85वतरणसभमà¥\87 à¤\85नà¥\8dतर",
        "difference-title-multipage": "\"$1\" आर \"$2\" पृष्ठसभ मे अंतर",
        "difference-multipage": "(पन्ना सभक बीचमे अन्तर)",
        "lineno": "पंक्त्ति $1:",
        "next-page": "अगला पृष्ठ",
        "prevn-title": "पहिलुका $1 {{PLURAL:$1|परिणाम|परिणामसभ}}",
        "nextn-title": "आगाँ $1 {{PLURAL:$1|परिणाम|परिणामसभ}}",
-       "shown-title": "पà¥\8dरति à¤ªà¤¨à¥\8dना $1 {{PLURAL:$1|परिणाम|परिणामसभ}} à¤¦à¥\87à¤\96ाà¤\89",
+       "shown-title": "पà¥\8dरति à¤ªà¤¨à¥\8dना $1 {{PLURAL:$1|परिणाम|परिणामसभ}} à¤¦à¥\87à¤\96ाबà¥\80",
        "viewprevnext": "देखी ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>ऐ विकीपर एकटा पन्ना अछि \"[[:$1]]\" नामसँ।<strong>{{PLURAL:$2|0=|अन्य भेटल परिणामसभ सेहो देखी}}",
        "searchmenu-new": "''' पन्ना निर्माण \"[[:$1]]\" ऐ विकीपर !'''",
        "enhancedrc-history": "इतिहास",
        "recentchanges": "लगक परिवर्तनसभ",
        "recentchanges-legend": "नव परिवर्तन सम्बन्धी विकल्प",
-       "recentchanges-summary": "ई पन्नापर विकीमे भेल सभसँ अद्यतन परिवर्तनपर नजरि राखू।",
+       "recentchanges-summary": "ई पन्नापर विकीमे भेल सभ सँ अद्यतन परिवर्तनपर नजरि राखी।",
        "recentchanges-noresult": "इ अवधिके दौरान इ मापदंडके पूर्ण करेत समय कोनो परिवर्तन नै केएल गेल अछि।",
        "recentchanges-feed-description": "ई सूचना-तंत्रांशमे विकीमे भेल सभसँ लगक परिवर्तन ताकी।",
        "recentchanges-label-newpage": "ई सम्पादन एकटा नव पन्नाक निर्माण केलक।",
        "recentchanges-label-minor": "ई एकटा लघु सम्पादन छी",
        "recentchanges-label-bot": "ई सम्पादन यान्त्रिक छल।",
        "recentchanges-label-unpatrolled": "ऐ सम्पादनक पुनरीक्षण अखन धरि नै कएल गेल अछि।",
-       "recentchanges-label-plusminus": "पन्नाके आकार इ बाइट संख्यासे बदलल गेल",
+       "recentchanges-label-plusminus": "पन्ना आकार ई बाइट सङ्ख्या सँ बदलल गेल",
        "recentchanges-legend-heading": "<strong>कुञ्जी:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|नव पन्नसभक सूची]] सेहो देखी)",
        "rcnotefrom": "नीचाँमे '''$2''' सँ भेल परिवर्तन अछि ('''$1''' धरि देखाएल)।",
        "rc_categories": "संवर्ग सीमित (\"|\" सँ हटाउ)",
        "rc_categories_any": "कोनो",
        "rc-change-size": "$1",
-       "rc-change-size-new": "बदललाक बाद $1 {{PLURAL:$1|बाइट}}",
+       "rc-change-size-new": "परिवरà¥\8dतनक बाद $1 {{PLURAL:$1|बाइट}}",
        "newsectionsummary": "/* $1 */ नव अनुभाग",
        "rc-enhanced-expand": "वर्णन देखाउ (जावास्क्रिप्ट चाही)",
        "rc-enhanced-hide": "विस्तृत जानकारी नुकाबी",
        "listfiles-summary": "ई विशिष्ट पन्ना सभटा उपारोपित संचिका देखबैए।\nप्रयोक्ता द्वारा चुनलापर अन्तिम उपारोपित संचिका देखबैत अछि।",
        "listfiles_search_for": "ऐ दृश्य-श्रव्य नामले ताकू:",
        "listfiles-userdoesnotexist": "प्रयोक्ता खाता \"$1\" पंजीकृत नै अछि।",
-       "imgfile": "सà¤\82चिका",
+       "imgfile": "सà¤\9eà¥\8dचिका",
        "listfiles": "संचिका सूची",
        "listfiles_thumb": "लघुचित्र",
        "listfiles_date": "तिथि",
        "filehist-filesize": "संचिका आकार",
        "filehist-comment": "समीक्षा",
        "imagelinks": "फाइलक उपयोग",
-       "linkstoimage": "à¤\90 {{PLURAL:$1|पनà¥\8dनाà¤\95 à¤²à¤¾à¤\97ि |$1 à¤ªà¤¨à¥\8dनाà¤\95 à¤²à¤¾à¤\97ि}} à¤\90 à¤«à¤¾à¤\87लसà¤\81:",
+       "linkstoimage": "à¤\88 {{PLURAL:$1|पà¥\83षà¥\8dठ|$1 à¤ªà¤¨à¥\8dनासभ}}मà¥\87 à¤\88 à¤«à¤¾à¤\87लà¤\95 à¤²à¤¿à¤\99à¥\8dà¤\95 à¤\85à¤\9bि:",
        "linkstoimage-more": "$1 सँ बेसी {{PLURAL:$1|page links|पन्ना सभक लागि}} ऐ संचिकाक।\nई सूची देखबैए {{PLURAL:$1|first page link|first $1 page links}} मात्र ऐ संचिकाक।\nएकटा [[Special:WhatLinksHere/$2|पूर्ण सूची]] उपलब्ध अछि।",
-       "nolinkstoimage": "à¤\8fà¤\95à¥\8bà¤\9fा à¤ªà¤¨à¥\8dना à¤¨à¥\88 à¤\85à¤\9bि à¤\9cà¤\95र à¤²à¤¾à¤\97ि à¤\90 à¤¸à¤\82à¤\9aिà¤\95ासà¤\81 à¤¹à¥\81à¤\85ए।",
+       "nolinkstoimage": "à¤\8fà¤\95à¥\8bà¤\9fा à¤ªà¤¨à¥\8dना à¤¨à¥\88 à¤\85à¤\9bि à¤\9cà¥\87 à¤\88 à¤¸à¤\9eà¥\8dà¤\9aिà¤\95ा à¤¸à¤\81 à¤\9cà¥\81डल à¤¹à¥\8bए।",
        "morelinkstoimage": "देखू [[Special:WhatLinksHere/$1|आर लागि]] ऐ संचिकाक।",
        "linkstoimage-redirect": "$1 (संचिका घुमौआ) $2",
        "duplicatesoffile": "ऐ संचिकाक {{PLURAL:$1|file is a duplicate|$1 संचिका सभ द्वितीयक अछि}} अछि ([[Special:FileDuplicateSearch/$2|आर वर्णन]]):",
        "shared-repo-from": "$1 सँ",
        "shared-repo": "एकटा साझी बखारी",
        "shared-repo-name-wikimediacommons": "सामान्य विकीमीडिया",
-       "upload-disallowed-here": "à¤\85पनà¥\87 à¤¯à¥\80 à¤«à¤¼à¤¾à¤\87लà¤\95à¥\87 à¤\85धिलà¥\87à¤\96ित à¤¨à¥\88 à¤\95à¥\88रऽ à¤¸à¤\95à¥\88 à¤\9bि।",
+       "upload-disallowed-here": "à¤\85हाà¤\81 à¤\88 à¤«à¤¾à¤\87लà¤\95à¥\87 à¤\85धिलà¥\87à¤\96ित à¤¨à¥\88 à¤\95रि à¤¸à¤\95à¥\88त à¤\9bà¥\80।",
        "filerevert": "$1 लग घुरु",
        "filerevert-legend": "घुराएल संचिका",
        "filerevert-intro": "अहाँ संचिका घुराबैले छी '''[[Media:$1|$1]]''' केँ [$4 संस्करण $3, $2 केँ] लग।",
        "removewatch": "साकांक्ष सूचीसँ हटाबी",
        "removedwatchtext": "अहाँक [[Special:Watchlist|ध्यानसूची]]सँ \"[[:$1]]\" आ एकर चर्चा पृष्ठ हटाएल गेल अछि।",
        "removedwatchtext-short": "इ पृष्ठ \"$1\" अहाँ के साकांक्ष सूची मे राखल गेल अछि।",
-       "watch": "धà¥\8dयान à¤°à¤¾à¤\96à¥\81",
+       "watch": "धà¥\8dयान à¤°à¤¾à¤\96à¥\80",
        "watchthispage": "ऐ पृष्ठपर ध्यान राखू",
        "unwatch": "छोडी",
        "unwatchthispage": "देखनाइ छोडी",
        "deleting-backlinks-warning": "'''चेतौनी:''' जे पृष्ठ अहाँ हटावए लेल जा रहल छी वोकरा में  [[Special:WhatLinksHere/{{FULLPAGENAME}}|अन्य पृष्ठ]] जुड़एत अछि अथवा वोकरा ट्रान्सक्ल्युड करएत अछि।",
        "rollback": "प्रत्यावर्तित सम्पादन",
        "rollbacklink": "प्रत्यावर्तन",
-       "rollbacklinkcount": "$1 {{PLURAL:$1|समà¥\8dपादन}} à¤ªà¥\82रà¥\8dववत à¤\95रà¥\82",
+       "rollbacklinkcount": "$1 {{PLURAL:$1|समà¥\8dपादन}} à¤ªà¥\82रà¥\8dववत à¤\95रà¥\80",
        "rollbacklinkcount-morethan": "$1 सँ अधिक {{PLURAL:$1|सम्पादन}} पूर्ववत करू",
        "rollbackfailed": "प्रत्यावर्तन असफल",
        "cantrollback": "सम्पादन आपस नै भऽ सकै अछि;\nअन्तिम योगदान दैबला ऐ पन्नाक एकमात्र लेखक छी।",
        "invert": "उनटा चयन",
        "tooltip-invert": "ऐ बक्साकेँ सही करू पन्ना परिवर्तनकेँ नुकेबा लेल चयनित नामस्थानक भीतर (आ संग लागल नामस्थान जँ सही कएल अछि तखन)",
        "namespace_association": "सम्बद्ध चेन्हासी",
-       "tooltip-namespace_association": "à¤\90 à¤¬à¤\95à¥\8dसाà¤\95à¥\87à¤\81 à¤¸à¤¹à¥\80 à¤\95रà¥\82 जइसँ वार्ता आ विषय नामस्थान समाहित कएल जा सकए चुनल नामस्थानमे",
+       "tooltip-namespace_association": "à¤\88 à¤¬à¤\95à¥\8dसाà¤\95à¥\87à¤\81 à¤¸à¤¹à¥\80 à¤\95रà¥\80 जइसँ वार्ता आ विषय नामस्थान समाहित कएल जा सकए चुनल नामस्थानमे",
        "blanknamespace": "(मुख्य)",
        "contributions": "{{GENDER:$1|प्रयोगकर्ता}} योगदान",
        "contributions-title": "$1 लेल प्रयोक्ताक अवदान",
        "nocontribs": "कोनो परिवर्तन ऐ सँ मेल नै खाइए।",
        "uctop": "(शिखर)",
        "month": "माससँ (आ पहिने)",
-       "year": "à¤\90 साल (आ पहिने)",
+       "year": "à¤\88 साल (आ पहिने)",
        "sp-contributions-newbies": "मात्र नव खाताक योगदान देखाबी",
        "sp-contributions-newbies-sub": "नब प्रयोक्ताकऽ लेल",
        "sp-contributions-newbies-title": "नब प्रयोक्ताकऽ योगदान",
        "sp-contributions-newonly": "मात्र ओइ सम्पादन देखाउ जे पृष्ठ निर्मित भेल अछि",
        "sp-contributions-submit": "ताकू",
        "whatlinkshere": "एतय कोन लिङ्क अछि",
-       "whatlinkshere-title": "\"$1\" सँ सम्बन्धित पन्ना सभ",
+       "whatlinkshere-title": "\"$1\" सँ सम्बन्धित पन्नासभ",
        "whatlinkshere-page": "पन्ना:",
        "linkshere": "ई सभ पन्ना सम्बन्धित अछि '''[[:$1]]''':",
        "nolinkshere": "'''[[:$1]]''' पर कोनो पन्नाक लागि नै अछि।",
        "nolinkshere-ns": "कोनो पन्नाक लागि '''[[:$1]]''' चुनल नामगाममे नै अछि।",
-       "isredirect": "पनà¥\8dनाà¤\95à¥\87à¤\81 à¤\98à¥\81राà¤\89",
+       "isredirect": "पà¥\81नरà¥\8dनिरà¥\8dदà¥\87शन à¤ªà¥\83षà¥\8dठ",
        "istemplate": "परागत",
-       "isimage": "फाइलकऽ जडी",
+       "isimage": "फाइल लिङ्क",
        "whatlinkshere-prev": "{{PLURAL:$1|पहिलुका|पहिलुका $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अगुलका|अगुलका $1}}",
        "whatlinkshere-links": "← जडीसभ",
-       "whatlinkshere-hideredirs": "$1 à¤¬à¤¦à¤²à¥\87न à¤¨à¥\81à¤\95ाबà¥\80",
-       "whatlinkshere-hidetrans": "$1 à¤ªà¤°à¤¾à¤\97त",
-       "whatlinkshere-hidelinks": "$1 à¤¸à¤®à¥\8dबनà¥\8dध à¤¸à¤­",
+       "whatlinkshere-hideredirs": "$1 à¤ªà¥\81नरà¥\8dनिरà¥\8dदà¥\87श",
+       "whatlinkshere-hidetrans": "$1 à¤\9fà¥\8dरानà¥\8dसà¥\8dà¤\95à¥\8dलà¥\8dयà¥\81à¤\9cनà¥\8dस",
+       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95",
        "whatlinkshere-hideimages": "$1 फाइल जडी सभ",
-       "whatlinkshere-filters": "चलनी सभ",
+       "whatlinkshere-filters": "चलनीसभ",
        "autoblockid": "स्वतःप्रतिबन्धित #$1",
        "block": "प्रयोक्ताकेँ प्रतिबन्धित करू",
        "unblock": "प्रयोक्ताकेँ प्रतिबन्धसँ हटाउ",
        "movepage-page-moved": "पन्ना $1 केँ $2 लग घसका देल गेल अछि।",
        "movepage-page-unmoved": "पन्ना $1 केँ $2 लग नै घसकाएल जा सकैए।",
        "movepage-max-pages": "बेसी सें बेसी $1 पृष्ठ बदलि के {{PLURAL:$1| क देल गेल अछि|क देल गेल अछि}}, आब आर पृष्ठ अपने आप नहि बदलत.",
-       "movelogpage": "वà¥\83तà¥\8dतलà¥\87à¤\96 à¤¹à¤\9fाà¤\89",
+       "movelogpage": "सà¥\8dथानानà¥\8dतरण à¤²à¤\97",
        "movelogpagetext": "नाम बदलल गेल लेख कऽ सूचि नीचां देल गेल अछि",
        "movesubpage": "{{PLURAL:$1|उप पन्ना|उप पन्ना}}",
        "movesubpagetext": "नीचां $1 {{PLURAL:$1| पन्ना देखाओल गएल अछि, जे अहि पन्नाकऽ उप पन्ना अछि|पन्ना देखावोल गएल अछि, जे अहि पन्नाकऽ उप पन्ना अछि}}।",
        "tooltip-pt-mytalk": "{{GENDER:|अहाँक}} वार्ता पृष्ठ",
        "tooltip-pt-anontalk": "ऐ अनिकेतसँ भेल सम्पादनक वार्ता",
        "tooltip-pt-preferences": "{{GENDER:|अहाँक}} अभिरुचीसभ",
-       "tooltip-pt-watchlist": "पन्ना सभ जकर परिवर्त्तन पर अहाँक नजरि अछि",
+       "tooltip-pt-watchlist": "पन्नासभ जेकर परिवर्तन पर अहाँक नजरि अछि",
        "tooltip-pt-mycontris": "{{GENDER:|अहाँक}} योगदानक सूची",
-       "tooltip-pt-login": "à¤\85हाà¤\81à¤\95 à¤\96ाता à¤\96à¥\8bलà¤\95 à¤²à¥\87ल à¤ªà¥\8dरà¥\8bतà¥\8dसाहित à¤\95à¥\87ल à¤\9cाà¤\8fत अछि; मुदा ई अनिवार्य नै अछि",
+       "tooltip-pt-login": "à¤\85हाà¤\81à¤\95 à¤\96ाता à¤\96à¥\8bलà¤\95 à¤²à¥\87ल à¤ªà¥\8dरà¥\8bतà¥\8dसाहित à¤\95à¤\8fल à¤\9cाà¤\87त अछि; मुदा ई अनिवार्य नै अछि",
        "tooltip-pt-logout": "फेर आयब",
-       "tooltip-pt-createaccount": "à¤\85हाà¤\81à¤\95 à¤\96ाता à¤\96à¥\8bलà¤\95 à¤²à¥\87ल à¤ªà¥\8dरà¥\8bतà¥\8dसाहित à¤\95à¥\87ल à¤\9cाà¤\8fत à¤\85à¤\9bि; à¤®à¥\81दा à¤\88 à¤\85निवारà¥\8dय à¤¨à¥\88 à¤\9bà¥\88",
+       "tooltip-pt-createaccount": "à¤\85हाà¤\81à¤\95 à¤\96ाता à¤\96à¥\8bलà¤\95 à¤²à¥\87ल à¤ªà¥\8dरà¥\8bतà¥\8dसाहित à¤\95à¤\8fल à¤\9cाà¤\87त à¤\85à¤\9bि; à¤®à¥\81दा à¤\88 à¤\85निवारà¥\8dय à¤¨à¥\88 à¤\85à¤\9bि",
        "tooltip-ca-talk": "विषयसूचीक पन्नाक सम्बन्धमे वर्त्तालाप",
        "tooltip-ca-edit": "ई पन्नाक सम्पादित करी",
        "tooltip-ca-addsection": "नव खण्ड शुरू करी",
-       "tooltip-ca-viewsource": "à¤\90 à¤ªà¤¨à¥\8dनापर à¤µà¤°à¤¦à¤¹à¤¸à¥\8dत à¤\9bà¥\88।\nà¤\85हाà¤\81 à¤\8fà¤\95र à¤\9cड़ि à¤¦à¥\87à¤\96 à¤¸à¤\95à¥\88 à¤\9bà¥\80।",
+       "tooltip-ca-viewsource": "à¤\88 à¤ªà¤¨à¥\8dना à¤¸à¤\82रà¤\95à¥\8dषित à¤\85à¤\9bि à¥¤\nà¤\85हाà¤\81 à¤\8fà¤\95र à¤¸à¥\8dरà¥\8bत à¤¦à¥\87à¤\96 à¤¸à¤\95à¥\88 à¤\9bà¥\80 ।",
        "tooltip-ca-history": "ई पृष्ठक पुरान अवतरण",
        "tooltip-ca-protect": "ऐ पन्नाकेँ बचाउ",
        "tooltip-ca-unprotect": "ऐ पन्नाक रक्षा कवच बदलू",
        "tooltip-ca-delete": "ऐ पन्नाकेँ मेटाउ",
        "tooltip-ca-undelete": "ई पन्ना मेटेबासँ पहिने भेल सम्पादन वापस करू",
-       "tooltip-ca-move": "à¤\90 à¤ªà¥\83षà¥\8dठà¤\95à¥\87à¤\81 à¤¹à¤\9fाà¤\89",
+       "tooltip-ca-move": "à¤\88 à¤ªà¥\83षà¥\8dठ à¤¸à¥\8dथानानतरित à¤\95रà¥\80",
        "tooltip-ca-watch": "ई पन्नाकेँ अपन साकांक्षसूचीमे राखी",
        "tooltip-ca-unwatch": "ऐ पन्नाकेँ हमर साकांक्ष सूचीसँ हटाउ",
        "tooltip-search": "{{SITENAME}}मे ताकी",
        "tooltip-n-currentevents": "लगक घटनाक विषयमे आधार सूचना प्राप्त करी।",
        "tooltip-n-recentchanges": "विकिमे लगक परिवर्तनक सूची",
        "tooltip-n-randompage": "कोनो अनिर्धारित पन्ना लोड करी",
-       "tooltip-n-help": "पता à¤²à¤\97ावà¤\8f वाला स्थान",
+       "tooltip-n-help": "पता à¤²à¤\97ावà¥\88वाला स्थान",
        "tooltip-t-whatlinkshere": "सभ विकी-पन्नाक सूची जकर एतय लिङ्क अछि",
        "tooltip-t-recentchangeslinked": "ई पृष्ठक लगक पन्नामे भेल नव परिवर्तनसभ",
        "tooltip-feed-rss": "ऐ पन्ना लेल आर.एस.एस. सूचना",
        "tooltip-t-print": "ई पृष्ठक छपैबला रूप",
        "tooltip-t-permalink": "पृष्ठक ई संस्करणक स्थायी लिङ्क",
        "tooltip-ca-nstab-main": "सामग्री वाला पृष्ठ देखी",
-       "tooltip-ca-nstab-user": "प्रयोक्ता पन्नाकेँ देखू",
+       "tooltip-ca-nstab-user": "प्रयोक्ता पन्ना देखी",
        "tooltip-ca-nstab-media": "मीडिया पृष्ठ देखू",
        "tooltip-ca-nstab-special": "ई एकटा विशिष्ट पन्ना छी, आ अहाँ एकरा सम्पादित नै कऽ सकै छी",
-       "tooltip-ca-nstab-project": "परियà¥\8bà¤\9cना à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\82",
+       "tooltip-ca-nstab-project": "परियà¥\8bà¤\9cना à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\80",
        "tooltip-ca-nstab-image": "सञ्चिकाक पृष्ठ देखी",
        "tooltip-ca-nstab-mediawiki": "प्रणालीक सन्देश देखी",
-       "tooltip-ca-nstab-template": "नमà¥\82ना à¤¦à¥\87à¤\96à¥\82",
+       "tooltip-ca-nstab-template": "नमà¥\82ना à¤¦à¥\87à¤\96à¥\80",
        "tooltip-ca-nstab-help": "सहायता पृष्ठ देखू",
-       "tooltip-ca-nstab-category": "सà¤\82वरà¥\8dà¤\97 à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\82",
+       "tooltip-ca-nstab-category": "शà¥\8dरà¥\87णà¥\80 à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\80",
        "tooltip-minoredit": "एकरा मामली सम्पादन चिन्हित करू",
-       "tooltip-save": "à¤\85पन à¤ªà¤°à¤¿à¤µà¤°à¥\8dतà¥\8dतनà¤\95à¥\87 à¤¸à¥\81रà¤\95à¥\8dषित à¤\95रà¥\82",
-       "tooltip-preview": "परिवरà¥\8dतà¥\8dतनà¤\95 à¤ªà¥\8dरदरà¥\8dशन, à¤¸à¤\82à¤\9cà¥\8bà¤\97बाà¤\95 à¤ªà¤¹à¤¿à¤¨à¥\87 à¤\8fà¤\95र à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95रà¥\82!",
-       "tooltip-diff": "दà¥\87à¤\96ाà¤\8a à¤\9cà¥\87 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतà¥\8dतन à¤\85हाà¤\81 à¤\8fहि à¤²à¥\87à¤\96मà¥\87 à¤\95à¤\8fलहà¥\81à¤\81।",
+       "tooltip-save": "à¤\85पन à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤¸à¥\81रà¤\95à¥\8dषित à¤\95रà¥\80",
+       "tooltip-preview": "परिवरà¥\8dतनà¤\95 à¤ªà¥\8dरदरà¥\8dशन, à¤¸à¤\82रà¤\95à¥\8dषण à¤¸à¤\81 à¤ªà¤¹à¤¿à¤¨à¥\87 à¤\8fà¤\95र à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95रà¥\80!",
+       "tooltip-diff": "à¤\88 à¤ªà¤¾à¤ à¤®à¥\87 à¤\85हाà¤\81दà¥\8dवारा à¤\95à¤\8fल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤¦à¥\87à¤\96à¥\80।",
        "tooltip-compareselectedversions": "ऐ पन्नाक दू टा चयन कएल संशोधनक बीचक अन्तर देखू",
        "tooltip-watch": "ऐ पन्नाकेँ अपन साकांक्ष सूचीमे जोड़ू",
        "tooltip-watchlistedit-normal-submit": "शीर्षक सभकेँ हटाउ",
        "file-info-size": "$1 × $2 चित्राणु, फाइल आकार: $3, माइम प्रकार: $4",
        "file-info-size-pages": "$1 × $2 चित्रकण, संचिका आकार : $3, माइम प्रकार: $4, $5 {{PLURAL:$5|पन्ना|पन्ना सभ}}",
        "file-nohires": "ऐसँ बेशी आनन्तर्य उपलब्ध नै अछि।",
-       "svg-long-desc": "एस.वी.जी. फाइल, मामूली रूपमे $1 × $2 चित्रकण, फाइलक आकार: $3",
+       "svg-long-desc": "एसभिजी फाइल, मामूली रूपमे $1 × $2 चित्रकण, फाइलक आकार: $3",
        "svg-long-desc-animated": "एनिमेटेड एस.वी.जी. फाइल,$1 × $2 चित्रकण, फाइलक आकार: $3",
        "svg-long-error": "अमान्य एस॰वी॰जी फ़ाइल: $1",
        "show-big-image": "पूर्ण आनन्तर्य",
        "exif-referenceblackwhite": "कारी आ उज्जर सन्दर्भ मूल्यक जोड़ा",
        "exif-datetime": "संचिका परिवर्तन तिथि आ समए",
        "exif-imagedescription": "चित्र शीर्षक",
-       "exif-make": "à¤\95à¥\88मरा निर्माता",
+       "exif-make": "à¤\95à¥\8dयामरा निर्माता",
        "exif-model": "क्यामरा मोडल",
-       "exif-software": "पà¥\8dरयà¥\81à¤\95à¥\8dत à¤¤à¤\82तà¥\8dराà¤\82श",
+       "exif-software": "पà¥\8dरयà¥\8bà¤\97 à¤\95à¤\8fल à¤¸à¤«à¥\8dà¤\9fवà¥\87यर",
        "exif-artist": "लिखैबला",
        "exif-copyright": "सर्वाधिकारी",
        "exif-exifversion": "एक्जिफ संस्करण",
        "exif-flashpixversion": "फ्लैशपिक्स संस्करण समर्थित",
-       "exif-colorspace": "रà¤\82गक स्थान",
+       "exif-colorspace": "रà¤\99à¥\8dगक स्थान",
        "exif-componentsconfiguration": "सभ घटकक अर्थ",
        "exif-compressedbitsperpixel": "चित्र संकुचन अवस्था",
        "exif-pixelxdimension": "तस्वीरक चौडाई",
        "exif-pixelydimension": "तस्वीरक ऊँचाई",
        "exif-usercomment": "सदस्यक टिप्पणी",
        "exif-relatedsoundfile": "संबंधित ध्वनि फ़ाईल",
-       "exif-datetimeoriginal": "डाà¤\9fा à¤¬à¤¨à¤¾à¤¬à¥\88à¤\95 à¤¤à¤¾à¤°à¥\80ख आ समय",
-       "exif-datetimedigitized": "à¤\85à¤\82à¤\95à¥\80à¤\95रण à¤\95à¥\87 à¤¤à¤¾à¤°à¥\80ख आ समय",
+       "exif-datetimeoriginal": "डाà¤\9fा à¤¬à¤¨à¤¾à¤¬à¥\88à¤\95 à¤¤à¤¾à¤°à¤¿ख आ समय",
+       "exif-datetimedigitized": "à¤\85à¤\99à¥\8dà¤\95à¥\80à¤\95रणà¤\95 à¤¤à¤¾à¤°à¤¿ख आ समय",
        "exif-subsectime": "दिनांकसमयक उपसेकंड",
        "exif-subsectimeoriginal": "मूलदिनांकसमयक उपसेकंड",
        "exif-subsectimedigitized": "मूलदिनांकअंकीकरणक उपसेकंड",
        "sqlite-no-fts": "$1 बिन पूर्ण-पाठ खोज सहायताक",
        "logentry-delete-delete": "$1 पृष्ठ $3 {{GENDER:$2|मेटौलक}}",
        "logentry-delete-restore": "$1 {{GENDER:$2|restored}} page $3",
-       "logentry-delete-event": "$1 {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 केँ",
-       "logentry-delete-revision": "$1 {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  पन्ना $3: $4 पर",
+       "logentry-delete-event": "$1 {{GENDER:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 केँ",
+       "logentry-delete-revision": "$1 {{GENDER:$2|परिवर्तन कियल गैल}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  पन्ना $3: $4 पर",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|changed}}  $3 पर वृत्तलेख दृश्य",
        "logentry-delete-revision-legacy": "$1 {{GENDER:$2|changed}}  $3 पर वृत्तलेख संशोधन",
-       "logentry-suppress-delete": "$1 {{लिंग:$2|दबाएल}} page $3",
-       "logentry-suppress-event": "$1 चोरिसँ {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 पर",
-       "logentry-suppress-revision": "$1 चोरिसँ {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  $3: $4 पर",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|दबाएल}} page $3",
+       "logentry-suppress-event": "$1 चोरिसँ {{GENDER:$2|परिवर्तन कियल गैल}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 पर",
+       "logentry-suppress-revision": "$1 चोरिसँ {{GENDER:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  $3: $4 पर",
        "logentry-suppress-event-legacy": "$1 नुका क {{GENDER:$2|परिवर्तन}}  $3 पर वृत्तलेख दृश्य",
        "logentry-suppress-revision-legacy": "$1 नुका कऽ {{GENDER:$2|changed}}  $3 पर संशोधन दृश्य",
        "revdelete-content-hid": "सामिग्री नुकाएल",
        "logentry-import-upload": "$1 {{GENDER:$2|आयात केल गेल}} $3 संचिका उपारोपन के माध्यम सँ",
        "logentry-import-interwiki": "$1 {{GENDER:$2|आयात केल गेल}} $3 कोनो और विकि सँ",
        "logentry-merge-merge": "$1 {{GENDER:$2|विलय केल गेल}} $3 के $4 में (संशोधन $5 धरि)",
-       "logentry-move-move": "$1 हटाएल पन्ना $3 सँ $4",
+       "logentry-move-move": "$1द्वारा $3 पृष्ठ $4 पर {{GENDER:$2|स्थानान्तरित}} कएलक",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआकेँ बिना छोड़ने",
        "logentry-move-move_redir": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतिरिक्त",
-       "logentry-move-move_redir-noredirect": "$1 {{लिंग:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतितिक्त घुमौआकेँ बिना छोड़ने",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतितिक्त घुमौआकेँ बिना छोड़ने",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
        "logentry-patrol-patrol-auto": "$1 स्वतः {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
        "logentry-newusers-newusers": "$1 {{GENDER:$2|बनाएल}} एकटा प्रयोक्ता खाता",
index 33ea41a..7f522e6 100644 (file)
        "yourpasswordagain": "Повторете ја лозинката:",
        "createacct-yourpasswordagain": "Потврда на лозинката",
        "createacct-yourpasswordagain-ph": "Повторно внесете ја лозинката",
-       "remembermypassword": "Запомни ме на овој сметач (највеќе $1 {{PLURAL:$1|ден|дена}})",
        "userlogin-remembermypassword": "Запомни ме",
        "userlogin-signwithsecure": "Користи безбеден опслужувач",
+       "cannotlogin-title": "Не можам да ве најавам",
+       "cannotlogin-text": "Најавата не е возможна.",
        "cannotloginnow-title": "Во моментов не можам да ве најавм",
        "cannotloginnow-text": "Не можам да ве најавам кога се користи $1.",
+       "cannotcreateaccount-title": "Не можам да создавам сметки",
+       "cannotcreateaccount-text": "Непосредното создавање на сметки не е овозможено на ова вики.",
        "yourdomainname": "Вашиот домен:",
        "password-change-forbidden": "Не можете да ја менувате лозинката на ова вики.",
        "externaldberror": "Настана грешка при надворешното најавување на базата или пак немате дозвола да ја подновите вашата надворешна сметка.",
        "botpasswords-updated-body": "Лозинката на ботот со име „$1“ на корисникот „$2“ е изменета.",
        "botpasswords-deleted-title": "Лозинка на ботот е избришана",
        "botpasswords-deleted-body": "Лозинката на ботот со име „$1“ на корисникот „$2“ е избришана.",
-       "botpasswords-newpassword": "Новата лозинка за најава <strong>$1</strong> е <strong>$2</strong>. <em>Запишете си ја за во иднина.</em>",
+       "botpasswords-newpassword": "Новата лозинка за најава <strong>$1</strong> е <strong>$2</strong>. <em>Запишете си ја за во иднина.</em> <br> (За стари ботови што бараат најавното име да биде исто како подоцнежното корисничко име, можете да ги употребите и <strong>$3</strong> како корисничко име и <strong>$4</strong> како лозинка.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider  е недостапен.",
        "botpasswords-restriction-failed": "Не можете да се најавите поради ограничувањата за лозинки на ботови.",
        "botpasswords-invalid-name": "Укажаното корисничко име не го содржи одделувачот ботовска лозинка („$1“).",
        "invalid-content-data": "Неважечки податоци од содржината",
        "content-not-allowed-here": "Содржините од моделот „$1“ не се допуштени на страницата [[$2]]",
        "editwarning-warning": "Ако ја напуштите страницата ќе ги изгубите сите промени кои сте ги направиле.\nАко сте најавени, можете да го исклучите ова предупредување во одделот „{{int:prefs-editing}}“ во вашите нагодувања.",
+       "editpage-invalidcontentmodel-title": "Содржинскиот модел не е поддржан",
+       "editpage-invalidcontentmodel-text": "Содржинскиот модел „$1“ не е поддржан.",
        "editpage-notsupportedcontentformat-title": "Форматот на содржината не е поддржан",
        "editpage-notsupportedcontentformat-text": "Форматот $1 is не е поддржан од содржинскиот модел $2.",
        "content-model-wikitext": "викитекст",
        "file-thumbnail-no": "Името на податотеката почнува со <strong>$1</strong>.\nИзгледа дека е слика со намалена големина ''(мини, thumbnail)''.\nАко ја имате оваа слика во изворна големина, подигнете ја неја. Во спротивно сменете го името на податотеката.",
        "fileexists-forbidden": "Податотека со тоа име веќе постои и не може да биде заменета.\nАко и понатаму сакате да ја подигнете вашата податотеката, ве молиме вратете се назад и подигнете ја под друго име. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Во заедничкото складиште веќе постои податотека со ова име.\nАко и понатаму сакате да ја подигнете, вратете се и подигнете ја под друго име. \n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Подигањето е истоветен дупликат на тековната верзија на <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Подигањето е истоветен дупликат на {{PLURAL:$2|постара верзија|постари верзии}} на <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Оваа податотека е дупликат со {{PLURAL:$1|следнава податотека|следниве податотеки}}:",
        "file-deleted-duplicate": "Податотека индентична со податотеката ([[:$1]]) претходно била избришана. Треба да проверите во дневникот на бришења за оваа податотека пред повторно да ја подигнете.",
        "file-deleted-duplicate-notitle": "Податотека сосем иста како оваа била претходно избришана, а насловот бил притаен.\nТреба да побарате од некој што има можност да гледа податоци за притаени податотеки да ја разгледа ситуацијата пред да продолжите со преподигањето.",
        "backend-fail-connect": "Не можев да се поврзам со складишната основа „$1“.",
        "backend-fail-internal": "Се појави непозната грешка во складишната основа „$1“.",
        "backend-fail-contenttype": "Не можев да утврдам каква содржина има податотеката што треба да ја складирам во „$1“.",
-       "backend-fail-batchsize": "Складишната основа доби блок од $1 податочна {{PLURAL:$1|операција|операции}}, а ограничувањето е $2 {{PLURAL:$2|операција|операции}}.",
+       "backend-fail-batchsize": "Складишната основа доби блок од $1 {{PLURAL:$1|податотечна постапка|податотечни постапки}}, а ограничувањето е $2 {{PLURAL:$2|постапка|постапки}}.",
        "backend-fail-usable": "Не можев да ја прочитам или запишам податотеката „$1“ бидејќи немате доволно дозволи или поради тоа што недостасуваат именици/содржатели.",
        "filejournal-fail-dbconnect": "Не можев да се поврзам со дневничката база за складишната основа „$1“.",
        "filejournal-fail-dbquery": "Не можев да ја подновам дневничката база за складишната основа „$1“.",
        "filerevert-submit": "Врати",
        "filerevert-success": "'''[[Media:$1|$1]]''' е вратен на [$4 верзијата од $3, $2].",
        "filerevert-badversion": "Нема претходна месна верзија на оваа податотека со даденото време.",
+       "filerevert-identical": "Тековната верзија на податотеката е веќе истоветна на избраната.",
        "filedelete": "Избриши го $1",
        "filedelete-legend": "Избриши податотека",
        "filedelete-intro": "Ја бришете податотеката '''[[Media:$1|$1]]''' заедно со нејзината историја.",
        "rollbacklinkcount-morethan": "отповикај повеќе од $1 {{PLURAL:$1|уредување|уредувања}}",
        "rollbackfailed": "Отповикувањето не успеа",
        "rollback-missingparam": "Недостасуваат задолжителни параметри за барањето.",
+       "rollback-missingrevision": "Не можам да ги вчитам податоците за преработката.",
        "cantrollback": "Уредувањето не може да се отповика.\nПоследниот уредник е воедно и единствениот автор на страницата.",
        "alreadyrolled": "Не може да се отповика последното уредување на страницата „[[:$1]]“ извршено од  [[User:$2|$2]] ([[User talk:$2|разговор]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nнекој друг веќе ја изменил или отповикал страницата.\n\nПоследното уредување го изврши [[User:$3|$3]] ([[User talk:$3|разговор]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Коментарот на уредувањето беше: <em>$1</em>.",
        "pageinfo-article-id": "Назнака на страницата",
        "pageinfo-language": "Јазик на содржината на страницата",
        "pageinfo-content-model": "Модел на содржината на страницата",
+       "pageinfo-content-model-change": "смени",
        "pageinfo-robot-policy": "Индексирање со роботи",
        "pageinfo-robot-index": "Дозволено",
        "pageinfo-robot-noindex": "Недозволено",
        "unit-pixel": "п",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Да го исчистам меѓускладот на страницава?",
-       "confirm-purge-bottom": "Со Ð¾Ð²Ð°Ð° Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\98а се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
+       "confirm-purge-bottom": "Со Ð¾Ð²Ð°Ð° Ð¿Ð¾Ñ\81Ñ\82апка се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
        "confirm-watch-button": "ОК",
        "confirm-watch-top": "Да ја додадам страницава во набљудуваните?",
        "confirm-unwatch-button": "ОК",
        "tag-filter": "[[Special:Tags|Филтер за ознаки]]:",
        "tag-filter-submit": "Филтер",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Ознака|Ознаки}}]]: $2)",
+       "tag-mw-contentmodelchange": "измена на содржинскиот модел",
+       "tag-mw-contentmodelchange-description": "Уредувања што го [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel менуваат содржинскиот модел] на една страница",
        "tags-title": "Ознаки",
        "tags-intro": "На оваа страница е даден список на ознаки со кои програмската опрема може да ги означи измените и нивното значење.",
        "tags-tag": "Име на ознака",
        "tags-actions-header": "Дејства",
        "tags-active-yes": "Да",
        "tags-active-no": "Не",
-       "tags-source-extension": "Ð\9eдÑ\80едени Ð¾Ð´ Ð´Ð¾Ð´Ð°Ñ\82ок",
+       "tags-source-extension": "Ð\9eдÑ\80едени Ð¾Ð´ Ð¿Ñ\80огÑ\80амоÑ\82",
        "tags-source-manual": "Применети рачно од корисници и ботови",
        "tags-source-none": "Вон употреба",
        "tags-edit": "уреди",
index 30f1f13..533ebef 100644 (file)
        "yourpasswordagain": "तुमचा परवलीचा शब्द पुन्हा टंका:",
        "createacct-yourpasswordagain": "परवलीच्या शब्दाची निश्चिती करा",
        "createacct-yourpasswordagain-ph": "पुन्हा परवलीचा शब्द टाका",
-       "remembermypassword": "माझा सनोंदप्रवेश (लॉग-ईन) या न्याहाळकावर लक्षात ठेवा (जास्तीत जास्त $1 {{PLURAL:$1|दिवसासाठी|दिवसांसाठी}})",
        "userlogin-remembermypassword": "मला नोंदीकृतच(लॉग्ड-ईन) ठेवा",
        "userlogin-signwithsecure": "सुरक्षित अनुबंध(सेक्युअर कनेक्शन) वापरा",
        "cannotloginnow-title": "आता सनोंद प्रवेश घेऊ शकत नाही",
        "filerevert-submit": "पूर्वपदास न्या",
        "filerevert-success": "[$3, $2 प्रमाणे आवर्तन $4]कडे<strong>[[Media:$1|$1]]</strong>उलटवण्यात आली.",
        "filerevert-badversion": "दिलेलेल्या वेळ मापनानुसार,या संचिकेकरिता कोणतीही पूर्वीची स्थानिक आवृत्ती नाही.",
+       "filerevert-identical": "या संचिकेची सध्याची आवृत्ती ही निवड केलेल्या आवृत्तीसमच आहे.",
        "filedelete": "$1 वगळा",
        "filedelete-legend": "संचिका वगळा",
        "filedelete-intro": "तुम्ही<strong>[[Media:$1|$1]]</strong>त्याच्या सर्व इतिहासासह,वगळण्याच्या तयारीत आहात.",
        "rollbacklinkcount": "उलटवा $1 {{PLURAL:$1|संपादन|संपादने}}",
        "rollbacklinkcount-morethan": "$1 पेक्षा अधिक {{PLURAL:$1|संपादन|संपादने}} उलटवा",
        "rollbackfailed": "द्रूतमाघार फसली",
+       "rollback-missingrevision": "आवृत्ती डाटा भारण करु शकत नाही.",
        "cantrollback": "जुन्या आवृत्तीकडे परतवता येत नाही; शेवटचा संपादक या पानाचा एकमात्र लेखक आहे.",
        "alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|Talk]] [[Special:Contributions/$2|{{int:contribslink}}]])चे शेवटाचे [[:$1]]वे संपादन माघारी परतवता येत नाही; पान आधीच कुणी माघारी परतवले आहे किंवा संपादित केले आहे.\n\nशेवटचे संपादन [[User:$3|$3]] ([[User talk:$3|Talk]] [[Special:Contributions/$3|{{int:contribslink}}]])-चे होते.",
        "editcomment": "संपादन सारांश <em>$1</em> होता.",
        "changecontentmodel": "पानाचा आशय नमूना (कंटेंट मॉडेल) बदला",
        "changecontentmodel-title-label": "लेखपान शीर्ष",
        "changecontentmodel-reason-label": "कारण:",
+       "changecontentmodel-submit": "बदला",
        "logentry-contentmodel-change-revertlink": "उलटवा",
        "logentry-contentmodel-change-revert": "उलटवा",
        "protectlogpage": "सुरक्षा नोंदी",
index 482171a..fb7b918 100644 (file)
        "yourpasswordagain": "စကားဝှက် ပြန်​ရိုက်​ပါ -",
        "createacct-yourpasswordagain": "စကားဝှက်ကို အတည်ပြုပါ",
        "createacct-yourpasswordagain-ph": "စကားဝှက်ကို ထပ်မံ ရိုက်ထည့်ပါ",
-       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွန်ုပ်ကို ​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
        "userlogin-remembermypassword": "Log in ဝင်ထားမည်",
        "userlogin-signwithsecure": "လုံခြုံသော ဆက်သွယ်မှုကို သုံးမည်",
        "yourdomainname": "သင့်ဒိုမိန်း -",
        "minoredit": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "watchthis": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "savearticle": "ဤစာမျက်နှာကို သိမ်းရန်",
+       "savechanges": "ပြောင်းလဲမှုများကို သိမ်းရန်",
        "publishpage": "စာမျက်နှာကို လွှင့်တင်ရန်",
        "publishchanges": "ပြောင်းလဲမှုများကို လွှင့်တင်ရန်",
        "preview": "နမူနာ",
index 9420fe4..c72ae68 100644 (file)
        "yourpasswordagain": "Têng phah bi̍t-bé:",
        "createacct-yourpasswordagain": "Khak-jīn bi̍t-bé",
        "createacct-yourpasswordagain-ph": "Koh phah chi̍t-pái bi̍t-bé",
-       "remembermypassword": "Tī chit ê liû-lám-khì kì góa ê teng-ji̍p chu-liāu.(siōng chē kì $1 {{PLURAL:$1|kang|kang}})",
        "userlogin-remembermypassword": "Kì-lo̍k goá teng-ji̍p--ê chu-liāu",
        "userlogin-signwithsecure": "用安全連線",
        "yourdomainname": "你的網域:",
        "backend-fail-delete": "Bô-hoat-tō· kā tóng-àn \"$1\" thâi tiāu",
        "license": "Siū-khoân:",
        "license-header": "Siū-khoân",
+       "imgfile": "tóng-àn",
        "listfiles": "Iáⁿ-siōng lia̍t-toaⁿ",
        "listfiles_date": "Ji̍t-kî",
        "listfiles_name": "Miâ",
index b13f5f9..5a1a330 100644 (file)
        "yourpasswordagain": "Ripete 'a password:",
        "createacct-yourpasswordagain": "Cunferma password",
        "createacct-yourpasswordagain-ph": "'Nserisce 'e nuovo 'a password",
-       "remembermypassword": "Allicuordate d\"a password (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "userlogin-remembermypassword": "Mantienime cullegato",
        "userlogin-signwithsecure": "Usa na conessione sicura",
+       "cannotlogin-title": "Nun se pò trasì",
+       "cannotlogin-text": "Trasì nun è possibbele mò.",
        "cannotloginnow-title": "Nun se pò trasì mò",
        "cannotloginnow-text": "'A connessione nun è possibbele quanno s'ausa $1.",
+       "cannotcreateaccount-title": "Nun se ponno crià cunte",
+       "cannotcreateaccount-text": "'A criazione diretta 'e cunte è stutata int'a stu wiki.",
        "yourdomainname": "Spiecà 'o dumminio",
        "password-change-forbidden": "Nun se ponno cagnà 'e password ncopp'a sta wiki.",
        "externaldberror": "Ce sta n'errore ch' 'e server d'autenticazione esterno, o pure nun v'è permesso accedere all'aghiurnamento d' 'o cunto sterno vuosto.",
        "content-json-empty-object": "Oggetto abbacante",
        "content-json-empty-array": "Array abbacante",
        "deprecated-self-close-category": "Paggene ausanno nu tag HTML auto-nchiuse nun valido",
+       "deprecated-self-close-category-desc": "'A paggena cuntenesse tag HTML auto-nchiuse nun valide, comme <code>&lt;b/></code> o <code>&lt;span/></code>. 'O cumpurtamento 'e chiste cagnarrà priesto pe' se ffà cuerente a le specifiche HTML5, è pecchesto ca mò nun è cunzigliato (deprecato) ll'uso 'e chiste dint' 'o wikitesto.",
        "duplicate-args-warning": "<strong>Attenziò:</strong> [[:$1]] sta chiammanno [[:$2]] cu cchiù 'e nu volore p' 'o parametro \"$3\". Surtanto ll'urdemo valore s'auserrà.",
        "duplicate-args-category": "Paggene c'ausano argomiente dupprecate dint' 'e chiammate a 'e mudelle",
        "duplicate-args-category-desc": "'A paggena tene chiammate a mudelle c'ausassero argomiente dupprecate, comme p'esempio <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "grant-group-high-volume": "Secuta attività 'e volume massivo",
        "grant-group-customization": "Personalizzaziona e preferenze",
        "grant-group-administration": "Secuta aziune ammenistrative",
+       "grant-group-private-information": "Tràse dint' 'e date private ncopp'a tte",
        "grant-group-other": "Attività differénte",
        "grant-blockusers": "Blocca e sblocca utente",
        "grant-createaccount": "Crìa cunte",
        "grant-highvolume": "Cagnamiente massive",
        "grant-oversight": "Annascunne utente e scancèlla 'e verziune",
        "grant-patrol": "Nzègna 'e cagnamiente a 'e paggene comme verificate",
+       "grant-privateinfo": "Tràse 'a 'e nfurmaziune private",
        "grant-protect": "Prutegge e sprutegge paggene",
        "grant-rollback": "Torna arrèto 'e cagnamiente a 'e paggene",
        "grant-sendemail": "Manna na mail a ll'at'utente",
        "file-thumbnail-no": "Stu filename accummencia pe' <strong>$1</strong>.\nPare ca ce sta n'immaggene piccerilla <em>(thumbnail)</em>.\nSi tiene st'immaggene 'n risoluzione origginale, pe' piacere carrecatela. Si nò, vedite 'e cagnà 'o nomme d' 'o file.",
        "fileexists-forbidden": "Nu file cu stu nomme esiste già, e nun se può sovrascrivere.<br/>\nPe' piacere turnat'arreto e cagnàte 'o nomme p' 'o turnà a carrecà.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Nu file cu stu nomme esiste già dint'a l'archivio 'e risorse multimediale spartute. Si vulite carrecà 'o file ancora, turnat'arreto e cagnate 'o nomme p' 'o turnà a carrecà.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "'O file carrecato è nu dupprecato eguale eguale d' 'a verziona 'e mò 'e <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "'O file carrecato è nu duprecato eguale eguale 'a {{PLURAL:$2|na verziona 'e primma|na quantità 'e verziune 'e primma}} 'e <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Stu file è nu duplicato {{PLURAL:$1|d' 'o|d' 'e}} file ccà abbascio:",
        "file-deleted-duplicate": "Nu file identico a chesto ([[:$1]]) è stato scancellato prima. Cuntrullate 'a cronologgia d' 'e scancellamiente apprimma d' 'o carrecà n'ata vota.",
        "file-deleted-duplicate-notitle": "Nu file eguale a stu file è stato previamente scancellato, e 'o titolo è stato sbaccantato. Chierete a coccheruno ca tenesse 'a posibbelità 'e vedé file luvate e sbaccantate pe' sapé nquale situazione ve truvate apprimma d' 'o ffà carrecà n'ata vota.",
        "filerevert-submit": "Arrepiglia",
        "filerevert-success": "'''[[Media:$1|$1]]''' è stat'arripigliato â verziona [$4 d' 'e $3 d' 'o $2].",
        "filerevert-badversion": "Nun ce sta na virziona lucale 'e stu file cu l'orario addimannato.",
+       "filerevert-identical": "'A verziona 'e mò d' 'o file è già eguale eguale a chilla scigliuta.",
        "filedelete": "Scancella $1",
        "filedelete-legend": "Scancella 'o file",
        "filedelete-intro": "State pe' scancellà 'o file '''[[Media:$1|$1]]''' cu tutta 'a cronologgia 'e chisto.",
        "rollbacklinkcount-morethan": "annulla cchiù 'e {{PLURAL:$1|nu cagnamiento|$1 cagnamiente}}",
        "rollbackfailed": "Annullamento fallito",
        "rollback-missingparam": "Parammetre obbligate mancante int' 'a richiesta.",
+       "rollback-missingrevision": "Nun se ponno carrecà 'e date d' 'a verziuna.",
        "cantrollback": "Nun se può annullà stu cagnamiento;\nsapite ca l'urdemo autore è stato pure sul'isso a faticà dint'a sta paggena (nun ce sta n'at'autore).",
        "alreadyrolled": "Nun se può turna arreto a l'urdemo cagnamiento [[:$1]] 'a [[User:$2|$2]] ([[User talk:$2|Chiacchiera]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\ncocch'ato ha cagnato o annullato 'a paggena già.\n\nL'urdemo cangamiento d' 'a paggena fuje 'a [[User:$3|$3]] ([[User talk:$3|Chiacchiera]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "'O riepilego d' 'o cagnamiento era: <em>$1</em>.",
index 581cc97..9fce778 100644 (file)
        "newwindow": "(åpnes i et nytt vindu)",
        "cancel": "Avbryt",
        "moredotdotdot": "Mer …",
-       "morenotlisted": "Denne lista er ufullstendig.",
+       "morenotlisted": "Denne lista er muligens ufullstendig.",
        "mypage": "Min brukerside",
        "mytalk": "Diskusjon",
        "anontalk": "Brukerdiskusjon",
        "yourpasswordagain": "Gjenta passord",
        "createacct-yourpasswordagain": "Bekreft passord",
        "createacct-yourpasswordagain-ph": "Gjenta passordet",
-       "remembermypassword": "Husk meg på denne datamaskinen (i maks $1 {{PLURAL:$1|dag|dager}})",
        "userlogin-remembermypassword": "Hold meg innlogget",
        "userlogin-signwithsecure": "Logg inn med sikker tjener",
+       "cannotlogin-title": "Kan ikke logge inn",
+       "cannotlogin-text": "Innlogging er ikke mulig.",
        "cannotloginnow-title": "Kan ikke logge inn nå",
        "cannotloginnow-text": "Å logge inn er ikke mulig ved bruk av $1.",
+       "cannotcreateaccount-title": "Kan ikke opprette kontoer",
+       "cannotcreateaccount-text": "Direkte kontooppretting er ikke slått på på denne wikien.",
        "yourdomainname": "Ditt domene",
        "password-change-forbidden": "Du kan ikke endre passord på denne wikien.",
        "externaldberror": "Det var en ekstern autentifiseringsfeil, eller du kan ikke oppdatere din eksterne konto.",
        "changepassword-success": "Passordet ditt er endret!",
        "changepassword-throttled": "Du har foretatt for mange nylige innloggingsforsøk.\nVær vennlig å vente $1 før du prøver igjen.",
        "botpasswords": "Robotpassord",
-       "botpasswords-summary": "<em>Robotpassord</em> gir tilgang til en brukerkonto via API uten å bruke hovedpassordet til kontoen. Brukerrettighetene kan bli begrenset ved bruk av dette passordet.\n\nHvis du ikke vet om du vil benytte dette, er det sannsynlig at du ikke bør fylle det ut. Det skal ikke være nødvendig for andre personer å be deg om å fylle ut dette for å gi det til de.",
+       "botpasswords-summary": "<em>Robotpassord</em> gir tilgang til en brukerkonto via API uten å bruke hovedpassordet til kontoen. Brukerrettighetene kan bli begrenset ved bruk av dette passordet.\n\nHvis du ikke vet om du vil benytte dette, er det sannsynlig at du ikke bør fylle det ut. Det skal ikke være nødvendig for andre personer å be deg om å fylle ut dette for å gi det til dem.",
        "botpasswords-disabled": "Robotpassord er deaktivert.",
        "botpasswords-no-central-id": "For å bruke robotpassord må du være logget inn med en sentralisert konto.",
        "botpasswords-existing": "Eksisterende robotpassord",
        "botpasswords-updated-body": "Robotpassordet for boten «$1» til brukeren «$2» ble oppdatert.",
        "botpasswords-deleted-title": "Robotpassord slettet",
        "botpasswords-deleted-body": "Robotpassordet for boten «$1» til brukeren «$2» ble slettet.",
-       "botpasswords-newpassword": "Det nye passordet for å logge inn med <strong>$1</strong> er <strong>$2</strong>. <em>Vennligst lagre dette for fremtidig referanse.</em>",
+       "botpasswords-newpassword": "Det nye passordet for å logge inn med <strong>$1</strong> er <strong>$2</strong>. <em>Vennligst lagre dette for fremtidig referanse.</em> <br /> (For gamle roboter som trenger samme innloggingsnavn og brukernavn kan du også bruke <strong>$3</strong> som brukernavn og <strong>$4</strong> som passord.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider er ikke tilgjengelig.",
        "botpasswords-restriction-failed": "Begrensninger for robotpassord tillater ikke denne innloggingen.",
        "botpasswords-invalid-name": "Det angitte brukernavnet inneholder ikke separasjonstegnet for robotpassord (\"$1\").",
        "passwordreset-emailsentusername": "Hvis det finnes en epostadresse knyttet til dette brukernavnet, vil en epost med informasjon om tilbakestilling av passord bli sendt.",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|E-post}} om passordtilbakestilling har blitt sendt. {{PLURAL:$1|Brukernavnet og passordet|Listen over brukernavn og passord}} vises under.",
        "passwordreset-emailerror-capture2": "Kunne ikke sende e-post til {{GENDER:$2|brukeren}}: $1 {{PLURAL:$3|Brukernavnet og passordet|Listen over brukernavn og passord}} vises under.",
+       "passwordreset-nocaller": "En bruker må angis",
+       "passwordreset-nosuchcaller": "Brukeren finnes ikke: $1",
+       "passwordreset-ignored": "Passordtilbakestillingen ble ikke håndtert. Har ingen leverandør blitt konfigurert?",
        "passwordreset-invalideamil": "Ugyldig e-postadresse",
        "passwordreset-nodata": "Verken et brukernavn eller en e-postadresse ble oppgitt",
        "changeemail": "Endre eller fjerne epostadresse",
        "savearticle": "Lagre siden",
        "savechanges": "Lagre endringer",
        "publishpage": "Publiser siden",
-       "publishchanges": "Publiser endringene",
+       "publishchanges": "Publiser endringer",
        "preview": "Forhåndsvisning",
        "showpreview": "Forhåndsvisning",
        "showdiff": "Vis endringer",
        "invalid-content-data": "Ugyldig innhold",
        "content-not-allowed-here": "Innholdsmodellen «$1» er ikke tillatt på siden [[$2]]",
        "editwarning-warning": "Ved å forlate siden kan du miste alle endringer du har gjort.\nHvis du er innlogget, kan du slå av denne advarselen under \"{{int:prefs-editing}}\"-avsnittet i dine innstillinger.",
+       "editpage-invalidcontentmodel-title": "Innholdsmodellen støttes ikke",
+       "editpage-invalidcontentmodel-text": "Innholdsmodellen «$1» støttes ikke.",
        "editpage-notsupportedcontentformat-title": "Innholdsformatet er ikke støttet",
        "editpage-notsupportedcontentformat-text": "Innholdsformatet $1 er ikke støttet av innholdsmodellen $2.",
        "content-model-wikitext": "wikitekst",
        "mergehistory-fail-bad-timestamp": "Tidsangivelsen er ugyldig.",
        "mergehistory-fail-invalid-source": "Kildesiden er ugyldig.",
        "mergehistory-fail-invalid-dest": "Målsiden er ugyldig.",
+       "mergehistory-fail-no-change": "Historieflettingen flettet ingen revisjoner. Vennligst sjekk siden og tidsparameterne igjen.",
        "mergehistory-fail-permission": "Utilstrekkelige tillatelser for å flette historikk.",
        "mergehistory-fail-self-merge": "Kilde- og målsiden er den samme.",
        "mergehistory-fail-timestamps-overlap": "Kilderevisjoner overlapper eller kommer etter målrevisjoner.",
        "prefs-help-prefershttps": "Denne preferansen vil virke etter neste innlogging.",
        "prefswarning-warning": "Du har gjort endringer i dine innstillinger som ikke er lagret ennå.\nDersom du forlater denne siden utenk å klikke på \"$1\" blir ikke innstillingene dine oppdatert.",
        "prefs-tabs-navigation-hint": "Tips: Du kan bruke venstre- og høyrepiltastene for å navigere mellom fanene i fanelisten",
-       "userrights": "Rettighets&shy;kontroll",
+       "userrights": "Bruker&shy;rettighets&shy;kontroll",
        "userrights-lookup-user": "Ordne brukergrupper",
        "userrights-user-editname": "Fyll inn et brukernavn:",
        "editusergroup": "Endre {{GENDER:$1|brukergrupper}}",
        "grant-group-high-volume": "Utføre høyvolumaktivitet",
        "grant-group-customization": "Tilpasninger og innstillinger",
        "grant-group-administration": "Utføre administrative handlinger",
+       "grant-group-private-information": "Få tilgang til private data om deg",
        "grant-group-other": "Andre ting",
        "grant-blockusers": "Blokkere og avblokkere brukere",
        "grant-createaccount": "Opprette kontoer",
        "grant-highvolume": "Høy&shy;volum&shy;redigering",
        "grant-oversight": "Skjule brukere og undertrykke revisjoner",
        "grant-patrol": "Patruljere sideendringer",
+       "grant-privateinfo": "Få tilgang til privat informasjon",
        "grant-protect": "Beskytte og avbeskytte sider",
        "grant-rollback": "Tilbakestille side&shy;endringer",
        "grant-sendemail": "Sende e-post til andre brukere",
        "action-createpage": "opprette denne siden",
        "action-createtalk": "opprette denne diskusjonssiden",
        "action-createaccount": "opprette denne kontoen",
+       "action-autocreateaccount": "automatisk opprette denne eksterne brukerkontoen",
        "action-history": "se historikken til denne siden",
        "action-minoredit": "merke denne redigeringen som mindre",
        "action-move": "flytte denne siden",
        "action-applychangetags": "bruk merker sammen med dine endringer",
        "action-changetags": "legg til og fjern vilkårlige merker på individuelle revisjoner og loggposter",
        "action-deletechangetags": "slette tagger fra databasen",
+       "action-purge": "gjenoppfriske denne siden",
        "nchanges": "$1 {{PLURAL:$1|endring|endringer}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|siden forrige besøk}}",
        "enhancedrc-history": "historikk",
        "file-thumbnail-no": "Filnavnet begynner med <strong>$1</strong>.\nDet virker som om det er et bilde av redusert størrelse ''(miniatyrbilde)''.\nOm du har dette bildet i stor utgave, last opp det, eller endre filnavnet på denne filen.",
        "fileexists-forbidden": "En fil med dette navnet finnes fra før, og kan ikke erstattes.\nOm du fortsatt ønsker å laste opp filen, gå tilbake og last den opp under et nytt navn. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Ei fil med dette navnet finnes fra før i det delte fillageret.\nOm du fortsatt ønsker å laste opp filen, gå tilbake og last den opp under et nytt navn. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Opplastingen er et eksakt duplikat av følgende versjon av <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Opplastingen er et eksakt duplikat av {{PLURAL:$2|en eldre versjon|eldre versjoner}} av <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Denne filen er en dublett av følgende {{PLURAL:$1|fil|filer}}:",
        "file-deleted-duplicate": "En fil identisk med denne filen ([[:$1]]) har tidligere blitt slettet. Du bør sjekke denne filens slettehistorikk før du prøver å laste den opp på nytt.",
        "file-deleted-duplicate-notitle": "En annen fil identisk med denne filen har tidligere blitt slettet og tittelen har blitt fjernet. Du bør sjekke med noen som kan se på fjernede fildata å vurdere saken før filen lastes opp igjen.",
        "upload-scripted-pi-callback": "Det er ikke tillatt å laste opp en fil som inneholder et kjørbart XML-stilark.",
        "uploaded-script-svg": "Fant et skriptelement \"$1\" i den opplastede SVG-koden.",
        "uploaded-hostile-svg": "Fant usikker CSS i stilelementet til opplastet SVG-fil",
+       "uploaded-event-handler-on-svg": "Å sette event-handler-attributtene <code>$1=\"$2\"</code> tillates ikke i SVG-filer.",
        "uploaded-href-attribute-svg": "href-attributter i SVG-filer tillates kun for http://- eller https://-mål; fant <code>&lt;$1 $2=\"$3\"%gt;</code>.",
        "uploaded-href-unsafe-target-svg": "Fant href til usikre data: URI-mål <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-filen.",
+       "uploaded-animate-svg": "Fant en «animate»-tagg som kan endre href, bruk attributtet «from» <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-event-handler-svg": "Setting av event-handler-attributter er blokkert, fant <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-href-svg": "Bruk av «set»-taggen for å legge til «href»-attributt til foreldreelementet er blokkert.",
+       "uploaded-wrong-setting-svg": "Bruk av «set»-taggen for å legge til et eksternt/data- eller skriptmål til attributter er blokkert. Fant <code>&lt;set to=\"$1\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-handler-svg": "SVG-er som setter «handler»-attributtet med remote/data/script er blokkert. Fant <code>$1=\"$2\"</code> i den opplastede SVG-fila.",
+       "uploaded-remote-url-svg": "SVG-er som setter et stilattributt med ekstern URL er blokkert. Fant <code>$1=\"$2\"</code> i den opplastede SVG-fila.",
+       "uploaded-image-filter-svg": "Fant bildefilter med URL: <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
        "uploadscriptednamespace": "Denne SVG-filen inneholder et ulovlig navnerom \"$1\"",
        "uploadinvalidxml": "XML-en i den opplastede filen kunne ikke tolkes.",
        "uploadvirus": "Denne filen inneholder virus! Detaljer: $1",
        "upload-too-many-redirects": "URL-en inneholdt for mange omdirigeringer",
        "upload-http-error": "En HTTP-feil oppstod: $1",
        "upload-copy-upload-invalid-domain": "Opplasting av kopier er ikke tilgjengelig fra dette domenet.",
+       "upload-foreign-cant-upload": "Denne wikien er ikke konfigurert til å laste opp filer til det forespurte eksterne fillageret.",
+       "upload-foreign-cant-load-config": "Lasting av konfigurasjonen for filopplastinger til det eksterne fillageret mislyktes.",
+       "upload-dialog-disabled": "Filopplastinger med denne dialogen er slått av for denne wikien.",
        "upload-dialog-title": "Last opp fil",
        "upload-dialog-button-cancel": "Avbryt",
        "upload-dialog-button-done": "Utført",
        "upload-dialog-button-upload": "Last opp",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Navn",
+       "upload-form-label-infoform-name-tooltip": "En unik beskrivende tittel for fila, som vil brukes som filnavn. Du kan bruke vanlig språk med mellomrom. Ikke ta med filendelsen.",
        "upload-form-label-infoform-description": "Beskrivelse",
+       "upload-form-label-infoform-description-tooltip": "Beskriv kort alt som er bemerkelsesverdig med verket.\nFor bilder, nevn hovedtingene som avbildes, anledningen eller stedet.",
        "upload-form-label-usage-title": "Bruk",
        "upload-form-label-usage-filename": "Filnavn",
        "upload-form-label-own-work": "Dette er mitt eget verk",
        "uploadstash-badtoken": "Utføringen av handlingen feilet, kanskje fordi redigeringsrettighetene dine har utløpt. Prøv igjen.",
        "uploadstash-errclear": "Filene lot seg ikke fjerne.",
        "uploadstash-refresh": "Oppdater listen over filer",
+       "uploadstash-thumbnail": "vis miniatyrbilde",
+       "uploadstash-exception": "Kunne ikke lagre opplastingen i stashen ($1): «$2».",
        "invalid-chunk-offset": "Ugyldig delforskyvning",
        "img-auth-accessdenied": "Ingen tilgang",
        "img-auth-nopathinfo": "Manglende PATH_INFO.\nTjeneren din er ikke satt opp til å gi denne informasjonen.\nDen er kanskje CGI-basert og støtter ikke img_auth.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Se bildeautorisasjon.",
        "filerevert-submit": "Tilbakestill",
        "filerevert-success": "'''[[Media:$1|$1]]''' ble tilbakestilt til [$4 versjonen à $2, $3].",
        "filerevert-badversion": "Det er ingen tidligere lokal versjon av denne filen med det gitte tidstrykket.",
+       "filerevert-identical": "Den nåværende versjonen av fila er allerede identisk med den valgte.",
        "filedelete": "Slett $1",
        "filedelete-legend": "Slett fil",
        "filedelete-intro": "Du er i ferd med å slette filen '''[[Media:$1|$1]]''' sammen med hele dens historikk.",
        "apihelp": "API hjelp",
        "apihelp-no-such-module": "Modulen «$1» ikke funnet.",
        "apisandbox": "API-sandkasse",
+       "apisandbox-jsonly": "JavaScript kreves for å bruke API-sandkassa.",
        "apisandbox-api-disabled": "API er deaktivert på dette nettstedet.",
        "apisandbox-intro": "Bruk denne siden for å eksperimentere med <strong>MediaWiki webtjeneste-APIet</strong>.\nSjekk [[mw:API:Main page|API-dokumentasjonen]] for mer informasjon om bruk av APIet. Eksempel: [https://www.mediawiki.org/wiki/API#A_simple_example hente innholdet til en hovedside]. Velg en handling for å se flere eksempler.\n\nMerk at du kan utføre handlinger her som fører til endringer på wikien.",
+       "apisandbox-fullscreen": "Utvid panelet",
+       "apisandbox-fullscreen-tooltip": "Utvid sandkassepanelet så det dekker nettleservinduet.",
+       "apisandbox-unfullscreen": "Vis siden",
+       "apisandbox-unfullscreen-tooltip": "Reduser størrelsen på sandkassepanelet, så MediaWikis navigasjonslenker er tilgjengelige.",
        "apisandbox-submit": "Foreta en forespørsel",
        "apisandbox-reset": "Tilbakestill",
+       "apisandbox-retry": "Prøv igjen",
+       "apisandbox-loading": "Laster informasjon for API-modulen «$1»...",
+       "apisandbox-load-error": "En feil oppsto under lasting av informasjon for API-modulen «$1»: $2",
+       "apisandbox-no-parameters": "Denne API-modulen har ingen parametre.",
+       "apisandbox-helpurls": "Hjelpelenker",
        "apisandbox-examples": "Eksempler",
+       "apisandbox-dynamic-parameters": "Ekstra parametre",
+       "apisandbox-dynamic-parameters-add-label": "Legg til parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parameternavn",
+       "apisandbox-dynamic-error-exists": "En parameter med navn «$1» finnes fra før.",
+       "apisandbox-deprecated-parameters": "Utgåtte parametre",
+       "apisandbox-fetch-token": "Fyll inn nøkkelen automatisk",
+       "apisandbox-submit-invalid-fields-title": "Noen felt er ugyldige",
+       "apisandbox-submit-invalid-fields-message": "Fiks de markerte feltene og prøv igjen.",
        "apisandbox-results": "Resultater",
+       "apisandbox-sending-request": "Sender API-forespørsel...",
+       "apisandbox-loading-results": "Mottar API-resultater...",
+       "apisandbox-results-error": "En feil oppsto under lasting av API-spørringssvaret: $1.",
        "apisandbox-request-url-label": "Forespurt URL:",
        "apisandbox-request-time": "Forespørselstid: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Fiks nøkkelen og send på nytt",
+       "apisandbox-results-fixtoken-fail": "Henting av nøkkelen «$1» mislyktes.",
+       "apisandbox-alert-page": "Felter på denne siden er ugyldige.",
+       "apisandbox-alert-field": "Verdien til dette feltet er ugyldig.",
        "booksources": "Bokkilder",
        "booksources-search-legend": "Søk etter bokkilder",
        "booksources-search": "Søk",
        "listgrouprights-namespaceprotection-header": "Navneromsbegrensinger",
        "listgrouprights-namespaceprotection-namespace": "Navnerom",
        "listgrouprights-namespaceprotection-restrictedto": "Rettighet(er) som tillater at brukeren redigerer",
+       "listgrants": "Tildelinger",
        "listgrants-summary": "Følgende er en liste over tildelinger samt hvilke brukerrettigheter de gir tilgang til. Brukere kan autorisere applikasjoner til å bruke kontoen deres, med rettigheter begrenset til de gitt av tildelingene brukeren har godkjent. En applikasjon som handler på vegne av en bruker kan imidlertid aldri benytte seg av rettigheter brukeren ikke selv har.\nDet kan finnes [[{{MediaWiki:Listgrouprights-helppage}}|ytterligere informasjon]] om de ulike rettighetene.",
+       "listgrants-grant": "Tildeling",
        "listgrants-rights": "Rettigheter",
        "trackingcategories": "Sporingskategori",
        "trackingcategories-summary": "Denne siden lister sporingskategorier som er automatisk befolket av Mediawiki-programvaren. Navnene deres kan endres ved å redigere de tilhørende systembeskjedene i {{ns:8}}-navnerommet.",
        "trackingcategories-name": "Beskjednavn",
        "trackingcategories-desc": "Kategori-inklusjonskriterium",
        "restricted-displaytitle-ignored": "Sider med ignorerte visningstitler",
+       "restricted-displaytitle-ignored-desc": "Denne sidens <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> er ignorert fordi den ikke tilsvarer sidens faktiske tittel.",
        "noindex-category-desc": "Denne siden indekseres ikke av roboter fordi den er merket med det magiske ordet <code><nowiki>__NOINDEX__</nowiki></code> og er i navnerom der dette flagget tillates.",
        "index-category-desc": "Denne siden er påført det magiske ordet <code><nowiki>__INDEX__</nowiki></code> (og er i et navnerom hvor flagget er tillatt), og vil derfor bli indeksert av roboter selv når det normalt ikke ville skjedd.",
        "post-expand-template-inclusion-category-desc": "Sidestørrelsen er større enn <code>$wgMaxArticleSize</code> etter at alle maler er utvidet, så noen maler ble ikke utvidet.",
        "watchnologin": "Ikke logget inn",
        "addwatch": "Legg til i overvåkningslisten",
        "addedwatchtext": "«[[:$1]]» og den tilhørende diskusjonssiden er lagt til i [[Special:Watchlist|overvåkningslisten]] din.",
+       "addedwatchtext-talk": "«[[:$1]]» og dens tilhørende side har blitt lagt til i [[Special:Watchlist|overvåkningslista di]].",
        "addedwatchtext-short": "Siden «$1» har blitt lagt til i overvåkningslisten din.",
        "removewatch": "Fjern fra overvåkningslisten",
        "removedwatchtext": "«[[:$1]]» og den tilhørende diskusjonssiden har blitt fjernet fra [[Special:Watchlist|overvåkningslisten din]].",
+       "removedwatchtext-talk": "«[[:$1]]» og dens tilhørende side har blitt fjernet fra [[Special:Watchlist|overvåkningslista di]].",
        "removedwatchtext-short": "Siden «$1» har blitt fjernet fra overvåkningslisten din.",
        "watch": "Overvåk",
        "watchthispage": "Overvåk denne siden",
        "rollbacklinkcount": "tilbakestill {{PLURAL:$1|én endring|$1 endringer}}",
        "rollbacklinkcount-morethan": "tilbakestill mer enn $1 {{PLURAL:$1|endring|endringer}}",
        "rollbackfailed": "Kunne ikke tilbakestille",
+       "rollback-missingparam": "Påkrevde parametere i forespørselen mangler.",
+       "rollback-missingrevision": "Kunne ikke laste revisjonsdata.",
        "cantrollback": "Kan ikke fjerne redigering; den siste brukeren er den eneste forfatteren.",
        "alreadyrolled": "Kan ikke fjerne den siste redigeringen på [[$1]] av [[User:$2|$2]] ([[User talk:$2|diskusjon]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); en annen har allerede redigert siden eller fjernet redigeringen.\n\nDen siste redigeringen ble foretatt av [[User:$3|$3]] ([[User talk:$3|diskusjon]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Redigeringskommentaren var: <em>$1</em>",
        "revertpage": "Tilbakestilte endringer av [[Special:Contributions/$2|$2]] ([[User talk:$2|brukerdiskusjon]]) til siste versjon av [[User:$1|$1]]",
        "revertpage-nouser": "Tilbakestilt endringer av skjult bruker til siste versjon av\n{{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Tilbakestilte endringer av $1; endret til siste versjon av $2.",
+       "rollback-success-notify": "Tilbakestilte endringer av $1;\nendret tilbake til siste revisjon av $2. [$3 Vis endringer]",
        "sessionfailure-title": "Sesjonsfeil",
        "sessionfailure": "Det ser ut til å være et problem med innloggingen din, og den ble avbrutt av sikkerhetshensyn. Trykk ''Tilbake'' i nettleseren din, oppdater siden og prøv igjen.",
        "changecontentmodel": "Endre innholdsmodell for en side",
        "changecontentmodel-title-label": "Sidetittel",
        "changecontentmodel-model-label": "Ny innholdsmodell",
        "changecontentmodel-reason-label": "Begrunnelse:",
+       "changecontentmodel-submit": "Endre",
        "changecontentmodel-success-title": "Innholdsmodellen ble endret",
        "changecontentmodel-success-text": "Innholdstypen for [[:$1]] har blitt endret.",
        "changecontentmodel-cannot-convert": "Innholdet på [[:$1]] kan ikke konverteres til en type av $2.",
        "changecontentmodel-nodirectediting": "Innholdsmodellen $1 støtter ikke direkte redigering",
+       "changecontentmodel-emptymodels-title": "Ingen innholdsmodeller er tilgjengelige",
+       "changecontentmodel-emptymodels-text": "Innholdet på [[:$1]] kan ikke konverteres til noen type.",
        "log-name-contentmodel": "Logg over endringer i endringsloggen",
        "log-description-contentmodel": "Hendelseslogg relatert til innholdsmodellen for en side",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|opprettet}} siden $3 med den ikke-standard innholdsmodellen «$5»",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|endret}} innholdsmodellen for siden $3 fra «$4» til «$5»",
        "logentry-contentmodel-change-revertlink": "tilbakestill",
        "logentry-contentmodel-change-revert": "tilbakestill",
        "undeletehistorynoadmin": "Denne artikkelen har blitt slettet. Grunnen for slettingen vises i oppsummeringen nedenfor, sammen med detaljer om brukerne som redigerte siden før den ble slettet. Teksten i disse slettede revisjonene er kun tilgjengelig for administratorer.",
        "undelete-revision": "Slettet revisjon av $1 (per $4 $5) av $3:",
        "undeleterevision-missing": "Ugyldig eller manglende revisjon. Du kan ha en ødelagt lenke, eller revisjonen har blitt fjernet fra arkivet.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Én revisjon|$1 revisjoner}} kunne ikke gjenopprettes, fordi {{PLURAL:$1|dens|deres}} <code>rev_id</code> allerede er i bruk.",
        "undelete-nodiff": "Ingen tidligere revisjoner funnet.",
        "undeletebtn": "Gjenopprett",
        "undeletelink": "vis/gjenopprett",
        "sp-contributions-newbies-title": "Bidrag av nye kontoer",
        "sp-contributions-blocklog": "blokkeringslogg",
        "sp-contributions-suppresslog": "undertrykte brukerbidrag",
-       "sp-contributions-deleted": "slettede brukerbidrag",
+       "sp-contributions-deleted": "slettede {{GENDER:$1|brukerbidrag}}",
        "sp-contributions-uploads": "opplastinger",
        "sp-contributions-logs": "logger",
        "sp-contributions-talk": "diskusjon",
        "sp-contributions-username": "IP-adresse eller brukernavn:",
        "sp-contributions-toponly": "Vis kun endringer som er gjeldende revisjoner",
        "sp-contributions-newonly": "Bare vis bidrag som er sideopprettinger",
+       "sp-contributions-hideminor": "Skjul mindre endringer",
        "sp-contributions-submit": "Søk",
        "whatlinkshere": "Hva lenker hit",
        "whatlinkshere-title": "Sider som lenker til «$1»",
        "unblock": "Fjern blokkering av bruker",
        "blockip": "Blokker {{GENDER:$1|bruker}}",
        "blockip-legend": "Blokker bruker",
-       "blockiptext": "Bruk skjemaet under for å blokkere en IP-adresses tilgang til å redigere artikler. Dette må kun gjøres for å forhindre hærverk, og i overensstemmelse med [[{{MediaWiki:Policy-url}}|retningslinjene]]. Fyll ut en spesiell begrunnelse under.",
+       "blockiptext": "Bruk skjemaet under for å blokkere skrivetilgangen til en spesifikk IP-adresse eller et brukernavn.\nDette bør kun gjøres for å forhindre vandalisme, og i samsvar med [[{{MediaWiki:Policy-url}}|retningslinjene]].\nSkriv inn en spesifikk grunn nedenfor (for eksempel ved å angi hvilke sider som ble vandalisert).\nDu kan blokkere IP-intervaller med [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-syntaks; det største tillatte intervallet er /$1 for IPv4 og /$2 for IPv6.",
        "ipaddressorusername": "IP-adresse eller brukernavn",
        "ipbexpiry": "Varighet:",
        "ipbreason": "Årsak:",
        "ipb-unblock": "Opphev blokkering av et brukernavn eller en IP-adresse",
        "ipb-blocklist": "Vis gjeldende blokkeringer",
        "ipb-blocklist-contribs": "Bidrag fra {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "$1 igjen",
        "unblockip": "Opphev blokkering",
        "unblockiptext": "Bruk skjemaet under for å gjenopprette skriveadgangen for en tidligere blokkert adresse eller bruker.",
        "ipusubmit": "Opphev blokkering",
        "block-log-flags-hiddenname": "brukernavn skjult",
        "range_block_disabled": "Muligheten til å blokkere flere IP-adresser om gangen er slått av.",
        "ipb_expiry_invalid": "Ugyldig utløpstid.",
+       "ipb_expiry_old": "Utløpstiden har allerede vært.",
        "ipb_expiry_temp": "For å skjule brukernavnet må blokkeringen være permanent.",
        "ipb_hide_invalid": "Denne kontoen kan ikke skjules; den har mer enn {{PLURAL:$1|én redigering|$1 redigeringer}}.",
        "ipb_already_blocked": "«$1» er allerede blokkert",
        "lockdbsuccesstext": "Databasen er låst.<br />Husk å [[Special:UnlockDB|låse den opp]] når du er ferdig med vedlikeholdet.",
        "unlockdbsuccesstext": "Databasen er låst opp.",
        "lockfilenotwritable": "Kan ikke skrive til databasen. For å låse eller åpne databasen, må denne kunne skrives til av tjeneren.",
+       "databaselocked": "Databasen er allerede låst.",
        "databasenotlocked": "Databasen er ikke låst.",
        "lockedbyandtime": "(av $1 den $2, kl $3)",
        "move-page": "Flytt $1",
        "move-page-legend": "Flytt side",
-       "movepagetext": "Når du bruker skjemaet nedenfor døper du om en side og flytter hele historikken til det nye navnet.\nDen gamle tittelen blir en omdirigeringsside til den nye tittelen.\nDu kan oppdatere omdirigeringer som peker til den opprinnelige tittelen automatisk.\nOm du velger å ikke gjøre det, sjekk at flyttingen ikke fører til [[Special:DoubleRedirects|doble]] eller [[Special:BrokenRedirects|ødelagte omdirigeringer]].\nDu er ansvarlig for at lenker fortsetter å peke til de sidene de er ment å peke til.\n\nLegg merke til at siden '''ikke''' kan flyttes hvis det allerede finnes en side med den nye tittelen, med mindre sistnevnte er tom eller er en omdirigeringsside uten historikk.\nDet betyr at du kan flytte en side tilbake dit den kom fra hvis du gjør en feil, og du kan ikke overskrive eksisterende sider ved et uhell.\n\n'''Advarsel!'''\nDette kan være en drastisk og uventet endring for en populær side;\nvær sikker på at du forstår konsekvensene av dette før du fortsetter.",
-       "movepagetext-noredirectfixer": "Skjemaet nedenfor vil gi en side ny tittel og flytte historikken dens til det nye navnet.\nDen gamle tittelen vil bli en omdirigering til den nye.\nSjekk om det blir [[Special:DoubleRedirects|doble]] eller [[Special:BrokenRedirects|ødelagte omdirigeringer]].\nDu er ansvarlig for å sjekke at lenker fortsetter å gå dit de skal.\n\nMerk at sider '''ikke''' blir flyttet om det allerede finnes en side med den tittelen, med mindre siden er tom eller en omdirigering og ikke har noen redigeringshistorikk.\nDette betyr at du kan endre tittelen til en tittel siden hadde tidligere, og at du ikke kan skrive over en eksisterende side.\n\n'''Advarsel!'''\nDette kan være en drastisk og uventet endring for en populær side;\nvær sikker på at du forstår konsekvensene av dette før du fortsetter.",
-       "movepagetalktext": "Den tilhørende diskusjonssiden vil automatisk bli flyttet sammen med siden '''med mindre:'''\n*Det allerede finnes en diskusjonsside som ikke er tom under det nye navnet, eller\n*Du fjerner markeringen i boksen nedenfor.\n\nI disse tilfellene er du nødt til å flytte eller flette siden manuelt, om ønskelig.",
+       "movepagetext": "Når du bruker skjemaet nedenfor døper du om en side og flytter hele historikken til det nye navnet.\nDen gamle tittelen blir en omdirigeringsside til den nye tittelen.\nDu kan oppdatere omdirigeringer som peker til den opprinnelige tittelen automatisk.\nOm du velger å ikke gjøre det, sjekk at flyttingen ikke fører til [[Special:DoubleRedirects|doble]] eller [[Special:BrokenRedirects|ødelagte omdirigeringer]].\nDu er ansvarlig for at lenker fortsetter å peke til de sidene de er ment å peke til.\n\nLegg merke til at siden <strong>ikke</strong> kan flyttes hvis det allerede finnes en side med den nye tittelen, med mindre sistnevnte er tom eller er en omdirigeringsside uten historikk.\nDet betyr at du kan flytte en side tilbake dit den kom fra hvis du gjør en feil, og du kan ikke overskrive eksisterende sider ved et uhell.\n\n<strong>Merk:</strong>\nDette kan være en drastisk og uventet endring for en populær side;\nvær sikker på at du forstår konsekvensene av dette før du fortsetter.",
+       "movepagetext-noredirectfixer": "Skjemaet nedenfor vil gi en side ny tittel og flytte historikken dens til det nye navnet.\nDen gamle tittelen vil bli en omdirigering til den nye.\nSjekk om det blir [[Special:DoubleRedirects|doble]] eller [[Special:BrokenRedirects|ødelagte omdirigeringer]].\nDu er ansvarlig for å sjekke at lenker fortsetter å gå dit de skal.\n\nMerk at sider <strong>ikke</strong> blir flyttet om det allerede finnes en side med den tittelen, med mindre siden er en omdirigering og ikke har noen redigeringshistorikk.\nDette betyr at du kan endre tittelen til en tittel siden hadde tidligere, og at du ikke kan skrive over en eksisterende side.\n\n<strong>Merk:</strong>\nDette kan være en drastisk og uventet endring for en populær side;\nvær sikker på at du forstår konsekvensene av dette før du fortsetter.",
+       "movepagetalktext": "Om du merker av denne boksen vil den tilhørende diskusjonssiden også flyttes til den nye tittelen, med mindre en ikke-tom diskusjonsside allerede finnes der.\n\nOm det er tilfelle må du flytte eller flette siden manuelt om det er ønskelig.",
        "moveuserpage-warning": "'''Advarsel:''' Du er i ferd med å flytte en brukerside. Merk at kun siden vil bli flyttet; brukernavnet vil ''ikke'' bli endret.",
        "movecategorypage-warning": "<strong>Advarsel:</strong> Du er i ferd med å flytte en kategoriside. Merk at kun siden blir flyttet, og at sider i det gamle kategorinavnet <em>ikke</em> blir omkategorisert til det nye navnet.",
        "movenologintext": "Du må være registrert bruker og være [[Special:UserLogin|logget på]] for å flytte en side.",
        "movenosubpage": "Denne siden har ingen undersider.",
        "movereason": "Årsak:",
        "revertmove": "tilbakestill",
-       "delete_and_move_text": "==Sletting nødvendig==\nMålsiden «[[:$1]]» finnes allerede. Vil du slette den så denne siden kan flyttes dit?",
+       "delete_and_move_text": "Målsiden «[[:$1]]» finnes fra før.\nØnsker du å slette den for å muliggjøre flyttingen?",
        "delete_and_move_confirm": "Ja, slett siden",
        "delete_and_move_reason": "Slettet for å muliggjøre flytting fra \"[[$1]]\"",
        "selfmove": "Kilde- og destinasjonstittel er den samme; kan ikke flytte siden.",
        "move-leave-redirect": "La det være igjen en omdirigering",
        "protectedpagemovewarning": "'''Advarsel:''' Denne siden har blitt låst slik at kun brukere med administratorrettigheter kan flytte den.\nDet siste loggelementet er oppgitt under som referanse:",
        "semiprotectedpagemovewarning": "'''Merk:''' Denne siden har blitt låst slik at kun registrerte brukere kan flytte den.\nDet siste loggelementet er oppgitt under som referanse:",
-       "move-over-sharedrepo": "== Filen finnes ==\n[[:$1]] finnes på en delt kilde. Dersom du flytter en fil til dette navnet, vil du overstyre den delte filen.",
+       "move-over-sharedrepo": "[[:$1]] finnes på et delt fillager. Flytting av filen til denne tittelen vil overstyre den delte filen.",
        "file-exists-sharedrepo": "Det valgte filnavnet er allerede i bruk på en delt kilde.\nVennligst velg et annet navn.",
        "export": "Eksporter sider",
        "exporttext": "Du kan eksportere teksten og redigeringshistorikken for en bestemt side eller en gruppe sider i XML.\nDette kan senere importeres til en annen wiki som bruker MediaWiki ved hjelp av [[Special:Import|importsiden]].\n\nFor å eksportere sider, skriv inn titler i tekstboksen under, én tittel per linje, og velg om du vil ha kun nåværende versjon, eller alle versjoner i historikken.\n\nDersom du bare vil ha nåværende versjon, kan du også bruke en lenke, for eksempel [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] for siden «[[{{MediaWiki:Mainpage}}]]».",
        "export-download": "Lagre som fil",
        "export-templates": "Ta med maler",
        "export-pagelinks": "Inkluder lenkede sider med en dybde på:",
+       "export-manual": "Legg til sider manuelt:",
        "allmessages": "Systemmeldinger",
        "allmessagesname": "Navn",
        "allmessagesdefault": "Standardtekst",
        "import-nonewrevisions": "Ingen revisjoner ble importert: De var enten allerede på plass, eller hoppet over pga. feil.",
        "xml-error-string": "$1 på linje $2, kolonne $3 (byte: $4): $5",
        "import-upload": "Last opp XML-data",
-       "import-token-mismatch": "Sesjonsdata mistet. Venligst prøv igjen.",
+       "import-token-mismatch": "Sesjonsdata mistet.\n\nDu kan ha blitt logget ut. <strong>Sjekk at du fortsatt er logget inn og prøv igjen.</strong>\nOm det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen, og sjekk om netteleseren din tillater informasjonskapsler fra denne siden.",
        "import-invalid-interwiki": "Kan ikke importere fra angitt wiki.",
        "import-error-edit": "Siden «$1» ble ikke importert fordi du ikke har tillatelse til å redigere den.",
        "import-error-create": "Siden «$1» ble ikke importert fordi du ikke har tillatelse til å opprette den.",
        "tooltip-feed-rss": "RSS-mating for denne siden",
        "tooltip-feed-atom": "Atom-mating for denne siden",
        "tooltip-t-contributions": "En liste over bidrag fra {{GENDER:$1|denne brukeren}}",
-       "tooltip-t-emailuser": "Send en e-post til denne brukeren",
+       "tooltip-t-emailuser": "Send en e-post til {{GENDER:$1|denne brukeren}}",
        "tooltip-t-info": "Mer informasjon om denne siden",
        "tooltip-t-upload": "Last opp filer",
        "tooltip-t-specialpages": "Liste over alle spesialsider",
        "tooltip-ca-nstab-category": "Vis kategorisiden",
        "tooltip-minoredit": "Merk dette som en mindre endring",
        "tooltip-save": "Lagre endringene dine",
+       "tooltip-publish": "Publiser endringene dine",
        "tooltip-preview": "Forhåndsvis endringene dine, vennligst gjør dette før du lagrer!",
        "tooltip-diff": "Vis hvilke endringer du har gjort på teksten",
        "tooltip-compareselectedversions": "Se forskjellen mellom de to valgte revisjonene av denne siden",
        "pageinfo-article-id": "Side-ID",
        "pageinfo-language": "Språk for sideinnholdet",
        "pageinfo-content-model": "Modell for sideinnhold",
+       "pageinfo-content-model-change": "endre",
        "pageinfo-robot-policy": "Bot-indeksering",
        "pageinfo-robot-index": "Tillatt",
        "pageinfo-robot-noindex": "Ikke tillatt",
        "pageinfo-category-files": "Antall filer",
        "markaspatrolleddiff": "Merk som patruljert",
        "markaspatrolledtext": "Merk denne siden som patruljert",
+       "markaspatrolledtext-file": "Merk denne filversjonen som patruljert",
        "markedaspatrolled": "Merket som patruljert",
        "markedaspatrolledtext": "Den valgte revisjonen av [[:$1]] har blitt merket som patruljert.",
        "rcpatroldisabled": "Siste endringer-patruljering er slått av",
        "newimages-legend": "Filnavn",
        "newimages-label": "Filnavn (helt eller delvis):",
        "newimages-showbots": "Vis opplastinger av botter",
+       "newimages-hidepatrolled": "Skjul patruljerte opplastinger",
        "noimages": "Ingenting å se.",
        "ilsubmit": "Søk",
        "bydate": "etter dato",
        "confirmemail_body_set": "Noen med IP-adresse $1, mest sannsynlig deg, har satt e-postadressen for kontoen «$2» til denne adressen på {{SITENAME}}.\n\nFor å bekrefte at denne kontoen faktisk tilhører deg og for å slå på e-post-tjenestene fra {{SITENAME}}, må du åpne denne lenken i nettleseren din:\n\n$3\n\nOm kontoen *ikke* tilhører deg, følg denne lenken for å avbryte e-post-bekreftelsen:\n\n$5\n\nDenne bekreftelseskoden utløper $4.",
        "confirmemail_invalidated": "Bekreftelse av e-postadresse avbrutt",
        "invalidateemail": "Avbryt bekreftelse av e-postadresse",
+       "notificationemail_subject_changed": "Registrert epostadresse på {{SITENAME}} har blitt endret",
+       "notificationemail_subject_removed": "Registrert epostadresse på {{SITENAME}} har blitt fjernet",
+       "notificationemail_body_changed": "Noen, antageligvis du (fra IP-adressen $1), har endret epostadressen til kontoen «$2» til «$3» på {{SITENAME}}.\n\nOm det ikke var du som gjorde det, kontakt en sideadministrator umiddelbart.",
+       "notificationemail_body_removed": "Noen, antageligvis deg (fra IP-adressen $1), har fjernet epostadressen til kontoen «$2» på {{SITENAME}}.\n\nOm det ikke var du som gjorde det, kontakt en sideadministrator umiddelbart.",
        "scarytranscludedisabled": "[Interwiki-transkludering er slått av]",
        "scarytranscludefailed": "[Malen kunne ikke hentes for $1]",
        "scarytranscludefailed-httpstatus": "[Henting av mal for $1 feilet: HTTP $2]",
        "confirm-watch-top": "Legg denne siden til overvåkningslisten din?",
        "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Fjern denne siden fra overvåkningslisten din?",
+       "confirm-rollback-button": "OK",
+       "confirm-rollback-top": "Tilbakestill redigeringer på denne siden?",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← forrige side",
        "imgmultipagenext": "neste side &rarr;",
        "hebrew-calendar-m11-gen": "Ab",
        "hebrew-calendar-m12-gen": "Elúl",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskusjon]])",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "Advarsel: Standardsorteringen «$2» tar over for den tidligere sorteringen «$1».",
        "duplicate-displaytitle": "<strong>Advarsel:</strong> Visningstittel \"$2\" erstatter tidligere visningstittel \"$1\".",
+       "restricted-displaytitle": "<strong>Advarsel:</strong> Visningstittelen «$1» ble ignorert siden den ikke tilsvarer sidens faktiske tittel.",
        "invalid-indicator-name": "<p>Feil:</strong> Sidestatus-indikatornes <code>navn</code>-attributt kan ikke være tomt.",
        "version": "Versjon",
        "version-extensions": "Installerte utvidelser",
        "version-libraries-license": "Lisens",
        "version-libraries-description": "Beskrivelse",
        "version-libraries-authors": "Forfattere",
-       "redirect": "Omdiriger via filnavn, bruker eller versjonsid",
-       "redirect-summary": "Denne spesialsiden omdirigerer til en fil (hvis et filnavn angis), en side (hvis et redigeringsnummer angis) eller en brukerside (hvis en numerisk brukeridentifikator angis).\nEksempler:[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
+       "redirect": "Omdiriger via filnavn, bruker-, side-, revisjons- eller logg-ID.",
+       "redirect-summary": "Denne spesialsiden omdirigerer til en fil (hvis et filnavn angis), en side (om revisjons- eller side-ID angis), en brukerside (om bruker-ID angis), eller en loggoppføring (om logg-ID angis). Bruk: [[{{#Special:Redirect}}/file/Eksempel.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] eller [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Gå",
        "redirect-lookup": "Oppslag:",
        "redirect-value": "Verdi:",
        "redirect-page": "Side-ID",
        "redirect-revision": "Sideversjon",
        "redirect-file": "Filnavn",
+       "redirect-logid": "Logg-ID",
        "redirect-not-exists": "Verdi er ikke funnet",
        "fileduplicatesearch": "Søk etter duplikatfiler",
        "fileduplicatesearch-summary": "Søk etter duplikatfiler basert på dets hash-verdi.",
        "tag-filter": "Filter for [[Special:Tags|tagger]]:",
        "tag-filter-submit": "Filtrer",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tagg|Tagger}}]]: $2)",
+       "tag-mw-contentmodelchange": "innholdsmodellendring",
+       "tag-mw-contentmodelchange-description": "Redigeringer som [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel endrer innholdsmodellen] til en side",
        "tags-title": "Tagger",
        "tags-intro": "Denne siden lister opp taggene programvaren kan merke en endring med, og hva de betyr.",
        "tags-tag": "Taggnavn",
        "tags-actions-header": "Handlinger",
        "tags-active-yes": "Ja",
        "tags-active-no": "Nei",
-       "tags-source-extension": "Definert av en utvidelse",
+       "tags-source-extension": "Definert av programvaren",
        "tags-source-manual": "Brukes manuelt av brukere og roboter",
        "tags-source-none": "Brukes ikke lenger",
        "tags-edit": "rediger",
        "tags-deactivate": "deaktiver",
        "tags-hitcount": "{{PLURAL:$1|én endring|$1 endringer}}",
        "tags-manage-no-permission": "Du har ikke tillatelse til å behandle tagger.",
+       "tags-manage-blocked": "Du kan ikke behandle endringstagger mens du er blokkert.",
        "tags-create-heading": "Opprett ny tagg",
        "tags-create-explanation": "Som standard vil nyopprettede tagger være tilgjengelige for brukere og roboter.",
        "tags-create-tag-name": "Taggnavn:",
        "tags-delete-not-found": "Taggen «$1» finnes ikke.",
        "tags-delete-too-many-uses": "Taggen «$1» brukes på mer enn $2 {{PLURAL:$2|revisjon|revisjoner}}, hvilket betyr at den ikke kan slettes.",
        "tags-delete-warnings-after-delete": "Taggen «$1» ble slettet, men følgende {{PLURAL:$2|advarsel|advarsler}} dukket opp:",
+       "tags-delete-no-permission": "Du har ikke tillatelse til å slette endringstagger.",
        "tags-activate-title": "Aktiver taggen",
        "tags-activate-question": "Du er i ferd med å aktivere taggen «$1».",
        "tags-activate-reason": "Årsak:",
        "tags-apply-not-allowed-one": "Merket «$1» kan ikke legges til manuelt.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Det følgende merket|De følgende merkene}} kan ikke legges til manuelt: $1",
        "tags-update-no-permission": "Du har ikke tilgang til å legge til eller fjerne merker fra individuelle revisjoner eller loggposter.",
+       "tags-update-blocked": "Du kan ikke legge til eller fjerne endringstagger mens du er blokkert.",
        "tags-update-add-not-allowed-one": "Merket «$1» kan ikke legges til manuelt.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Det følgende merket|De følgende merkene}} kan ikke legges til manuelt: $1",
        "tags-update-remove-not-allowed-one": "Merket «$1» kan ikke fjernes.",
        "feedback-useragent": "Brukeragent",
        "searchsuggest-search": "Søk",
        "searchsuggest-containing": "inneholder …",
+       "api-error-autoblocked": "Din IP-adresse har blitt blokkert automatisk fordi den ble brukt av en blokkert bruker.",
        "api-error-badaccess-groups": "Du har ikke tillatelse til å laste opp filer til denne wikien.",
        "api-error-badtoken": "Intern feil: Ugyldig nøkkel.",
+       "api-error-blocked": "Du har blitt blokkert fra å redigere.",
        "api-error-copyuploaddisabled": "Opplasting ved URL er deaktivert på denne tjeneren.",
        "api-error-duplicate": "Det er allerede {{PLURAL:$1|en annen fil|flere andre filer}} på denne siden med samme innhold.",
        "api-error-duplicate-archive": "Det fantes {{PLURAL:$1|en annen fil|noen andre filer}} på siden som hadde samme innhold, men {{PLURAL:$1|den|de}} ble slettet.",
        "api-error-nomodule": "Intern feil: ingen opplastningsmodul har blitt valgt.",
        "api-error-ok-but-empty": "Intern feil: ingen svar fra server.",
        "api-error-overwrite": "Det er ikke tillatt å overskrive eksisterende filer.",
+       "api-error-ratelimited": "Du prøver å laste opp flere filer enn wikien tillater i et kort tidsrom.\nPrøv igjen om noen minutter.",
        "api-error-stashfailed": "Internal error: tjeneren greide ikke å lagre midlertidig fil.",
        "api-error-publishfailed": "Intern feil: Tjeneren greide ikke å publisere midlertidig fil.",
        "api-error-stasherror": "Det oppstod en feil mens filen ble lastet opp til stash.",
        "api-error-unknownerror": "Ukjent feil: «$1».",
        "api-error-uploaddisabled": "Opplastning har blitt deaktivert på denne wikien.",
        "api-error-verification-error": "Filen kan være korrupt, eller ha feil filendelse.",
+       "api-error-was-deleted": "En fil med dette navnet har tidligere blitt lastet opp og senere slettet.",
        "duration-seconds": "$1 {{PLURAL:$1|sekund|sekunder}}",
        "duration-minutes": "$1 {{PLURAL:$1|minutt|minutter}}",
        "duration-hours": "$1 {{PLURAL:$1|time|timer}}",
        "expand_templates_generate_xml": "Vis parsetre som XML",
        "expand_templates_generate_rawhtml": "Vis ubehandlet HTML",
        "expand_templates_preview": "Forhåndsvisning",
-       "expand_templates_preview_fail_html": "<em>Fordi {{SITENAME}} har slått på rå HTML og sesjonsdata ble tapt er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, prøv igjen.</strong> Om det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen.",
+       "expand_templates_preview_fail_html": "<em>Fordi {{SITENAME}} har slått på rå HTML og sesjonsdata ble tapt er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, prøv på nytt.</strong> Om det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen, og sjekk at nettleseren din godtar nettkapsler fra dette nettstedet.",
        "expand_templates_preview_fail_html_anon": "<em>Fordi {{SITENAME}} har slått på rå HTML og du ikke er logget inn er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, [[Special:UserLogin|logg inn]] og prøv igjen.</strong>",
+       "expand_templates_input_missing": "Du må angi noe inndata.",
        "pagelanguage": "Endre sidespråk",
        "pagelang-name": "Side",
        "pagelang-language": "Språk",
        "log-name-pagelang": "Logg for språkendringer",
        "log-description-pagelang": "Dette er en logg som viser endringer i sidespråk",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|endret}} språk for $3 fra $4 til $5.",
-       "default-skin-not-found": "Ops! Standarddrakten for wikien din, definert i <code dir=\"ltr\">$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nInstallasjonen din ser ut til å inneholde følgende {{PLURAL:$4|drakt|drakter}}. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for informasjon om hvordan du kan slå {{PLURAL:$4|denne på|disse på og velge en standarddrakt}}.\n\n$2\n\n; Om du nettopp har installert MediaWiki:\n: Du har trolig installert fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org sin draktbase] ved å\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med flere drakter og utvidelser. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakter fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* klone en av <code>mediawiki/skins/*</code>-lagrene via git inn i <code>skins/</code> -mappen av din MediaWiki-installasjon.\n: Å gjøre dette skal ikke forstyrre git-mappen din om du er en MediaWiki-utvikler.\n\n; Om du nettopp har oppgradert MediaWiki:\n: MediaWiki 1.24 og nyere slår ikke lenger på automatisk installerte drakter (se [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Du kan lime inn følgende {{PLURAL:$5|linje|linjer}} i <code>LocalSettings.php</code> for å slå på {{PLURAL:$5|den|alle}} nåværende installerte {{PLURAL:$5|drakten|drakter}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Om du nettopp har endret <code>LocalSettings.php</code>:\n: Dobbelsjekk draktnavnene for skrivefeil.",
-       "default-skin-not-found-no-skins": "Ops! Standarddrakten for wikien din, definert i <code>$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nDu har ingen installerte drakter.\n\n;Om du nettopp har installert eller oppgradert MediaWiki:\n: Du installerte trolig fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. MediaWiki 1.24 og nyere inkluderer ingen drakter i hovedarkivet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.orgs draktmappe], ved å:\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med mange drakter og tillegg. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakt-tarballer fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* klone en av <code>mediawiki/skins/*</code>-arkivene via git til <code dir=\"ltr\">skins/</code>-mappa i din MediaWiki-installasjon.\n: Å gjøre dette vil ikke forstyrre ditt git-arkiv om du er en MediaWiki-utvikler. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual:Skin configuration] for informasjon om hvordan du slår på drakter og velger en standarddrakt.",
+       "default-skin-not-found": "Ops! Standarddrakten for wikien din, definert i <code dir=\"ltr\">$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nInstallasjonen din ser ut til å inneholde følgende {{PLURAL:$4|drakt|drakter}}. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for informasjon om hvordan du kan slå {{PLURAL:$4|denne på|disse på og velge en standarddrakt}}.\n\n$2\n\n; Om du nettopp har installert MediaWiki:\n: Du har trolig installert fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org sin draktbase] ved å\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med flere drakter og utvidelser. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakter fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Bruk Git for å laste ned drakter].\n: Å gjøre dette skal ikke forstyrre git-mappen din om du er en MediaWiki-utvikler.\n\n; Om du nettopp har oppgradert MediaWiki:\n: MediaWiki 1.24 og nyere slår ikke lenger på automatisk installerte drakter (se [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Du kan lime inn følgende {{PLURAL:$5|linje|linjer}} i <code>LocalSettings.php</code> for å slå på {{PLURAL:$5|den|alle}} installerte {{PLURAL:$5|drakten|drakter}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Om du nettopp har endret <code>LocalSettings.php</code>:\n: Dobbelsjekk draktnavnene for skrivefeil.",
+       "default-skin-not-found-no-skins": "Ops! Standarddrakten for wikien din, definert i <code>$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nDu har ingen installerte drakter.\n\n;Om du nettopp har installert eller oppgradert MediaWiki:\n: Du installerte trolig fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. MediaWiki 1.24 og nyere inkluderer ingen drakter i hovedarkivet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.orgs draktmappe], ved å:\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med mange drakter og tillegg. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakt-tarballer fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Bruk Git for å laste ned drakter].\n: Å gjøre dette vil ikke forstyrre ditt git-arkiv om du er en MediaWiki-utvikler. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual:Skin configuration] for informasjon om hvordan du slår på drakter og velger en standarddrakt.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (slått på)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>slått av</strong>)",
        "mediastatistics": "Mediestatistikk",
        "mediastatistics-summary": "Statistikk over opplastede filtyper. Dette inkluderer bare den nyeste versjonen av hver fil. Eldre eller slettede versjoner av filene er eksludert.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3 %)",
-       "mediastatistics-bytespertype": "Total filstørrelse for denne seksjonen: $1 byte.",
-       "mediastatistics-allbytes": "Total filstørrelse for alle filer: $1 byte.",
+       "mediastatistics-bytespertype": "Total filstørrelse for denne seksjonen: {{PLURAL:$1|$1 byte}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Total filstørrelse for alle filer: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "MIME-type",
        "mediastatistics-table-extensions": "Mulige filtyper",
        "mediastatistics-table-count": "Antall filer",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Symboler",
        "special-characters-group-greek": "Gresk",
+       "special-characters-group-greekextended": "Utvidet gresk",
        "special-characters-group-cyrillic": "Kyrillisk",
        "special-characters-group-arabic": "Arabisk",
        "special-characters-group-arabicextended": "Utvidet arabisk",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
        "mw-widgets-titleinput-description-new-page": "siden eksisterer ikke ennå",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
+       "sessionmanager-tie": "Kan ikke kombinere flere forespørselsautentiseringstyper: $1",
        "sessionprovider-generic": "$1 sesjoner",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "informasjons&shy;kapsel-baserte sesjoner",
-       "randomrootpage": "Tilfeldig rotside"
+       "sessionprovider-nocookies": "Informasjonskapsler er kanskje slått av. Sjekk at du har slått på informasjonskapsler og prøv igjen.",
+       "randomrootpage": "Tilfeldig rotside",
+       "log-action-filter-block": "Type blokkering:",
+       "log-action-filter-contentmodel": "Type innholdsmodellendring:",
+       "log-action-filter-delete": "Type sletting:",
+       "log-action-filter-import": "Type import:",
+       "log-action-filter-managetags": "Type tagghåndteringshandling:",
+       "log-action-filter-move": "Type flytting:",
+       "log-action-filter-newusers": "Type kontooppretting:",
+       "log-action-filter-patrol": "Type patruljering:",
+       "log-action-filter-protect": "Type beskyttelse:",
+       "log-action-filter-rights": "Type rettighetsendring:",
+       "log-action-filter-upload": "Type opplasting:",
+       "log-action-filter-all": "Alle",
+       "log-action-filter-block-block": "Blokkering",
+       "log-action-filter-block-reblock": "Blokkeringsendring",
+       "log-action-filter-block-unblock": "Avblokkering",
+       "log-action-filter-contentmodel-change": "Endring av innholdsmodell",
+       "log-action-filter-contentmodel-new": "Oppretting av side med ikke-standard innholdsmodell",
+       "log-action-filter-delete-delete": "Sidesletting",
+       "log-action-filter-delete-restore": "Sidegjenoppretting",
+       "log-action-filter-delete-event": "Loggsletting",
+       "log-action-filter-delete-revision": "Revisjonssletting",
+       "log-action-filter-import-interwiki": "Transwiki-importering",
+       "log-action-filter-import-upload": "XML-opplastingsimportering",
+       "log-action-filter-managetags-create": "Taggopprettelse",
+       "log-action-filter-managetags-delete": "Taggsletting",
+       "log-action-filter-managetags-activate": "Taggaktivering",
+       "log-action-filter-managetags-deactivate": "Taggdeaktivering",
+       "log-action-filter-move-move": "Flytting uten overskriving av omdirigeringer",
+       "log-action-filter-move-move_redir": "Flytting med overskriving av omdirigeringer",
+       "log-action-filter-newusers-create": "Opprettelse av anonym bruker",
+       "log-action-filter-newusers-create2": "Opprettelse av registrert bruker",
+       "log-action-filter-newusers-autocreate": "Automatisk opprettelse",
+       "log-action-filter-newusers-byemail": "Opprettelse med passord sendt på epost",
+       "log-action-filter-patrol-patrol": "Manuell patruljering",
+       "log-action-filter-patrol-autopatrol": "Automatisk patruljering",
+       "log-action-filter-protect-protect": "Beskyttelse",
+       "log-action-filter-protect-modify": "Beskyttelsesendring",
+       "log-action-filter-protect-unprotect": "Avbeskyttelse",
+       "log-action-filter-protect-move_prot": "Flyttingsbeskyttelse",
+       "log-action-filter-rights-rights": "Manuell endring",
+       "log-action-filter-rights-autopromote": "Automatisk endring",
+       "log-action-filter-upload-upload": "Ny opplasting",
+       "log-action-filter-upload-overwrite": "Gjenopplasting",
+       "authmanager-authn-not-in-progress": "Autentisering foregår ikke eller sesjonsdata er tapt. Start igjen fra begynnelsen.",
+       "authmanager-authn-no-primary": "De oppgitte akkreditivene kunne ikke autentiseres.",
+       "authmanager-authn-no-local-user": "De oppgitte akkreditivene tilhører ingen bruker på denne wikien.",
+       "authmanager-authn-no-local-user-link": "De oppgitte akkreditivene er gyldige men tilhører ingen brukere på denne wikien. Logg inn på en annen måte eller opprett en ny bruker, og du vil ha mulighet til å lenke dine tidligere akkreditiver med den kontoen.",
+       "authmanager-authn-autocreate-failed": "Autooprrettelse av lokal konto mislyktes: $1",
+       "authmanager-change-not-supported": "De oppgitte akkreditivene kan ikke endres, siden ingenting ville bruke dem.",
+       "authmanager-create-disabled": "Kontoopprettelse er deaktivert.",
+       "authmanager-create-from-login": "For å opprette kontoen din, fyll inn feltene nedenfor.",
+       "authmanager-create-not-in-progress": "Kontoopprettelse foregår ikke eller sesjonsdata er tapt. Start igjen fra begynnelsen.",
+       "authmanager-create-no-primary": "De oppgitte akkreditivene kunne ikke brukes for kontooppretting.",
+       "authmanager-link-no-primary": "De oppgitte akkreditivene kunne ikke brukes for kontolenking.",
+       "authmanager-link-not-in-progress": "Kontolenking foregår ikke eller sesjonsdata er tapt. Start igjen fra begynnelsen.",
+       "authmanager-authplugin-setpass-failed-title": "Passordendring mislyktes",
+       "authmanager-authplugin-setpass-failed-message": "Autentiseringspluginen avviste passordendringen.",
+       "authmanager-authplugin-create-fail": "Autentiseringspluginen avviste kontoopprettelsen.",
+       "authmanager-authplugin-setpass-denied": "Autentiseringspluginen tillater ikke endring av passord.",
+       "authmanager-authplugin-setpass-bad-domain": "Ugyldig domene.",
+       "authmanager-autocreate-noperm": "Automatisk kontoopprettelse tillates ikke.",
+       "authmanager-autocreate-exception": "Automatisk kontoopprettelse er midlertidig deaktivert på grunn av tidligere feil.",
+       "authmanager-userdoesnotexist": "Brukerkontoen «$1» er ikke registrert.",
+       "authmanager-userlogin-remembermypassword-help": "Hvorvidt passordet skal huskes lenger enn sesjonslengden.",
+       "authmanager-username-help": "Brukernavn for autentisering.",
+       "authmanager-password-help": "Passord for autentisering.",
+       "authmanager-domain-help": "Domene for ekstern autentisering.",
+       "authmanager-retype-help": "Passord igjen for å bekrefte.",
+       "authmanager-email-label": "Epost",
+       "authmanager-email-help": "Epostadresse",
+       "authmanager-realname-label": "Virkelig navn",
+       "authmanager-realname-help": "Brukerens virkelige navn",
+       "authmanager-provider-password": "Passordbasert autentisering",
+       "authmanager-provider-password-domain": "Passord- og domenebasert autentisering",
+       "authmanager-provider-temporarypassword": "Midlertidig passord",
+       "authprovider-confirmlink-message": "Basert på dine nylige innloggingsforsøk kan følgende kontoer lenkes til den wikikonto. Å lenke dem muliggjør innlogging via de kontoene. Vennligst velg hvilke som skal lenkes.",
+       "authprovider-confirmlink-request-label": "Kontoer som skal lenkes",
+       "authprovider-confirmlink-success-line": "$1: Lenking gjennomført.",
+       "authprovider-confirmlink-failed": "Konto kunne ikke lenkes fullstendig: $1",
+       "authprovider-confirmlink-ok-help": "Fortsett etter at feilmeldinger om lenking har blitt vist.",
+       "authprovider-resetpass-skip-label": "Hopp over",
+       "authprovider-resetpass-skip-help": "Hopp over nullstilling av passordet.",
+       "authform-nosession-login": "Autentiseringen lyktes, men nettleseren din kan ikke «huske» å være innlogget.\n\n$1",
+       "authform-nosession-signup": "Kontoen ble opprettet, men nettleseren kan ikke «huske» å være innlogget.\n\n$1",
+       "authform-newtoken": "Manglende nøkkel. $1",
+       "authform-notoken": "Mangler nøkkel",
+       "authform-wrongtoken": "Feil nøkkel",
+       "specialpage-securitylevel-not-allowed-title": "Ikke tillatt",
+       "specialpage-securitylevel-not-allowed": "Beklager, du har ikke tillatelse til å bruke denne siden fordi identiteten din ikke kunne bekreftes.",
+       "authpage-cannot-login": "Kunne ikke starte innlogging.",
+       "authpage-cannot-login-continue": "Kunne ikke fortsette innlogging. Sesjonen din har trolig fått et tidsavbrudd.",
+       "authpage-cannot-create": "Kunne ikke starte kontoopprettelse.",
+       "authpage-cannot-create-continue": "Kunne ikke fortsette kontoopprettelse. Sesjonen din har trolig fått et tidsavbrudd.",
+       "authpage-cannot-link": "Kunne ikke starte kontolenking.",
+       "authpage-cannot-link-continue": "Kunne ikke fortsette kontolenking. Sesjonen din har trolig fått et tidsavbrudd.",
+       "cannotauth-not-allowed-title": "Ingen tilgang",
+       "cannotauth-not-allowed": "Du har ikke tillatelse til å bruke denne siden",
+       "changecredentials": "Endre akkreditiver",
+       "changecredentials-submit": "Endre akkreditiver",
+       "changecredentials-invalidsubpage": "$1 er ikke en gyldig akkreditivtype.",
+       "changecredentials-success": "Akkreditivene dine har blitt endret.",
+       "removecredentials": "Fjern akkreditiver",
+       "removecredentials-submit": "Fjern akkreditiver",
+       "removecredentials-invalidsubpage": "$1 er ikke en gyldig akkreditivtype.",
+       "removecredentials-success": "Akkreditivene dine har blitt fjernet.",
+       "credentialsform-provider": "Akkreditivtype:",
+       "credentialsform-account": "Kontonavn:",
+       "cannotlink-no-provider-title": "Det er ingen kontoer som kan lenkes",
+       "cannotlink-no-provider": "Det er ingen kontoer som kan lenkes.",
+       "linkaccounts": "Lenk kontoer",
+       "linkaccounts-success-text": "Kontoen ble lenket.",
+       "linkaccounts-submit": "Lenk kontoer",
+       "unlinkaccounts": "Fjern lenking av kontoer",
+       "unlinkaccounts-success": "Kontoens lenking ble fjernet.",
+       "userjsispublic": "Merk: JavaScript-undersidene bør ikke inneholde konfidensielle data, siden de kan ses av andre brukere.",
+       "usercssispublic": "Merk: CSS-undersidene bør ikke inneholde konfidensielle data siden de kan ses av andre brukere."
 }
index 4a97196..fd3ccea 100644 (file)
        "faqpage": "Project:धैरै सोधिएका प्रश्नहरु",
        "actions": "कार्यहरु",
        "namespaces": "नेमस्पेस",
-       "variants": "बहà¥\81रà¥\81पहरà¥\81",
+       "variants": "बहà¥\81रà¥\81पहरà¥\82",
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "त्रुटि",
        "returnto": "$1 मा फर्कनुहोस् ।",
        "yourpasswordagain": "पासवर्ड फेरि टाईप गर्नुहोस्",
        "createacct-yourpasswordagain": "पासवर्ड निश्चित गर्नुहोस्",
        "createacct-yourpasswordagain-ph": "फेरि पासवर्ड लेख्नुहोस्",
-       "remembermypassword": "यो कम्प्युटरमा मेरो प्रवेश याद राख्ने (धेरैमा $1 {{PLURAL:$1|दिन|दिनहरू}})",
        "userlogin-remembermypassword": "मलाई प्रवेश गराइराख्ने",
        "userlogin-signwithsecure": "सुक्षित जडान प्रयोग गर्ने",
        "yourdomainname": "तपाईंको ज्ञानक्षेत्र(डोमेन):",
        "image_tip": "इम्बेडेड(जडान गरिएको) फाइल",
        "media_sample": "उदाहरण.ogg",
        "media_tip": "फाइल लिङ्क",
-       "sig_tip": "तपाà¤\88à¤\81को समयछाप सहितको दस्तखत",
+       "sig_tip": "तपाà¤\88à¤\82को समयछाप सहितको दस्तखत",
        "hr_tip": "क्षितिजिय रेखा (कम प्रयोग गर्नुहोस्)",
        "summary": "सारांश:",
        "subject": "विषय/शीर्षक:",
        "showpreview": "पूर्वालोकन देखाउनुहोस्",
        "showdiff": "परिवर्तन देखाउनुहोस्",
        "blankarticle": "<strong>चेतावनी:</strong> तपाईं एउटा खालि पृष्ठको निर्माण गर्दै हुनुहुन्छ।\nयदि तपाईं \"{{int:savearticle}}\" लाई पुनः थिच्नुहुन्छ भने पृष्ठ बिना कुनै सामग्री नै निर्मित गरिनेछ।",
-       "anoneditwarning": "<strong>à¤\9aà¥\87तावनà¥\80:</strong> à¤¤à¤ªà¤¾à¤\88à¤\81लà¥\87 à¤ªà¥\8dरवà¥\87श à¤\97रà¥\8dनà¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\9bà¥\88न à¥¤ à¤¤à¤ªà¤¾à¤\88à¤\81à¤\95à¥\8b à¤\86à¤\87पि à¤ à¥\87à¤\97ाना à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤\87तिहासमा à¤¦à¤°à¥\8dता à¤\97रिनà¥\87 à¤\9b à¤° à¤¯à¥\8b à¤¸à¤¬à¥\88लà¥\87 à¤¹à¥\87रà¥\8dन à¤¸à¤\95à¥\8dà¤\9bन à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤ªà¤¾à¤\88à¤\82 <strong>[$1 à¤²à¤\97à¤\88न]</strong> à¤µà¤¾ <strong>[$2 à¤¨à¤¯à¤¾à¤\81 à¤\96ाता à¤¬à¤¨à¤¾à¤\89नà¥\87] à¤\97रà¥\8dनà¥\81भयà¥\8b à¤­à¤¨à¥\87 à¤¤à¤ªà¤¾à¤\88à¤\82दà¥\8dवारा à¤\97रिà¤\8fà¤\95à¥\8b à¤¸à¤®à¥\8dपादन à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dतानाममा à¤\9cà¥\8bडिनà¥\87à¤\9b।",
+       "anoneditwarning": "<strong>à¤\9aà¥\87तावनà¥\80:</strong> à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤ªà¥\8dरवà¥\87श à¤\97रà¥\8dनà¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\9bà¥\88न à¥¤ à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤\86à¤\87पि à¤ à¥\87à¤\97ाना à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤\87तिहासमा à¤¦à¤°à¥\8dता à¤\97रिनà¥\87 à¤\9b à¤° à¤¯à¥\8b à¤¸à¤¬à¥\88लà¥\87 à¤¹à¥\87रà¥\8dन à¤¸à¤\95à¥\8dà¤\9bनà¥\8d à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤ªà¤¾à¤\88à¤\82 <strong>[$1 à¤²à¤\97à¤\88न]</strong> à¤µà¤¾ <strong>[$2 à¤¨à¤¯à¤¾à¤\81 à¤\96ाता à¤¬à¤¨à¤¾à¤\89नà¥\87] à¤\97रà¥\8dनà¥\81भयà¥\8b à¤­à¤¨à¥\87 à¤¤à¤ªà¤¾à¤\88à¤\82दà¥\8dवारा à¤\97रिà¤\8fà¤\95à¥\8b à¤¸à¤®à¥\8dपादन à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dतानाममा à¤\9cà¥\8bडिनà¥\87à¤\9b ।",
        "anonpreviewwarning": "''तपाईंले प्रवेश गर्नु भएको छैन। संग्रह (Save) गरेको खण्डमा पृष्ठको इतिहासमा तपाईंको IP ठेगाना अंकित गरिनेछ।''",
        "missingsummary": "'''यादगर्नुहोस् :''' तपाईंले सम्पादन सारांश दिनुभएको छैन ।\nयदि तपाईंले \"{{int:savearticle}}\"  थिच्नुभयो भने , सारांश बिना नै संग्रहित गरिने छ ।",
        "selfredirect": "<strong>चेतावनी:</strong> तपाईं यस पृष्ठलाई आफुमा पुनः निर्देशित गर्दै हुनुहुन्छ।\nहुनसक्छ तपाईं अनुप्रेषितको लागि गलत लक्ष्य निर्दिष्ट गर्दै हुनुहुन्छ, वा गलत पृष्ठको सम्पादन गर्दै हुनुहुन्छ।\nतपाईं पुनः एकपटक \"{{int:savearticle}}\" क्लिक गर्नुहुन्छ, पुनः निर्देशित त्यसै पनि बनाइनेछ।",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद राख्नुहोस् यो केवल पूर्वावलोकन मात्र हो; तपाईंका परिवर्तनहरू संग्रहित भएका छैनन्!'''",
        "continue-editing": "सम्पादन क्षेत्रमा जानुहोस",
-       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤\82पादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\87लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\87 सेभ गरेपछि देखापर्छ।",
+       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤®à¥\8dपादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\88 सेभ गरेपछि देखापर्छ।",
        "session_fail_preview": "'''माफ गर्नुहोस्! सत्र-आँकड़ा (session data) हराउनाले हामीले तपाईंको सम्पादन प्रक्रिया अघि बढाउन सकेनौं।.'''\nकृपया पुनः प्रयास गर्नुहोस्।\nयदि फेरि पनि काम भएन भनें, [[Special:UserLogout|बाहिर गई(लग आउट गरी)]]  फेरि प्रवेश गर्नुहोस्।",
-       "session_fail_preview_html": "'''माफ गर्नुहोला! सत्र को डेटा को नोकसान को कारण ले गर्दा तपाइको सम्पादन लाइ जारी राख्न सकिएन।'''\n\n''जावास्क्रिप्ट हमलाहरु रोक्नको लागि यो पूर्वावलोकन लाइ देखाइएको छैन किन कि {{SITENAME}} मा काँचो HTML को प्रयोग गर्न मिल्ने बनाइएको छ।''\n\n'''यदि यो एक वैध प्रयास हो भने, कृपया पुन: प्रयास गर्नुहोला.'''\nयदि अझै पनि काम गरेन भने [[Special:UserLogout|निर्गमन(logging out)]] र पुन:आगमन(login) गर्ने प्रयास गर्नुहोला।",
+       "session_fail_preview_html": "माफ गर्नुहोला ! सेशन डाटा नष्ट भएको कारण तपाईंको परिवर्तन शुरक्षित गर्न सकिएन ।\n\n<em>किनकी {{SITENAME}}मा raw HTML सक्षम छ, जावास्क्रिप्ट हमहरूबाट बचाउनको लागि झलक नहीं देखाइएको छैन ।</em>\n\n<strong>यदी यो तपाईंको वैध सम्पादन यत्न थियो भने कृपया पुनः प्रयास गर्नुहोस् ।</strong>\nयदी यस पनि यस्तै भयो भने कृपया [[Special:UserLogout|लग आउट]] गरेर पुनः लग इन गर्नुहोस् तथा तपाईंको ब्राउजरले यस साइटसँग कुकीजको अनुमति दिन्छ दिन्न जाँच गर्नुहोस् ।",
        "token_suffix_mismatch": "'''सम्पादन टोकनमा विराम चिह्न र वर्ण सम्बन्धित गड़बड़ीको कारण तपाईंको सम्पादन अस्वीकार गरिएको छ'''\nपृष्ठको पाठ बचाउन सम्पादन अस्वीकार गरिएको हो।\nयस्तो त्यसबेला हुन्छ जब तपाईंले बगी वेवमा आधारित अज्ञात प्रोक्सी सेवा प्रयोग गर्नुहुन्छ।",
        "edit_form_incomplete": "'''सम्पादनको केहि भाग सर्वरसम्म पुग्न सकेन, दुइपल्ट जाँच गर्नुहोस्, तपाईंको सम्पादन यथावत रहे पुनः प्रयास गर्नुहोस्'''",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 सम्पादन गरिदै",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझियो: $1",
        "explainconflict": "तपाईंले सम्पादन कार्य सुरु गरेपछि कसैले यस पृष्टलाई परिवर्तन गरेकोछ।\nमाथिल्लो पाठक्षेत्रमा पृष्ठको वर्तमान पाठ छ।\nतपाईंको परिवर्तन तल्लो भागमा दर्शाइएकोछ। \nतपाईंले गर्नुभएको परिवर्तनलाई वर्तमान पाठसित मिसाउनु पर्नेछ, यदि तपाईंले \"{{int:savearticle}}\" थिच्नु भयो भनें पाठको माथिल्लो भाग '''मात्र''' संग्रह गरिनेछ।",
        "diff-multi-manyusers": "($2 {{PLURAL:$2|भन्दा अधिक प्रयोगकर्ता|भन्दा अधिक प्रयोगकर्ताहरू}}द्वारा {{PLURAL:$1|एउटा मध्यवर्ती संशोधन|$1 मध्यवर्ती संशोधनहरू}} नदेखाइएको)",
        "difference-missing-revision": "यस अन्तर {{PLURAL:$2|को एक अवतरण|को $2 अवतरण}} ($1)  {{PLURAL:$2|भेटिएन|खोज्न सकिएन}}।\n\nयो सामान्य रूपमा एउटा हताइएको पृष्ठको अवतरणहरूमा अन्तर खोज्दा हुन्छ । अधिक जानकारी [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} हटाइएको लग]मा हेर्न सकिन्छ।",
        "searchresults": "खोज नतिजाहरू",
-       "searchresults-title": " \"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\96à¥\8bà¤\9c à¤¨à¤¤à¤¿à¤\9cाहरà¥\81",
+       "searchresults-title": " \"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\96à¥\8bà¤\9c à¤¨à¤¤à¤¿à¤\9cाहरà¥\82",
        "titlematches": "पृष्ठ शिर्षक मिल्छ",
        "textmatches": "पृष्ठ पाठ मिल्छ",
        "notextmatches": "अक्षरस् पेज भेटिएन",
        "next-page": "अर्को पृष्ठ",
        "prevn-title": "पहिलेको  $1 {{PLURAL:$1|नतिजा|नतिजाहरु}}",
        "nextn-title": "यस पछिको $1 {{PLURAL:$1|नतिजा |नतिजाहरु}}",
-       "shown-title": "दà¥\87à¤\96ाà¤\89नà¥\87 $1 {{PLURAL:$1|नतिà¤\9cा|नतिà¤\9cाहरà¥\81}} प्रति पृष्ठ",
+       "shown-title": "दà¥\87à¤\96ाà¤\89नà¥\87 $1 {{PLURAL:$1|नतिà¤\9cा|नतिà¤\9cाहरà¥\82}} प्रति पृष्ठ",
        "viewprevnext": "हेर्नुहोस् ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "''' \"[[:$1]]\" नाम गरेको पृष्ठ  यो विकीमा रहेको छ'''",
        "searchmenu-new": "<strong>\"[[:$1]]\" पृष्ठ यस विकिमा बनाउनुहोस्!</strong> {{PLURAL:$2|0=|तपाईंले खोज गरी भटिएको पृष्ठ पनि मिलान गर्नुहोस्।|तपाईंको खोज परिणाम पनि हेर्नुहोस।}}",
        "showingresults": "देखाउँदै  {{PLURAL:$1|'''१''' नतिजा|'''$1''' नतिजाहरू }} , #'''$2''' बाट सुरुहुने ।",
        "showingresultsinrange": "देखाई रहेको छ{{PLURAL:$1|<strong>1</strong> result|<strong>$1</strong> परिणाम}} सम्म पहुँच  #<strong>$2</strong> देखि #<strong>$3</strong> मा।",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> मा बाट <strong>$1</strong> परिणाम|<strong>$3</strong> मा बाट परिणाम <strong>$1 - $2</strong>}}",
-       "search-nonefound": "तपाà¤\88à¤\81को क्वेरीसँग मेल खाने नतिजाहरू भेटिएनन्",
+       "search-nonefound": "तपाà¤\88à¤\82को क्वेरीसँग मेल खाने नतिजाहरू भेटिएनन्",
        "powersearch-legend": "उन्नत खोज",
        "powersearch-ns": "नेमस्पेसेजहरूमा खोज्ने :",
        "powersearch-togglelabel": "जाँच्ने :",
        "recentchangeslinked-feed": "सम्बन्धित परिवर्तनहरू",
        "recentchangeslinked-toolbox": "सम्बन्धित परिवर्तनहरू",
        "recentchangeslinked-title": "\"$1\" सँग सम्बन्धित परिवर्तन",
-       "recentchangeslinked-summary": "यो सूची निर्दिष्ट पृष्ठ (वा निर्दिष्ट श्रेणी)सित जोडिएका भर्खरै परिवर्तन भएका पृष्ठको  हो। [[Special:Watchlist|तपाईँको निगरानी सूची]]का पृष्ठहरू <strong>गाढा अक्षरमा</strong> छन्।",
+       "recentchangeslinked-summary": "यो सूची निर्दिष्ट पृष्ठ (वा निर्दिष्ट श्रेणी)सित जोडिएका भर्खरै परिवर्तन भएका पृष्ठको  हो । [[Special:Watchlist|तपाईंको निगरानी सूची]]का पृष्ठहरू <strong>गाढा अक्षरमा</strong> छन् ।",
        "recentchangeslinked-page": "पृष्ठ नाम:",
        "recentchangeslinked-to": "यसको सट्टा यो पृष्ठसँग जोडिएका पृष्ठहरूको परिवर्तन देखाउने",
        "upload": "फाइल उर्ध्वभरण",
        "filehist-dimensions": "आकारहरू",
        "filehist-filesize": "फाइल आकार",
        "filehist-comment": "टिप्पणी",
-       "imagelinks": "फाà¤\87लà¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97हरà¥\81",
+       "imagelinks": "फाà¤\87लà¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97हरà¥\82",
        "linkstoimage": "यस फाइलमा निम्न{{PLURAL:$1|पृष्ठ जोडिन्छ|$1 पृष्ठहरू जोडिन्छन्}}:",
        "linkstoimage-more": "$1 भन्दा अधिक {{PLURAL:$1|पृष्ठ लिङ्क|पृष्ठ लिङ्कहरू}} यस फाइलसँग जोडिएको छ। \nनिम्नलिखित सूची फाइलसँग {{PLURAL:$1|पहिलो पृष्ठ लिङ्क|पहिलो $1 पृष्ठ लिङ्कहरू}} जोडिने देखाउँछ।\n[[Special:WhatLinksHere/$2|पूर्ण सूची]] पनि उपलब्ध छ।",
        "nolinkstoimage": "यो फाईलसंग लिंकभएको कुनै पृष्ठ छैन.",
        "duplicatesoffile": "निम्नलिखित {{PLURAL:$1|फाइलको प्रतिलिपि हो|$1 फाइलहरूको प्रतिलिपि हो}} ([[Special:FileDuplicateSearch/$2|अधिक जानकारीहरू]]):",
        "sharedupload": "यो फाइल $1 को हो र अन्य परियोजनामा प्रयोग गरिएको हुनसक्छ।",
        "sharedupload-desc-there": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिनेछ। अधिक जानकारीको लागि कृपया [$2 फाइल विवरण पृष्ठ] हेर्नुहोस।",
-       "sharedupload-desc-here": "यà¥\8b à¤«à¤¾à¤\87ल $1 à¤¬à¤¾à¤\9f à¤¹à¥\8b à¤° à¤\85नà¥\8dय à¤ªà¤°à¤¿à¤¯à¥\8bà¤\9cनाहरà¥\82 à¤¦à¥\8dवारा à¤ªà¤¨à¤¿ à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रà¥\8dन à¤¸à¤\95िनà¥\8dà¤\9b। \nतà¥\8dयहाà¤\81 à¤¨à¥\87र à¤¯à¤¸à¤\95à¥\8b [$2 à¤«à¤¼à¤¾à¤\87ल à¤µà¤¿à¤µà¤°à¤£ à¤ªà¥\83षà¥\8dठ]मा à¤°à¤¹à¥\87à¤\95à¥\8b à¤µà¤¿à¤µà¤°à¤£ à¤¤à¤² à¤¦à¤¿à¤\87à¤\8fà¤\95à¥\8b à¤\9b।",
+       "sharedupload-desc-here": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nत्यहाँ नेर यसको [$2 फाइल विवरण पृष्ठ]मा रहेको विवरण तल दिइएको छ।",
        "sharedupload-desc-edit": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nशायद तपाईं [$2 त्यहाँ यसको फाइल विवरण पृष्ठ]लाई सम्पादित गर्न चाहनुहुन्छ।",
        "sharedupload-desc-create": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nशायद तपाईं [$2 त्यहाँ यसको फाइल विवरण पृष्ठ]लाई सम्पादित गर्न चाहनुहुन्छ।",
        "filepage-nofile": "यस नामको फाइल छैन।",
        "shared-repo-from": " $1 बाट",
        "shared-repo": "एल साझा भण्डार",
        "shared-repo-name-wikimediacommons": "विकिमीडिया कमन्स",
-       "upload-disallowed-here": "तपाà¤\88à¤\81ले यो फाइल अधिलेखन गर्न सक्नुहुन्न ।",
+       "upload-disallowed-here": "तपाà¤\88à¤\82ले यो फाइल अधिलेखन गर्न सक्नुहुन्न ।",
        "filerevert": "पूर्वस्थिति $1 मा फर्काउने",
        "filerevert-legend": " फाइल पूर्वस्थितीमा फर्काउने",
        "filerevert-intro": "तपाईं <strong>[[Media:$1|$1]]</strong>लाई [$4 $2 मा $3 बजेको अवतरण] लाई पूर्ववत गर्दै हुनुहुन्छ।",
        "double-redirect-fixed-maintenance": "[[$1]]बाट [[$2]]मा दोहोरो अनुप्रेषण स्वत तय गरिंदै।",
        "double-redirect-fixer": "अनुप्रेषण तय गर्ने",
        "brokenredirects": "टुटेका रिडाइरेक्टहरू",
-       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81 à¤²à¥\87 à¤¹à¥\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहररसँग जोडिन्छन्:",
+       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82लà¥\87 à¤¹à¥\81à¤\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहरà¥\82सँग जोडिन्छन्:",
        "brokenredirects-edit": "सम्पादन",
        "brokenredirects-delete": "मेट्ने",
        "withoutinterwiki": "भाषा नभएको पृष्ठहरू",
        "protectedpages-timestamp": "समय चिन्ह",
        "protectedpages-page": "पृष्ठ",
        "protectedpages-expiry": "सकिनेछ",
-       "protectedpages-performer": "पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¸à¥\81रà¤\95à¥\8dषित à¤\97रिà¤\81दà¥\88",
+       "protectedpages-performer": "प्रयोगकर्ता सुरक्षित गरिदै",
        "protectedpages-params": "सुरक्षा प्यारामेटर",
        "protectedpages-reason": "कारण",
        "protectedpages-submit": "पानाहरू देखाउनुहोस्",
        "watchlist-options": "निगरानि सूची विकल्प",
        "watching": "निगरानी गर्दै...",
        "unwatching": "निगरानीबाट हटाउँदै...",
-       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\87à¤\81à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤¯à¥\8cà¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b।",
+       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤\8fà¤\89à¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b ।",
        "enotif_reset": "सबै पृष्ठहरू भनी दाग दिने",
        "enotif_impersonal_salutation": "{{SITENAME}} प्रयोगकर्ता",
        "enotif_subject_deleted": "{{SITENAME}} पृष्ठ $1 $2 ले {{GENDER:$2|मेटाउनु}} भयो ।",
        "whatlinkshere-links": "← लिंकहरू",
        "whatlinkshere-hideredirs": "$1 अनुप्रेषित हुन्छ",
        "whatlinkshere-hidetrans": "$1 पारदर्शन",
-       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81",
+       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82",
        "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
        "whatlinkshere-filters": "फिल्टरहरू",
        "whatlinkshere-submit": "जानुहोस्",
        "ipb-edit-dropdown": "निषेध कारण सम्पादन गर्नुहोस्",
        "ipb-unblock-addr": "$1 निषेध खारेज गर्ने",
        "ipb-unblock": "प्रयोगकर्ता वा IP माथिको निषेध खारेज गर्ने",
-       "ipb-blocklist": "हाल à¤°à¤¹à¥\87à¤\95ा à¤¨à¤¿à¤·à¥\87धहरà¥\81 हेर्नुहोस्",
+       "ipb-blocklist": "हाल à¤°à¤¹à¥\87à¤\95ा à¤¨à¤¿à¤·à¥\87धहरà¥\82 हेर्नुहोस्",
        "ipb-blocklist-contribs": "{{GENDER:$1|$1}}को लागि योगदान",
        "unblockip": "प्रयोगकर्ताको निषेध खारेज गर्नुहोस्",
        "unblockiptext": "IP ठेगाना अथवा प्रयोगकर्तामाथि पहिले लगाइएको रोक फुकुवा गर्न तलको प्रपत्र प्रयोग गर्नुहोस्।",
        "import-upload-filename": "फाइल नाम:",
        "import-comment": "टिप्पणी :",
        "importtext": "कृपया स्रोत विकिबाट फाइल निर्यात गर्नका लागि [[Special:Export|निर्यात सुविधा]]को प्रयोग गर्नुहोस। यसलाई आफ्नो कम्प्युटरमा सङ्ग्रह गरे यहाँ अपलोड गर्नुहोस।",
-       "importstart": "पà¥\83षà¥\8dठ à¤\86यात à¤\97रिà¤\81दà¥\88...",
+       "importstart": "पृष्ठ आयात गरिदै...",
        "import-revision-count": "$1 {{PLURAL:$1|पुनरावलोकन|पुनरावलोकनहरु}}",
        "importnopages": "आयातगर्नको लागि कुनै पृष्ठ छैन।",
        "imported-log-entries": "आयातित $1 {{PLURAL:$1|लग प्रविष्टी|लग प्रविष्टीहरू}}",
        "javascripttest": "JavaScript जाँच गरिदै",
        "javascripttest-pagetext-unknownaction": "अज्ञात कारवाही \"$1\" ।",
        "javascripttest-qunit-intro": "mediawiki.org मा [$1 जाँचको कागजात] हेर्नुहोस् ।",
-       "tooltip-pt-userpage": "तपाईंको प्रयोगकर्ता पृष्ठ",
+       "tooltip-pt-userpage": "{{GENDER:| तपाईंको प्रयोगकर्ता}} पृष्ठ",
        "tooltip-pt-anonuserpage": "तपाईँ जुन IP ठेगानाको रुपमा सम्पादन गर्दै हुनुहुन्छ , त्यसको प्रयोगकर्ता पृष्ठ निम्न छ :",
-       "tooltip-pt-mytalk": "तपाईंको वार्ता पृष्ठ",
+       "tooltip-pt-mytalk": "{{GENDER:|तपाईंको}} वार्ता पृष्ठ",
        "tooltip-pt-anontalk": "यो IP ठेगानाबाट गरिएका सम्पादनका बारेमा बार्तालाप",
-       "tooltip-pt-preferences": "तपाईंका अभिरुचिहरू",
+       "tooltip-pt-preferences": "{{GENDER:|तपाईंका}} अभिरुचिहरू",
        "tooltip-pt-watchlist": "पृष्ठहरूको सूची जसका परिवर्तनहरूलाई तपाईँले निगरानी गरिरहनु भएको छ",
-       "tooltip-pt-mycontris": "तपाईंका योगदानहरूको सूची",
-       "tooltip-pt-login": "तपाà¤\88à¤\81लाà¤\88 à¤ªà¥\8dरवà¥\87शगर्न सुझाव दिइन्छ ; तर यो जरुरी भने छैन",
+       "tooltip-pt-mycontris": "{{GENDER:|तपाईंका}} योगदानहरूको सूची",
+       "tooltip-pt-login": "तपाà¤\88à¤\82लाà¤\88 à¤ªà¥\8dरवà¥\87स गर्न सुझाव दिइन्छ ; तर यो जरुरी भने छैन",
        "tooltip-pt-logout": "निर्गमन (लग आउट) गर्नुहोस्",
-       "tooltip-pt-createaccount": "तपाईंलाई खाता बनाउन र लग इन गर्न हामि प्रोत्साहित गर्छौ; तथापि, यो अनिवार्य भने छैन।",
+       "tooltip-pt-createaccount": "तपाईंलाई खाता बनाउन र लग इन गर्न हामि प्रोत्साहित गर्छौ; तथापि, यो अनिवार्य भने छैन ।",
        "tooltip-ca-talk": "सामग्री पृष्ठबारेमा छलफल",
        "tooltip-ca-edit": "यो पृष्ठ सम्पादन गर्ने",
        "tooltip-ca-addsection": "नयाँ खण्ड सुरु गर्नुहोस्",
        "tooltip-ca-viewsource": "यो पृष्ठ सुरक्षित गरिएको छ। यसको श्रोत हेर्न सक्नुहुन्छ।",
-       "tooltip-ca-history": "यस à¤ªà¥\83षà¥\8dठà¤\95à¥\8b à¤ªà¤¹à¤¿à¤²à¥\87à¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\81",
+       "tooltip-ca-history": "यस à¤ªà¥\83षà¥\8dठा à¤ªà¤¹à¤¿à¤²à¥\87à¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\82",
        "tooltip-ca-protect": "यो पृष्ठलाई संरक्षित गर्नुहोस्",
        "tooltip-ca-unprotect": "यस पृष्ठको सुरक्षा परिवर्तन गर्ने",
        "tooltip-ca-delete": "यो पृष्ठ मेटाउनुहोस्",
        "tooltip-ca-undelete": "मेटिएको भए पनि यो पृष्ठको सम्पादनहरू पुन:प्राप्त गर्नुहोस्",
        "tooltip-ca-move": "यो पृष्ठलाई सार्नुहोस्",
-       "tooltip-ca-watch": "यà¥\8b à¤ªà¥\83षà¥\8dठलाà¤\88 à¤¤à¤ªà¤¾à¤\88à¤\81को अवलोकनसूचीमा थप्नुहोस्",
+       "tooltip-ca-watch": "यà¥\8b à¤ªà¥\83षà¥\8dठलाà¤\88 à¤¤à¤ªà¤¾à¤\88à¤\82को अवलोकनसूचीमा थप्नुहोस्",
        "tooltip-ca-unwatch": "यो पृष्ठलाई तपाईँको अवलोकनसूचीबाट हटाउनुहोस्",
        "tooltip-search": "{{SITENAME}} मा खोज्नुहोस्",
        "tooltip-search-go": "यदि यो नामको पृष्ठ रहेको छ भने त्यसमा जाने",
        "tooltip-search-fulltext": "यो पाठको लागि पृष्ठहरू खोज्नुहोस्",
        "tooltip-p-logo": "मुख्य पृष्ठ",
        "tooltip-n-mainpage": "मुख्य पृष्ठमा जाने",
-       "tooltip-n-mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¥\83षà¥\8dठमा à¤\9cानà¥\81हà¥\8bà¥\8dसà¥\8d",
-       "tooltip-n-portal": "à¤\86यà¥\8bà¤\9cनाà¤\95ा à¤¬à¤¾à¤°à¥\87मा, à¤¤à¤ªà¤¾à¤\88à¤\81 के गर्न सक्नुहुन्छ, सामग्री कहाँ भेट्टाउने",
+       "tooltip-n-mainpage-description": "मुख्य पृष्ठमा जानुहोस्",
+       "tooltip-n-portal": "à¤\86यà¥\8bà¤\9cनाà¤\95ा à¤¬à¤¾à¤°à¥\87मा, à¤¤à¤ªà¤¾à¤\88à¤\82 के गर्न सक्नुहुन्छ, सामग्री कहाँ भेट्टाउने",
        "tooltip-n-currentevents": "हालैको घटनाको बारेमा पृष्ठभूमि जानकारी पत्ता लगाउनुहोस्",
-       "tooltip-n-recentchanges": "विà¤\95िमा à¤\97रिà¤\8fà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\81को सूची",
-       "tooltip-n-randompage": "à¤\9cà¥\81न à¤\95à¥\81नà¥\88 पृष्ठ खोल्ने",
+       "tooltip-n-recentchanges": "विà¤\95िमा à¤\97रिà¤\8fà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82को सूची",
+       "tooltip-n-randompage": "à¤\95à¥\81नà¥\88 à¤\8fà¤\95 पृष्ठ खोल्ने",
        "tooltip-n-help": "पत्तालगाउनु पर्ने स्थान",
        "tooltip-t-whatlinkshere": "यो सँग जोडिएका सबै विकि पृष्ठहरूको सूची",
        "tooltip-t-recentchangeslinked": "यस पृष्ठमा जोडिएका पृष्ठहरूमा हालैको परिवर्तन",
        "tooltip-ca-nstab-help": "सहायता पृष्ठ हेर्नुहोस्",
        "tooltip-ca-nstab-category": "श्रेणी पृष्ठ हेर्ने",
        "tooltip-minoredit": "यसलाई सामान्य सम्पादनको रुपमा चिनो लगाउने",
-       "tooltip-save": "तपाà¤\88à¤\81लà¥\87 à¤\97रà¥\87à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82 à¤¸à¤\82ग्रह गर्नुहोस्",
-       "tooltip-preview": "तपाà¤\88à¤\81à¤\95à¥\8b à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤ªà¥\82रà¥\8dवरà¥\82प , à¤\95à¥\83पया à¤¸à¤\82ग्रह गर्नु अघि यो प्रयोग गर्नुहोला !",
-       "tooltip-diff": "तपाà¤\88à¤\81ले पाठमा के के परिवर्तन गर्नुभयो भनेर देखाउने",
+       "tooltip-save": "तपाà¤\88à¤\82à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82 à¤¸à¤\99à¥\8dग्रह गर्नुहोस्",
+       "tooltip-preview": "तपाà¤\88à¤\82à¤\95à¥\8b à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤ªà¥\82रà¥\8dवरà¥\82प , à¤\95à¥\83पया à¤¸à¤\99à¥\8dग्रह गर्नु अघि यो प्रयोग गर्नुहोला !",
+       "tooltip-diff": "तपाà¤\88à¤\82ले पाठमा के के परिवर्तन गर्नुभयो भनेर देखाउने",
        "tooltip-compareselectedversions": "यस पृष्ठको छानिएका दुई पुनरावलोकन बीच फरक हेर्नुहोस्",
        "tooltip-watch": "यो पृष्ठलाई तपाईँको अवलोकनसूचीमा थप्नुहोस्",
        "tooltip-watchlistedit-normal-submit": "शीर्षकहरू हटाउने",
        "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|पृष्ठ|पृष्ठहरू}}",
        "file-info": "फाइल आकार: $1, MIME प्रकार: $2",
-       "file-info-size": "$1 Ã\97 $2 à¤ªà¤¿à¤\95à¥\8dसà¥\87लहरà¥\81, फाइल आकार: $3, MIME प्रकार: $4",
+       "file-info-size": "$1 Ã\97 $2 à¤ªà¤¿à¤\95à¥\8dसà¥\87लहरà¥\82, फाइल आकार: $3, MIME प्रकार: $4",
        "file-info-size-pages": "$1 × $2 पिक्सेलहरू, फाइल आकार: $3, MIME प्रकार: $4, $5 {{PLURAL:$5|पृष्ठ|पृष्ठहरू}}",
        "file-nohires": "उच्च रिजोल्युशन अनुपलब्ध",
        "svg-long-desc": "SVG फाइल,साधारण $1 × $2 पिक्सेलहरु, फाइल आकार: $3",
        "svg-long-error": "अमान्य एसभिजी फाइल: $1",
        "show-big-image": "मूल फाइल",
        "show-big-image-preview": "यस पूर्व रुपको आकार: $1।",
-       "show-big-image-other": "à¤\85रà¥\81 {{PLURAL:$2|resolution|रिà¤\9cà¥\8bलà¥\8dयà¥\81शनहरà¥\81}}: $1।",
+       "show-big-image-other": "à¤\85रà¥\81 {{PLURAL:$2|resolution|रिà¤\9cà¥\8bलà¥\8dयà¥\81शनहरà¥\82}}: $1।",
        "show-big-image-size": "$1 × $2 पिक्सल",
        "file-info-gif-looped": "चकृय गरिएको",
        "file-info-gif-frames": "$1 {{PLURAL:$1|फ्रेम|फ्रेमहरु}}",
        "yesterday-at": "हिजो $1मा",
        "bad_image_list": "(* बाट शुरु हुने पंक्ति)को  विषय सूची मात्र मान्य छ।  पंक्तिको पहिलो लिङ्क नराम्रो फाइलसित लिङ्क हुनैपर्छ । एउटै पंक्तिमा कुनै पछिबाट हुने लिंकलाई अपवाद मानिनेछ अर्थात् जुन पृष्ठमा फाइल इन-लाइन हुनसक्छ।",
        "metadata": "मेटाडेटा",
-       "metadata-help": "यस à¤«à¤¾à¤\87लमा à¤\85तिरिà¤\95à¥\8dत à¤\9cानà¤\95ारà¥\80हरà¥\81 à¤\9bनà¥\8d, à¤¯à¤¸à¤²à¤¾à¤\88 à¤¬à¤¨à¤¾à¤\89न à¤¸à¤®à¥\8dभवतà¤\83 à¤¡à¤¿à¤\9cिà¤\9fल à¤\95à¥\8dयामà¥\87रा à¤\85थवा à¤¸à¥\8dà¤\95à¥\8dयानर à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\81नà¥\81परà¥\8dà¤\9b। à¤¯à¤¦à¤¿ à¤¯à¤¸ à¤«à¤¾à¤\87ललाà¤\88 à¤®à¥\82ल à¤\85वसà¥\8dथाबाà¤\9f à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\8b à¤­à¤¨à¥\87à¤\82  यस फाइलले  सम्पूर्ण विवरण प्रतिबिम्बित गर्न सक्नेछैन ।",
+       "metadata-help": "यस à¤«à¤¾à¤\87लमा à¤\85तिरिà¤\95à¥\8dत à¤\9cानà¤\95ारà¥\80हरà¥\82 à¤\9bनà¥\8d, à¤¯à¤¸à¤²à¤¾à¤\88 à¤¬à¤¨à¤¾à¤\89न à¤¸à¤®à¥\8dभवतà¤\83 à¤¡à¤¿à¤\9cिà¤\9fल à¤\95à¥\8dयामà¥\87रा à¤\85थवा à¤¸à¥\8dà¤\95à¥\8dयानर à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\81नà¥\81परà¥\8dà¤\9b à¥¤ à¤¯à¤¦à¤¿ à¤¯à¤¸ à¤«à¤¾à¤\87ललाà¤\88 à¤®à¥\82ल à¤\85वसà¥\8dथाबाà¤\9f à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\8b à¤­à¤¨à¥\87  यस फाइलले  सम्पूर्ण विवरण प्रतिबिम्बित गर्न सक्नेछैन ।",
        "metadata-expand": "लामो विबरण हेर्ने",
        "metadata-collapse": "लामो विवरण लुकाउने",
-       "metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "metadata-fields": "मेटाडाटा तालिकालाई लघुरूप गरियो भने यस सन्देशमा सूचीबद्ध इएक्सआयएफ मेटाडाटा जानकारिहरू छवि प्रदर्शित हुने बेला सम्मिलित गरिने छ ।\nअन्य डिफल्ट रूपसँग लुकिरहने छ ।\n* make \n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "चौडाइ",
        "exif-imagelength": "उचाई",
        "exif-bitspersample": "घटक प्रति बिट्स",
        "revdelete-content-unhid": "सामग्री देखाइएको",
        "revdelete-summary-unhid": "सम्पादन सारांस देखाइएको",
        "revdelete-uname-unhid": "प्रयोगकर्ता देखाइएको",
-       "revdelete-restricted": "पà¥\8dरबनà¥\8dधà¤\95हरà¥\81माथि सीमितता लागू गरियो",
-       "revdelete-unrestricted": "प्रवन्धककोलागि निषेधहरु हटाइयो ।",
+       "revdelete-restricted": "पà¥\8dरबनà¥\8dधà¤\95हरà¥\82 माथि सीमितता लागू गरियो",
+       "revdelete-unrestricted": "प्रवन्धकको लागि निषेधहरू हटाइयो ।",
        "logentry-block-block": "$1 {{GENDER:$2|प्रतिबन्धित}}{{GENDER:$4|$3}} जसमा समय समाप्तिको अवधि छ $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|खुल्ला गरिएो}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|परिवर्तन गर्यो}} प्रतिबन्ध सेटिङ्ग {{GENDER:$4|$3}} को लागि जसमा समय समाप्तिको अवधि छ $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|द्वारा}} $3 पृष्ठलाई $4 मा सारियो",
        "logentry-move-move-noredirect": "$1 ले $3 मा पुनर्निर्देश नछोडि त्यसलाई $4 मा {{GENDER:$2|सारेको}} हो",
        "logentry-move-move_redir": "$1 ले $4 बाट पुनर्निर्देश हटाएर $3 लाई त्यसमाथि {{GENDER:$2|सारेको}} हो",
-       "logentry-move-move_redir-noredirect": "$1 à¤²à¥\87 $4 à¤¬à¤¾à¤\9f à¤ªà¥\81नारà¥\8dनिरà¥\8dदà¥\87श à¤¹à¤\9fाà¤\8fर $3 à¤®à¤¾ à¤ªà¥\81नरà¥\8dनिरà¥\8dदà¥\87श à¤¨à¥\8dनाà¤\9bà¥\8bडि $3 लाई $4 मा {{GENDER:$2|सारेको}} हो",
+       "logentry-move-move_redir-noredirect": "$1 à¤²à¥\87 $4 à¤¬à¤¾à¤\9f à¤ªà¥\81नारà¥\8dनिरà¥\8dदà¥\87श à¤¹à¤\9fाà¤\8fर $3 à¤®à¤¾ à¤ªà¥\81नरà¥\8dनिरà¥\8dदà¥\87श à¤¨à¤\9bà¥\8bडà¥\80 $3 लाई $4 मा {{GENDER:$2|सारेको}} हो",
        "logentry-patrol-patrol": "$1 ले $3 पृष्ठको $4 अवतरणलाई गस्ती गरिएको {{GENDER:$2|चिन्हित}} गरेको हो",
        "logentry-patrol-patrol-auto": "$1 ले $3 पृष्ठको $4 अवतरणलाई स्वचालित रूपले गस्ती गरिएको {{GENDER:$2|चिन्हित}} गरेको हो",
        "logentry-newusers-newusers": "प्रयोगकर्ता खाता $1 {{GENDER:$2|खोलियो}}",
index 5c69a6d..442c685 100644 (file)
        "history": "Geschiedenis",
        "history_short": "Geschiedenis",
        "updatedmarker": "bewerkt sinds mijn laatste bezoek",
-       "printableversion": "Printervriendelijke versie",
+       "printableversion": "Printvriendelijke versie",
        "permalink": "Permanente koppeling",
        "print": "Afdrukken",
        "view": "Lezen",
        "yourpasswordagain": "Geef uw wachtwoord opnieuw in:",
        "createacct-yourpasswordagain": "Bevestig wachtwoord",
        "createacct-yourpasswordagain-ph": "Geef het wachtwoord opnieuw in",
-       "remembermypassword": "Aanmeldgegevens onthouden (maximaal $1 {{PLURAL:$1|dag|dagen}})",
        "userlogin-remembermypassword": "Aangemeld blijven",
        "userlogin-signwithsecure": "Beveiligde verbinding gebruiken",
        "cannotloginnow-title": "Niet mogelijk om aan te melden",
        "log-action-filter-newusers": "Type accountaanmaak:",
        "log-action-filter-patrol": "Soort markering:",
        "log-action-filter-protect": "Soort beveiliging:",
+       "log-action-filter-rights": "Soort verandering van rechten:",
+       "log-action-filter-upload": "Soort upload:",
        "log-action-filter-all": "Alles",
        "log-action-filter-block-block": "Blokkade",
        "log-action-filter-block-reblock": "Aanpassing van blokkade",
        "log-action-filter-block-unblock": "Opheffing van blokkade",
        "log-action-filter-delete-delete": "Verwijderen van pagina",
        "log-action-filter-delete-restore": "Terugplaatsen van pagina",
+       "log-action-filter-managetags-create": "Aanmaken van label",
+       "log-action-filter-managetags-delete": "Verwijderen van label",
+       "log-action-filter-managetags-activate": "Activeren van label",
+       "log-action-filter-managetags-deactivate": "Deactiveren van label",
+       "log-action-filter-move-move": "Verplaatsing zonder overschrijven van doorverwijzingen",
+       "log-action-filter-move-move_redir": "Verplaatsing met overschrijven van doorverwijzingen",
        "log-action-filter-newusers-create": "Aangemaakt door een anonieme gebruiker",
        "log-action-filter-newusers-create2": "Aangemaakt door een geregistreerde gebruiker",
        "log-action-filter-newusers-autocreate": "Automatische aanmaak",
        "log-action-filter-protect-move_prot": "Beveiliging verplaatst",
        "log-action-filter-rights-rights": "Handmatige aanpassing",
        "log-action-filter-rights-autopromote": "Automatische aanpassing",
+       "log-action-filter-suppress-event": "Verbergen van logboekregel",
+       "log-action-filter-suppress-revision": "Verbergen van versie",
+       "log-action-filter-suppress-delete": "Verbergen van pagina",
        "log-action-filter-upload-upload": "Nieuwe upload",
        "log-action-filter-upload-overwrite": "Herupload",
        "authmanager-authn-autocreate-failed": "Het automatisch aanmaken van een lokaal account is mislukt: $1",
index 10d005d..58b6b3d 100644 (file)
        "yourpasswordagain": "Skriv opp att passordet",
        "createacct-yourpasswordagain": "Stadfest passord",
        "createacct-yourpasswordagain-ph": "Skriv inn passordet på nytt",
-       "remembermypassword": "Hugs innlogginga mi på denne datamaskinen (høgst {{PLURAL:$1|éin dag|$1 dagar}})",
        "userlogin-remembermypassword": "Hald meg innlogga",
        "userlogin-signwithsecure": "Nytt trygg kopling",
        "yourdomainname": "Domenet ditt",
        "minoredit": "Småplukk",
        "watchthis": "Overvak sida",
        "savearticle": "Lagra sida",
+       "savechanges": "Publiser endringane",
        "publishpage": "Publiser sida",
        "publishchanges": "Publiser endringar",
        "preview": "Førehandsvising",
        "withoutinterwiki-legend": "Prefiks",
        "withoutinterwiki-submit": "Vis",
        "fewestrevisions": "Sidene med færrast endringar",
-       "nbytes": "$1 {{PLURAL:$1|byte|byte}}",
+       "nbytes": "$1 {{PLURAL:$1|byte}}",
        "ncategories": "$1 {{PLURAL:$1|kategori|kategoriar}}",
        "ninterwikis": "{{PLURAL:$1|éin interwiki|$1 interwikiar}}",
        "nlinks": "{{PLURAL:$1|Éi lenkje|$1 lenkjer}}",
index c61a5c6..4048ef8 100644 (file)
        "yourpasswordagain": "Confirmar lo senhal :",
        "createacct-yourpasswordagain": "Confirmatz lo senhal",
        "createacct-yourpasswordagain-ph": "Entratz lo senhal tornarmai",
-       "remembermypassword": "Me reconnectar automaticament a las visitas venentas (al maximum $1 {{PLURAL:$1|jorn|jorns}})",
        "userlogin-remembermypassword": "Gardar ma session activa",
        "userlogin-signwithsecure": "Utilizar una connexion securizada",
        "cannotloginnow-title": "Impossible de se connectar ara",
        "minoredit": "Aquò es un cambiament menor",
        "watchthis": "Seguir aquesta pagina",
        "savearticle": "Salvar",
+       "savechanges": "Enregistrar los cambiaments",
        "publishpage": "Publicar la pagina",
        "publishchanges": "Publicar las modificacions",
        "preview": "Previsualizar",
        "right-applychangetags": "Aplicar [[Special:Tags|las balisas]] amb sas pròprias modificacions",
        "grant-generic": "ensemble de dreits « $1 »",
        "grant-blockusers": "Blocar e desblocar d'utilizaires",
-       "grant-patrol": "Marcar de paginas coma patrolhadas",
+       "grant-patrol": "Verificar las modificacions de paginas",
        "newuserlogpage": "Istoric de las creacions de comptes",
        "newuserlogpagetext": "Jornal de las creacions de comptes d'utilizaires.",
        "rightslog": "Istoric de las modificacions d'estatut",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|utilizaire seguent|utilizaires seguents}}]",
-       "rc_categories": "Limit de las categorias (separacion amb « | »)",
+       "rc_categories": "Limitar a las categorias (separadas per « | ») :",
        "rc_categories_any": "Una de las seleccionadas",
        "rc-change-size-new": "$1 {{PLURAL:$1|octet|octets}} aprèp cambiament",
        "newsectionsummary": "/* $1 */ seccion novèla",
        "undeletedrevisions": "{{PLURAL:$1|1 revision restablida|$1 revisions restablidas}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revision|$1 revisions}} e {{PLURAL:$2|1 fichièr restablit|$2 fichièrs restablits}}",
        "undeletedfiles": "$1 {{PLURAL:$1|fichièr restablit|fichièrs restablits}}",
-       "cannotundelete": "Fracàs del restabliment :\n$1",
+       "cannotundelete": "Certanas o totas las restitucions an fracassat :\n$1",
        "undeletedpage": "<strong>La pagina $1 es estada restablida</strong>.\n\nConsultatz l’[[Special:Log/delete|istoric de las supressions]] per veire la lista de las supressions e dels restabliments recents.",
        "undelete-header": "Consultatz l’[[Special:Log/delete|istoric de las supressions]] per veire las paginas recentament suprimidas.",
        "undelete-search-title": "Recercar las paginas suprimidas",
        "tooltip-feed-rss": "Flux RSS per aquesta pagina",
        "tooltip-feed-atom": "Flux Atom per aquesta pagina",
        "tooltip-t-contributions": "Veire la lista de las contribucions d'{{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
-       "tooltip-t-emailuser": "Mandar un corrièr electronic a aqueste utilizaire",
+       "tooltip-t-emailuser": "Mandar un corrièr electronic a {{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
        "tooltip-t-info": "Mai d’informacion sus aquesta pagina",
        "tooltip-t-upload": "Mandar un imatge o fichièr mèdia sul servidor",
        "tooltip-t-specialpages": "Lista de totas las paginas especialas",
        "watchlistedit-raw-done": "Vòstra lista de seguiment es estada mesa a jorn.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Una pagina es estada aponduda|$1 paginas son estadas apondudas}} :",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Una pagina es estada levada|$1 paginas son estadas levadas}} :",
-       "watchlistedit-clear-title": "Lista de seguiment voidada",
+       "watchlistedit-clear-title": "Voidar la lista de seguiment",
        "watchlistedit-clear-legend": "Escafar la lista de seguiment",
        "watchlistedit-clear-explain": "Totes los títols seràn suprimits de vòstra lista de seguiment",
        "watchlistedit-clear-titles": "Títols :",
        "version-libraries-license": "Licéncia",
        "version-libraries-description": "Descripcion",
        "version-libraries-authors": "Autors",
-       "redirect": "Redirigit per fichièr, utilizaire, pagina o ID de revision.",
+       "redirect": "Redirigir per ID de fichièr, utilizaire, pagina, revision o jornal.",
        "redirect-submit": "Validar",
        "redirect-lookup": "Recèrca :",
        "redirect-value": "Valor :",
        "tags-edit-title": "Modificar las balisas",
        "tags-edit-manage-link": "Gerir las balisas",
        "tags-edit-existing-tags": "Balisas existentas :",
-       "tags-edit-existing-tags-none": "\"Pas cap\"",
+       "tags-edit-existing-tags-none": "<em>Pas cap</em>",
        "tags-edit-new-tags": "Balisas novèlas :",
        "tags-edit-add": "Apondre aquestas balisas :",
        "tags-edit-remove": "Suprimir aquestas balisas :",
index 4f80dee..ba5db3b 100644 (file)
        "yourpasswordagain": "ପାସୱାର୍ଡ଼ ଆଉଥରେ:",
        "createacct-yourpasswordagain": "ପାସୱାର୍ଡ଼ ନିଶ୍ଚିତ କରିବେ",
        "createacct-yourpasswordagain-ph": "ଆଉଥରେ ପାସୱାର୍ଡ଼ ଦିଅନ୍ତୁ",
-       "remembermypassword": "ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ",
        "userlogin-remembermypassword": "ମୋତେ ଲଗ-ଇନ କରି ରଖିଥାନ୍ତୁ",
        "userlogin-signwithsecure": "ନିରାପଦ କନେକସନ ବ୍ୟବ‌ହାର କରନ୍ତୁ",
        "cannotloginnow-title": "ଏବେ ଲଗ ଇନ ହୋଇପାରିବ ନାହିଁ",
        "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-emailsentemail": "ଏକ ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲ ପଠାଇଦିଆଯାଇଅଛି ।",
-       "passwordreset-emailsent-capture": "ତଳେ ଦେଖାଯାଉଥିବା ଭଳି, ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲଟିଏ ପଠାଇଦିଆଯାଇଛି ।",
-       "passwordreset-emailerror-capture": "ପାସୱାର୍ଡ଼ ବଦଳାଇବା ସୂଚନା ସହ ଇମେଲଟିଏ ତିଆରି ହୋଇଛି, ଯାହା ତଳେ ଦେଖିପାରିବେ । କିନ୍ତୁ ଏହାକୁ {{GENDER:$2|ସଭ୍ୟ}}ଙ୍କୁ ପଠାଇବାରେ ବିଫଳ ହେଲୁ, କାରଣ: $1",
        "changeemail": "ଇ-ମେଲ ଠିକଣା ବଦଳାଇବେ କିମ୍ବା କାଢିବେ",
        "changeemail-header": "ଖାତା ଇ-ମେଲ ଠିକଣା ବଦଳାଇବେ",
        "changeemail-no-info": "ଏହି ପୃଷ୍ଠାଟିକୁ ସିଧା ଖୋଲିବା ନିମନ୍ତେ ଆପଣଙ୍କୁ ଲଗ ଇନ କରିବାକୁ ପଡ଼ିବ ।",
        "undo-nochange": "ଏହି ସମ୍ପାଦନା ପଛକୁ ଫେରାଇଦିଆଯାଇଥିବା ଭଳି ଲାଗୁଛି ।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ଆଲୋଚନା]]) ଙ୍କ ଦେଇ କରାଯାଇଥିବା $1 ସଙ୍କଳନଟି ପଛକୁ ଫେରାଇନିଆଗଲା",
        "undo-summary-username-hidden": "ଜଣେ ଅଜଣା ସଭ୍ୟଙ୍କ ଦେଇ ହୋଇଥିବା $1 ସଂସ୍କରଣଟି ପଛକୁ ଫେରାନ୍ତୁ",
-       "cantcreateaccounttitle": "ଖାତାଟିଏ ତିଆରି କରାଯାଇପାରିବ ନାହିଁ",
        "cantcreateaccount-text": "[[User:$3|$3]]ଙ୍କ ଦେଇ ('''$1''') IP ଠିକଣାରୁ ଖାତା ଖୋଲିବାକୁ ବାରଣ କରାଯାଇଅଛି ।\n\n$3ଙ୍କ ଦେଇ ଦିଆଯାଇଥିବା କାରଣ ହେଲା ''$2''",
        "cantcreateaccount-range-text": "ଆପଣଙ୍କ IP Address (<strong>$4</strong>) ସମେତ <strong>$1</strong> ସୀମା ଭିତରେ ଥିବା IP Address ରୁ [[User:$3|$3]]ଙ୍କ ଦ୍ୱାରା ନୂଆ ଖାତା ତିଆରିକୁ ଅଟକାଯାଇଛି ।\n\n$3ଙ୍କ ଦ୍ୱାରା ଏହାର କାରଣ ଦିଆଯାଇଛି: <em>$2</em>",
        "viewpagelogs": "ଏହି ପୃଷ୍ଠା ପାଇଁ ଲଗଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ।",
        "thumbnail_image-missing": "ଫାଇଲଟି ନଥିଲା ଭଳି ଲାଗୁଛି : $1",
        "thumbnail_image-failure-limit": "ଏହି ଥମ୍ବନେଲ ରେଣ୍ଡର କରିବା ପାଇଁ ନିକଟରେ ଅନେକ ($1 କିମ୍ବା ଅଧିକ) ବିଫଳ ଚେଷ୍ଟା କରାଯାଇଛି । ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।",
        "import": "ପୃଷ୍ଠା ଆମଦାନି କରିବେ",
-       "importinterwiki": "à¬\9fà­\8dରାନà­\8dସà¬\89à¬\87à¬\95ି à¬\88ମà­\8dପà­\8bରà­\8dà¬\9f",
+       "importinterwiki": "à¬\86à¬\89 à¬\8fà¬\95 à¬\89à¬\87à¬\95ିରà­\81 à¬\86ମଦାନà­\80 à¬\95ରନà­\8dତà­\81",
        "import-interwiki-text": "ଏକ ଉଇକି ଓ ପୃଷ୍ଠା ନାମ ଆମଦାନି କରିବା ନିମନ୍ତେ ଦିଅନ୍ତୁ ।\nସଂସ୍କରଣ ତାରିଖ ଓ ସମ୍ପାଦକଙ୍କ ନାମ ସାଇତା ହୋଇ ରହିବ ।\nଅନ୍ତଉଇକି ଆମଦାନି କାମସବୁ [[Special:Log/import|ଆମଦାନି ଇତିହାସ]]ରେ ସାଇଟ ହୋଇ ରହିଛି ।",
        "import-interwiki-sourcewiki": "ମୂଳ ଉଇକି:",
        "import-interwiki-sourcepage": "ମୂଳ ପୃଷ୍ଠା:",
index 02be7a2..b2fcdde 100644 (file)
        "newwindow": "(otwiera się w nowym oknie)",
        "cancel": "Anuluj",
        "moredotdotdot": "Więcej...",
-       "morenotlisted": "Nie jest to kompletna lista.",
+       "morenotlisted": "Ta lista może być niekompletna.",
        "mypage": "Strona",
        "mytalk": "Dyskusja",
        "anontalk": "Dyskusja",
        "yourpasswordagain": "Powtórz hasło:",
        "createacct-yourpasswordagain": "Potwierdź hasło",
        "createacct-yourpasswordagain-ph": "Wprowadź hasło jeszcze raz",
-       "remembermypassword": "Zapamiętaj moje logowanie na tym komputerze (maksymalnie przez $1 {{PLURAL:$1|dzień|dni}})",
        "userlogin-remembermypassword": "Nie wylogowuj mnie",
        "userlogin-signwithsecure": "Użyj bezpiecznego połączenia",
+       "cannotlogin-title": "Nie można się zalogować",
+       "cannotlogin-text": "Logowanie nie jest możliwe.",
        "cannotloginnow-title": "W tej chwili nie można się teraz zalogować",
        "cannotloginnow-text": "Podczas korzystania z $1 nie można się zalogować.",
+       "cannotcreateaccount-title": "Nie można utworzyć kont",
+       "cannotcreateaccount-text": "Bezpośrednie tworzenie konta nie jest włączone na tej wiki.",
        "yourdomainname": "Twoja domena:",
        "password-change-forbidden": "Nie można zmieniać haseł na tej wiki.",
        "externaldberror": "Wystąpił błąd autentyfikacyjnej bazy danych lub nie posiadasz uprawnień koniecznych do aktualizacji zewnętrznego konta.",
        "invalid-content-data": "Zawartość strony zawiera nieprawidłowe dane",
        "content-not-allowed-here": "Zawartość tego typu ($1) nie jest dozwolona na stronie [[$2]]",
        "editwarning-warning": "Opuszczenie tej strony może spowodować utratę wprowadzonych przez Ciebie zmian.\nJeśli jesteś zalogowany, możesz wyłączyć wyświetlanie tego ostrzeżenia w zakładce „{{int:prefs-editing}}” w swoich preferencjach.",
+       "editpage-invalidcontentmodel-title": "Model zawartości nie jest obsługiwany",
+       "editpage-invalidcontentmodel-text": "Model zawartości „$1” nie jest obsługiwany.",
        "editpage-notsupportedcontentformat-title": "Nieobsługiwany format zawartości",
        "editpage-notsupportedcontentformat-text": "Format zawartości $1 nie jest obsługiwany modelem treści $2.",
        "content-model-wikitext": "wikitekst",
        "file-thumbnail-no": "Nazwa pliku zaczyna się od <strong>$1</strong>.\nWydaje się, że jest to pomniejszona grafika ''(miniaturka)''.\nJeśli posiadasz tę grafikę w pełnym rozmiarze – prześlij ją. Jeśli chcesz wysłać tę – zmień nazwę przesyłanego obecnie pliku.",
        "fileexists-forbidden": "Plik o tej nazwie już istnieje i nie może zostać nadpisany.\nJeśli chcesz przesłać plik cofnij się i prześlij go pod inną nazwą. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Plik o tej nazwie już istnieje we współdzielonym repozytorium plików.\nCofnij się i załaduj plik pod inną nazwą. [[File:$1|thumb|center|$1]]",
+       "fileexists-duplicate-version": "{{PLURAL:$2|Przesłany plik jest dokładną kopią starszej wersji pliku|Przesłane pliki są dokładnymi kopiami starszych wersji plików}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ten plik jest kopią {{PLURAL:$1|pliku|następujących plików}}:",
        "file-deleted-duplicate": "Identyczny do tego plik ([[:$1]]) został wcześniej usunięty.\nSprawdź historię usunięć tamtego pliku zanim prześlesz go ponownie.",
        "file-deleted-duplicate-notitle": "Plik jest identyczny z plikiem, który został wcześniej usunięty, a jego nazwa została ukryta. Należy poprosić kogoś z możliwością przeglądania ukrytych danych, aby przeanalizował sytuację przed przystąpieniem do jego ponownego przesłania.",
        "undeletedrevisions": "odtworzono {{PLURAL:$1|1 wersję|$1 wersje|$1 wersji}}",
        "undeletedrevisions-files": "odtworzono $1 {{PLURAL:$1|wersję|wersje|wersji}} i $2 {{PLURAL:$2|plik|pliki|plików}}",
        "undeletedfiles": "odtworzył $1 {{PLURAL:$1|plik|pliki|plików}}",
-       "cannotundelete": "Odtworzenie nie powiodło się:\n$1",
+       "cannotundelete": "Niektóre lub wszystkie odtworzenia nie powiodły się:\n$1",
        "undeletedpage": "'''Odtworzono stronę $1.'''\n\nZobacz [[Special:Log/delete|rejestr usunięć]], jeśli chcesz przejrzeć ostatnie operacje usuwania i odtwarzania stron.",
        "undelete-header": "Zobacz [[Special:Log/delete|rejestr usunięć]], aby sprawdzić ostatnio usunięte strony.",
        "undelete-search-title": "Przeszukiwanie usuniętych stron",
        "pageinfo-article-id": "Identyfikator strony",
        "pageinfo-language": "Język zawartości strony",
        "pageinfo-content-model": "Model zawartości",
+       "pageinfo-content-model-change": "zmień",
        "pageinfo-robot-policy": "Indeksowanie przez roboty",
        "pageinfo-robot-index": "Dozwolone",
        "pageinfo-robot-noindex": "Niedozwolone",
        "tags-actions-header": "Działania",
        "tags-active-yes": "Tak",
        "tags-active-no": "Nie",
-       "tags-source-extension": "Określony przez rozszerzenie",
+       "tags-source-extension": "Określony przez oprogramowanie",
        "tags-source-manual": "Ręcznie wprowadzany przez użytkowników i boty",
        "tags-source-none": "Nieużywany",
        "tags-edit": "edytuj",
        "authpage-cannot-create-continue": "Nie można kontynuować tworzenia konta. Twoja sesja najprawdopodobniej wygasła.",
        "cannotauth-not-allowed-title": "Brak dostępu",
        "cannotauth-not-allowed": "Nie masz uprawnień, aby skorzystać z tej strony",
-       "changecredentials-submit": "Zmień poświadczenia",
-       "removecredentials-submit": "Usuń poświadczenia",
+       "changecredentials": "Zmiana poświadczeń",
+       "changecredentials-submit": "Zmień poświadczenie",
+       "removecredentials": "Usuwanie poświadczeń",
+       "removecredentials-submit": "Usuń poświadczenie",
+       "credentialsform-provider": "Rodzaj poświadczeń:",
        "credentialsform-account": "Nazwa konta:",
        "linkaccounts": "Połącz konta",
        "linkaccounts-success-text": "Konto zostało połączone.",
index f40ed82..c7c6ac6 100644 (file)
        "yourpasswordagain": "پټنوم بيا وليکه",
        "createacct-yourpasswordagain": "پټنوم مو تاييد کړۍ",
        "createacct-yourpasswordagain-ph": "پټنوم مو بيا وټاپئ",
-       "remembermypassword": "زما پټنوم په دې کمپيوټر (تر $1 {{PLURAL:$1|ورځې|ورځو}}) په ياد وساته!",
        "userlogin-remembermypassword": "غونډال کې مې ننوتلی وساته",
        "userlogin-signwithsecure": "خوندي اړيکتيا کارول",
+       "cannotcreateaccount-title": "گڼونونه نه شي جوړېدای",
        "yourdomainname": "ستاسې شپول:",
        "password-change-forbidden": "تاسې په دې ويکي باندې خپل پټنوم نه شی بدلولی.",
        "login": "ننوتل",
        "passwordreset-emailtitle": "د {{SITENAME}} د گڼون څرگندنې",
        "passwordreset-emailelement": "کارن-نوم: \n$1\n\nلنډمهاله پټنوم: \n$2",
        "passwordreset-emailsentemail": "د پټنوم بيا پرځای کېدنې لپاره برېښليک درولېږل شو.",
-       "passwordreset-emailsent-capture": "د پټنوم بياپرځای کېدنې لپار مو يو برېښليک درولېږه، برېښليک په لاندې توگه ښودل شوی.",
        "passwordreset-invalideamil": "ناسمه برېښليک پته",
        "changeemail": "برېښليک پته بدلول يا ليرې کول",
        "changeemail-header": "د گڼون برېښليک پته بدلول",
        "post-expand-template-argument-warning": "'''گواښنه:''' دا مخ لږ تر لږه د يوې کينډۍ عاملين لري چې بې حده لوی دی.\nدا عاملين ړنگ شول.",
        "post-expand-template-argument-category": "هغه مخونه چې د کينډۍ ړنگ شوي عاملين لري.",
        "undo-norev": "دا سمون ناکړل کېدای نه شي دا ځکه چې دا سمون نشته او يا هم ړنگ شوی.",
-       "cantcreateaccounttitle": "گڼون نه شي جوړېدای",
        "viewpagelogs": "د دې مخ يادښتونه کتل",
        "nohistory": "ددې مخ د سمون کوم پېښليک نه شته.",
        "currentrev": "اوسنۍ بڼه",
        "pageinfo-article-id": "د مخ پېژند",
        "pageinfo-language": "د مخ د مېنځپانگې ژبه",
        "pageinfo-content-model": "د مخ مېنځپانگې جوړښت",
+       "pageinfo-content-model-change": "بدلول",
        "pageinfo-robot-policy": "ليکلړ اوډنه د روباټونو لخوا",
        "pageinfo-robot-index": "پرېښل",
        "pageinfo-robot-noindex": "ناپرېښل",
index a4514e7..5dac084 100644 (file)
@@ -98,7 +98,8 @@
                        "Luan",
                        "Anderson Costa",
                        "LucyDiniz",
-                       "Tusca"
+                       "Tusca",
+                       "Cristofer Alves"
                ]
        },
        "tog-underline": "Sublinhar links:",
        "yourpasswordagain": "Redigite sua senha",
        "createacct-yourpasswordagain": "Confirmar senha",
        "createacct-yourpasswordagain-ph": "Digite a senha novamente",
-       "remembermypassword": "Lembrar meu login neste navegador (por no máximo $1 {{PLURAL:$1|dia|dias}})",
        "userlogin-remembermypassword": "Mantenha-me conectado",
        "userlogin-signwithsecure": "Use a conexão segura",
+       "cannotlogin-title": "Não é possível entrar com sua conta",
+       "cannotlogin-text": "Não é possível conectar-se.",
        "cannotloginnow-title": "Não é possível iniciar a sessão agora",
        "cannotloginnow-text": "Não é possível autenticar usando $1.",
+       "cannotcreateaccount-title": "Não é possível criar uma conta",
+       "cannotcreateaccount-text": "A criação direta de contas não está habilitada nessa wiki.",
        "yourdomainname": "Seu domínio:",
        "password-change-forbidden": "Você não pode alterar senhas nessa wiki.",
        "externaldberror": "Ocorreu ou um erro no banco de dados durante a autenticação ou não lhe é permitido atualizar a sua conta externa.",
        "userlogin-resetpassword-link": "Esqueceu sua senha?",
        "userlogin-helplink2": "Ajuda com o login",
        "userlogin-loggedin": "Você já está conectado como {{GENDER:$1|$1}}.\nUse o formulário abaixo para iniciar sessão como outro usuário.",
+       "userlogin-reauth": "Deve iniciar novamente a sessão para verificar se é {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Crie uma outra conta",
        "createacct-emailrequired": "Endereço de e-mail",
        "createacct-emailoptional": "Endereço de e-mail (opcional)",
        "createacct-email-ph": "Confirme seu endereço de e-mail",
        "createacct-another-email-ph": "Forneça o endereço de e-mail",
        "createaccountmail": "Usar uma senha aleatória e temporária que será enviada ao endereço de e-mail especificado a seguir",
+       "createaccountmail-help": "Pode ser utilizado para criar uma conta para outra pessoa sem saber a senha.",
        "createacct-realname": "Nome real (opcional)",
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Por que você está criando outra conta",
+       "createacct-reason-help": "Mensagem mostrada no registro de criação de conta",
        "createacct-submit": "Crie sua conta",
        "createacct-another-submit": "Criar conta",
+       "createacct-continue-submit": "Continuar criação de conta",
+       "createacct-another-continue-submit": "Continuar criação de conta",
        "createacct-benefit-heading": "{{SITENAME}} é feita por pessoas como você.",
        "createacct-benefit-body1": "{{PLURAL:$1|edição|edições}}",
        "createacct-benefit-body2": "{{PLURAL:$1|página|páginas}}",
        "nocookiesnew": "A conta do usuário foi criada, mas você não foi autenticado.\n{{SITENAME}} utiliza ''cookies'' para autenticar os usuários.\nVocê tem os ''cookies'' desativados no seu navegador.\nPor favor ative-os, depois autentique-se com o seu novo nome de usuário e a sua senha.",
        "nocookieslogin": "Você tem os <i>cookies</i> desativados no seu navegador, e a {{SITENAME}} utiliza <i>cookies</i> para ligar os usuários às suas contas. Por favor os ative e tente novamente.",
        "nocookiesfornew": "A conta de usuário não foi criada porque não foi possível confirmar a sua origem.\nCertifique-se de que tem os cookies ativados, recarregue esta página e tente novamente.",
+       "createacct-loginerror": "A conta foi criada com êxito, mas não pôde ser autenticado automaticamente. Por favor, faça o [[Special:UserLogin|início de sessão manualmente]].",
        "noname": "Você não colocou um nome de usuário válido.",
        "loginsuccesstitle": "Autenticado",
        "loginsuccess": "'''Agora você está {{GENDER:autenticado|autenticada}} ao wiki {{SITENAME}} como \"$1\"'''.",
        "botpasswords-label-delete": "Apagar",
        "botpasswords-label-resetpassword": "Redefinir a sua senha",
        "botpasswords-label-grants": "Permissões aplicáveis",
+       "botpasswords-help-grants": "Cada permissão da acesso à lista permissões de usuários que um usuário já tenha. Veja o [[Special:ListGrants|Lista de Permissões]] para mais informações.",
        "botpasswords-label-restrictions": "Restrições de uso:",
        "botpasswords-label-grants-column": "Concedido",
        "botpasswords-bad-appid": "O nome de robô \"$1\" não é válido.",
        "botpasswords-updated-body": "A senha de robô para o robô de nome \"$1\" do usuário \"$2\" foi atualizada.",
        "botpasswords-deleted-title": "Senha de bot apagada",
        "botpasswords-deleted-body": "A senha de robô para o robô de nome \"$1\" do usuário \"$2\" foi apagada.",
-       "botpasswords-newpassword": "A nova senha para se autenticar com <strong>$1</strong> é <strong>$2</strong>. <em>Por favor, guarde isto para referência futura.",
+       "botpasswords-newpassword": "A nova senha para se autenticar com <strong>$1</strong> é <strong>$2</strong>. <em>Por favor, guarde isto para referência futura.</em> <br> (para bots antigos que requisitam que o nome da conta seja o mesmo que o eventual nome de usuário, Você também pode usar <strong>$3</strong>como nome de usuário e <strong>$4</strong> como senha.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider não está disponível.",
        "botpasswords-restriction-failed": "Restrições de senha de robô evitam esta autenticação.",
        "botpasswords-invalid-name": "O nome de usuário especificado não contém o separador de senha de robô (\"$1\").",
        "passwordreset-emailsentemail": "Se este é um endereço de e-mail registrado para a sua conta, em seguida, um e-mail de redefinição de senha será enviada.",
        "passwordreset-emailsentusername": "Se houver um endereço de email associado a esta conta, ser-lhe-á enviada uma mensagem para redefinir a sua senha.",
        "passwordreset-emailsent-capture2": "A redefinição da senha {{PLURAL:$1|do e-mail|dos e-mails}} foi enviada. {{PLURAL:$1|O nome de usuário e senha|A lista de nomes de usuário e senhas}} encontram-se a seguir.",
+       "passwordreset-emailerror-capture2": "O envio do e-mail {{GENDER:$2|usuário}} falhou: $1 Os {{PLURAL:$3|nome de usuário e senha|lista de nomes de usuários e senhas}} são mostrados abaixo.",
+       "passwordreset-nocaller": "Um interlocutor deve ser fornecido",
+       "passwordreset-nosuchcaller": "O interlocutor não existe: $1",
+       "passwordreset-ignored": "A redefinição da senha não foi realizada. Talvez o provedor não tenha sido configurado?",
+       "passwordreset-invalideamil": "Endereço de e-mail inválido",
+       "passwordreset-nodata": "Não foram fornecidos nome de usuário nem endereço de e-mail",
        "changeemail": "Alterar ou remover endereço de email",
        "changeemail-header": "Preencha este formulário para alterar seu endereço de e-mail. Se você gostaria de remover a associação de qualquer endereço de e-mail da sua conta, deixe o novo endereço de email em branco quando enviar o formulário.",
        "changeemail-no-info": "Para acessar diretamente esta página você tem de estar autenticado.",
        "minoredit": "Marcar como edição menor",
        "watchthis": "Vigiar esta página",
        "savearticle": "Salvar página",
+       "savechanges": "Salvar alterações",
        "publishpage": "Publicar página",
        "publishchanges": "Publicar alterações",
        "preview": "Pré-visualização",
        "accmailtext": "Uma senha gerada aleatoriamente para [[User talk:$1|$1]] foi enviada para $2.\n\nEla pode ser alterada na página ''[[Special:ChangePassword|de troca de senha]]'', após o início de sessão.",
        "newarticle": "(Nova)",
        "newarticletext": "Você seguiu um link para uma página que ainda não existe.\nPara criá-la, comece escrevendo na caixa abaixo (veja [$1 a página de ajuda] para mais informações).\nSe você chegou aqui por engano, clique no botão '''voltar''' do seu navegador.",
-       "anontalkpagetext": "---- ''Esta é a página de discussão para um usuário anônimo que ainda não criou uma conta ou que não a usa, de forma que temos de utilizar o endereço de IP para identificá-lo(a). Tal endereço de IP pode ser compartilhado por vários usuários. Se você é um usuário anônimo e acha que comentários irrelevantes foram direcionados a você, por gentileza, [[Special:CreateAccount|crie uma conta]] ou [[Special:UserLogin|autentique-se]], a fim de evitar futuras confusões com outros usuários anônimos.''",
+       "anontalkpagetext": "---\n<em> Esta é a pagina de discussão para usuários anônimos que ainda não ciaram uma conta, ou para aqueles que a usa. </em>\nNós entretanto temos que usar o endereço numérico de IP para identifica-lo/a.\nEste endereço de IP pode ser compartilhado por vários usuários.\nse você é um usuário anônimo e sente que aquele comentário irrelevante foi direcionado à você, por favor [[Special:CreateAccount|Criar Conta]] ou [[Special:UserLogin|Logar]] para evitar futuras confusões com outros usuários anônimos.",
        "noarticletext": "Não há conteúdo nesta página no momento.\nVocê pode [[Special:Search/{{PAGENAME}}|pesquisar pelo título desta página]] em outras páginas, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar por registros relacionados],\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} criar esta página]</span>.",
        "noarticletext-nopermission": "No momento, não há conteúdo nesta página\nVocê pode [[Special:Search/{{PAGENAME}}|pesquisar pelo título desta página]] em outras páginas,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar por registros relacionados] </span>. Note que, no entanto, você não tem permissão para criar esta página.",
        "missing-revision": "A revisão #$1 da página denominada \"{{FULLPAGENAME}}\" não existe.\n\nIsto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].",
        "userpage-userdoesnotexist": "A conta \"<nowiki>$1</nowiki>\" não se encontra registrada.\nVerifique se deseja mesmo criar/editar esta página.",
        "userpage-userdoesnotexist-view": "A conta de usuário \"$1\" não está registrada.",
        "blocked-notice-logextract": "Este usuário está atualmente bloqueado.\nO registro de bloqueio mais recente é fornecido abaixo, para referência:",
-       "clearyourcache": "Nota:''' Depois de salvar, você terá de limpar o ''cache'' do seu navegador para ver as alterações.\n* '''Firefox / Safari:''' pressione ''Shift'' enquanto clica em ''Recarregar'', ou pressione ''Ctrl-F5'' ou ''Ctrl-R'' (''Command-R'' para Mac);\n* '''Google Chrome:''' pressione ''Ctrl-Shift-R'' (''Command-Shift-R'' em um Mac)\n* '''Internet Explorer:''' pressione ''Ctrl'' enquanto clica em ''Recarregar'' ou pressione ''Ctrl-F5'';\n* '''Opera:''' limpe o ''cache'' em ''Ferramentas → Preferências'' (''Tools → Preferences'')",
+       "clearyourcache": "<strong>Nota:</strong> Após salvar, você pode ter que limpar o \"cache\" do seu navegador para ver as alterações.\n*<strong>Firefox / Safari:</strong> Pressione <em>Shift</em> enquanto clica <em>Recarregar</em>, ou pressione <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> no Mac)\n*<strong>Google Chorme:</strong> Pressione <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> no Mac)\n* <strong>Internet Explorer:</strong> Pressione<em>Ctrl</em> enquanto clica <em>Recarregar</em>, ou Pressione <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Vá para <em>Menu → Configurações</em> (<em>Opera → Preferencias</em> no Mac) e depois para <em>Privacidade e Segurança → Limpar dados de navegação → Imagens e arquivos em cache</em>.",
        "usercssyoucanpreview": "'''Dica:''' Utilize o botão \"{{int:showpreview}}\" para testar seu novo CSS antes de salvar.",
        "userjsyoucanpreview": "'''Dica:''' Utilize o botão \"{{int:showpreview}}\" para testar seu novo JavaScript antes de salvar.",
        "usercsspreview": "'''Lembre-se de que você está apenas previsualizando o seu CSS particular.'''\n'''Ele ainda não foi salvo!'''",
        "invalid-content-data": "Dados de conteúdo inválidos",
        "content-not-allowed-here": "Conteúdo do tipo \"$1\" não é permitido na página [[$2]]",
        "editwarning-warning": "Abandonar esta página pode fazer com que você perca todas as alterações que fez.\nSe você estiver autenticado, você pode desabilitar este aviso na seção {{int:prefs-editing}}\"  de suas preferências.",
+       "editpage-invalidcontentmodel-title": "Modelo do conteúdo não suportado",
+       "editpage-invalidcontentmodel-text": "O modelo do conteúdo \"$1\" não é suportado.",
        "editpage-notsupportedcontentformat-title": "Formato do conteúdo não suportado",
        "editpage-notsupportedcontentformat-text": "O formato de conteúdo $1 não é suportando pelo modelo de conteúdo $2.",
        "content-model-wikitext": "wikitexto",
        "content-json-empty-object": "Objeto vazio",
        "content-json-empty-array": "Array vazia",
        "deprecated-self-close-category": "Páginas com etiquetas HTML de autofechamento não válidas",
+       "deprecated-self-close-category-desc": "A página contém tags HTML auto-fechadas inválidas, como <code>&lt;b/></code> ou <code>&lt;span/></code>. O comportamento destas mudará em breve para coincidam com as especificações do HTML5, pelo que seu uso no wikitext está obsoleto.",
        "duplicate-args-warning": "<strong> Aviso: </strong> [[:$1]] está chamando [[:$2]] com mais de um valor para o parâmetro \"$3\". Será utilizado apenas o último valor fornecido.",
        "duplicate-args-category": "Páginas com argumentos de predefinições duplicados",
        "duplicate-args-category-desc": "A pagina contem modelos que usam argumentos duplicados, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ou <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "grant-group-high-volume": "Realizar grande volume de atividades",
        "grant-group-customization": "Personalização e preferências",
        "grant-group-administration": "Realizar ações administrativas",
+       "grant-group-private-information": "acessar os dados privados sobre você",
        "grant-group-other": "Atividade diversa",
        "grant-blockusers": "Bloquear e desbloquear usuários",
        "grant-createaccount": "Criar contas",
        "grant-highvolume": "Edição de grandes volumes",
        "grant-oversight": "Ocultar usuários e revisões suprimidas",
        "grant-patrol": "Patrulhar as alterações nas páginas",
+       "grant-privateinfo": "acessar informações privadas",
        "grant-protect": "Proteger e desproteger páginas",
        "grant-rollback": "Reverter alterações nas páginas",
        "grant-sendemail": "Enviar e-mail a outros usuários",
        "rightslogtext": "Este é um registro de mudanças nos privilégios de usuários.",
        "action-read": "ler esta página",
        "action-edit": "editar esta página",
-       "action-createpage": "criar páginas",
-       "action-createtalk": "criar páginas de discussão",
+       "action-createpage": "criar esta páginas",
+       "action-createtalk": "criar esta páginas de discussão",
        "action-createaccount": "criar esta conta de usuário",
        "action-autocreateaccount": "Criar uma conta de usuário externa automaticamente",
        "action-history": "Ver o histórico desta página",
        "action-applychangetags": "aplicar etiquetas juntamente com suas alterações",
        "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",
        "nchanges": "$1 {{PLURAL:$1|alteração|alterações}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
        "enhancedrc-history": "histórico",
        "file-thumbnail-no": "O nome do arquivo começa com <strong>$1</strong>.\nIsso faz parecer se tratar de uma imagem de tamanho reduzido (''miniatura'', ou ''thumbnail'').\nSe você tem esta imagem em sua resolução completa, envie-a no lugar desta. Caso contrário, altere o nome de arquivo.",
        "fileexists-forbidden": "Já existe um arquivo com este nome e ele não pode ser sobrescrito.\nSe ainda pretende enviar seu arquivo, volte e use um novo nome.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Já existe um arquivo com este nome no repositório de arquivos compartilhados.\nSe você ainda quer enviar seu arquivo, volte e use um novo nome.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "O arquivo carregado é uma duplicata exata da versão atual de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Este arquivo é uma duplicata do seguinte {{PLURAL:$1|arquivo|arquivos}}:",
        "file-deleted-duplicate": "Um arquivo idêntico a este ([[:$1]]) foi eliminado anteriormente.\nVerifique o histórico de eliminação de tal arquivo antes de tentar re-enviar.",
        "file-deleted-duplicate-notitle": "Um arquivo idêntico a este foi anteriormente excluído, e o título foi suprimido. Você deve comunicar com alguém capaz de visualizar dados suprimidos, para verificar a situação antes de enviá-lo novamente.",
        "uploaded-event-handler-on-svg": "Não é permitido configurar atributos que manipulem eventos  <code>$1=\"$2\"</code> em arquivos SVG.",
        "uploaded-href-attribute-svg": "os atributos href nos ficheiros SVG só están autorizados a ligar a direccións http:// ou https://, atopado <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-href-unsafe-target-svg": "Encontrado href para dados não seguros: alvo URI <code>&lt;$1 $2=\"$3\"&gt;</code> no arquivo SVG carregado.",
+       "uploaded-animate-svg": "Encontrado a tag \"animate\" que pode estar mudando \"href\", usando o atributo \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> no arquivo SVG carregado.",
        "uploadscriptednamespace": "Este aruivo SVG contém um espaço nominal probido \"$1\"",
        "uploadinvalidxml": "O XML no arquivo enviado não pôde ser analisado.",
        "uploadvirus": "O arquivo contém vírus!\nDetalhes: $1",
        "trackingcategories-msg": "Categoria de monitoramento",
        "trackingcategories-name": "Nome da mensagem",
        "trackingcategories-desc": "Critérios de inclusão de categoria",
+       "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+       "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
        "noindex-category-desc": "A página não é indexada por robôs, porque possui a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está em um namespace onde a flag é permitida.",
        "index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
        "post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
index 2989d3b..16d9f85 100644 (file)
@@ -71,7 +71,8 @@
                        "Josep Maria Roca Peña",
                        "Luan",
                        "Gato Preto",
-                       "Jdforrester"
+                       "Jdforrester",
+                       "Mansil"
                ]
        },
        "tog-underline": "Sublinhar ligações:",
        "tog-enotifminoredits": "Notificar-me por correio electrónico também sobre edições menores de páginas ou ficheiros",
        "tog-enotifrevealaddr": "Revelar o meu endereço de correio electrónico nas notificações",
        "tog-shownumberswatching": "Mostrar o número de utilizadores a vigiar",
-       "tog-oldsig": "Assinatura atual:",
+       "tog-oldsig": "A sua assinatura atual:",
        "tog-fancysig": "Tratar assinatura como texto wiki (sem hiperligações automáticas)",
        "tog-uselivepreview": "Usar a antevisão ao vivo",
        "tog-forceeditsummary": "Avisar-me se deixar o resumo da edição vazio",
        "newwindow": "(abre numa janela nova)",
        "cancel": "Cancelar",
        "moredotdotdot": "Mais...",
-       "morenotlisted": "Esta lista não está completa.",
+       "morenotlisted": "Esta lista pode estar incompleta.",
        "mypage": "Página",
        "mytalk": "Discussão",
        "anontalk": "Discussão",
        "yourpasswordagain": "Repita a palavra-passe:",
        "createacct-yourpasswordagain": "Confirme a palavra-passe",
        "createacct-yourpasswordagain-ph": "Digite a palavra-passe novamente",
-       "remembermypassword": "Recordar os meus dados neste computador (no máximo, por $1 {{PLURAL:$1|dia|dias}})",
        "userlogin-remembermypassword": "Manter-me autenticado",
        "userlogin-signwithsecure": "Usar uma ligação segura",
+       "cannotlogin-title": "Não é possível iniciar sessão",
+       "cannotlogin-text": "Não é possível iniciar sessão.",
        "cannotloginnow-title": "Não é possível iniciar sessão agora",
        "cannotloginnow-text": "Não pode iniciar a sessão quando utilizar $1.",
+       "cannotcreateaccount-title": "Não é possível criar contas",
        "yourdomainname": "O seu domínio:",
        "password-change-forbidden": "Não pode alterar palavras-passe nesta wiki.",
        "externaldberror": "Ocorreu um erro externo à base de dados durante a autenticação ou não lhe é permitido atualizar a sua conta externa.",
        "botpasswords-deleted-body": "O robô palavra-passe para o nome do robô \"$1\"do utilizador \"$2\" foi eliminado.",
        "botpasswords-newpassword": "A nova palavra-passe para iniciar sessão com <strong>$1</strong> é <strong>$2</strong>. Por favor, recorde-se dela para futura referência.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider não está disponível.",
+       "botpasswords-restriction-failed": "Restrições de palavra-passe de robô evitam esta autenticação.",
+       "botpasswords-invalid-name": "O nome de utilizador especificado não contém o separador de palavra-passe de robô (\"$1\").",
+       "botpasswords-not-exist": "O utilizador \"$1\" não possui uma palavra-passe de robô \"$2\".",
        "resetpass_forbidden": "Não é possível alterar palavras-passe",
        "resetpass_forbidden-reason": "As palavras-passe não podem ser alteradas: $1",
        "resetpass-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
        "passwordreset-emailelement": "{{GENDER:$1|Utilizador|Utilizadora}}: \n$1\n\nPalavra-passe temporária: \n$2",
        "passwordreset-emailsentemail": "Se este é o endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma palavra-passe de reposição.",
        "passwordreset-emailsentusername": "Se houver um endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma mensagem para redefinir a sua palavra-passe.",
+       "passwordreset-emailsent-capture2": "A redefinição da palavra-passe {{PLURAL:$1|do e-mail|dos e-mails}} foi enviada. {{PLURAL:$1|O nome de utilizador e palavra-passe|A lista de nomes de utilizador e palavras-passe}} encontram-se a seguir.",
+       "passwordreset-emailerror-capture2": "O envio do correio {{GENDER:$2|ao utilizador|à utilizadora|a(o) utilizador(a)}} falhou: $1 {{PLURAL:$3|O nome de utilizador e palavra-passe são mostradas abaixo|A lista de nomes de utilizadores e palavras-passe é mostrada abaixo}}.",
+       "passwordreset-nocaller": "Um interlocutor deve ser fornecido",
+       "passwordreset-nosuchcaller": "A pessoa que chama não existe: $1",
+       "passwordreset-ignored": "A reposição de palavra-passe não foi realizada. Talvez não tenha sido configurado o provedor?",
        "passwordreset-invalideamil": "Correio eletrónico inválido",
        "passwordreset-nodata": "Não foram fornecidos nome de utilizador(a) nem endereço de correio eletrónico",
        "changeemail": "Alterar ou remover o endereço de correio eletrónico",
        "file-thumbnail-no": "O nome do ficheiro começa por <strong>$1</strong>.\nParece ser uma imagem de tamanho reduzido (uma ''miniatura'' ou ''thumbnail)''.\nSe tiver a imagem original de maior dimensão, envie-a em vez desta. Se não, altere o nome do ficheiro, por favor.",
        "fileexists-forbidden": "Já existe um ficheiro com este nome, e não pode ser reescrito.\nSe ainda pretende carregar o seu ficheiro volte atrás e use outro nome, por favor. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Já existe um ficheiro com este nome no repositório de ficheiros partilhados.\nCaso deseje, mesmo assim, carregar o seu ficheiro, volte atrás e envie-o com um novo nome. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "O ficheiro carregado é um duplicado exato da versão atual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "O ficheiro carregado é um duplicado exato {{PLURAL:$2|de uma versão anterior|de uma das versões anteriores}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Este ficheiro é um duplicado {{PLURAL:$1|do seguinte|dos seguintes}}:",
        "file-deleted-duplicate": "Um ficheiro idêntico a este ([[:$1]]) foi eliminado anteriormente.\nVerifique o motivo da eliminação do ficheiro antes de prosseguir com o re-envio.",
        "file-deleted-duplicate-notitle": "Um ficheiro idêntico já foi eliminado e o seu título suprimido. Devia pedir a alguém capaz de ver os dados dos ficheiros eliminados para verificar a situação antes de carregá-lo novamente.",
        "apisandbox-loading-results": "A receber resultados da API...",
        "apisandbox-request-url-label": "URL do pedido:",
        "apisandbox-request-time": "Tempo de processamento: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Corrija o identificador e volte a submete-lo",
+       "apisandbox-results-fixtoken-fail": "Não foi possível obter o identificador \"$1\".",
+       "apisandbox-alert-page": "Os campos nesta página não são válidos.",
        "apisandbox-alert-field": "O valor deste campo não é válido.",
        "booksources": "Fontes bibliográficas",
        "booksources-search-legend": "Pesquisar referências bibliográficas",
        "trackingcategories-msg": "Categoria monitorada",
        "trackingcategories-name": "Nome da mensagem",
        "trackingcategories-desc": "Critérios de inclusão",
+       "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+       "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
        "noindex-category-desc": "A página não é indexada por robôs porque contém a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está num domínio onde o estatuto é permitido.",
        "index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
        "post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
        "rollbacklinkcount-morethan": "reverter mais do que $1 {{PLURAL:$1|edição|edições}}",
        "rollbackfailed": "A reversão falhou",
        "rollback-missingparam": "Faltam parâmetros obrigatórios no pedido.",
+       "rollback-missingrevision": "Não é possível carregar os dados de revisão.",
        "cantrollback": "Não foi possível reverter a edição; o último contribuidor é o único autor desta página",
        "alreadyrolled": "Não foi possível reverter as edições de [[:$1]] por [[User:$2|$2]] ([[User talk:$2|discussão]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguém editou ou já reverteu a página.\n\nA última edição foi de [[User:$3|$3]] ([[User talk:$3|discussão]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "O resumo da edição era: <em$1</em>.",
        "pageinfo-article-id": "ID da página",
        "pageinfo-language": "Idioma do conteúdo da página",
        "pageinfo-content-model": "Modelo de conteúdo de página",
+       "pageinfo-content-model-change": "alterar",
        "pageinfo-robot-policy": "Indexação por robôs",
        "pageinfo-robot-index": "Permitida",
        "pageinfo-robot-noindex": "Não permitida",
        "tags-actions-header": "Ações",
        "tags-active-yes": "Sim",
        "tags-active-no": "Não",
-       "tags-source-extension": "Definida por uma extensão",
+       "tags-source-extension": "Definida pelo software",
        "tags-source-manual": "Aplicada manualmente pelos utilizadores e robôs",
        "tags-source-none": "Já não está em uso",
        "tags-edit": "editar",
        "special-characters-group-ipa": "AFI (IPA)",
        "special-characters-group-symbols": "Símbolos",
        "special-characters-group-greek": "Grego",
+       "special-characters-group-greekextended": "Grego estendido",
        "special-characters-group-cyrillic": "Cirílico",
        "special-characters-group-arabic": "Árabe",
        "special-characters-group-arabicextended": "Arábico estendido",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "a página ainda não existe.",
        "mw-widgets-titleinput-description-redirect": "redirecionar para $1",
+       "sessionmanager-tie": "Não se pode combinar múltiplas solicitações de tipos de autenticação: $1.",
        "sessionprovider-generic": "Sessões $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessões baseadas em cookie",
        "sessionprovider-nocookies": "Os cookies podem estar desativados. Certifique-se de que os cookies estão ativados e inicie novamente.",
        "log-action-filter-suppress-block": "Supressão de utilizadores por bloqueio",
        "log-action-filter-upload-upload": "Novo carregamento",
        "log-action-filter-upload-overwrite": "Recarregar",
+       "authmanager-authn-no-primary": "As informações de identificação fornecidas não podem ser autenticadas.",
+       "authmanager-authn-autocreate-failed": "A criação automática de uma conta local falhou: $1",
        "authmanager-create-disabled": "A criação de contas está desativada.",
        "authmanager-create-from-login": "Para criar a sua conta, por favor, preencha os campos abaixo.",
        "authmanager-authplugin-setpass-failed-title": "A alteração de palavra-passe falhou",
        "authpage-cannot-login-continue": "Não é possível continuar a iniciar sessão. A sua sessão pode ter expirado.",
        "authpage-cannot-create": "Não é possível iniciar a criação da conta.",
        "authpage-cannot-create-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
+       "authpage-cannot-link": "Não é possível iniciar a associação da conta.",
+       "authpage-cannot-link-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
        "cannotauth-not-allowed-title": "Permissão negada",
        "cannotauth-not-allowed": "Não possui permissão para utilizar esta página",
        "changecredentials": "Alterar credenciais",
index 2c5041c..fbf95cc 100644 (file)
                        "2axterix2",
                        "Ата",
                        "Matěj Suchánek",
-                       "Chaduvari"
+                       "Chaduvari",
+                       "MarcoAurelio"
                ]
        },
        "sidebar": "{{notranslate}}",
        "yourpasswordagain": "Since 1.22 no longer used in core, but may be used by some extensions. DEPRECATED",
        "createacct-yourpasswordagain": "In create account form, label for field to re-enter password\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]\n{{Identical|Confirm password}}",
        "createacct-yourpasswordagain-ph": "Placeholder text in create account form for re-enter password field.\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]",
-       "remembermypassword": "Used as checkbox label on [[Special:ChangePassword]]. Parameters:\n* $1 - number of days\n{{Identical|Remember my login on this computer}}",
        "userlogin-remembermypassword": "The text for a check box in [[Special:UserLogin]].",
        "userlogin-signwithsecure": "Text of link to HTTPS login form.\n\nSee example: [[Special:UserLogin]]",
-       "cannotloginnow-title": "Error page title shown when logging in is not possible.",
-       "cannotloginnow-text": "Error page text shown when logging in is not possible. Parameters:\n* $1 - Session type in use that makes it not possible to log in, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
+       "cannotlogin-title": "Error page title shown when logging in is not possible. This is a catch-all when a more specific reason is not available.",
+       "cannotlogin-text": "Error page text shown when logging in is not possible. This is a catch-all when a more specific reason is not available.",
+       "cannotloginnow-title": "Error page title shown when logging in is not possible becuse the session provider in use does the user authentication itself.",
+       "cannotloginnow-text": "Error page text shown when logging in is not possible becuse the session provider in use does the user authentication itself. Parameters:\n* $1 - Session type in use that makes it not possible to log in, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
+       "cannotcreateaccount-title": "Error page title shown when manual account creation is not possible. That probably means the wiki supports some other account creation method, e.g. autocreation by the session provider. (When account creation is possible but the current user does not have permission to do it, a more specific error message is displayed.)",
+       "cannotcreateaccount-text": "Error page text shown when manual account creation is not possible. That probably means the wiki supports some other account creation method, e.g. autocreation by the session provider. (When account creation is possible but the current user does not have permission to do it, a more specific error message is displayed.)",
        "yourdomainname": "Used as label for listbox.",
        "password-change-forbidden": "Error message shown when an external authentication source does not allow the password to be changed.",
        "externaldberror": "This message is thrown when a valid attempt to change the wiki password for a user fails because of a database error or an error from an external system.",
        "botpasswords-updated-body": "Success message when a bot password is updated. Parameters:\n* $1 - Bot name\n* $2 - User name",
        "botpasswords-deleted-title": "Title of the success page when a bot password is deleted.",
        "botpasswords-deleted-body": "Success message when a bot password is deleted. Parameters:\n* $1 - Bot name\n* $2 - User name",
-       "botpasswords-newpassword": "Success message to display the new password when a bot password is created or updated. Parameters:\n* $1 - User name to be used for login.\n* $2 - Password to be used for login.",
+       "botpasswords-newpassword": "Success message to display the new password when a bot password is created or updated. Parameters:\n* $1 - User name to be used for login.\n* $2 - Password to be used for login.\n* $3, $4 - an alternative version of the user name and password, respectively, which is less preferred, but more compatible with old bots.",
        "botpasswords-no-provider": "Error message when login is attempted but the BotPasswordsSessionProvider is not included in <code>$wgSessionProviders</code>.",
        "botpasswords-restriction-failed": "Error message when login is rejected because the configured restrictions were not satisfied.",
        "botpasswords-invalid-name": "Error message when a username lacking the separator character is passed to BotPassword. Parameters:\n* $1 - The separator character.",
        "invalid-content-data": "Error message indicating that the page's content can not be saved because it is invalid. This may occurr for content types with internal consistency constraints.",
        "content-not-allowed-here": "Error message indicating that the desired content model is not supported in given localtion.\n* $1 - the human readable name of the content model: {{msg-mw|Content-model-wikitext}}, {{msg-mw|Content-model-javascript}}, {{msg-mw|Content-model-css}} or {{msg-mw|Content-model-text}}\n* $2 - the title of the page in question",
        "editwarning-warning": "Uses {{msg-mw|Prefs-editing}}",
+       "editpage-invalidcontentmodel-title": "Title of error page shown when using an unrecognized content model on EditPage",
+       "editpage-invalidcontentmodel-text": "Error message shown when using an unrecognized content model on EditPage. $1 is the user's invalid input",
        "editpage-notsupportedcontentformat-title": "Title of error page shown when using an incompatible format on EditPage.\n\nUsed as title for the following error message:\n* {{msg-mw|Editpage-notsupportedcontentformat-text}}.",
        "editpage-notsupportedcontentformat-text": "Error message shown when using an incompatible format on EditPage.\n\nThe title for this error is {{msg-mw|Editpage-notsupportedcontentformat-title}}.\n\nParameters:\n* $1 - the format id\n* $2 - the content model name",
        "content-model-wikitext": "Name for the wikitext content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}",
        "file-thumbnail-no": "Error message at [[Special:Upload]]. Parameters:\n* $1 - String (e.g. \"180px-\")",
        "fileexists-forbidden": "{{doc-important|''thumb'' and ''center'' are magic words. Leave it untranslated!}}\nParameters:\n* $1 - name of the existing file",
        "fileexists-shared-forbidden": "{{doc-important|''thumb'' and ''center'' are magic words. Leave it untranslated!}}\nError message at [[Special:Upload]].\nParameters:\n* $1 - name of the existing file",
+       "fileexists-no-change": "Used in [[Special:Upload]] to warn when the upload is an exact duplicate of the current version of the file.\nParameters:\n* $1 - name of the existing file",
+       "fileexists-duplicate-version": "Used in [[Special:Upload]] to warn when the upload is an exact duplicate of (an) older version(s) of the file\nParameters:\n* $1 - name of the existing file\n* $2 - Amount of matching older versions (PLURAL support)",
        "file-exists-duplicate": "Used as warning in [[Special:Upload]].\nThis message is followed by the gallery of the duplicate files.\n\nParameters:\n* $1 - number of duplicate files",
        "file-deleted-duplicate": "Used in [[Special:Upload]. Parameters:\n* $1 - page title of the file\n\nSee also:\n* {{msg-mw|file-deleted-duplicate-notitle}}",
        "file-deleted-duplicate-notitle": "Used in [[Special:Upload]] when the title of the deleted duplicate is not available.\n\nSee also:\n* {{msg-mw|file-deleted-duplicate}}",
        "filerevert-submit": "{{Identical|Revert}}",
        "filerevert-success": "Message displayed when you succeed in reverting a version of a file.\n* $1 is the name of the media\n* $2 is a date\n* $3 is a time\n* $4 is an URL and must follow square bracket: [$4\n{{Identical|Revert}}",
        "filerevert-badversion": "Used as error message.",
+       "filerevert-identical": "Used as error message.",
        "filedelete": "Used as page title. Parameters:\n* $1 - file title\nSee also:\n* {{msg-mw|Filedelete-intro}}",
        "filedelete-legend": "Used as fieldset label in the \"Delete file\" form.\n{{Identical|Delete file}}",
        "filedelete-intro": "Used as introduction for FileDelete form. Parameters:\n* $1 - page title for file\nSee also:\n* {{msg-mw|Filedelete|page title}}",
        "rollbacklinkcount-morethan": "{{doc-actionlink}}\nText of the rollback link when a greater number of edits is to be rolled back. See also {{msg-mw|rollbacklink}}.\n\nWhen the number of edits rolled back is smaller than [[mw:Special:MyLanguage/Manual:$wgShowRollbackEditCount|$wgShowRollbackEditCount]], {{msg-mw|rollbacklinkcount}} is used instead.\n\nParameters:\n* $1 - number of edits",
        "rollbackfailed": "{{Identical|Rollback}}",
        "rollback-missingparam": "Used as error message that rollback is accessed without the required parameters\n\nSee also:\n* {{msg-mw|Rollbackfailed}}",
+       "rollback-missingrevision": "Used as error message that rollback failed to load revision data\n\nSee also:\n* {{msg-mw|Rollbackfailed}}",
        "cantrollback": "Used as error message when rollback fails due to there not being a valid revision to revert back to.\n\nSee also:\n* {{msg-mw|Notvisiblerev}}\n{{Identical|Revert}}\n{{Identical|Rollback}}",
        "alreadyrolled": "Appear when there's rollback and/or edit collision.\n\nRefers to:\n* {{msg-mw|Pipe-separator}}\n* {{msg-mw|Contribslink}}\nParameters:\n* $1 - the page to be rolled back\n* $2 - the editor to be rolled-back of that page\n* $3 - the editor that cause collision\n{{Identical|Rollback}}",
        "editcomment": "Only shown if there is an edit {{msg-mw|Summary}}. Parameters:\n* $1 - the edit summary",
        "undeletehistorynoadmin": "Used in [[Special:Undelete]].\n\nSee also:\n* {{msg-mw|Undeletehistory}}\n* {{msg-mw|Undeleterevdel}}",
        "undelete-revision": "Shown in \"View and restore deleted pages\" ([[Special:Undelete/$1]]).\nParameters:\n* $1 - deleted page name\n* $2 - (unused)\n* $3 - username (author of revision, not who deleted it)\n* $4 - date of the revision (localized)\n* $5 - time of the revision (localized)\nExample (in English):\n* Deleted revision of [[Main Page]] (as of 14 September 2013, at 08:17) by [[User:Username|Username]]:",
        "undeleterevision-missing": "Used as warning when undeleting the revision.",
-       "undeleterevision-duplicate-revid": "Used as warning when some revisions could not be undeleted due to <code>rev_id</code> collisions.  Parameters:\n* - Number of revisions that could not be restored for this reason.",
+       "undeleterevision-duplicate-revid": "Used as warning when some revisions could not be undeleted due to <code>rev_id</code> collisions.  Parameters:\n* $1 - Number of revisions that could not be restored for this reason.",
        "undelete-nodiff": "Used in [[Special:Undelete]].",
        "undeletebtn": "Shown on [[Special:Undelete]] as button caption and on [[Special:Log/delete|deletion log]] after each entry (for sysops).\n\n{{Identical|Restore}}",
        "undeletelink": "Display name of link to undelete a page used on [[Special:Log/delete]]\n\n{{Identical|View}}\n{{Identical|Restore}}",
        "pageinfo-article-id": "The numeric identifier of the page.\n{{Identical|Page ID}}",
        "pageinfo-language": "Language in which the page content is written.",
        "pageinfo-content-model": "The model in which the page content is written.\n\nUsed as label at [{{fullurl:Main Page|action=info}} action=info]. Followed by one of the following messages:\n* {{msg-mw|Content-model-wikitext}}\n* {{msg-mw|Content-model-javascript}}\n* {{msg-mw|Content-model-css}}\n* {{msg-mw|Content-model-text}}",
+       "pageinfo-content-model-change": "Link text for a link to Special:ChangeContentModel. The link will be wrapped in parenthesis.\n{{Identical|Change}}",
        "pageinfo-robot-policy": "The search engine status of the page.\n\nUsed as label. Followed by any one of the following messages:\n*{{msg-mw|Pageinfo-robot-index}}\n*{{msg-mw|Pageinfo-robot-noindex}}",
        "pageinfo-robot-index": "An indication that the page is indexable by search engines, that is listed in their search results.\n\nPreceded by the label {{msg-mw|Pageinfo-robot-policy}}.\n{{Identical|Allowed}}",
        "pageinfo-robot-noindex": "An indication that the page is not indexable (that is, is not listed on the results page of a search engine).\n\nPreceded by the label {{msg-mw|Pageinfo-robot-policy}}.",
        "tag-filter": "Caption of a filter shown on lists of changes (e.g. [[Special:Log]], [[Special:Contributions]], [[Special:Newpages]], [[Special:Recentchanges]], [[Special:Recentchangeslinked]], page histories)",
        "tag-filter-submit": "Caption of the submit button displayed next to the tag filter on lists of changes (e.g. [[Special:Log]], [[Special:Contributions]], [[Special:Newpages]], [[Special:Recentchanges]], [[Special:Recentchangeslinked]], page histories)\n\n{{Identical|Filter}}",
        "tag-list-wrapper": "Wrapper for the list of tags shown on recent changes, watchlists, history pages and diffs.\n\nParameters:\n* $1 - number of distinct tags for given edit\n* $2 - comma-separated list of tags for given edit",
+       "tag-mw-contentmodelchange": "Change tag for edits that change the content model of a page",
+       "tag-mw-contentmodelchange-description": "Description for \"content model change\" change tag",
        "tags-title": "The title of [[Special:Tags]].\n{{Identical|Tag}}",
        "tags-intro": "Explanation on top of [[Special:Tags]]. For more information on tags see [[mw:Manual:Tags|MediaWiki]].",
        "tags-tag": "Caption of a column in [[Special:Tags]]. For more information on tags see [[mw:Manual:Tags|MediaWiki]].",
        "tags-actions-header": "Caption of a column in [[Special:Tags]]. The column contains action links like \"delete\". For more information on tags see [[mw:Manual:Tags|MediaWiki]].\n{{Identical|Action}}",
        "tags-active-yes": "Table cell contents if given tag is \"active\".\n\nSee also:\n* {{msg-mw|Tags-active-no}}\n{{Identical|Yes}}",
        "tags-active-no": "Table cell contents if given tag is not \"active\".\n\nSee also:\n* {{msg-mw|Tags-active-yes}}\n{{Identical|No}}",
-       "tags-source-extension": "Table cell contents if given tag can be applied automatically by a software [[mw:Manual:Extensions|extension]].\n\nSee also:\n* {{msg-mw|Tags-source-manual}}\n* {{msg-mw|Tags-source-none}}",
+       "tags-source-extension": "Table cell contents if given tag can be applied automatically by the MediaWiki software.\n\nSee also:\n* {{msg-mw|Tags-source-manual}}\n* {{msg-mw|Tags-source-none}}",
        "tags-source-manual": "\"Applied\" is not past tense, but an adjective that describes an action that sometimes happens, as in the sentence: \"(this tag is usually) applied by users and bots\".\n\nTable cell contents if given tag can be applied by users or bots.\n\nSee also:\n* {{msg-mw|Tags-source-extension}}\n* {{msg-mw|Tags-source-none}}",
        "tags-source-none": "Table cell contents if given tag is no longer in use. (It was applied in the past, but it is currently not applied.)\n\nSee also:\n* {{msg-mw|Tags-source-extension}}\n* {{msg-mw|Tags-source-manual}}",
        "tags-edit": "Used on [[Special:Tags]]. Verb. Used as display text on a link to create/edit a description.\n{{Identical|Edit}}",
        "logentry-delete-revision": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3",
        "logentry-delete-event-legacy": "{{Logentry|[[Special:Log/delete]]}}",
        "logentry-delete-revision-legacy": "{{Logentry|[[Special:Log/delete]]}}",
-       "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.",
+       "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.\n\n'''This is not a normal deletion log entry''' and is used only when the page is removed even from administrators view (on WMF wikis this is called 'oversight' or 'suppression'.",
        "logentry-suppress-event": "{{Logentry}}\n{{Logentryparam}}\n* $5 - count of affected log events",
        "logentry-suppress-revision": "{{Logentry}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3.",
        "logentry-suppress-event-legacy": "{{Logentry}}",
index de84e8e..bb89a09 100644 (file)
        "yourpasswordagain": "Yaykuna rimaykita kutipayay",
        "createacct-yourpasswordagain": "Yaykuna rimata takyachiy",
        "createacct-yourpasswordagain-ph": "Yaykuna rimata musuqmanta yaykuchiy",
-       "remembermypassword": "Ruraqpa sutiyta yaykuna rimaytapas yuyaykuy llamk'ay tiyayniypura ({{PLURAL:$1|huk p'unchawkama|$1 p'unchawkama}})",
        "userlogin-remembermypassword": "Yaykusqa kakunaytam munani",
        "userlogin-signwithsecure": "Amachasqa t'inkinakusqata llamk'achiy",
        "yourdomainname": "Duminyuykip sutin",
        "passwordreset-emailtext-user": "{{SITENAME}}-pi kaq $1 sutiyuq ruraqqa {{SITENAME}}-paq ($4)\nrakiqunaykipaq yaykuna rimata kutichinatam mañakurqan. Kay qatiq ruraqpa {{PLURAL:$3|rakiqunanmi|rakiqunankunam}}\nkay e-chaski imamaytayuq kachkan:\n\n$2\n\nKay mit'alla yaykuna {{PLURAL:$3|rimaqa|rimakunaqa}} kunanmanta {{PLURAL:$5|huk p'unchawpi|$5 p'unchawpi}} mawk'ayanqam.\nYaykuspayki musuq yaykuna rimaykitam akllankiman. Pi wakiykipas kayta mañakurqaptinqa,\nicha qam ñawpaq yaykuna rimaykita yuyaspayki manaña wakinchayta munaspaykiqa,\nkay willayta mana qhawaspa mana imatapas ruraspa ñawpaq yaykuna rimaykiwanmi llamk'ayta atinki.",
        "passwordreset-emailelement": "Ruraqpa sutin: \n$1\n\nMit'alla yaykuna rima: \n$2",
        "passwordreset-emailsentemail": "Yaykuna rimata kutichina e-chaskiqa kachasqañam.",
-       "passwordreset-emailsent-capture": "Yaykuna rimata kutichina e-chaskiqa kachasqañam, kay qatiqpi rikunki.",
-       "passwordreset-emailerror-capture": "{{GENDER:$2|}}Yaykuna rimata kutichina e-chaskiqa rurasqa karqan, imatachus kay qatiqpi rikunki, ichataq kachasqa kaptin pantasqam tukurqan: $1",
        "changeemail": "E-chaski imamaytata wakinchay",
        "changeemail-header": "Rakiqunap e-chaski imamaytanta wakinchay",
        "changeemail-no-info": "Yaykunaykim tiyan kay p'anqata chiqalla aypanaykipaq.",
        "minoredit": "Kayqa uchuylla hukchaymi",
        "watchthis": "Kay qillqata watiqay",
        "savearticle": "P'anqata waqaychay",
+       "savechanges": "Hukchasqata waqaychay",
        "preview": "Manaraq waqaychaspa qhawariy",
        "showpreview": "Ñawpaqta qhawallay",
        "showdiff": "Hukchasqakunata rikuchiy",
        "undo-norev": "Manam atinichu llamk'apusqata kutichiyta, mana kaptinmi icha qullusqa kaptinmi.",
        "undo-summary": "[[Special:Contributions/$2|$2]]-pa $1 hukchasqanta kutichisqa ([[User talk:$2|rimay]])",
        "undo-summary-username-hidden": "Pakasqa ruraqpa $1 nisqa musuqchasqata kutichiy",
-       "cantcreateaccounttitle": "Manam atinichu rakiqunata kichayta",
        "cantcreateaccount-text": "Kay IP tiyaymanta ('''$1''') rakiquna kichariyqa [[User:$3|$3]]-pa hark'asqanmi.\n\n$3-qa nirqan kayraykum: ''$2''",
        "viewpagelogs": "Kay p'anqamanta hallch'akunata qhaway",
        "nohistory": "Kay p'anqamantaqa manam llamk'apuy wiñay kawsay kanchu.",
index 2dafc36..f591aba 100644 (file)
        "perfcachedts": "Informațiile de mai jos provin din cache, ultima actualizare efectuându-se la $1. Un maxim de {{PLURAL:$4|un rezultat este disponibil|$4 rezultate sunt disponibile}} în cache.",
        "querypage-no-updates": "Actualizările acestei pagini sunt momentan dezactivate. Informațiile de aici nu sunt împrospătate.",
        "viewsource": "Sursă pagină",
-       "viewsource-title": "Vizualizare sursă pentru $1",
+       "viewsource-title": "Vizualizare sursă pentru „$1”",
        "actionthrottled": "Acțiune limitată",
        "actionthrottledtext": "Ca o măsură anti-spam, aveți permisiuni limitate în a efectua această acțiune de prea multe ori într-o perioadă scurtă de timp, iar dumneavoastră tocmai ați depășit această limită.\nVă rugăm să încercați din nou în câteva minute.",
        "protectedpagetext": "Această pagină este protejată împotriva modificărilor sau a altor acțiuni.",
        "yourpasswordagain": "Repetați parola:",
        "createacct-yourpasswordagain": "Confirmare parolă",
        "createacct-yourpasswordagain-ph": "Introduceți parola din nou",
-       "remembermypassword": "Autentificare automată de la acest calculator (expiră după {{PLURAL:$1|24 de ore|$1 zile|$1 de zile}})",
        "userlogin-remembermypassword": "Păstrează-mă autentificat",
        "userlogin-signwithsecure": "Utilizează conexiunea securizată",
+       "cannotlogin-title": "Imposibil de autentificat",
+       "cannotlogin-text": "Autentificarea nu este posibilă.",
        "cannotloginnow-title": "Nu se poate conecta acum",
        "cannotloginnow-text": "Conectarea nu este posibilă când se utilizează $1.",
+       "cannotcreateaccount-title": "Imposibil de creat conturi",
+       "cannotcreateaccount-text": "Crearea directă de conturi nu este activată pe acest wiki.",
        "yourdomainname": "Domeniul dumneavoastră:",
        "password-change-forbidden": "Nu puteți schimba parole pe acest wiki.",
        "externaldberror": "A fost fie o eroare de bază de date pentru o autentificare extenă sau nu aveți permisiunea să actualizați contul extern.",
        "minoredit": "Aceasta este o modificare minoră",
        "watchthis": "Urmărește această pagină",
        "savearticle": "Salvare pagină",
+       "savechanges": "Salvează modificările",
        "publishpage": "Publică pagina",
        "publishchanges": "Publică modificările",
        "preview": "Previzualizare",
        "rightslogtext": "Acest jurnal cuprinde modificările permisiunilor utilizatorilor.",
        "action-read": "citiți această pagină",
        "action-edit": "modificați această pagină",
-       "action-createpage": "creați pagini",
-       "action-createtalk": "creați pagini de discuție",
+       "action-createpage": "creați această pagină",
+       "action-createtalk": "creați această pagină de discuție",
        "action-createaccount": "creați acest cont de utilizator",
        "action-history": "vizualizați istoricul acestei pagini",
        "action-minoredit": "marcați această modificare ca minoră",
        "watchlistedit-raw-done": "Lista paginilor urmărite a fost actualizată.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 titlu a fost adăugat|$1 titluri au fost adăugate}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 titlu a fost șters|$1 titluri au fost șterse}}:",
-       "watchlistedit-clear-title": "Listă de pagini urmărite golită",
+       "watchlistedit-clear-title": "Golire listă de pagini urmărite",
        "watchlistedit-clear-legend": "Golire listă de pagini urmărite",
        "watchlistedit-clear-explain": "Toate titlurile vor fi înlăturate din lista dumnevoastră de pagini urmărite",
        "watchlistedit-clear-titles": "Titluri:",
index a40270b..7e6289d 100644 (file)
        "yourpasswordagain": "Повторный набор пароля:",
        "createacct-yourpasswordagain": "Подтвердите пароль",
        "createacct-yourpasswordagain-ph": "Введите пароль еще раз",
-       "remembermypassword": "Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней}})",
        "userlogin-remembermypassword": "Оставаться в системе",
        "userlogin-signwithsecure": "Защищённое соединение",
+       "cannotlogin-title": "Невозможно войти",
+       "cannotlogin-text": "Вход в систему невозможен.",
        "cannotloginnow-title": "Невозможно войти прямо сейчас",
        "cannotloginnow-text": "Нельзя войти во время использования $1.",
+       "cannotcreateaccount-title": "Невозможно создать учётные записи",
+       "cannotcreateaccount-text": "Прямое создание учетных записей не включено в этой вики.",
        "yourdomainname": "Ваш домен:",
        "password-change-forbidden": "Вы не можете изменить пароль в этой вики.",
        "externaldberror": "Произошла ошибка при аутентификации с помощью внешней базы данных или у вас недостаточно прав для внесения изменений в свою внешнюю учётную запись.",
        "botpasswords-updated-body": "Пароль бота для бота «$1» участника «$2» был обновлён.",
        "botpasswords-deleted-title": "Пароль бота удалён",
        "botpasswords-deleted-body": "Пароль бота для бота «$1» участника «$2» был удалён.",
-       "botpasswords-newpassword": "Новый пароль для входа под <strong>$1</strong> — <strong>$2</strong>. <em>Запишите его для последующего использования.</em>",
+       "botpasswords-newpassword": "Новый пароль для входа под <strong>$1</strong> — <strong>$2</strong>. <em>Запишите его для последующего использования.</em> <br /> (Для старых ботов, которые требуют, чтоб логин участника был таким же, как имя потенциального участника, вы можете также использовать <strong>$3</strong> как имя участника и <strong>$4</strong> в качестве пароля.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider недоступен.",
        "botpasswords-restriction-failed": "Из-за ограничений, связанных с паролем бота, вход не произведён.",
        "botpasswords-invalid-name": "Указанное имя участника не содержит разделителя для пароля бота («$1»).",
        "invalid-content-data": "Недопустимые данные",
        "content-not-allowed-here": "Содержимое \"$1\" недопустимо на странице [[$2]]",
        "editwarning-warning": "Переход на другую страницу может привести к потере внесённых вами изменений.\nЕсли вы зарегистрированы в системе, то вы можете отключить это предупреждение в разделе «{{int:prefs-editing}}» ваших настроек.",
+       "editpage-invalidcontentmodel-title": "Модель содержимого не поддерживается",
+       "editpage-invalidcontentmodel-text": "Модель содержимого «$1» не поддерживается.",
        "editpage-notsupportedcontentformat-title": "Формат содержимого не поддерживается",
        "editpage-notsupportedcontentformat-text": "Формат содержимого $1 не поддерживается моделью содержимого $2.",
        "content-model-wikitext": "вики-текст",
        "prefs-watchlist-token": "Токен списка наблюдения:",
        "prefs-misc": "Другие настройки",
        "prefs-resetpass": "Изменить пароль",
-       "prefs-changeemail": "Ð\98зменить или удалить адрес электронной почты",
+       "prefs-changeemail": "изменить или удалить адрес электронной почты",
        "prefs-setemail": "Установка адреса эл. почты",
        "prefs-email": "Параметры электронной почты",
        "prefs-rendering": "Внешний вид",
        "hide": "Скрыть",
        "show": "Показать",
        "minoreditletter": "м",
-       "newpageletter": "н",
+       "newpageletter": "Ð\9d",
        "boteditletter": "б",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдающий участник|наблюдающих участника|наблюдающих участников}}]",
        "file-thumbnail-no": "Название файла начинается с <strong>$1</strong>.\nВероятно, это уменьшенная копия изображения ''(миниатюра)''.\nЕсли у вас есть данное изображение в полном размере, пожалуйста, загрузите его или измените имя файла.",
        "fileexists-forbidden": "Файл с этим именем уже существует и не может быть перезаписан.\nЕсли всё равно хотите загрузить данный файл, пожалуйста, вернитесь назад и загрузите его под другим именем. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл с этим именем уже существует в общем хранилище файлов.\nЕсли вы всё-таки хотите загрузить этот файл, пожалуйста, вернитесь назад и измените имя файла. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Эта загрузка является точной копией текущей версии файла <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Эта загрузка является точной копией {{PLURAL:$2|более старой версии|более старых версий}} файла <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Этот файл — дубликат {{PLURAL:$1|1=следующего файла|следующих файлов}}:",
        "file-deleted-duplicate": "Подобный файл ([[:$1]]) уже удалялся. Пожалуйста, ознакомьтесь с историей удаления файла, прежде чем загружать его снова.",
        "file-deleted-duplicate-notitle": "Файл, идентичный этому файлу, был ранее удалён, а имя файла было запрещено.\nВам следует попросить кого-нибудь с правами просмотра данных по запрещённым файлам, чтобы он проанализировал ситуацию перед тем, как загружать файл снова.",
        "zip-wrong-format": "Указанный файл — не ZIP-архив.",
        "zip-bad": "ZIP-файл повреждён или не может быть прочитан.\nОн не может быть должным образом проверен.",
        "zip-unsupported": "Этот ZIP-файл использует возможности, не поддерживаемые MediaWiki.\nОн не может быть должным образом проверен.",
-       "uploadstash": "СкÑ\80Ñ\8bÑ\82наÑ\8f Ð·Ð°Ð³Ñ\80Ñ\83зка",
+       "uploadstash": "Ð\97агÑ\80Ñ\83зка Ð²Ð¾ Ð²Ñ\80еменное Ñ\85Ñ\80анилиÑ\89е",
        "uploadstash-summary": "Данная страница предоставляет доступ к файлам, которые были загружены (или находятся в процессе загрузки), но ещё не были опубликованы в вики. Эти файлы никому не видны, кроме загрузившего их участника.",
-       "uploadstash-clear": "Очистить скрытые файлы",
-       "uploadstash-nofiles": "У вас нет скрытых файлов.",
+       "uploadstash-clear": "Очистить временные файлы",
+       "uploadstash-nofiles": "У вас нет временных файлов.",
        "uploadstash-badtoken": "Не удалось выполнить указанные действия. Возможно, истёк срок действия ваших учётных данных. Пожалуйста, пробуйте ещё раз.",
        "uploadstash-errclear": "Очистка файлов не удалась.",
        "uploadstash-refresh": "Обновить список файлов",
        "uploadstash-thumbnail": "показать миниатюру",
+       "uploadstash-exception": "Не удалось сохранить загрузку во временное хранилище ($1): «$2».",
        "invalid-chunk-offset": "Недопустимое смещение фрагмента",
        "img-auth-accessdenied": "Доступ запрещён",
        "img-auth-nopathinfo": "Отсутствует <code>PATH_INFO</code>.\nВаш сервер не настроен для передачи этих сведений.\nВозможно, он работает на основе CGI и не поддерживает <code>img_auth</code>.\nСм. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Возвратить",
        "filerevert-success": "'''[[Media:$1|$1]]''' был возвращён к [$4 версии от $3, $2].",
        "filerevert-badversion": "Не существует предыдущей локальной версии этого файла с указанной меткой времени.",
+       "filerevert-identical": "Текущая версия файла уже идентична выбранной.",
        "filedelete": "$1 — удаление",
        "filedelete-legend": "Удалить файл",
        "filedelete-intro": "Вы собираетесь удалить файл '''[[Media:$1|$1]]''' со всей его историей.",
        "rollbacklinkcount-morethan": "откатить больше, чем $1 {{PLURAL:$1|правку|правки|правок}}",
        "rollbackfailed": "Ошибка при совершении отката",
        "rollback-missingparam": "Отсутствуют обязательные параметры по запросу.",
+       "rollback-missingrevision": "Не удалось загрузить данные версии.",
        "cantrollback": "Невозможно откатить изменения. Последним, кто вносил изменения, был единственный автор этой страницы.",
        "alreadyrolled": "Невозможно откатить последние изменения страницы «[[:$1]]», совершённые [[User:$2|$2]] ([[User talk:$2|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]),\nпоскольку кто-то другой уже успел откатить эти правки или отредактировать страницу.\n\nПоследние изменения {{GENDER:$3|внёс|внесла}} [[User:$3|$3]] ([[User talk:$3|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Было дано описание изменения: <em>$1</em>.",
        "pageinfo-article-id": "Идентификатор страницы",
        "pageinfo-language": "Язык страницы",
        "pageinfo-content-model": "Модель содержимого страницы",
+       "pageinfo-content-model-change": "изменить",
        "pageinfo-robot-policy": "Индексация поисковыми роботами",
        "pageinfo-robot-index": "Разрешено",
        "pageinfo-robot-noindex": "Не разрешено",
        "tag-filter": "Фильтр [[Special:Tags|меток]]:",
        "tag-filter-submit": "Отфильтровать",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Метка|Метки}}]]: $2)",
+       "tag-mw-contentmodelchange": "изменение модели содержимого",
+       "tag-mw-contentmodelchange-description": "Правки, которые [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel изменяют модель содержимого] страницы",
        "tags-title": "Метки",
        "tags-intro": "На этой странице приведён список меток, которыми программное обеспечение отмечает правки, а также значения этих меток.",
        "tags-tag": "Имя метки",
        "tags-actions-header": "Действия",
        "tags-active-yes": "Да",
        "tags-active-no": "Нет",
-       "tags-source-extension": "Определяется расширением",
+       "tags-source-extension": "Определяется программным обеспечением",
        "tags-source-manual": "Вносятся вручную участниками и ботами",
        "tags-source-none": "Больше не используется",
        "tags-edit": "править",
        "api-error-ratelimited": "Вы пытаетесь загрузить несколько файлов за более короткий промежуток времени, чем это позволено.\nПожалуйста, попробуйте ещё раз через несколько минут.",
        "api-error-stashfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
        "api-error-publishfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
-       "api-error-stasherror": "При загрузке файла в хранилище произошла ошибка.",
+       "api-error-stasherror": "При загрузке файла во временное хранилище произошла ошибка.",
        "api-error-stashedfilenotfound": "При попытке загрузить файл из временного хранилища исходный файл не найден.",
        "api-error-stashpathinvalid": "Путь, по которому должен располагаться файл, загруженный во временное хранилище, некорректен.",
        "api-error-stashfilestorage": "При загрузке файла во временное хранилище произошла ошибка.",
        "linkaccounts-submit": "Связать учётные записи",
        "unlinkaccounts": "Отвязать учётные записи",
        "unlinkaccounts-success": "Учетная запись была отвязан.",
-       "authenticationdatachange-ignored": "Изменение данных для проверки подлинности не было обработано. Может быть, не был настроен ни один провайдер?"
+       "authenticationdatachange-ignored": "Изменение данных для проверки подлинности не было обработано. Может быть, не был настроен ни один провайдер?",
+       "userjsispublic": "Обратите внимание: подстраницы JavaScript не должны содержать конфиденциальные сведения, поскольку они доступны для просмотра другим участникам.",
+       "usercssispublic": "Обратите внимание: подстраницы CSS не должны содержать конфиденциальные сведения, поскольку они доступны для просмотра другим участникам."
 }
index f4ff3ad..018652b 100644 (file)
        "yourpasswordagain": "कूटशब्दः पुनः लिख्यताम् :",
        "createacct-yourpasswordagain": "कूटशब्दस्य पुष्टिं करोतु ।",
        "createacct-yourpasswordagain-ph": "कूटशब्दः पुनः लिख्यताम्",
-       "remembermypassword": "अस्मिन् सङ्गणके मम प्रवेशः स्मर्यताम् (अधिकतमम् $1 {{PLURAL:$1|दिनम्|दिनानि}})",
        "userlogin-remembermypassword": "अहं प्रविष्ट एव स्याम्",
        "userlogin-signwithsecure": "संरक्षितः सम्पर्कः (https) उपयुज्यताम्",
        "yourdomainname": "भवतः प्रदेशः (domain) :",
        "passwordreset-capture-help": "अस्यां मञ्जूषायां यदि भवता अङ्क्यते तर्हि वि-पत्रम् (अस्थायिकूटशब्देन सह) दर्श्यते प्रेष्यते च ।",
        "passwordreset-email": "वि-पत्रसङ्केतः",
        "passwordreset-emailtitle": "{{SITENAME}} इत्यत्र योजकविषये",
-       "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य  कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|योजकः|योजकाः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
-       "passwordreset-emailtext-user": "$1 सदस्यः {{SITENAME}}($4) जालस्थानस्य  कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|सदस्यः|सदस्याः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विज्ञप्तिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
+       "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
+       "passwordreset-emailtext-user": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
        "passwordreset-emailelement": "सदस्यनाम : \n$1\n\nअल्पकालीनकूटशब्दः : \n$2",
        "passwordreset-emailsentemail": "परिवर्तितकूटशब्दस्य वि-पत्रं प्रेषितम् अस्ति ।",
        "changeemail": "वि-पत्रसङ्केतः परिवर्त्यताम्",
        "minoredit": "इदं लघु सम्पादनम्",
        "watchthis": "इदं पृष्ठं निरीक्षताम्",
        "savearticle": "पृष्ठं रक्ष्यताम्",
+       "savechanges": "परिवर्तनानि रक्ष्यन्ताम्",
        "publishpage": "पृष्ठं प्रकाश्यताम्",
        "publishchanges": "परिवर्तनानि प्रकाश्यन्ताम्",
        "preview": "प्राग्दृश्यम्",
        "whatlinkshere-prev": "{{PLURAL:$1|पुरस्तात् (previous) $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अग्रिमम् $1}}",
        "whatlinkshere-links": "← परिसन्धयः",
-       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि पृष्ठानि",
-       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\8dयलà¥\87à¤\96भाà¤\97ाः (transclusions)",
+       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि",
+       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\81वादाः (transclusions)",
        "whatlinkshere-hidelinks": "$1 परिसन्धयः",
        "whatlinkshere-hideimages": "$1 चित्रपरिसन्धिः",
        "whatlinkshere-filters": "शोधनी",
index efe836e..d17cdac 100644 (file)
        "tog-numberheadings": "Automaticky číslovať nadpisy",
        "tog-showtoolbar": "Zobraziť panel nástrojov úprav",
        "tog-editondblclick": "Upravovať stránky po dvojitom kliknutí",
-       "tog-editsectiononrightclick": "Umožniť upravovanie sekcie pravým kliknutím na nadpisy sekcií",
+       "tog-editsectiononrightclick": "Umožniť upravovanie sekcie kliknutím pravým tlačidlom myši na nadpisy sekcií",
        "tog-watchcreations": "Pridávať stránky, ktoré vytvorím a súbory, ktoré nahrám medzi sledované",
        "tog-watchdefault": "Pridávať stránky a súbory, ktoré upravím medzi sledované",
        "tog-watchmoves": "Pridávať stránky a súbory, ktoré presuniem medzi sledované",
        "tog-watchdeletion": "Pridávať stránky a súbory, ktoré zmažem medzi sledované",
-       "tog-watchrollback": "Pridať stránky na ktorých som použil rollback do môjho zoznamu sledovaných stránok",
+       "tog-watchuploads": "Pridať nové súbory, ktoré nahrám, do môjho zoznamu sledovaných",
+       "tog-watchrollback": "Pridať do môjho zoznamu sledovaných stránok stránky, na ktorých som použil vrátenie",
        "tog-minordefault": "Označovať všetky zmeny štandardne ako drobné",
        "tog-previewontop": "Zobrazovať náhľad pred textovým poľom úprav, nie až za ním",
        "tog-previewonfirst": "Zobraziť náhľad pred prvou úpravou",
        "tog-enotifwatchlistpages": "Upozorniť ma e-mailom, keď sa zmení stránka alebo súbor z môjho zoznamu sledovaných",
        "tog-enotifusertalkpages": "Upozorniť ma e-mailom po zmene mojej používateľskej diskusnej stránky",
        "tog-enotifminoredits": "Upozorniť ma e-mailom aj na drobné úpravy stránok a súborov",
-       "tog-enotifrevealaddr": "Zobraziť moju mailovú adresu v notifikačných e-mailoch",
+       "tog-enotifrevealaddr": "Zobraziť moju emailovú adresu v emailoch s upozornením",
        "tog-shownumberswatching": "Zobraziť počet používateľov sledujúcich stránku",
        "tog-oldsig": "Súčasný podpis:",
        "tog-fancysig": "Považovať podpisy za wikitext (bez automatických odkazov)",
        "tagline": "Z {{GRAMMAR:genitív|{{SITENAME}}}}",
        "help": "Pomoc",
        "search": "Hľadať",
+       "search-ignored-headings": " #<!-- tento riadok je nutné nechať bez zmeny --> <pre>\n# Nadpisy, ktoré bude vyhľadávanie ignorovať.\n# Tieto zmenu sa prejavia akonáhle bude stránka s nadpisom zaindexovaná.\n# Reindexovanie stránky môžete vynútiť uložením prázdnej úpravy.\n# Syntax je nasledovná:\n#   * Všetko počínajúc znakom „#“ do konca riadka je komentár.\n#   * Každý neprázdny riadok je presný názov, ktorý má byť ignorovaný, presne ako je napísaný, pričom na veľkosti písmen záleží.\nReferencie\nExterné odkazy\nPozri aj\n #</pre> <!-- tento riadok je nutné nechať bez zmeny -->",
        "searchbutton": "Hľadať",
        "go": "Vykonať",
        "searcharticle": "Ísť na",
        "pool-timeout": "Bol prekročený vyhradený čas čakania na zámok",
        "pool-queuefull": "Front je plný",
        "pool-errorunknown": "Neznáma chyba",
-       "pool-servererror": "Služba riadiaca prístup k serverom nieje dostupná ($1).",
+       "pool-servererror": "Služba riadiaca prístup k serverom nie je dostupná ($1).",
        "poolcounter-usage-error": "Chyba použitia: $1",
        "aboutsite": "O {{GRAMMAR:lokál|{{SITENAME}}}}",
        "aboutpage": "Project:Úvod",
        "toc": "Obsah",
        "showtoc": "zobraziť",
        "hidetoc": "skryť",
-       "collapsible-collapse": "skry",
-       "collapsible-expand": "rozbaľ",
+       "collapsible-collapse": "Skryť",
+       "collapsible-expand": "Rozbaliť",
        "confirmable-confirm": "Ste si {{GENDER:$1|istý|istá|istí}}?",
        "confirmable-yes": "Áno",
        "confirmable-no": "Nie",
        "nospecialpagetext": "<strong>Vyžiadali ste si neplatnú špeciálnu stránku.</strong>\n\nZoznam platných špeciálnych stránok nájdete na [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Chyba",
        "databaseerror": "Chyba v databáze",
-       "databaseerror-text": "Došlo k chybe pri otázke do databázy.\nMôže to byť spôsobené chybou v softvéri.",
+       "databaseerror-text": "Došlo k chybe pri kladení požiadavky do databázy.\nMôže to byť spôsobené chybou v softvéri.",
        "databaseerror-textcl": "Vyskytla sa chyba dopytu do databázy.",
-       "databaseerror-query": "Otázka: $1",
+       "databaseerror-query": "Požiadavka: $1",
        "databaseerror-function": "Funkcia: $1",
        "databaseerror-error": "Chyba: $1",
+       "transaction-duration-limit-exceeded": "Aby predišlo veľkému oneskoreniu pri replikácii, táto transakcia bola prerušená, pretože doba jej zápisu ($1) prekročila limit $2 sekúnd.\nAk robíte naraz veľa zmien, skúste radšej robiť viacero menších operácií.",
        "laggedslavemode": "Upozornenie: Je možné, že stránka neobsahuje posledné aktualizácie.",
        "readonly": "Databáza je zamknutá",
        "enterlockreason": "Zadajte dôvod požadovaného zamknutia vrátane odhadu, kedy očakávate odomknutie",
        "missingarticle-rev": "(č. revízie: $1)",
        "missingarticle-diff": "(rozdiel: $1, $2)",
        "readonly_lag": "Databáza bola automaticky zamknutá pokým záložné databázové servery nedoženú hlavný server",
+       "nonwrite-api-promise-error": "Bola odoslaná hlavička HTTP „Promise-Non-Write-API-Action“, ale požiadavka smerovala do modulu API na zápis.",
        "internalerror": "Vnútorná chyba",
        "internalerror_info": "Vnútorná chyba: $1",
        "internalerror-fatal-exception": "Kritická výnimka typu „$1“",
        "filerenameerror": "Nebolo možné premenovať súbor „$1“ na „$2“.",
        "filedeleteerror": "Nebolo možné vymazať súbor „$1“.",
        "directorycreateerror": "Nebolo možné vytvoriť adresár „$1“.",
-       "directoryreadonlyerror": "Adresár \"$1\" je iba na čítanie.",
-       "directorynotreadableerror": "Adresár \"$1\" sa nedá čítať.",
+       "directoryreadonlyerror": "Adresár „$1“ je iba na čítanie.",
+       "directorynotreadableerror": "Adresár „$1“ nie je možné čítať.",
        "filenotfound": "Nebolo možné nájsť súbor „$1“.",
        "unexpected": "Neočakávaná hodnota: „$1“=„$2“.",
        "formerror": "Chyba: nepodarilo sa odoslať formulár",
        "no-null-revision": "Nepodarilo sa vytvoriť novú prázdnu revíziu stránky „$1“",
        "badtitle": "Neplatný nadpis",
        "badtitletext": "Požadovaný nadpis bol neplatný, nezadaný, alebo nesprávne odkazovaný z inej jazykovej verzie {{GRAMMAR:genitív|{{SITENAME}}}}. Mohol tiež obsahovať jeden alebo viac znakov, ktoré nie je možné použiť v nadpisoch.",
+       "title-invalid-empty": "Požadovaný názov stránky je prázdny alebo obsahuje iba menný priestor.",
+       "title-invalid-utf8": "Požadovaný názov stránky obsahuje neplatnú postupnosť UTF-8.",
+       "title-invalid-interwiki": "Požadovaný názov stránky obsahuje odkaz interwiki, ktorý nie je možné používať v názvoch stránky.",
+       "title-invalid-talk-namespace": "Požadovaný názov stránky odkazuje na neexistujúcu diskusnú stránku.",
+       "title-invalid-characters": "Požadovaný názov stránky obsahuje neplatné znaky: „$1“.",
+       "title-invalid-relative": "Názov stránky má relatívnu cestu. Relatívne názvy stránky (./, ../) sú neplatné, pretože by často boli nedostupné, keď s nimi pracuje prehliadač používateľa.",
+       "title-invalid-magic-tilde": "Požadovaný názov stránky obsahuje neplatnú magickú postupnosť vlnoviek (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "Požadovaný názov stránky je príliš dlhý. Nesmie byť dlhšií ako $1 {{PLURAL:$1|bajt|bajty|bajtov}} v kódovaní UTF-8.",
+       "title-invalid-leading-colon": "Požadovaný názov stránky obsahuje na začiatku neplatnú dvojbodku.",
        "perfcached": "Nasledujúce údaje pochádzajú z vyrovnávacej pamäte a nemusia byť úplne aktuálne. Vo vyrovnávacej pamäti {{PLURAL:$1|je dostupný|sú dostupné|je dostupných}} najviac {{PLURAL:$1|jeden výsledok|$1 výsledky|$1 výsledkov}}.",
        "perfcachedts": "Nasledujúce údaje pochádzajú z vyrovnávacej pamäte a naposledy boli aktualizované $1. Vo vyrovnávacej pamäti {{PLURAL:$4|je dostupný|sú dostupné|je dostupných}} najviac {{PLURAL:$4|jeden výsledok|$4 výsledky|$4 výsledkov}}.",
        "querypage-no-updates": "Aktualizácie tejto stránky sú momentálne vypnuté. Tieto dáta sa v súčasnosti nebudú obnovovať.",
        "viewsourcetext": "Môžete si zobraziť a kopírovať zdroj tejto stránky:",
        "viewyourtext": "Môžete si prehliadnuť a skopírovať zdrojový kód <strong>vašich úprav</strong> tejto stránky:",
        "protectedinterface": "Táto stránka poskytuje text používateľského rozhrania tejto wiki a je zamknutá, aby sa predišlo jej zneužitiu.\nAk chcete pridať alebo zmeniť preklady pre všetky wiki, prosím, použite [https://translatewiki.net/ translatewiki.net], projekt lokalizácie MediaWiki.",
-       "editinginterface": "'''Upozornenie:''' Upravujete stránku, ktorá poskytuje text používateľského rozhrania.\nZmeny tejto stránky ovplyvnia vzhľad používateľského rozhrania ostatným používateľom.\nAk chcete pridať alebo zmeniť preklady pre všetky wiki, prosím, použite [https://translatewiki.net/ translatewiki.net], projekt lokalizácie MediaWiki.",
+       "editinginterface": "<strong>Upozornenie:</strong> Upravujete stránku, ktorá poskytuje text používateľského rozhrania.\nZmeny tejto stránky ovplyvnia vzhľad používateľského rozhrania ostatným používateľom.\nAk chcete pridať alebo zmeniť preklady pre všetky wiki, prosím, použite [https://translatewiki.net/ translatewiki.net], projekt lokalizácie MediaWiki.",
        "translateinterface": "Na pridanie a zmeny prekladov pre všetky wiki použite [https://translatewiki.net/ translatewiki.net], projekt na lokalizáciu MediaWiki.",
        "cascadeprotected": "Táto stránka bola zamknutá proti úpravám, pretože je použitá na {{PLURAL:$1|nasledovnej stránke, ktorá je zamknutá|nasledovných stránkach, ktoré sú zamknuté}} voľbou „kaskádového zamknutia“:\n$2",
        "namespaceprotected": "Nemáte povolenie upravovať stránky v mennom priestore '''$1'''.",
        "invalidtitle-unknownnamespace": "Neplatný názov s neznámym číslom menného priestoru „$1“ a textom „$2“",
        "exception-nologin": "Nie ste prihlásený",
        "exception-nologin-text": "Táto stránka alebo operácia vyžaduje, aby ste boli prihlásený.",
-       "exception-nologin-text-manual": "Pre prístup na túto stránku alebo k tejto akcii sa musíte $1.",
+       "exception-nologin-text-manual": "Pre prístup na túto stránku alebo k tejto operácii sa musíte $1.",
        "virus-badscanner": "Chybná konfigurácia: neznámy antivírus: ''$1''",
        "virus-scanfailed": "kontrola zlyhala (kód $1)",
        "virus-unknownscanner": "neznámy antivírus:",
-       "logouttext": "'''Práve ste sa odhlásili.'''\n\nUvedomte si, že niektoré stránky sa môžu naďalej zobrazovať ako keby ste boli prihlásený, až kým nevymažete vyrovnávaciu pamäť vášho prehliadača.",
+       "logouttext": "<strong>Práve ste sa odhlásili.</strong>\n\nUvedomte si, že niektoré stránky sa môžu naďalej zobrazovať ako keby ste boli prihlásený, až kým nevymažete vyrovnávaciu pamäť vášho prehliadača.",
+       "cannotlogoutnow-title": "Teraz nie je možné odhlásiť sa",
+       "cannotlogoutnow-text": "Nie je možné odhlásiť sa, keď používate $1",
        "welcomeuser": "Vitajte,  $1 !",
        "welcomecreation-msg": "Váš účet bol vytvorený.\nNezabudnite zmeniť svoje [[Special:Preferences|Predvoľby {{GRAMMAR:genitív|{{SITENAME}}}}]].",
        "yourname": "Používateľské meno:",
        "yourpasswordagain": "Zopakujte heslo:",
        "createacct-yourpasswordagain": "Potvrdiť heslo",
        "createacct-yourpasswordagain-ph": "Zadajte heslo znova",
-       "remembermypassword": "Pamätať si prihlásenie na tomto počítači (naviac $1 {{PLURAL:$1|deň|dni|dní}})",
-       "userlogin-remembermypassword": "Zapamätať si ma",
+       "userlogin-remembermypassword": "Zapamätať si moje prihlásenie",
        "userlogin-signwithsecure": "Použiť zabezpečené pripojenie",
+       "cannotlogin-title": "Nie je možné prihlásiť sa",
+       "cannotlogin-text": "Prihlásenie nie je možné.",
+       "cannotloginnow-title": "Teraz nie je možné prihlásiť sa",
+       "cannotloginnow-text": "Nie je možné odhlásiť sa, keď používate $1.",
+       "cannotcreateaccount-title": "Nie je možné vytvárať účty",
+       "cannotcreateaccount-text": "Priame vytváranie účtu nie je na tejto wiki povolené.",
        "yourdomainname": "Vaša doména:",
        "password-change-forbidden": "Na tejto wiki si nemôžete zmeniť heslo.",
        "externaldberror": "Buď nastala chyba externej autentifikačnej databázy alebo vám nie je povolené aktualizovať váš externý účet.",
        "userlogin-resetlink": "Zabudli ste svoje prihlasovacie údaje?",
        "userlogin-resetpassword-link": "Zabudli ste heslo?",
        "userlogin-helplink2": "Pomoc s prihlásením",
-       "userlogin-loggedin": "Ste už {{GENDER:$1|prihĺasený|prihlásená}} ako $1.\nPomocou formulára nižšie sa môžete prihlásiť ako iný redaktor.",
+       "userlogin-loggedin": "Ste už {{GENDER:$1|prihlasený|prihlásená}} ako $1.\nPomocou formulára nižšie sa môžete prihlásiť ako iný používateľ.",
        "userlogin-reauth": "Aby ste preukázali, že ste $1, musíte sa znovu prihlásiť.",
        "userlogin-createanother": "Vytvoriť ďalší účet",
-       "createacct-emailrequired": "E-mailová adresa",
-       "createacct-emailoptional": "E-mailová adresa (nepovinné)",
-       "createacct-email-ph": "Zadajte vašu e-mailovú adresu",
-       "createacct-another-email-ph": "Zadajte vašu e-mailovú adresu",
-       "createaccountmail": "Použiť dočasné náhodné heslo a poslať ho na uvedenú e-mailovú adresu",
+       "createacct-emailrequired": "Emailová adresa",
+       "createacct-emailoptional": "Emailová adresa (nepovinné)",
+       "createacct-email-ph": "Zadajte svoju emailovú adresu",
+       "createacct-another-email-ph": "Zadajte svoju emailovú adresu",
+       "createaccountmail": "Použiť dočasné náhodné heslo a poslať ho na uvedenú emailovú adresu",
        "createaccountmail-help": "Môže byť použité na vytvorenie účtu pre inú osobu bez prezradenia hesla.",
        "createacct-realname": "Skutočné meno (nepovinné)",
        "createaccountreason": "Dôvod:",
        "createacct-reason": "Dôvod",
        "createacct-reason-ph": "Prečo si vytvárate ďalší účet",
        "createacct-reason-help": "Správa zobrazená v knihe nových používateľov",
-       "createacct-submit": "Vytvoriť účet",
+       "createacct-submit": "Vytvoriť si účet",
        "createacct-another-submit": "Vytvoriť účet",
-       "createacct-continue-submit": "Pokračovať v zakladaní účtu",
-       "createacct-another-continue-submit": "Pokračovať v zakladaní účtu",
-       "createacct-benefit-heading": "{{GRAMMAR:akuzatív|{{SITENAME}}}} tvoria ľudia ako vy.",
+       "createacct-continue-submit": "Pokračovať vo vytváraní účtu",
+       "createacct-another-continue-submit": "Pokračovať vo vytváraní účtu",
+       "createacct-benefit-heading": "{{GRAMMAR:akuzatív|{{SITENAME}}}} tvoria ľudia ako ste vy.",
        "createacct-benefit-body1": "{{PLURAL:$1|úprava|úpravy|úprav}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stránka|stránky|stránok}}",
        "createacct-benefit-body3": "{{PLURAL:$1|nedávny prispievateľ|nedávni prispievatelia|nedávnych prispievateľov}}",
        "badretype": "Zadané heslá nie sú rovnaké.",
-       "usernameinprogress": "Vytváranie účtu s týmto menom už prebieha. Počkajte prosím.",
+       "usernameinprogress": "Vytváranie účtu s týmto menom už prebieha. Prosím, počkajte.",
        "userexists": "Zadané používateľské meno sa už používa.\nProsím, zvoľte si iné meno.",
        "loginerror": "Chyba pri prihlasovaní",
        "createacct-error": "Chyba pri vytváraní účtu",
        "botpasswords-no-central-id": "Aby ste mohli použiť heslá pre botov musíte byť prihlásený k centrálnemu účtu.",
        "botpasswords-existing": "Jestvujúce heslá pre botov",
        "botpasswords-createnew": "Vytvoriť nové heslo pre botov",
+       "botpasswords-editexisting": "Zmeniť existujúce heslo bota",
        "botpasswords-label-appid": "Názov bota:",
        "botpasswords-label-create": "Vytvoriť",
        "botpasswords-label-update": "Aktualizovať",
        "botpasswords-label-cancel": "Zrušiť",
        "botpasswords-label-delete": "Vymazať",
        "botpasswords-label-resetpassword": "Obnoviť heslo",
+       "botpasswords-label-grants": "Príslušné oprávnenia:",
+       "botpasswords-help-grants": "Každé oprávnenie poskytuje prístup k uvedeným právam používateľa, ktoré už používateľský účet má. Ďalšie informácie nájdete v [[Special:ListGrants|tabuľke oprávnení]].",
+       "botpasswords-label-restrictions": "Obmedzenie použitia:",
+       "botpasswords-label-grants-column": "Udelené",
+       "botpasswords-bad-appid": "Názov bota „$1“ nie je platný.",
+       "botpasswords-insert-failed": "Nepodarilo sa pridať názov bota „$1“. Je už pridaný?",
+       "botpasswords-update-failed": "Nepodarilo sa aktualizovať názov bota „$1“. Bol zmazaný?",
+       "botpasswords-created-title": "Heslo bota bolo vytvorené",
+       "botpasswords-created-body": "Heslo bota s názvom „$1“ patriaceho používateľovi „$2“ bolo vytvorené.",
+       "botpasswords-updated-title": "Heslo bota bolo aktualizované",
+       "botpasswords-updated-body": "Heslo bota s názvom „$1“ patriaceho používateľovi „$2“ bolo aktualizované.",
+       "botpasswords-deleted-title": "Heslo bota bolo odstránené",
+       "botpasswords-deleted-body": "Heslo bota s názvom „$1“ patriaceho používateľovi „$2“ bolo odstránené.",
+       "botpasswords-newpassword": "Nové prihlasovacie heslo pre <strong>$1</strong> je <strong>$2</strong>. <em>Prosím, zaznačte si ho na použitie v budúcnosti.</em> <br> (Pre staré boty, ktoré vyžadujú, aby prihlasovacie meno bolo rovnaké ako prípadné používateľské meno môžu tiež použiť <strong>$3</strong> ako používateľské meno a <strong>$4</strong> ako heslo.)",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider nie je k dispozícii.",
+       "botpasswords-restriction-failed": "Obmedzenia hesla bota bránenia tomuto prihláseniu.",
+       "botpasswords-invalid-name": "Zadané používateľské meno neobsahuje oddeľovač hesla bota („$1“).",
+       "botpasswords-not-exist": "Používateľ „$1“ nemá heslo bota s názvom „$2“.",
        "resetpass_forbidden": "Heslá nie je možné zmeniť",
+       "resetpass_forbidden-reason": "Heslá nie je možné zmeniť: $1",
        "resetpass-no-info": "Aby ste mohli priamo pristupovať k tejto stránke, musíte sa prihlásiť.",
        "resetpass-submit-loggedin": "Zmeniť heslo",
        "resetpass-submit-cancel": "Zrušiť",
        "passwordreset-emailelement": "Používateľské meno: \n$1\n\nDočasné heslo:\n$2",
        "passwordreset-emailsentemail": "Pokiaľ je toto e-mailová adresa zaregistrovaná k vášmu účtu, bude na ňu zaslaný e-mail pre získanie nového hesla.",
        "passwordreset-emailsentusername": "Pokiaľ je príslušná mailová adresa zaregistrovaná, bude na ňu zaslaný e-mail s novým heslom.",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|Email|Emaily}} na obnovenie hesla {{PLURAL:$1|bol odoslaný|boli odoslané}}. {{PLURAL:$1|Používateľské meno a heslo|Zoznam používateľských mien a hesiel}} je uvedený nižšie.",
        "changeemail": "Zmeniť alebo odstrániť e-mailovú adresu",
-       "changeemail-header": "Zmena e-mailovej adresy pre účet",
+       "changeemail-header": "Vyplňte tento formulár, ak chcete zmeniť svoju emailovú adresu. Ak chcete odstrániť priradenie akejkoľvek emailovej adresy k vášmu účtu, nechajte pri odosielaní formulára emailovú adresu nevyplnenú",
        "changeemail-no-info": "Na prístup k tejto stránke musíte byť prihlásený.",
        "changeemail-oldemail": "Súčasná e-mailová adresa:",
        "changeemail-newemail": "Nová e-mailová adresa:",
        "missingsummary": "'''Pripomienka:''' Neposkytli ste zhrnutie úprav. Ak kliknete znova na Uložiť, vaše úpravy sa uložia bez zhrnutia úprav.",
        "selfredirect": "<strong>Upozornenie:</strong> Snažíte sa túto stránku presmerovať samú na seba.\nMožno ste zadali chybný cieľ presmerovania, alebo editujete nesprávnu stránku.\nAk znova kliknete na „{{int:savearticle}}“, bude presmerovanie aj napriek tomu vytvorené.",
        "missingcommenttext": "Prosím, dolu napíšte komentár.",
-       "missingcommentheader": "'''Pripomienka:''' Neposkytli ste predmet/hlavičku tohto komentára.\nAk znova kliknete na tlačidlo „{{int:savearticle}}“, vaša úprava sa uloží bez nej.",
+       "missingcommentheader": "<strong>Pripomienka:</strong> Neposkytli ste predmet/hlavičku tohto komentára.\nAk znova kliknete na tlačidlo „{{int:savearticle}}“, vaša úprava sa uloží bez nej.",
        "summary-preview": "Náhľad zhrnutia:",
-       "subject-preview": "Náhľad predmetu/hlavičky:",
+       "subject-preview": "Náhľad predmetu:",
        "previewerrortext": "Pri pokuse o zobrazenie náhľadu došlo k chybe.",
        "blockedtitle": "Používateľ je zablokovaný",
        "blockedtext": "'''Vaše používateľské meno alebo IP adresa bola zablokovaná.'''\n\nZablokoval vás správca $1. Udáva tento dôvod:<br />''$2''\n\n* Blokovanie začalo: $8\n* Blokovanie vyprší: $6\n* Kto mal byť zablokovaný: $7\n\nMôžete kontaktovať $1 alebo s jedného z ďalších [[{{MediaWiki:Grouppage-sysop}}|správcov]] a prediskutovať blokovanie.\nUvedomte si, že nemôžete použiť funkciu „{{int:Emailuser}}“, pokiaľ nemáte registrovanú platnú e-mailovú adresu vo svojich [[Special:Preferences|nastaveniach]].\nVaša IP adresa je $3 a ID blokovania je #$5.\nProsím, uveďte oba tieto údaje do každej správy, ktorú posielate.",
        "accmailtext": "Náhodne vytvorené heslo pre používateľa [[User talk:$1|$1]] bolo poslané na $2. Je možné ho zmeniť na stránke ''[[Special:ChangePassword|zmena hesla]]'' po prihlásení.",
        "newarticle": "(Nový)",
        "newarticletext": "Sledovali ste odkaz na stránku, ktorá zatiaľ neexistuje.\nStránku vytvoríte tak, že začnete písať do poľa nižšie (viac informácií nájdete na stránkach [$1 nápovedy]).\nAk ste sa sem dostali nechtiac, kliknite na tlačidlo <strong>späť</strong> vo svojom prehliadači.",
-       "anontalkpagetext": "----''Toto je diskusná stránka anonymného používateľa, ktorý nemá vytvorené svoje konto alebo ho nepoužíva.\nPreto musíme na jeho identifikáciu použiť numerickú IP adresu. Je možné, že takúto IP adresu používajú viacerí používatelia.\nAk ste anonymný používateľ a máte pocit, že vám boli adresované irelevantné diskusné príspevky, [[Special:CreateAccount|vytvorte si konto]] alebo sa [[Special:UserLogin|prihláste]], aby sa zamedzilo budúcim zámenám s inými anonymnými používateľmi.''",
+       "anontalkpagetext": "----\n<em>Toto je diskusná stránka anonymného používateľa, ktorý nemá vytvorené svoje konto alebo ho nepoužíva.</em>\nPreto musíme na jeho identifikáciu použiť numerickú IP adresu. Je možné, že takúto IP adresu používajú viacerí používatelia.\nAk ste anonymný používateľ a máte pocit, že vám boli adresované irelevantné diskusné príspevky, [[Special:CreateAccount|vytvorte si konto]] alebo sa [[Special:UserLogin|prihláste]], aby sa zamedzilo budúcim zámenám s inými anonymnými používateľmi.",
        "noarticletext": "Na tejto stránke sa momentálne nenachádza žiadny text.\nMôžete [[Special:Search/{{PAGENAME}}|vyhľadávať názov tejto stránky]] v obsahu iných stránok,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} vyhľadávať v súvisiacich záznamoch] alebo [{{fullurl:{{FULLPAGENAME}}|action=edit}} vytvoriť túto stránku]</span>.",
        "noarticletext-nopermission": "Táto stránka momentálne neobsahuje žiadny text.\nMôžete [[Special:Search/{{PAGENAME}}|hľadať názov tejto stránky]] v texte iných stránok\nalebo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} hľadať v súvisiacich záznamoch]</span>, ale nemáte oprávnenie túto stránku vytvoriť.",
        "missing-revision": "Revízia #$1 stránky s názvom „{{FULLPAGENAME}}“ neexistuje.\n\nPravdepodobne ste nasledovali zastaraný odkaz na historickú verziu stránky, ktorá bola medzičasom odstránená.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname zmazaní].",
        "userpage-userdoesnotexist": "Používateľský účet „<nowiki>$1</nowiki>“ nie je registrovaný. Prosím, skontrolujte, či naozaj chcete vytvoriť/upravovať túto stránku.",
        "userpage-userdoesnotexist-view": "Používateľský účet „$1“ nie je registrovaný.",
        "blocked-notice-logextract": "Tento používateľ je momentálne zablokovaný.\nDolu je pre informáciu posledná položka zo záznamu blokovaní:",
-       "clearyourcache": "'''Poznámka:''' Aby sa zmeny prejavili, po uložení musíte vymazať vyrovnávaciu pamäť vášho prehliadača.\n* '''Mozilla Firefox / Safari:''' Držte stlačený ''Shift'' a kliknite na ''Reload'' alebo stlačte buď ''Ctrl-F5'' alebo ''Ctrl-R'' (''⌘-R'' na Mac)\n* '''Google Chrome:''' Stlačte ''Ctrl-Shift-R'' (''⌘-Shift-R'' na Mac)\n* '''Internet Explorer:''' Držte ''Ctrl'' a kliknite na ''Refresh'' alebo stlačte ''Ctrl-F5''\n* '''Opera:''' Vymazať vyrovnávaciu pamäť prehliadača v ponuke ''Tools→Preferences''",
+       "clearyourcache": "<strong>Poznámka:</strong> Aby sa zmeny prejavili, po uložení musíte vymazať vyrovnávaciu pamäť vášho prehliadača.\n* <strong>Mozilla Firefox / Safari:</strong> Držte stlačený <em>Shift</em> a kliknite na ''Reload'' alebo stlačte buď <em>Ctrl-F5</em> alebo <em>Ctrl-R</em> (<em>⌘-R</em> na Mac)\n* <strong>Google Chrome:</strong> Stlačte <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> na Mac)\n* <strong>Internet Explorer:</strong> Držte <em>Ctrl</em> a kliknite na <em>Refresh</em> alebo stlačte <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Prejdite do <em>Menu → Settings</em> (<em>Opera → Preferences</em> on a Mac) a potom do <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Tip:''' Váš nový CSS pred uložením otestujete stlačením tlačidla „{{int:showpreview}}“.",
        "userjsyoucanpreview": "'''Tip:''' Váš nový JS pred uložením otestujete stlačením tlačidla „{{int:showpreview}}“.",
        "usercsspreview": "'''Nezabudnite, že toto je iba náhľad vášho používateľského CSS, ešte nebolo uložené!'''",
        "previewnote": "'''Nezabudnite, toto je iba náhľad stránky, ktorú upravujete.\nZmeny ešte nie sú uložené!'''",
        "continue-editing": "Pokračovať v úpravách",
        "previewconflict": "Tento náhľad upravenej stránky zobrazuje text z horného poľa s textom tak, ako sa zobrazí potom, keď ju uložíte.",
-       "session_fail_preview": "'''Prepáčte, nemohli sme spracovať váš príspevok kvôli strate údajov relácie.\nSkúste to prosím ešte raz.\nAk to nebude fungovať, skúste sa [[Special:UserLogout|odhlásiť]] a znovu prihlásiť.'''",
-       "session_fail_preview_html": "'''Prepáčte! Nemohli sme spracovať vašu úpravu kvôli strate údajov relácie.'''\n\n''Pretože {{SITENAME}} má použitie HTML umožnené, náhľad sa nezobrazí (prevencia pred JavaScript útokmi).''\n\n'''Ak je toto legitímny pokus o úpravu, skúste to prosím znova. Ak to stále nefunguje, skúste sa [[Special:UserLogout|odhlásiť]] a znovu prihlásiť.'''",
+       "session_fail_preview": "Prepáčte, nemohli sme spracovať váš príspevok kvôli strate údajov relácie.\n\n<strong>Prosím, uistite sa, že ste prihlásený a skúste to prosím znova.</strong>.\nAk to nebude fungovať, skúste sa [[Special:UserLogout|odhlásiť]] a znovu prihlásiť. Skontrolujte, či váš prehliadač prijíma cookies z tejto webstránky.",
+       "session_fail_preview_html": "Prepáčte! Nemohli sme spracovať vašu úpravu kvôli strate údajov relácie.\n\n<em>Pretože {{SITENAME}} má použitie HTML umožnené, náhľad sa nezobrazí (prevencia pred JavaScript útokmi).</em>\n\n<strong>Ak je toto legitímny pokus o úpravu, skúste to prosím znova.</strong>.\nAk to nebude fungovať, skúste sa [[Special:UserLogout|odhlásiť]] a znovu prihlásiť. Skontrolujte, či váš prehliadač prijíma cookies z tejto webstránky.",
        "token_suffix_mismatch": "'''Vaša úprava bola zamietnutá, pretože váš klient pokazil znaky s diakritikou v editačnom symbole (token). Úprava bola zamietnutá, aby sa zabránilo poškodeniu textu stránky. Toto sa občas stáva, keď používate chybnú anonymnú proxy službu cez webové rozhranie.'''",
        "edit_form_incomplete": "'''Niektoré časti editačného formulára nedosiahli server. Prosím, znova skontrolujte, že vaše úpravy sú nepoškodené a skúste to znova.'''",
        "editing": "Úprava stránky $1",
        "explainconflict": "Niekto iný zmenil túto stránku, zatiaľ čo ste ju upravovali vy.\nHorné okno na úpravy obsahuje text stránky tak, ako je momentálne platný.\nVaše úpravy sú uvedené v dolnom okne na úpravy.\nBudete musieť zlúčiť vaše zmeny s existujúcim textom.\n'''Iba''' obsah horného okna sa uloží, keď stlačíte „{{int:savearticle}}“.",
        "yourtext": "Váš text",
        "storedversion": "Uložená verzia",
-       "nonunicodebrowser": "'''UPOZORNENIE: Váš prehliadač nepodporuje unicode. Dočasným riešením ako bezpečne upravovať stránky je, že ne-ASCII znaky sa v upravovacom textovom poli zobrazia ako zodpovedajúce hexadecimálne hodnoty.'''",
-       "editingold": "'''UPOZORNENIE: Upravujete starú\nverziu tejto stránky. Ak vašu úpravu uložíte, prepíšete tým všetky úpravy, ktoré nasledovali po tejto starej verzii.'''",
+       "nonunicodebrowser": "<strong>UPOZORNENIE: Váš prehliadač nepodporuje Unicode.</strong>\nDočasným riešením ako bezpečne upravovať stránky je, že ne-ASCII znaky sa v upravovacom textovom poli zobrazia ako zodpovedajúce hexadecimálne hodnoty.",
+       "editingold": "<strong>UPOZORNENIE: Upravujete starú verziu tejto stránky.</strong>\nAk vašu úpravu uložíte, prepíšete tým všetky úpravy, ktoré nasledovali po tejto starej verzii.",
        "yourdiff": "Rozdiely",
-       "copyrightwarning": "Nezabudnite, že všetky príspevky do {{GRAMMAR:genitív|{{SITENAME}}}} sa považujú za príspevky pod licenciou $2 (podrobnosti pozri pod $1). Ak nechcete, aby bolo to, čo ste napísali, neúprosne upravované a ďalej ľubovoľne rozširované, tak sem váš text neumiestňujte.<br />\n\nTýmto sa právne zaväzujete, že ste tento text buď napísali sám, alebo že je skopírovaný\nz voľného diela (public domain) alebo podobného zdroja neobmedzeného autorskými právami.\n'''NEUMIESTŇUJTE TU BEZ POVOLENIA DIELA CHRÁNENÉ AUTORSKÝM PRÁVOM!'''",
-       "copyrightwarning2": "Prosím uvedomte si, že všetky príspevky do {{GRAMMAR:genitív|{{SITENAME}}}} môžu byť upravované, skracované alebo odstránené inými prispievateľmi. Ak nechcete, aby Vaše texty boli menené, tak ich tu neuverejňujte.<br />\n\nTýmto sa právne zaväzujete, že ste tento text buď napísali sám, alebo že je skopírovaný\nz voľného diela (public domain) alebo podobného zdroja neobmedzeného autorskými právami (podrobnosti: $1).\n'''NEUMIESTŇUJTE SEM BEZ POVOLENIA DIELA CHRÁNENÉ AUTORSKÝM PRÁVOM!'''",
+       "copyrightwarning": "Nezabudnite, že všetky príspevky do {{GRAMMAR:genitív|{{SITENAME}}}} sa považujú za príspevky pod licenciou $2 (podrobnosti pozri pod $1). Ak nechcete, aby bolo to, čo ste napísali, neúprosne upravované a ďalej ľubovoľne rozširované, tak sem váš text neumiestňujte.<br />\n\nTýmto sa právne zaväzujete, že ste tento text buď napísali sám, alebo že je skopírovaný\nz voľného diela (public domain) alebo podobného zdroja neobmedzeného autorskými právami.\n<strong>NEUMIESTŇUJTE SEM BEZ POVOLENIA DIELA CHRÁNENÉ AUTORSKÝM PRÁVOM!</strong>",
+       "copyrightwarning2": "Prosím uvedomte si, že všetky príspevky do {{GRAMMAR:genitív|{{SITENAME}}}} môžu byť upravované, skracované alebo odstránené inými prispievateľmi. Ak nechcete, aby Vaše texty boli menené, tak ich tu neuverejňujte.<br />\n\nTýmto sa právne zaväzujete, že ste tento text buď napísali sám, alebo že je skopírovaný\nz voľného diela (public domain) alebo podobného zdroja neobmedzeného autorskými právami (podrobnosti: $1).\n<strong>NEUMIESTŇUJTE SEM BEZ POVOLENIA DIELA CHRÁNENÉ AUTORSKÝM PRÁVOM!</strong>",
        "longpageerror": "'''Chyba: Text, ktorý ste poslali má {{PLURAL:$1|jeden kilobajt|$1 kilobajty|$1 kilobajtov}}, čo je viac ako maximum {{PLURAL:$2|jeden kilobajt|$2 kilobajty|$2 kilobajtov}}.'''",
-       "readonlywarning": "'''UPOZORNENIE: Databáza bola počas upravovania stránky zamknutá z dôvodu údržby,\ntakže svoje úpravy momentálne nemôžete uložiť.'''\nMôžete skopírovať a vložiť text do textového súboru a uložiť si ho na neskôr.\n\nSprávca, ktorý ju zamkol, uviedol nasledovné vysvetlenie: $1",
+       "readonlywarning": "<strong>UPOZORNENIE: Databáza bola počas upravovania stránky zamknutá z dôvodu údržby,\ntakže svoje úpravy momentálne nemôžete uložiť.</strong>\nMôžete skopírovať a vložiť text do textového súboru a uložiť si ho na neskôr.\n\nSprávca systému, ktorý ju zamkol, uviedol nasledovné vysvetlenie: $1",
        "protectedpagewarning": "'''Upozornenie: Táto stránka bola zamknutá, takže ju môžu upravovať iba používatelia s oprávnením správcu.''' Dolu je pre informáciu posledná položka zo záznamu:",
        "semiprotectedpagewarning": "'''Poznámka:''' Táto stránka bola zamknutá tak, aby ju mohli upravovať iba registrovaní používatelia. Dolu je pre informáciu posledná položka zo záznamu:",
        "cascadeprotectedwarning": "'''Upozornenie:''' Táto stránka bola zamknutá (takže ju môžu upravovať iba používatelia s privilégiami správcu), pretože je použitá na {{PLURAL:$1|nasledovnej stránke|nasledovných stránkach}} s kaskádovým zamknutím:",
        "invalid-content-data": "Neplatné dáta obsahu",
        "content-not-allowed-here": "Obsah „$1“ nie je povolený na stránke [[$2]]",
        "editwarning-warning": "Ak opustíte túto stránku, môžete tým stratiť všetky vykonané zmeny.\nAk ste prihlásený, toto upozornenie môžete vypnúť v sekcii „{{int:prefs-editing}}“ svojich nastavení.",
-       "editpage-notsupportedcontentformat-title": "Obsahový formát nieje podporovaný",
+       "editpage-notsupportedcontentformat-title": "Formát obsahu nie je podporovaný",
        "content-model-wikitext": "wikitext",
        "content-model-text": "čistý text",
        "content-model-javascript": "JavaScript",
        "parser-unstrip-loop-warning": "Zistené zacyklenie volania rozširovacej značky",
        "parser-unstrip-recursion-limit": "Prektočený limit rekurzie volania rozširovacej značky ($1)",
        "converter-manual-rule-error": "Bola zistená chyba v pravidle manuálnej konverzie jazyka",
-       "undo-success": "Úpravu je možné vrátiť. Prosím skontrolujte tento rozdiel, čím overíte, že táto úprava je tá, ktorú chcete, a následne uložte zmeny, čím ukončíte vrátenie.",
+       "undo-success": "Úpravu je možné vrátiť.\nProsím, skontrolujte tento rozdiel, čím overíte, že táto úprava je tá, ktorú chcete. Následne uložte zmeny, čím ukončíte vrátenie.",
        "undo-failure": "Úpravu nie je možné vrátiť kvôli konfliktným medziľahlým úpravám.",
        "undo-norev": "Túto úpravu nie je možné vrátiť, pretože neexistuje alebo bola zmazaná.",
        "undo-nochange": "Zdá sa, že úprava už bola zrušená.",
        "undo-summary": "Revízia $1 používateľa [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusia]]) bola vrátená",
        "undo-summary-username-hidden": "Vrátiť revíziu $1, ktorú vykonal skrytý používateľ",
-       "cantcreateaccount-text": "Zakladanie nových účtov z tejto IP adresy ('''$1''') bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: ''$2''",
-       "cantcreateaccount-range-text": "Zakladanie nových účtov z IP adries v rozsahu <strong>$1</strong>, ktorý zahŕňa aj vašu IP adresu (<strong>$4</strong>), bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: <em>$2</em>",
+       "cantcreateaccount-text": "Zakladanie nových účtov z tejto IP adresy (<strong>$1</strong>) bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: <em>$2</em>",
+       "cantcreateaccount-range-text": "Zakladanie nových účtov z IP adries v rozsahu <strong>$1</strong>, do ktorého spadá aj vaša IP adresu (<strong>$4</strong>), bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: <em>$2</em>",
        "viewpagelogs": "Zobraziť záznamy pre túto stránku",
        "nohistory": "Pre túto stránku neexistuje história.",
        "currentrev": "Aktuálna verzia",
        "rev-deleted-user-contribs": "[používateľské meno alebo IP adresa odstránená - úprava skrytá pred prispievateľmi]",
        "rev-deleted-text-permission": "Táto revízia stránky bola '''zmazaná'''.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
        "rev-suppressed-text-permission": "Táto revízia stránky bola <strong>potlačená</strong>. Podrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
-       "rev-deleted-text-unhide": "Táto revízia stránky bola '''zmazaná'''.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].\nAko správca máte stále možnosť [$1 zobraziť túto revíziu] ak chcete.",
-       "rev-suppressed-text-unhide": "Táto revízia stránky bola '''potlačená'''.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].\nAko správca máte stále možnosť [$1 zobraziť túto revíziu] ak chcete.",
+       "rev-deleted-text-unhide": "Táto revízia stránky bola <strong>zmazaná</strong>.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].\nAko správca máte stále možnosť [$1 zobraziť túto revíziu] ak chcete.",
+       "rev-suppressed-text-unhide": "Táto revízia stránky bola <strong>potlačená</strong>.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].\nAko správca máte stále možnosť [$1 zobraziť túto revíziu] ak chcete.",
        "rev-deleted-text-view": "Táto revízia stránky bola '''zmazaná'''.\nAko správca {{GRAMMAR:genitív|{{SITENAME}}}} si ju môžete prezrieť;\npodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
-       "rev-suppressed-text-view": "Táto revízia stránky bola '''potlačená'''.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
+       "rev-suppressed-text-view": "Táto revízia stránky bola <strong>potlačená</strong>.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
        "rev-deleted-no-diff": "Tento rozdiel nemôžete zobraziť, pretože bol '''zmazaný'''.\nPodrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
        "rev-suppressed-no-diff": "Nemôžete zobraziť tento rozdiel, pretože jedna z revízií bola '''zmazaná'''.",
-       "rev-deleted-unhide-diff": "Jedna z revízií tohto rozdielu bola '''zmazaná'''.\nPodrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].\nAko správca {{GRAMMAR:genitív|{{SITENAME}}}} si [$1 tento rozdiel môžete prezrieť].",
-       "rev-suppressed-unhide-diff": "Jedna z revízií tohto rozdielu bola '''potlačená'''.\nPodrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].\nAko správca {{GRAMMAR:genitív|{{SITENAME}}}} si [$1 tento rozdiel môžete prezrieť].",
-       "rev-deleted-diff-view": "Jedna z revízií tohto rozdielu bola '''zmazaná'''.\nAko správca si môžete tento rozdiel zobraziť; podrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
-       "rev-suppressed-diff-view": "Jedna z revízií tohto rozdielu bola '''potlačená'''.\nAko správca si môžete tento rozdiel zobraziť; podrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
+       "rev-deleted-unhide-diff": "Jedna z revízií tohto rozdielu bola <strong>zmazaná</strong>.\nPodrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].\nAko správca {{GRAMMAR:genitív|{{SITENAME}}}} si [$1 tento rozdiel môžete prezrieť].",
+       "rev-suppressed-unhide-diff": "Jedna z revízií tohto rozdielu bola <strong>potlačená</strong>.\nPodrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].\nAko správca {{GRAMMAR:genitív|{{SITENAME}}}} si [$1 tento rozdiel môžete prezrieť].",
+       "rev-deleted-diff-view": "Jedna z revízií tohto rozdielu bola <strong>zmazaná</strong>.\nAko správca si môžete tento rozdiel zobraziť; podrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname mazaní].",
+       "rev-suppressed-diff-view": "Jedna z revízií tohto rozdielu bola <strong>potlačená</strong>.\nAko správca si môžete tento rozdiel zobraziť; podrobnosti môžete nájsť v [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zázname potlačení].",
        "rev-delundel": "zobraziť/skryť",
        "rev-showdeleted": "zobraziť",
        "revisiondelete": "Zmazať/obnoviť revízie",
        "revdelete-legend": "Nastaviť obmedzenia viditeľnosti",
        "revdelete-hide-text": "Text revízie",
        "revdelete-hide-image": "Skryť obsah súboru",
-       "revdelete-hide-name": "Skryť činnosť a cieľ",
+       "revdelete-hide-name": "Skryť cieľ a parametre",
        "revdelete-hide-comment": "Zhrnutie úprav",
        "revdelete-hide-user": "Používateľské meno/IP redaktora",
        "revdelete-hide-restricted": "Zatajiť údaje pred všetkými, aj pred správcami",
        "revdelete-submit": "Použiť na {{PLURAL:$1|zvolenú revíziu|zvolené revízie}}",
        "revdelete-success": "'''Viditeľnosť revízie bola úspešne aktualizovaná.'''",
        "revdelete-failure": "'''Viditeľnosť revízie nebolo možné aktualizovať:'''\n$1",
-       "logdelete-success": "'''Viditeľnosť záznamu bola úspešne nastavená.'''",
+       "logdelete-success": "Viditeľnosť záznamu bola úspešne nastavená.",
        "logdelete-failure": "'''Viditeľnosť záznamu nebolo možné nastaviť:'''\n$1",
        "revdel-restore": "Zmeniť viditeľnosť",
        "pagehist": "História stránky",
        "showhideselectedversions": "Zobraziť/skryť vybrané revízie",
        "editundo": "vrátiť",
        "diff-empty": "(Žiaden rozdiel)",
-       "diff-multi-sameuser": "({{PLURAL:$1|Jedna medziľahlá úprava|$1 medziľahlé úpravy|$1 medziľahlých úprav}} od rovnakého používateľa.)",
+       "diff-multi-sameuser": "({{PLURAL:$1|Jedna medziľahlá úprava|$1 medziľahlé úpravy|$1 medziľahlých úprav}} od rovnakého používateľa nie {{PLURAL:$1|je zobrazená|sú zobrazené|je zobrazených}}.)",
        "diff-multi-otherusers": "({{PLURAL:$1|Jedna medziľahlá úprava|$1 medziľahlé úpravy|$1 medziľahlých úprav}} od {{PLURAL:$2|jedného ďalšieho používateľa|$2 ďalších používateľov}} {{PLURAL:$1|nie je zobrazená|nie sú zobrazené|nie je zobrazených}})",
        "diff-multi-manyusers": "({{PLURAL:$1|$1 medziľahlá revízia|$1 medziľahlé revízie|$1 medziľahlých revízií}} od viac ako {{PLURAL:$2|$2 používateľa|$2 používateľov}} {{PLURAL:$1|nie je zobrazená|nie sú zobrazené|nie je zobrazených}})",
        "difference-missing-revision": "{{PLURAL:$2|$2 revízia|$2 revízie|$2 revízií}} pre požadovaný rozdiel ($1) {{PLURAL:$2|neexistuje|neexistujú|neexistuje}}.\n\nPravdepodobne ste nasledovali zastaraný odkaz na rozdiel revízií, z ktorých niektorá bola medzičasom odstránená.\nPodrobnosti nájdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zázname zmazaní].",
        "search-suggest": "Mali ste na mysli „$1“?",
        "search-rewritten": "Zobrazujú sa výsledky pre $1. Vyhľadať namiesto toho $2.",
        "search-interwiki-caption": "Sesterské projekty",
-       "search-interwiki-default": "$1 výsledkov:",
+       "search-interwiki-default": "Výsledky z $1:",
        "search-interwiki-more": "(viac)",
        "search-relatedarticle": "Súvisiace",
        "searchrelated": "súvisiace",
        "rows": "Riadky:",
        "columns": "Stĺpce:",
        "searchresultshead": "Vyhľadávanie",
-       "stub-threshold": "Prah formátovania odkazu ako výhonok:",
+       "stub-threshold": "Prah formátovania odkazu ako výhonok ($1):",
        "stub-threshold-sample-link": "príklad",
        "stub-threshold-disabled": "Vypnuté",
        "recentchangesdays": "Koľko dní zobrazovať v posledných úpravách:",
        "badsig": "Neplatný podpis v pôvodnom tvare; skontrolujte HTML značky.",
        "badsiglength": "Váš podpis je príliš dlhý.\nMusí obsahovať menej ako $1 {{PLURAL:$1|znak|znaky|znakov}}.",
        "yourgender": "Ako si želáte byť označovaný?",
-       "gender-unknown": "Radšej nechcem uviesť",
+       "gender-unknown": "Softvér bude používať rodovo neutrálne slová, vždy keď je to možné, keď vás spomína",
        "gender-male": "On upravuje wiki stránky",
        "gender-female": "Ona upravuje wiki stránky",
        "prefs-help-gender": "Nastavenie tejto voľby nie je povinné.\nSoftvér používa toto nastavenie na správne oslovenie a označenie vás ostatným v závislosti od gramatického rodu. Táto informácia bude verejná.",
        "prefs-dateformat": "Formát dátumu",
        "prefs-timeoffset": "Časový posun",
        "prefs-advancedediting": "Všeobecné možnosti",
-       "prefs-editor": "Redaktor",
+       "prefs-editor": "Používateľ",
        "prefs-preview": "Náhľad",
        "prefs-advancedrc": "Rozšírené možnosti",
        "prefs-advancedrendering": "Rozšírené možnosti",
        "editusergroup": "Upraviť skupiny {{GENDER:$1|používateľa|používateľky}}",
        "editinguser": "Zmena práv používateľa '''[[User:$1|$1]]''' $2",
        "userrights-editusergroup": "Upraviť skupiny používateľa",
-       "saveusergroups": "Uložiť skupiny používateľa",
+       "saveusergroups": "Uložiť skupiny {{GENDER:$1|používateľa|používateľky}}",
        "userrights-groupsmember": "{{GENDER:$2|Člen|Členka}} {{PLURAL:$1|skupiny|skupín}}:",
        "userrights-groupsmember-auto": "Implicitne {{GENDER:$2|člen|členka}} {{PLURAL:$1|skupiny|skupín}}:",
        "userrights-groups-help": "Môžete zmeniť skupiny, do ktorých je {{GENDER:$1|používateľ zaradený|používateľka zaradená}}.\n* Zaškrtnuté pole znamená, že {{GENDER:$1|používateľ|používateľka}} je v skupine.\n* Nezaškrtnuté pole znamená, že {{GENDER:$1|používateľ|používateľka}} nie je v skupine.\n* Hviezdička (*) znamená, že nemôžete odstrániť skupinu, keď ste ju už pridali resp. naopak.",
        "right-move": "Presúvať stránky",
        "right-move-subpages": "Presunúť stránky aj s podstránkami",
        "right-move-rootuserpages": "Presunúť koreňové stránky používateľa",
-       "right-move-categorypages": "Premiestňovanie stránok kategórií",
+       "right-move-categorypages": "Premiestňovať stránky kategórií",
        "right-movefile": "Presunúť súbory",
        "right-suppressredirect": "Nevytvoriť presmerovanie zo starého názvu pri presúvaní stránky",
        "right-upload": "Nahrávať súbory",
        "right-writeapi": "Použitie API na zápis",
        "right-delete": "Mazať stránky",
        "right-bigdelete": "Mazať stránky s veľkou históriou",
-       "right-deletelogentry": "Odstrániť a obnoviť špecifické položky",
+       "right-deletelogentry": "Odstrániť a obnoviť špecifické položky záznamu",
        "right-deleterevision": "Mazať a obnovovať konkrétne revízie stránok",
        "right-deletedhistory": "Zobrazovať zmazané položky histórie bez ich plného textu",
        "right-deletedtext": "Zobrazovať zmazané texty a zmeny medzi zmazanými verziami",
        "right-override-export-depth": "Exportovať stránky vrátane okdazovaných stránok do hĺbky 5 odkazov",
        "right-sendemail": "Posielať e-mail ostatným používateľom",
        "right-passwordreset": "Prezeranie e-mailov pre znovunastavovanie hesla",
+       "grant-generic": "balík práv „$1“",
+       "grant-group-page-interaction": "Interagovať so stránkami",
+       "grant-group-file-interaction": "Interagovať s multimédiami",
+       "grant-group-watchlist-interaction": "Interagovať s vašim zoznamom sledovaných stránok",
        "grant-group-email": "Poslať email",
+       "grant-group-high-volume": "Vykonávať činnosti vo veľkom objeme",
+       "grant-group-customization": "Nastavenie a prispôsobenie",
+       "grant-group-administration": "Vykonávať činnosti správcu",
+       "grant-group-private-information": "Pristupovať k osobným údajom o vás",
+       "grant-group-other": "Rozličné činnosti",
+       "grant-blockusers": "Blokovať a odblokovať používateľov",
+       "grant-createaccount": "Vytvárať účty",
+       "grant-createeditmovepage": "Vytvárať, upravovať a presúvať stránky",
+       "grant-delete": "Odstraňovať stránky, revízie a položky záznamu",
+       "grant-editinterface": "Upravovať menný priestor MediaWiki a používateľský CSS/JavaScript",
+       "grant-editmycssjs": "Upravovať váš používateľský CSS/JavaScript",
+       "grant-editmyoptions": "Upravovať nastavenia vášho používateľského účtu",
+       "grant-editmywatchlist": "Upravovať váš zoznam sledovaných stránok",
+       "grant-editpage": "Upravovať existujúce stránky",
+       "grant-editprotected": "Upravovať chránené stránky",
+       "grant-highvolume": "Úpravy vo veľkom objeme",
+       "grant-oversight": "Skrývať používateľov a potláčať revízie",
+       "grant-patrol": "Sledovať zmeny stránok",
+       "grant-privateinfo": "Pristupovať k súkromným informáciám",
+       "grant-protect": "Zapínať a vypínať ochranu stránok",
+       "grant-rollback": "Vracať zmeny stránok",
+       "grant-sendemail": "Posielať emaily ostatným používateľom",
+       "grant-uploadeditmovefile": "Nahrávať, nahradzovať a presúvať súbory",
+       "grant-uploadfile": "Nahrávať nové súbory",
+       "grant-basic": "Základné oprávnenia",
+       "grant-viewdeleted": "Zobrazovať vymazané súbory a stránky",
+       "grant-viewmywatchlist": "Zobrazovať váš zoznam sledovaných stránok",
        "newuserlogpage": "Záznam vytvorených používateľov",
        "newuserlogpagetext": "Toto je záznam naposledy vytvorených používateľských účtov.",
        "rightslog": "Záznam používateľských práv",
        "rightslogtext": "Toto je záznam zmien práv používateľa.",
        "action-read": "čítať túto stránku",
        "action-edit": "upravovať túto stránku",
-       "action-createpage": "vytvárať stránky",
-       "action-createtalk": "vytvárať diskusné stránky",
+       "action-createpage": "vytvoriť túto stránku",
+       "action-createtalk": "vytvoriť túto diskusnú stránku",
        "action-createaccount": "vytvoriť tento používateľský účet",
+       "action-autocreateaccount": "automaticky vytvoriť tento externý používateľský účet",
        "action-history": "zobraziť históriu tejto stránky",
        "action-minoredit": "označiť túto úpravu ako drobnú",
        "action-move": "presunúť túto stránku",
        "action-viewmyprivateinfo": "zobraziť vaše súkromné údaje",
        "action-editmyprivateinfo": "upraviť vaše súkromné údaje",
        "action-editcontentmodel": "upraviť model obsahu stránky",
-       "action-managechangetags": "vytvorte a odstráňte značky z databázy",
+       "action-managechangetags": "vytvoriť a (de)aktivovať značky",
+       "action-applychangetags": "použiť značky spolu s vašimi zmenami",
+       "action-changetags": "pridávať a odstraňovať ľubovoľné značky na jednotlivé revízie a položky záznamu",
+       "action-deletechangetags": "odstraňovať značky z databázy",
+       "action-purge": "vyčistiť vyrovnávacou pamäť tejto stránky",
        "nchanges": "$1 {{PLURAL:$1|úprava|úpravy|úprav}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|od poslednej návštevy}}",
        "enhancedrc-history": "história",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|sledujúci používateľ|sledujúci používatelia|sledujúcich používateľov}}]",
        "rc_categories": "Obmedziť na kategórie (oddeľte znakom „|“)",
-       "rc_categories_any": "akékoľvek",
+       "rc_categories_any": "Akékoľvek z vybraných",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajty|bajtov}} po zmene",
        "newsectionsummary": "/* $1 */ nová sekcia",
        "rc-enhanced-expand": "Zobraziť podrobnosti",
        "recentchangeslinked-page": "Názov stránky:",
        "recentchangeslinked-to": "Zobraziť zmeny na stránkach, ''ktoré odkazujú na'' zadanú stránku",
        "recentchanges-page-added-to-category": "[[:$1]] zaradená do kategórie",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]] a [[Special:WhatLinksHere/$1|{{PLURAL:$2|jedna ďalšia zaradené|$2 ďalšie zaradené|$2 ďalších zaradených}}]] do kategórie",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] zaradená do kategórie. [[Special:WhatLinksHere/$1|Táto stránka je vložená do iných stránok.]",
        "recentchanges-page-removed-from-category": "[[:$1]] vyradená z kategórie",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] a {{PLURAL:$2|jedna ďalšia vyradené|$2 ďalšie vyradené|$2 ďalších vyradených}} z kategórie",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] odstránená z kategórie. [[Special:WhatLinksHere/$1|Táto stránka je vložená do iných stránok.]",
        "autochange-username": "Automatická úprava MediaWiki",
        "upload": "Nahrať súbor",
        "uploadbtn": "Nahrať súbor",
        "file-thumbnail-no": "Názov súboru začína <strong>$1</strong>.\nZdá sa, že je to obrázok redukovanej veľkosti ''(náhľad)''.\nAk máte tento obrázok v plnom rozlíšení, nahrajte ho, inak prosím zmeňte názov.",
        "fileexists-forbidden": "Súbor s týmto názvom už existuje a nie je možné ho prepísať.\nAk si aj tak želáte nahrať svoj súbor, choďte prosím späť a nahrajte tento súbor pod iným názvom. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Súbor s týmto názvom už existuje v zdieľanom úložisku súborov.\nAk ho chcete aj napriek tomu nahrať, choďte prosím späť a použite iný názov. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Nahraný súbor je presný duplikát aktuálnej verzie súboru <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Nahraný súbor je presný duplikát {{PLURAL:$2|staršej verzie|starších verzií}} súboru <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Tento súbor je duplikátom {{PLURAL:$1|nasledovného súboru|nasledovných súborov}}:",
        "file-deleted-duplicate": "Súbor zhodný s týmto súborom ([[:$1]]) už bol v minulosti zmazaný. Mali by ste skontrolovať históriu nahrávania tohto súboru predtým, než budete pokračovať v jeho nahrávaní.",
+       "file-deleted-duplicate-notitle": "Súbor rovnaký ako tento súbor už bol v minulosti odstránený a jeho názov bol potlačený.\nMali by ste sa spýtať niekoho s oprávnením prehliadať potlačené údaje súbor, aby preskúmal situáciu predtým, než ho nahráte.",
        "uploadwarning": "Varovanie pri nahrávaní",
        "uploadwarning-text": "Prosím, zmeňte popis súboru nižšie a skúste to znova.",
        "savefile": "Uložiť súbor",
        "uploaddisabledtext": "Nahrávanie súborov je vypnuté.",
        "php-uploaddisabledtext": "Nahrávanie PHP súborov je vypnuté. Prosím, skontrolujte nastavenie file_uploads.",
        "uploadscripted": "Tento súbor obsahuje kód HTML alebo skript, ktorý može byť chybne interpretovaný prehliadačom.",
+       "upload-scripted-pi-callback": "Nemožno nahrať súbor, ktorý obsahuje inštrukcie spracovania štýlu XML",
+       "uploaded-script-svg": "V nahranom súbore SVG bol nájdený skriptovateľný prvok „$1“.",
+       "uploaded-hostile-svg": "V elemente „style“ nahraného súboru SVG bolo nájdené nebezpečné CSS.",
+       "uploaded-event-handler-on-svg": "Nastavenie atribútov <code>$1=\"$2\"</code> na obsluhu udalostí v súboroch SVG nie je povolené.",
+       "uploaded-href-attribute-svg": "V súboroch SVG je pri atribútoch href povolené iba to, aby odkazovali na ciele http:// alebo https://, ale našiel sa odkaz <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Boli nájdené nebezpečné dáta: cieľ URI <code>&lt;$1 $2=\"$3\"&gt;</code> v nahranom súbore SVG",
        "uploadvirus": "Súbor obsahuje vírus! Podrobnosti: $1",
        "uploadjava": "Súbor je vo formáte ZIP, ktorý obsahuje Java súbor .class.\nNahrávanie súborov Java nie je povolené, pretože môžu spôsobiť obídenie bezpečnostných obmedzení.",
        "upload-source": "Zdrojový súbor",
        "upload-form-label-infoform-title": "Podrobnosti",
        "upload-form-label-infoform-name": "Meno",
        "upload-form-label-infoform-name-tooltip": "Jedinečný popis súboru, ktorý bude slúžiť ako názov súboru. Môžete použiť bežný jazyk s medzerami, ako aj znaky s diakritikou. Nezadávajte príponu súboru.",
+       "upload-form-label-infoform-description": "Popis",
+       "upload-form-label-usage-title": "Použitie",
+       "upload-form-label-usage-filename": "Názov súboru",
+       "upload-form-label-own-work": "Toto je moje vlastné dielo",
+       "upload-form-label-infoform-categories": "Kategórie",
+       "upload-form-label-infoform-date": "Dátum",
+       "upload-form-label-own-work-message-generic-local": "Potvrdzujem, že som nahrávam tento súbor v súlade s podmienkami služby a licenčnou politikou {{GRAMMAR:genitív|{{SITENAME}}}}.",
+       "upload-form-label-not-own-work-message-generic-local": "Ak tento súbor nie ste schopní nahrať v súlade s politikou {{GRAMMAR:genitív|{{SITENAME}}}}, prosím zatvorte toto dialógové okno a skúste použiť iný spôsob.",
+       "upload-form-label-not-own-work-local-generic-local": "Môžete tiež skúsiť [[Special:Upload|predvolenú nahrávaciu stránku]].",
+       "upload-form-label-own-work-message-generic-foreign": "Rozumiem, že nahrávam tento súbor do zdieľaného úložiska. Potvrdzujem, že pritom dodržiavam tamojšie podmienky služby a licenčné politiky.",
+       "upload-form-label-not-own-work-message-generic-foreign": "Ak tento súbor nie ste schopní nahrať v súlade s politikou zdieľaného úložiska, prosím zatvorte toto dialógové okno a skúste použiť iný spôsob.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Môžete tiež skúsiť použiť [[Special:Upload|nahrávaciu stránku na {{GRAMMAR:genitív|{{SITENAME}}}}]] ak je v súlade s ich politikou možné tento súbor nahrať.",
        "backend-fail-stream": "$1 je názov súboru.",
        "backend-fail-backup": "Nebolo možné zálohovať súbor $1.",
        "backend-fail-notexists": "Súbor $1 neexistuje.",
        "backend-fail-read": "Nebolo možné prečítať súbor „$1“.",
        "backend-fail-create": "Nebolo možné zapísať súbor $1.",
        "backend-fail-maxsize": "Nie je možné zapísať súbor  $1  pretože je väčší ako  {{PLURAL:$2| jeden byte| $2  bajtov}}.",
-       "backend-fail-readonly": "Úložisko „$1“ je momentálne v režime len na čítanie. Udaný dôvod: „$2“",
+       "backend-fail-readonly": "Úložisko „$1“ je momentálne v režime len na čítanie. Udaný dôvod: „<em>$2</em>“",
        "backend-fail-synced": "Súbor „$1“ je v nekonzistentnom stave v rámci vnútorného úložiska",
        "backend-fail-connect": "Nepodarilo sa pripojiť k úložisku „$1“.",
        "backend-fail-internal": "Vyskytla sa neznáma chyba v úložisku „$1“.",
        "uploadstash-summary": "Táto stránka poskytuje prístup k súborom nahraným (alebo práve nahrávaným), ktoré zatiaľ nie sú zverejnené na wiki. Tieto súbory nevidí nikto iný okrem používateľa, ktorý ich nahral.",
        "uploadstash-clear": "Vymazať skrýšu nahraných súborov",
        "uploadstash-nofiles": "Nemáte žiadne súbory v skrýši nahraných súborov.",
-       "uploadstash-badtoken": "Vykonanie operácie sa nepodarilo, možno preto, že platnosť vašich prihlasovacích údajov vypršala. Skúste to znova.",
+       "uploadstash-badtoken": "Vykonanie operácie sa nepodarilo, možno preto, že platnosť vašich prihlasovacích údajov vypršala. Prosím, skúste to znova.",
        "uploadstash-errclear": "Vymazanie súborov bolo neúspešné.",
        "uploadstash-refresh": "Obnoviť zoznam súborov",
+       "uploadstash-thumbnail": "zobraziť náhľad",
+       "uploadstash-exception": "Načítaný súbor sa nepodarilo uložiť do skrýše ($1): „$2“.",
        "invalid-chunk-offset": "Neplatný posun bloku",
        "img-auth-accessdenied": "Prístup zamietnutý",
        "img-auth-nopathinfo": "Váš server nie je nastavený tak, aby poskytoval tieto informácie.\nMôže byť založený na CGI a nedokáže podporovať img_auth.\nPozri https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Obnoviť",
        "filerevert-success": "'''[[Media:$1|$1]]''' bol obnovený na [$4 verziu z $2, $3].",
        "filerevert-badversion": "Neexistuje predchádzajúca lokálna verzia tohto súboru s požadovanou časovou značkou.",
+       "filerevert-identical": "Aktuálna verzia súboru už zodpovedá zadanej verzii.",
        "filedelete": "Zmazať $1",
        "filedelete-legend": "Zmazať súbor",
        "filedelete-intro": "Chystáte sa zmazať súbor '''[[Media:$1|$1]]''' spolu s celou jeho históriou.",
        "download": "stiahnuť",
        "unwatchedpages": "Nesledované stránky",
        "listredirects": "Zoznam presmerovaní",
+       "listduplicatedfiles": "Zoznam súborov s duplikátmi",
+       "listduplicatedfiles-summary": "Toto je zoznam súborov, u ktorých najnovšia verzia súboru je duplikát najnovšej verzie nejakého iného súboru. Berú sa do úvahy iba lokálne súbory.",
+       "listduplicatedfiles-entry": "[[:File:$1|$1]] má [[$3|{{PLURAL:$2|duplikát|$2 duplikáty|$2 duplikátov}}]].",
        "unusedtemplates": "Nepoužité šablóny",
        "unusedtemplatestext": "Táto stránka obsahuje zoznam všetkých stránok v mennom priestore {{ns:template}}:, ktoré nie sú vložené v žiadnej inej stránke. Pred zmazaním nezabudnite skontrolovať ostatné odkazy!",
        "unusedtemplateswlh": "iné odkazy",
        "doubleredirects": "Dvojité presmerovania",
        "doubleredirectstext": "Táto stránka obsahuje zoznam stránok, ktoré presmerovávajú na iné presmerovacie stránky.\nKaždý riadok obsahuje odkaz na prvé a druhé presmerovanie a tiež prvý riadok z textu na ktorý odkazuje druhé presmerovanie, ktoré zvyčajne odkazuje na „skutočný“ cieľ, na ktorý má odkazovať prvé presmerovanie.\n<del>Prečiarknuté</del> položky boli vyriešené.",
        "double-redirect-fixed-move": "Stránka [[$1]] bola presunutá.\nBola automaticky aktualizovaná a teraz presmerováva na [[$2]]",
-       "double-redirect-fixed-maintenance": "Opravuje sa dvojité presmerovanie z [[$1]] na [[$2]].",
+       "double-redirect-fixed-maintenance": "V rámci úlohy údržby sa automaticky sa opravuje dvojité presmerovanie z [[$1]] na [[$2]].",
        "double-redirect-fixer": "Korektor presmerovaní",
        "brokenredirects": "Pokazené presmerovania",
        "brokenredirectstext": "Nasledovné presmerovania odkazujú na neexistujúce stránky:",
        "wantedpages-badtitle": "Neplatný názov vo výsledkoch: $1",
        "wantedfiles": "Žiadané súbory",
        "wantedfiletext-cat": "Nasledovné súbory sa používajú, ale nie sú k dispozícii. Súbory z cudzích repozitárov môžu byť uvedené aj napriek tomu, že existujú. Takéto falošné poplachy budú <del>prečiarknuté</del>. Okrem toho stránky, ktoré obsahujú vložené súbory, ktoré nie sú k dispozícii sú uvedené na [[:$1]].",
+       "wantedfiletext-cat-noforeign": "Následujúce súbory sa používajú, ale neexistujú. Stránky, ktoré vkladajú neexistujúce súbory, sú naviac uvedené v [[:$1]].",
        "wantedfiletext-nocat": "Nasledovné súbory sa používajú, ale nie sú k dispozícii. Súbory z cudzích repozitárov môžu byť uvedené aj napriek tomu, že existujú. Takéto falošné poplachy budú <del>prečiarknuté</del>.",
+       "wantedfiletext-nocat-noforeign": "Následujúce súbory sa používajú, ale neexistujú.",
        "wantedtemplates": "Žiadané šablóny",
        "mostlinked": "Najčastejšie odkazované stránky",
        "mostlinkedcategories": "Najčastejšie odkazované kategórie",
        "protectedpages-reason": "Dôvod",
        "protectedpages-submit": "Zobraziť stránky",
        "protectedpages-unknown-timestamp": "Neznáme",
-       "protectedpages-unknown-performer": "Neznámy redaktor",
+       "protectedpages-unknown-performer": "Neznámy používateľ",
        "protectedtitles": "Zamknuté názvy",
        "protectedtitles-summary": "Táto stránka obsahuje zoznam názvov, ktoré sú momentálne zamknuté proti vytvoreniu. Zoznam existujúcich zamknutých stránok nájdete na stránke [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Tieto parametre momentálne nezamykajú žiadne názvy stránok.",
        "nopagetext": "Cieľová stránka, ktorú ste uviedli neexistuje.",
        "pager-newer-n": "{{PLURAL:$1|1 novší|$1 novšie|$1 novších}}",
        "pager-older-n": "{{PLURAL:$1|1 starší|$1 staršie|$1 starších}}",
-       "suppress": "Dozor",
+       "suppress": "Potlačenie",
        "querypage-disabled": "Táto špeciálna stránka bola zakázaná z výkonnostných dôvodov.",
+       "apihelp": "Pomocník API",
+       "apihelp-no-such-module": "Modul „$1” nebol nájdený.",
        "apisandbox": "API pieskovisko",
+       "apisandbox-jsonly": "Na použitie pieskoviska API je nutný JavaScript.",
        "apisandbox-api-disabled": "API je na tejto stránke vypnuté.",
+       "apisandbox-intro": "Pomocou tejto stránky môžete experimentovať s <strong>API webovej služby MediaWiki</strong>.\nPodrobnosti využitia API nájdete v [[mw:API:Main page|jeho dokumentácii]]. Príklad: [https://www.mediawiki.org/wiki/API#A_simple_example získanie obsahu Hlavnej stránky]. Ďalšie príklady uvidíte vybraním operácie.\n\nUvedomte si, že napriek tomu, že ste na pieskovisku, môžu operácie vykonané na tejto stránke wiki zmeniť.",
        "apisandbox-fullscreen": "Rozbaliť panel",
        "apisandbox-unfullscreen": "Zobraziť stránku",
        "apisandbox-submit": "Odoslať dopyt",
        "apisandbox-reset": "Vyčistiť",
        "apisandbox-retry": "Skúsiť znova",
        "apisandbox-examples": "Príklady",
-       "apisandbox-results": "Výsledok",
+       "apisandbox-results": "Výsledky",
        "apisandbox-request-url-label": "URL požiadavky:",
        "booksources": "Knižné zdroje",
        "booksources-search-legend": "Vyhľadávať knižné zdroje",
        "booksources-text": "Nižšie je zoznam odkazov na iné stránky, ktoré predávajú nové a použité knihy a tiež môžu obsahovať ďalšie informácie o knihách, ktoré hľadáte:",
        "booksources-invalid-isbn": "Zdá sa, že dané ISBN nie je platné. Skontrolujte, či ste neurobili chybu pri kopírovaní z pôvodného zdroja.",
        "specialloguserlabel": "Pôvodca:",
-       "speciallogtitlelabel": "Cieľ (názov alebo používateľ):",
+       "speciallogtitlelabel": "Cieľ (názov alebo {{ns:user}}:Používateľské meno):",
        "log": "Záznamy",
        "logeventslist-submit": "Zobraziť",
        "all-logs-page": "Všetky verejné záznamy",
        "wlshowlast": "Zobraziť posledných $1 hodín $2 dní",
        "watchlist-hide": "Skryť",
        "watchlist-submit": "Zobraziť",
-       "wlshowtime": "Zobraziť posl.:",
+       "wlshowtime": "Zobrazené obdobie:",
        "wlshowhideminor": "drobné úpravy",
        "wlshowhidebots": "botov",
        "wlshowhideliu": "registrovaných",
        "exbeforeblank": "obsah pred vyčistením stránky bol: '$1'",
        "delete-confirm": "Zmazať „$1“",
        "delete-legend": "Zmazať",
-       "historywarning": "'''Upozornenie:''' Stránka, ktorú sa chystáte zmazať má históriu obsahujúcu približne $1 {{PLURAL:$1|revíziu|revízie|revízií}}:",
+       "historywarning": "<strong>Upozornenie:</strong> Stránka, ktorú sa chystáte zmazať má históriu obsahujúcu $1 {{PLURAL:$1|revíziu|revízie|revízií}}:",
        "historyaction-submit": "Zobraziť",
        "confirmdeletetext": "Chystáte sa trvalo zmazať z databázy stránku alebo obrázok spolu so všetkými jeho/jej predošlými verziami. Potvrďte, že máte v úmysle tak urobiť, že ste si vedomý následkov, a že to robíte v súlade so [[{{MediaWiki:Policy-url}}|zásadami a smernicami {{GRAMMAR:genitív|{{SITENAME}}}}]].",
        "actioncomplete": "Úloha bola dokončená",
        "delete-toobig": "Táto stránka má veľkú históriu úprav, viac ako $1 {{PLURAL:$1|revíziu|revízie|revízií}}. Mazanie takýchto stránok bolo obmedzené, aby sa zabránilo náhodnému poškodeniu {{GRAMMAR:genitív|{{SITENAME}}}}.",
        "delete-warning-toobig": "Táto stránka má veľkú históriu úprav, viac ako $1 {{PLURAL:$1|revíziu|revízie|revízií}}. Jej zmazanie by mohlo narušiť databázové operácie {{GRAMMAR:genitív|{{SITENAME}}}}; postupujte opatrne.",
        "deleteprotected": "Túto stránku nemôžete vymazať, pretože je zamknutá.",
-       "deleting-backlinks-warning": "'''Upozornenie:''' Stránka, ktorú sa chystáte zmazať, je odkazovaná [[Special:WhatLinksHere/{{FULLPAGENAME}}|z iných stránok]], prípadne do nich vložená.",
+       "deleting-backlinks-warning": "<strong>Upozornenie:</strong> Na stránku, ktorú sa chystáte zmazať, odkazujú [[Special:WhatLinksHere/{{FULLPAGENAME}}|iné stránky]], prípadne je do nich vložená.",
        "rollback": "Vrátiť späť úpravy",
        "rollbacklink": "vrátiť",
        "rollbacklinkcount": "vrátenie $1 {{PLURAL:$1|úpravy|úprav}}",
        "rollback-success": "Úpravy $1 vrátené; obnovená posledná verzia od $2.",
        "sessionfailure-title": "Chyba relácie",
        "sessionfailure": "Zdá sa, že je problém s vašou prihlasovacou reláciou;\ntáto akcia bola zrušená ako prevencia proti zneužitiu relácie (session).\nProsím, stlačte \"naspäť\", obnovte stránku, z ktorej ste sa sem dostali, a skúste to znova.",
+       "changecontentmodel": "Zmeniť model obsahu stránky",
+       "changecontentmodel-legend": "Zmeniť model obsahu",
+       "changecontentmodel-title-label": "Názov stránky",
+       "changecontentmodel-model-label": "Nový model obsahu",
+       "changecontentmodel-reason-label": "Dôvod:",
+       "changecontentmodel-submit": "Zmeniť",
+       "changecontentmodel-success-title": "Model obsahu bol zmenený",
+       "changecontentmodel-success-text": "Typ obsahu [[:$1]] bol zmenený.",
+       "changecontentmodel-cannot-convert": "Obsah na [[:$1]] nie je možné konvertovať na typ $2.",
+       "changecontentmodel-nodirectediting": "Model obsahu $1 nepodporuje priame úpravy",
+       "changecontentmodel-emptymodels-title": "Žiadne modely obsahu nie sú k dispozícii",
+       "changecontentmodel-emptymodels-text": "Obsah na [[:$1]] nie je možné konvertovať na žiaden typ.",
        "log-name-contentmodel": "Záznam zmien modelov obsahu",
        "log-description-contentmodel": "Udalosti, týkajúce sa modelov obsahu stránok",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|vytvoril|vytvorila}} stránku $3 pomocou neštandardného modelu obsah „$5“",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|zmenil|zmenila}} model obsahu stránky $3 zo „$4“ na „$5“",
+       "logentry-contentmodel-change-revertlink": "vrátiť",
+       "logentry-contentmodel-change-revert": "vrátiť",
        "protectlogpage": "Záznam zamknutí",
        "protectlogtext": "Nižšie je zoznam zmien stavu ochrany stránok.\nMôžete si pozrieť aj [[Special:ProtectedPages|zoznam momentálne platných ochrán stránok]].",
        "protectedarticle": "zamyká „[[$1]]“",
        "protect-locked-blocked": "Nemôžete meniť úroveň ochrany, kým ste zablokovaný.\nTu sú aktuálne nastavenia stránky '''$1''':",
        "protect-locked-dblock": "Nie je možné zmeniť úroveň ochrany z dôvodu aktívneho zámku databázy.\nTu sú aktuálne nastavenia stránky '''$1''':",
        "protect-locked-access": "Váš účet nemá oprávnenie meniť úroveň ochrany stránky.\nTu sú aktuálne nastavenia stránky '''$1''':",
-       "protect-cascadeon": "Táto stránka je momentálne zamknutá, lebo je použitá na {{PLURAL:$1|nasledovnej stránke, ktorá má|nasledovných stránkach, ktoré majú}} zapnutú kaskádovú ochranu. Zmeny úrovne ochrany tejto stránky neovplyvnia kaskádovú ochranu.",
+       "protect-cascadeon": "Táto stránka je momentálne zamknutá, lebo je vložená v {{PLURAL:$1|nasledovnej stránke, ktorá má|nasledovných stránkach, ktoré majú}} zapnutú kaskádovú ochranu. Zmeny úrovne ochrany tejto stránky neovplyvnia kaskádovú ochranu.",
        "protect-default": "Povoliť všetkých používateľov",
        "protect-fallback": "Povoliť iba používateľov s oprávnením „$1“",
        "protect-level-autoconfirmed": "Povoliť iba používateľov s potvrdeným emailom",
        "undeletepagetext": "{{PLURAL:$1|Táto stránka bola zmazaná, ale je stále v archíve a\nmožno ju obnoviť|Tieto stránky boli zmazané, ale sú stále v archíve a\nmožno ich obnoviť}}. Archív môže byť pravidelne vyprázdnený.",
        "undelete-fieldset-title": "Obnoviť revízie",
        "undeleteextrahelp": "Ak chcete obnoviť celú stránku, nechajte všetky zaškrtávacie polia nezaškrtnuté a kliknite na '''''{{int:undeletebtn}}'''''.\nAk chcete vykonať selektívnu obnovu, zašktrnite polia zodpovedajúce revíziám, ktoré sa majú obnoviť a kliknite na '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:verzia je archivovaná|verzie sú archivované|verzií je archivovaných}}",
+       "undeleterevisions": "$1 {{PLURAL:verzia je zmazaná|verzie sú zmazané|verzií je zmazaných}}",
        "undeletehistory": "Ak obnovíte túto stránku, obnovia sa aj všetky predchádzajúce verzie do histórie predchádzajúcich verzií.\nAk bola od zmazania vytvorená nová stránka s rovnakým názvom, obnovené revízie sa objavia v histórii stránky.",
        "undeleterevdel": "Obnovenie sa nevykoná, ak by malo mať za dôsledok čiastočné zmazanie poslednej revízie. V takých prípadoch musíte odznačiť alebo odkryť najnovšie zmazané revízie.",
        "undeletehistorynoadmin": "Táto stránka bola zmazaná. Dôvod zmazania je zobrazený dolu v zhrnutí spolu s podrobnosťami o používateľoch, ktorí túto stránku upravovali pred zmazaním. Samotný text týchto zmazaných revízií je prístupný iba správcom.",
        "undelete-revision": "Zmazaná revízia $1 ($4, $5) od $3:",
        "undeleterevision-missing": "Neplatná alebo chýbajúca revízia. Zrejme ste použili zlý odkaz alebo revízia bola obnovená alebo odstránená z histórie.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Jednu revíziu|$1 revízií}} nebolo možné obnoviť, pretože {{PLURAL:$1|jej|ich}} <code>rev_id</code> sa už požíval.",
        "undelete-nodiff": "Nebola nájdená žiadna predošlá revízia.",
        "undeletebtn": "Obnoviť!",
        "undeletelink": "zobraziť/obnoviť",
        "undeletedrevisions": "{{PLURAL:$1|jedna verzia bola obnovená|$1 verzie boli obnovené|$1 verzií bolo obnovených}}",
        "undeletedrevisions-files": "{{PLURAL:$1|Jedna revízia|$1 revízie|$1 revízií}} a {{PLURAL:$2|jeden súbor bol obnovený|$2 súbory boli obnovené|$2 súborov bolo obnovených}}",
        "undeletedfiles": "{{PLURAL:$1|Jeden súbor bol obnovený|$1 súbory boli obnovené|$1 súborov bolo obnovených}}",
-       "cannotundelete": "Obnovenie sa nepodarilo:\n$1",
+       "cannotundelete": "Časť alebo celé obnovenie sa nepodarilo:\n$1",
        "undeletedpage": "'''$1 bol obnovený'''\n\nZoznam posledných mazaní a obnovení nájdete v [[Special:Log/delete|Zázname mazaní]].",
        "undelete-header": "Pozri nedávno zmazané stránky v [[Special:Log/delete|zázname mazaní]].",
        "undelete-search-title": "Hľadať zmazané stránky",
        "sp-contributions-newbies-sub": "Príspevky nováčikov",
        "sp-contributions-newbies-title": "Príspevky nových používateľov",
        "sp-contributions-blocklog": "záznam blokovaní",
-       "sp-contributions-suppresslog": "utajené príspevky redaktora",
-       "sp-contributions-deleted": "zmazané príspevky používateľa",
+       "sp-contributions-suppresslog": "utajené príspevky {{GENDER:$1|používateľa|používateľky}}",
+       "sp-contributions-deleted": "zmazané príspevky {{GENDER:$1|používateľa|používateľky}}",
        "sp-contributions-uploads": "nahrané súbory",
        "sp-contributions-logs": "záznamy",
        "sp-contributions-talk": "diskusia",
        "sp-contributions-username": "IP adresa alebo meno používateľa:",
        "sp-contributions-toponly": "Zobraziť len posledné revízie",
        "sp-contributions-newonly": "Zobraziť len založenia stránok",
+       "sp-contributions-hideminor": "Skryť drobné úpravy",
        "sp-contributions-submit": "Hľadať",
        "whatlinkshere": "Odkazy na túto stránku",
        "whatlinkshere-title": "Stránky odkazujúce na „$1“",
        "whatlinkshere-hideredirs": "$1 presmerovania",
        "whatlinkshere-hidetrans": "$1 transklúzie",
        "whatlinkshere-hidelinks": "$1 odkazy",
-       "whatlinkshere-hideimages": "$1 odkazov na súbor",
+       "whatlinkshere-hideimages": "$1 {{PLURAL:$1|odkaz|odkazy|odkazov}} na súbor",
        "whatlinkshere-filters": "Filtre",
        "whatlinkshere-submit": "Zobraziť",
        "autoblockid": "Autoblokovanie #$1",
        "block": "Zablokovať používateľa",
        "unblock": "Odblokovať používateľa",
-       "blockip": "Zablokovať používateľa",
+       "blockip": "Zablokovať {{GENDER:$1|používateľa|používateľku}}",
        "blockip-legend": "Zablokovať používateľa",
-       "blockiptext": "Použite tento formulár na zablokovanie možnosti zápisov uskutočnených z konkrétnej IP adresy alebo od používateľa.\nMali by ste to urobiť len v prípade bránenia vandalizmu a v súlade so [[{{MediaWiki:Policy-url}}|zásadami a smernicami {{GRAMMAR:genitív|{{SITENAME}}}}]].\nNižšie uveďte konkrétny dôvod (napríklad uveďte konkrétne stránky, ktoré padli za obeť vandalizmu).",
+       "blockiptext": "Tento formulár použite na zablokovanie možnosti zápisu z konkrétnej IP adresy alebo od konkrétneho používateľa.\nMali by ste to urobiť len na zabránenie vandalizmu a v súlade so [[{{MediaWiki:Policy-url}}|zásadami a smernicami {{GRAMMAR:genitív|{{SITENAME}}}}]].\nNižšie uveďte konkrétny dôvod (napríklad uveďte konkrétne stránky, ktoré padli za obeť vandalizmu).\nRozsahy IP adreies môžete blokovať pomocou syntaxe [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; najväčší povolený rozsah je /$1 v prípade IPv4 a /$2 v prípade IPv6.",
        "ipaddressorusername": "IP adresa/meno používateľa:",
        "ipbexpiry": "Ukončenie:",
        "ipbreason": "Dôvod:",
        "ipb-unblock": "Odblokovať používateľa alebo IP adresu",
        "ipb-blocklist": "Zobraziť existujúce blokovania",
        "ipb-blocklist-contribs": "Príspevky {{GENDER:redaktora|redaktorky}} $1",
+       "ipb-blocklist-duration-left": "zostáva $1",
        "unblockip": "Odblokovať používateľa",
        "unblockiptext": "Použite tento formulár na obnovenie možnosti zápisov\nz/od momentálne zablokovanej IP adresy/používateľa.",
        "ipusubmit": "Zrušiť toto blokovanie",
        "block-log-flags-hiddenname": "používateľské meno skryté",
        "range_block_disabled": "Možnosť správcov vytvárať rozsah zablokovaní je vypnutá.",
        "ipb_expiry_invalid": "Neplatný čas ukončenia.",
+       "ipb_expiry_old": "Čas uplynutia je v minulosti.",
        "ipb_expiry_temp": "Blokovania skrytých používateľských mien by mali byť trvalé.",
        "ipb_hide_invalid": "Nepodarilo sa potlačiť tento účet; má viac ako {{PLURAL:$1|jednu úpravu|$1 úpravy|$1 úprav}}.",
        "ipb_already_blocked": "„$1“ je už zablokovaný",
        "lockdbsuccesstext": "Databáza bola zamknutá.<br />\nNezabudnite po dokončení údržby [[Special:UnlockDB|odstrániť zámok]].",
        "unlockdbsuccesstext": "Databáza {{GRAMMAR:genitív|{{SITENAME}}}} bola odomknutá.",
        "lockfilenotwritable": "Súbor, ktorý zamyká databázu nie je zapisovateľný. Aby bolo možné zamknúť či odomknúť databázu, je potrebné, aby doňho mohol web server zapisovať.",
+       "databaselocked": "Databáza už je zamknutá.",
        "databasenotlocked": "Databáza nie je zamknutá.",
        "lockedbyandtime": "({{GENDER:$1|$1}}, $2 $3 )",
        "move-page": "Presunúť $1",
        "move-page-legend": "Presunúť stránku",
        "movepagetext": "Pomocou tohto formulára premenujete stránku a premiestnite všetky jej predchádzajúce verzie pod zadaný nový názov.\nStarý názov sa stane presmerovacou stránkou na nový názov.\nMôžete automaticky aktualizovať odkazy odkazujúce na pôvodný názov.\nAk sa rozhodnete túto možnosť nevyužiť, ubezpečte sa, že ste skontrolovali\nvýskyt [[Special:DoubleRedirects|dvojitých]] a [[Special:BrokenRedirects|pokazených]] presmerovaní.\nVy ste zodpovedný za to, aby odkazy naďalej ukazovali tam, kam majú.\n\nUvedomte si, že stránka sa <strong>nepremiestni</strong>, ak pod novým názvom už stránka existuje.\nToto neplatí iba ak je stránka prázdna alebo presmerovacia a nemá žiadne predchádzajúce verzie.\nTo znamená, že môžete premenovať stránku späť na názov, ktorý mala pred premenovaním, ak ste sa pomýlili, a že nemôžete prepísať\nexistujúcu stránku.\n\n<strong>UPOZORNENIE!</strong>\nToto môže byť drastická a nečakaná zmena pre populárnu stránku;\nubezpečte sa preto, skôr ako budete pokračovať, že chápete dôsledky svojho činu.",
        "movepagetext-noredirectfixer": "Pomocou tohto formulára premenujete stránku a premiestnite všetky jej predchádzajúce verzie pod zadaný nový názov.\nStarý názov sa stane presmerovacou stránkou na nový názov.\nUbezpečte sa, že ste skontrolovali výskyt [[Special:DoubleRedirects|dvojitých]] a [[Special:BrokenRedirects|pokazených]] presmerovaní.\nVy ste zodpovedný za to, aby odkazy naďalej ukazovali tam, kam majú.\n\nUvedomte si, že stránka sa <strong>nepremiestni</strong>, ak pod novým názvom už stránka existuje.\nToto neplatí iba ak je stránka prázdna alebo presmerovacia a nemá žiadne predchádzajúce verzie.\nTo znamená, že môžete premenovať stránku späť na názov, ktorý mala pred premenovaním, ak ste sa pomýlili, a že nemôžete prepísať\nexistujúcu stránku.\n\n<strong>UPOZORNENIE!</strong>\nToto môže byť drastická a nečakaná zmena pre populárnu stránku;\nubezpečte sa preto, skôr ako budete pokračovať, že chápete dôsledky svojho činu.",
-       "movepagetalktext": "Príslušná diskusná stránka (ak existuje) bude premiestnená spolu so samotnou stránkou; '''nestane sa tak, iba ak:'''\n*už existuje Diskusná stránka pod týmto novým menom, alebo\n*nezaškrtnete nižšie sa nachádzajúci textový rámček.\n\nV takých prípadoch budete musieť, ak si to želáte, premiestniť alebo zlúčiť stránku ručne.",
+       "movepagetalktext": "Ak zaškrtnete toto pole, príslušná diskusná stránka (ak existuje) bude automaticky premiestnená na nový názov; nestane sa tak iba ak už pod týmto novým menom existuje neprázdna Diskusná stránka.\n\nV takom prípade budete musieť, ak si to želáte, premiestniť alebo zlúčiť stránku ručne.",
        "moveuserpage-warning": "'''Upozornenie:''' Chystáte sa presunúť používateľskú stránku. Pamätajte, že týmto presuniete iba stránku a používateľ ''nebude'' premenovaný.",
        "movecategorypage-warning": "<strong>Upozornenie:</strong> Chystáte sa presunúť stránku kategórie. Uvedomte si, že presunutá bude iba táto stránka a že žiadne stránky v pôvodnej kategórii <em>nebudú</em> do novej kategórie automaticky preradené.",
        "movenologintext": "Musíte byť registrovaný používateľ a [[Special:UserLogin|prihlásený]], aby ste mohli presunúť stránku.",
        "cant-move-to-user-page": "Nemáte oprávnenie presunúť stránku na stránku používateľa (iba na podstránku používateľa).",
        "cant-move-category-page": "Nemáte oprávnenie presúvať stránky kategórií.",
        "cant-move-to-category-page": "Nemáte oprávnenie presunúť stránku na stránku kategórie.",
-       "newtitle": "Na nový názov:",
+       "newtitle": "Nový názov:",
        "move-watch": "Sledovať túto stránku",
        "movepagebtn": "Presunúť stránku",
        "pagemovedsub": "Presun bol úspešný",
        "movenosubpage": "Táto stránka nemá podstránky.",
        "movereason": "Dôvod:",
        "revertmove": "obnoviť",
-       "delete_and_move_text": "==Je potrebné zmazať stránku==\n\nCieľová stránka „[[:$1]]“ už existuje. Chcete ho vymazať a vytvoriť tak priestor pre presun?",
+       "delete_and_move_text": "Cieľová stránka „[[:$1]]“ už existuje.\nChcete ju vymazať a vytvoriť tak priestor na presun?",
        "delete_and_move_confirm": "Áno, zmaž stránku",
        "delete_and_move_reason": "Vymazané, aby sa umožnil presun z „[[$1]]“",
        "selfmove": "Zdrojový a cieľový názov sú rovnaké; nemožno presunúť stránku na seba samú.",
        "move-leave-redirect": "Zanechať presmerovanie",
        "protectedpagemovewarning": "'''Upozornenie:''' Táto stránka bola zamknutá, aby ju mohli presunúť iba používatelia s oprávnením správcu. Dolu je pre informáciu posledná položka zo záznamu:",
        "semiprotectedpagemovewarning": "'''Poznámka:''' Táto stránka bola zamknutá, aby ju mohli presunúť iba zaregistrovaní používatelia. Dolu je pre informáciu posledná položka zo záznamu:",
-       "move-over-sharedrepo": "== Súbor existuje ==\n[[:$1]] existuje v zdieľanom úložisku. Presunutím súboru na tento názov prekryjete zdieľaný súbor.",
+       "move-over-sharedrepo": "[[:$1]] existuje v zdieľanom úložisku. Presunutím súboru na tento názov prekryjete zdieľaný súbor.",
        "file-exists-sharedrepo": "Názov súboru, ktorý ste zvolili sa už používa na zdieľanom úložisku.\nProsím, zvoľte iný názov.",
        "export": "Exportovať stránky",
        "exporttext": "Môžete exportovať text a históriu úprav konkrétnej\nstránky alebo množiny stránok do XML; tieto môžu byť potom importované do inej\nwiki používajúceho MediaWiki softvér pomocou stránky Special:Import.\n\nPre export stránok zadajte názvy do tohto poľa, jeden názov na riadok, a zvoľte, či chcete iba súčasnú verziu s informáciou o poslednej úprave alebo aj všetky staršie verzie s históriou úprav.\n\nV druhom prípade môžete tiež použiť odkaz, napr. [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pre stránku [[{{MediaWiki:Mainpage}}]].",
        "export-download": "Ponúknuť uloženie ako súbor",
        "export-templates": "Vrátane šablón",
        "export-pagelinks": "Vrátane odkazovaných stránok do hĺbky:",
+       "export-manual": "Pridať stránky ručne:",
        "allmessages": "Všetky systémové správy",
        "allmessagesname": "Názov",
        "allmessagesdefault": "štandardný text",
        "thumbnail_image-missing": "Zdá sa, že súbor chýba: $1",
        "thumbnail_image-failure-limit": "V poslednej dobe došlo k nejmenej $1 pokusom o vygenerovanie tohoto náhľadu. Skúste to prosím neskôr.",
        "import": "Import stránok",
-       "importinterwiki": "Transwiki import",
-       "import-interwiki-text": "Zvoľte wiki a názov stránky, ktorá sa má importovať.\nDátumy revízií a mená používateľov budú zachované.\nVšetky transwiki importy sa zaznamenávajú v [[Special:Log/import|Zázname importov]].",
+       "importinterwiki": "Importovať z inej wiki",
+       "import-interwiki-text": "Zvoľte wiki a názov stránky, ktorá sa má importovať.\nDátumy revízií a mená používateľov budú zachované.\nVšetky importy z iných sa zaznamenávajú v [[Special:Log/import|Zázname importov]].",
        "import-interwiki-sourcewiki": "Zdrojová wiki:",
        "import-interwiki-sourcepage": "Zdrojová stránka:",
        "import-interwiki-history": "Skopírovať všetky historické revízie tejto stránky",
        "import-interwiki-templates": "Vložiť všetky šablóny",
        "import-interwiki-submit": "Importovať",
+       "import-mapping-default": "Importovať na predvolené umiestnenia",
+       "import-mapping-namespace": "Importovať do menného priestoru:",
+       "import-mapping-subpage": "Importovať ako podstránky nasledujúcej stránky:",
        "import-upload-filename": "Názov súboru:",
        "import-comment": "komentár:",
        "importtext": "Prosím, exportujte súbor zo zdrojovej wiki použitím [[Special:Export|nástroja na export]].\nUložte ho na svoj disk a nahrajte sem.",
        "importcantopen": "Nedal sa otvoriť súbor importu",
        "importbadinterwiki": "Zlý interwiki odkaz",
        "importsuccess": "Import dokončený!",
-       "importnosources": "Neboli definované žiadne zdroje pre transwiki import a priame nahranie histórie je vypnuté.",
+       "importnosources": "Neboli definované žiadne wiki, z ktorých sa má importovať a priame nahranie histórie je vypnuté.",
        "importnofile": "Nebol nahraný import súbor.",
        "importuploaderrorsize": "Nahranie alebo import súboru zlyhal. Súbor je väčší ako maximálna povolená veľkosť.",
        "importuploaderrorpartial": "Nahranie alebo import súboru zlyhal. Súbor bol nahraný iba čiastočne.",
        "import-nonewrevisions": "Žiadne revízie neboli importované (buď už boli všetky  importované skôr, alebo boli preskočené kvôli chybám).",
        "xml-error-string": "$1 na riadku $2, stĺpec $3 (bajt $4): $5",
        "import-upload": "Nahrať XML údaje",
-       "import-token-mismatch": "Strata údajov relácie. Prosím, skúste to znova.",
+       "import-token-mismatch": "Strata údajov relácie.\n\nJe možné, že ste boli odhlásení. <strong>Prosím, overte, či ste ešte prihlásení a skúste to znova</strong>.\nAk to stále nefunguje, skúste [[Special:UserLogout|sa odhlásiť]] a opäť prihlásiť a skontrolujte, či váš prehliadač povoľuje cookies z týchto stránok.",
        "import-invalid-interwiki": "Nie je možné importovať zo zadanej wiki.",
        "import-error-edit": "Stránka „$1“ nebola importovaná, pretože nemáte oprávnenie na jej úpravu.",
        "import-error-create": "Stránka „$1“ nebola importovaná, pretože nemáte oprávnenie na jej vytvorenie.",
-       "import-error-interwiki": "Stránka „$1“ nie je importovaná, pretože jej názov je vyhradený pre externé odkazy (interwiki).",
-       "import-error-special": "Stránka „$1“ nie je importovaná, pretože patrí do špeciálneho menného priestoru, ktorý nepovoľuje stránky.",
-       "import-error-invalid": "Stránka „$1“ nie je importovaná, pretože jej názov je neplatný.",
+       "import-error-interwiki": "Stránka „$1“ nebola importovaná, pretože jej názov je vyhradený pre externé odkazy (interwiki).",
+       "import-error-special": "Stránka „$1“ nebola importovaná, pretože patrí do špeciálneho menného priestoru, ktorý nepovoľuje stránky.",
+       "import-error-invalid": "Stránka „$1“ nebola importovaná, pretože názov, na ktorý by bola importovaná je na tejto wiki neplatný.",
        "import-error-unserialize": "Nepodarilo sa deserializovať revíziu $2 stránky „$1“. Revízia mala používať model obsahu $3 serializovaný ako $4.",
        "import-error-bad-location": "Revíziu $2 s modelom obsahu $3 nie je možné uložiť na \"$1\" na tejto wiki. Takýto model obsahu tu nie je podporovaný.",
        "import-options-wrong": "{{PLURAL:$2|Nesprávna voľba|Nesprávne voľby}}: <nowiki>$1</nowiki>",
        "import-rootpage-nosubpage": "Menný priestor „$1“ koreňovej stránky nepodporuje podstránky.",
        "importlogpage": "Záznam importov",
        "importlogpagetext": "Administratívny import stránok vrátane histórie úprav z iných wiki.",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revízia|revízie|revízií}}",
-       "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revízia|revízie|revízií}} z $2",
+       "import-logentry-upload-detail": "{{PLURAL:$1|importovaná $1 revízia|importované $1 revízie|importovaných $1 revízií}}",
+       "import-logentry-interwiki-detail": "{{PLURAL:$1|importovaná $1 revízia|importované $1 revízie|importovaných $1 revízií}} z $2",
        "javascripttest": "Testovanie JavaScriptu",
        "javascripttest-pagetext-unknownaction": "Neznáma akcia: „$1“.",
        "javascripttest-qunit-intro": "Pozri [$1 dokumentácia testovania] na mediawiki.org.",
        "tooltip-ca-nstab-category": "Zobraziť stránku s kategóriami",
        "tooltip-minoredit": "Označiť túto úpravu ako drobnú",
        "tooltip-save": "Uložiť vaše úpravy",
+       "tooltip-publish": "Zverejniť vaše zmeny",
        "tooltip-preview": "Náhľad úprav, prosím použite pred uložením!",
        "tooltip-diff": "Zobraziť, aké zmeny ste urobili v texte.",
        "tooltip-compareselectedversions": "Zobraziť rozdiely medzi dvomi zvolenými verziami tejto stránky.",
        "pageinfo-article-id": "ID stránky",
        "pageinfo-language": "Jazyk obsahu stránky",
        "pageinfo-content-model": "Model obsahu stránky",
+       "pageinfo-content-model-change": "zmeniť",
        "pageinfo-robot-policy": "Indexovanie robotmi",
        "pageinfo-robot-index": "Povolené",
        "pageinfo-robot-noindex": "Nepovolené",
        "exif-compression-4": "CCITT Group 4 faxové kódovanie",
        "exif-copyrighted-true": "Chránené autorským právom",
        "exif-copyrighted-false": "Príznak ochrany autorským právom nenastavený",
+       "exif-photometricinterpretation-1": "Čierna a biela (čierna je 0)",
        "exif-unknowndate": "Neznámy dátum",
        "exif-orientation-1": "Normálna",
        "exif-orientation-2": "Horizontálne prevrátená",
        "confirmemail_body_set": "Niekto, pravdepodobne vy, z IP adresy $1\nnastavil e-mailovú adresu účtu „$2“ na túto adresu na {{GRAMMAR:genitív|{{SITENAME}}}}.\n\nAk chcete potvrdiť, že tento účet skutočne patrí vám a aktivovať\ne-mailové funkcie na {{GRAMMAR:genitív|{{SITENAME}}}}, otvorte tento odkaz vo vašom prehliadači:\n\n$3\n\nAk účet nie je *nepatrí* patrí k vám, nasledujte tento odkaz,\nktorý zruší potvrdenie e-mailovej adresy:\n\n$5\n\nPlatnosť tohto potvrdzovacieho kódu vyprší $4.",
        "confirmemail_invalidated": "Potvrdenie emailovej adresy bolo zrušené",
        "invalidateemail": "Zrušiť potvrdenie emailovej adresy",
+       "notificationemail_subject_changed": "Email zaregistrovaný na {{GRAMMAR:lokál|{{SITENAME}}}} bol zmenený",
+       "notificationemail_subject_removed": "Email zaregistrovaný na {{GRAMMAR:lokál|{{SITENAME}}}} bol odstránený",
+       "notificationemail_body_changed": "Niekoho, pravdepodobne vy, z IP adresy $1, zmenil na {{GRAMMAR:lokál|{{SITENAME}}}} emailovú adresu účtu „$2“ na „$3“.\n\nAk ste to neboli vy, čo najskôr sa obráťte na správcu {{GRAMMAR:akuzatív|{{SITENAME}}}}.",
+       "notificationemail_body_removed": "Niekoho, pravdepodobne vy, z IP adresy $1, odstránil na {{GRAMMAR:lokál|{{SITENAME}}}} emailovú adresu účtu „$2“.\n\nAk ste to neboli vy, čo najskôr sa obráťte na správcu {{GRAMMAR:akuzatív|{{SITENAME}}}}.",
        "scarytranscludedisabled": "[Transklúzia interwiki je vypnutá]",
        "scarytranscludefailed": "[Nepodarilo sa priniesť šablónu pre $1]",
        "scarytranscludefailed-httpstatus": "[Stiahnutie šablóny zlyhalo pre $1: HTTP $2]",
        "scarytranscludetoolong": "[URL je príliš dlhé]",
        "deletedwhileediting": "'''Upozornenie''': Táto stránka bola zmazaná potom ako ste začali s jej úpravami!",
-       "confirmrecreate": "Používateľ [[User:$1|$1]] ([[User talk:$1|diskusia]]) zmazal túto stránku potom, ako ste ju začali upravovať, s odôvodnením:\n: ''$2''\nProsím, potvrďte, že túto stránku chcete skutočne znovu vytvoriť.",
-       "confirmrecreate-noreason": "Používateľ [[User:$1|$1]] ([[User talk:$1|diskusia]]) zmazal túto stránku potom, ako ste ju začali upravovať. Prosím, potvrďte, že túto stránku chcete skutočne znovu vytvoriť.",
+       "confirmrecreate": "{{GENDER:$1|Používateľ|Používateľka}} [[User:$1|$1]] ([[User talk:$1|diskusia]]) {{GENDER:$1|zmazal|zmazala}} túto stránku potom, ako ste ju začali upravovať, s odôvodnením:\n: <em>$2</em>\nProsím, potvrďte, že túto stránku chcete skutočne znovu vytvoriť.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Používateľ|Používateľka}} [[User:$1|$1]] ([[User talk:$1|diskusia]]) {{GENDER:$1|zmazal|zmazala}} túto stránku potom, ako ste ju začali upravovať. Prosím, potvrďte, že túto stránku chcete skutočne znovu vytvoriť.",
        "recreate": "Znova vytvoriť",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Vyčistiť vyrovnávaciu pamäť (cache) tejto stránky?",
        "confirm-watch-top": "Pridať túto stránku do vášho zoznamu sledovaných?",
        "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Odstrániť túto stránku z vášho zoznamu sledovaných?",
+       "confirm-rollback-button": "OK",
+       "confirm-rollback-top": "Vrátiť úpravy na tejto stránke?",
        "quotation-marks": "„$1“",
        "imgmultipageprev": "&larr; predošlá stránka",
        "imgmultipagenext": "ďalšia stránka &rarr;",
        "watchlistedit-raw-done": "Váš zoznam sledovaných stránok bol aktualizovaný.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Jedna položka bola pridaná|$1 položky boli pridané|$1 položiek bolo pridaných}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Jedna položka bola odstránená|$1 položky boli odstránené|$1 položiek bolo odstránených}}:",
-       "watchlistedit-clear-title": "Vyprázdnenie zoznamu sledovaných stránok",
+       "watchlistedit-clear-title": "Vyprázdniť zoznam sledovaných stránok",
        "watchlistedit-clear-legend": "Vyprázdniť zoznam sledovaných stránok",
        "watchlistedit-clear-explain": "Z vášho zoznamu sledovaných stránok budú odstránené všetky názvy",
        "watchlistedit-clear-titles": "Názvy:",
        "timezone-local": "miestny čas",
        "duplicate-defaultsort": "Upozornenie: DEFAULTSORT s triediacim kľúčom „$2“ prepisuje vyššie nastavenú hodnotu „$1“.",
        "duplicate-displaytitle": "<strong>Upozornenie:</strong> Predchádzajúci titulok (DISPLAYTITLE) „$1“ je nahradený titulkom „$2“.",
+       "restricted-displaytitle": "<strong>Upozornenie:</strong> Zobrazovaný názov „$1“ bol ignorovaný pretože sa nezhoduje so skutočným názvom stránky.",
        "invalid-indicator-name": "<strong>Chyba:</strong> Atribút <code>name</code> indikátoru stavu stránky nesmie byť prázdny.",
        "version": "Verzia",
        "version-extensions": "Nainštalované rozšírenia",
        "version-ext-colheader-description": "Popis",
        "version-ext-colheader-credits": "Autori",
        "version-license-title": "Licencia pre $1",
+       "version-license-not-found": "Nenašli sa žiadne podrobné licenčné informácie k tomuto rozšíreniu.",
        "version-poweredby-credits": "Táto wiki beží na '''[https://www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
        "version-poweredby-others": "ďalší",
        "version-poweredby-translators": "prekladatelia na translatewiki.net",
        "redirect-page": "ID stránky",
        "redirect-revision": "Revíziu stránky",
        "redirect-file": "Názov súboru",
+       "redirect-logid": "ID záznamu",
        "redirect-not-exists": "Hodnota nebola nájdená",
        "fileduplicatesearch": "Hľadať duplicitné súbory",
        "fileduplicatesearch-summary": "Hľadanie duplicitných súborov na základe ich haš hodnôt.",
        "tag-filter": "Filter [[Special:Tags|značiek]]:",
        "tag-filter-submit": "Filter",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Značka|Značky}}]]: $2)",
+       "tag-mw-contentmodelchange": "zmena modelu obsahu",
+       "tag-mw-contentmodelchange-description": "Úpravy, ktoré [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel menia model obsahu] stránky",
        "tags-title": "Značky",
        "tags-intro": "Táto stránka obsahuje zoznam a význam značiek, ktorými môže softvér označovať jednotlivé úpravy.",
        "tags-tag": "Názov značky",
        "tags-actions-header": "Akcie",
        "tags-active-yes": "Áno",
        "tags-active-no": "Nie",
+       "tags-source-extension": "Definované softvérom",
+       "tags-source-none": "Už sa nepoužíva",
        "tags-edit": "upraviť",
+       "tags-delete": "zmazať",
+       "tags-activate": "aktivovať",
+       "tags-deactivate": "deaktivovať",
        "tags-hitcount": "$1 {{PLURAL:$1|úprava|úpravy|úprav}}",
+       "tags-manage-no-permission": "Nemáte oprávnenie spravovať značky zmien.",
+       "tags-manage-blocked": "Nemôžete spravovať značky, pokým ste {{GENDER:|zablokovaný|zablokovaná}}.",
+       "tags-create-heading": "Vytvoriť novú značku",
+       "tags-create-explanation": "Novo vytvorené značky sú implicitne k dispozícii používateľom a botom.",
+       "tags-create-tag-name": "Názov stránky:",
+       "tags-create-reason": "Dôvod:",
+       "tags-create-submit": "Vytvoriť",
+       "tags-create-no-name": "Musíte zadať názov značky.",
+       "tags-create-invalid-chars": "Názvy značiek nesmú obsahovať čiarky (<code>,</code>) ani lomky (<code>/</code>).",
+       "tags-create-invalid-title-chars": "Názvy značiek nesmú obsahovať znaky, ktoré nemožno použiť v názvoch stránok.",
+       "tags-create-already-exists": "Značka „$1“ už existuje",
+       "tags-create-warnings-above": "Pri pokuse vytvoriť značku „$1“ {{PLURAL:$2|bolo zistené následujúce upozornenie|boli zistené následujúce upozornenia}}:",
+       "tags-create-warnings-below": "Chcete napriek tomu značku vytvoriť?",
+       "tags-delete-title": "Zmazať značku",
+       "tags-delete-explanation-initial": "Chystáte sa zmazať značku „$1“ z databázy.",
+       "tags-delete-reason": "Dôvod:",
        "tags-deactivate-reason": "Dôvod:",
        "tags-edit-title": "Upraviť značky",
        "tags-edit-new-tags": "Nové značky:",
index f1fad3c..221e53a 100644 (file)
@@ -40,7 +40,7 @@
        "tog-enotifminoredits": "Pošlji e-pošto tudi za manjše spremembe strani in datotek",
        "tog-enotifrevealaddr": "V sporočilih z obvestili o spremembah razkrij moj e-poštni naslov",
        "tog-shownumberswatching": "Prikaži število uporabnikov, ki spremljajo temo",
-       "tog-oldsig": "Trenutni podpis:",
+       "tog-oldsig": "Vaš trenutni podpis:",
        "tog-fancysig": "Obravnavaj podpis kot wikibesedilo (brez samodejne povezave)",
        "tog-uselivepreview": "Uporabi hitri predogled",
        "tog-forceeditsummary": "Ob vpisu praznega povzetka urejanja me opozori",
        "newwindow": "(odpre se novo okno)",
        "cancel": "Prekliči",
        "moredotdotdot": "Več ...",
-       "morenotlisted": "Seznam ni popoln.",
+       "morenotlisted": "Seznam morda ni popoln.",
        "mypage": "Stran",
        "mytalk": "Pogovor",
        "anontalk": "Pogovorna stran",
        "yourpasswordagain": "Ponovno vpišite geslo",
        "createacct-yourpasswordagain": "Potrdite geslo",
        "createacct-yourpasswordagain-ph": "Ponovno vnesite geslo",
-       "remembermypassword": "Zapomni si me na tem računalniku (za največ $1 {{PLURAL:$1|dan|dneva|dni}})",
        "userlogin-remembermypassword": "Zapomni si me",
        "userlogin-signwithsecure": "Uporabi varno povezavo",
+       "cannotlogin-title": "Ne moremo vas prijaviti",
+       "cannotlogin-text": "Prijava ni mogoča.",
        "cannotloginnow-title": "Trenutno se ne morete prijaviti",
        "cannotloginnow-text": "Prijava ni možna pri uporabi $1.",
+       "cannotcreateaccount-title": "Ne moremo ustvariti računov",
+       "cannotcreateaccount-text": "Neposredno ustvarjanje računov na tem wikiju ni omogočeno.",
        "yourdomainname": "Domena",
        "password-change-forbidden": "Na tem wikiju ne morete spreminjati gesel.",
        "externaldberror": "Pri potrjevanju istovetnosti je prišlo do notranje napake ali pa za osveževanje zunanjega računa nimate dovoljenja.",
        "botpasswords-updated-body": "Posodobili smo geslo bota »$1« uporabnika »$2«.",
        "botpasswords-deleted-title": "Izbrisali smo geslo bota",
        "botpasswords-deleted-body": "Izbrisali smo geslo bota »$1« uporabnika »$2«.",
-       "botpasswords-newpassword": "Novo geslo za prijavo z imenom <strong>$1</strong> je <strong>$2</strong>. <em>Prosimo, zabeležite si to za uporabo v prihodnje.</em>",
+       "botpasswords-newpassword": "Novo geslo za prijavo z imenom <strong>$1</strong> je <strong>$2</strong>. <em>Prosimo, zabeležite si to za uporabo v prihodnje.</em> <br> (Za starejše bote, pri katerih mora biti prijavno ime enako uporabniškemu imenu, lahko uporabite <strong>$3</strong> kot uporabniško ime in <strong>$4</strong> kot geslo.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider ni na voljo.",
        "botpasswords-restriction-failed": "Omejitve gesla bota preprečujejo to prijavo.",
        "botpasswords-invalid-name": "Navedeno uporabniško ime ne vsebuje ločila za geslo bota (»$1«).",
        "invalid-content-data": "Neveljavni podatki vsebine",
        "content-not-allowed-here": "Vsebina »$1« ni dovoljena na strani [[$2]]",
        "editwarning-warning": "Če zapustite stran, boste morda izgubili vse spremembe, ki ste jih naredili.\nČe ste prijavljeni, lahko to opozorilo onemogočite v razdelku »{{int:prefs-editing}}« v svojih nastavitvah.",
+       "editpage-invalidcontentmodel-title": "Model vsebine ni podprt",
+       "editpage-invalidcontentmodel-text": "Model vsebine »$1« ni podprt.",
        "editpage-notsupportedcontentformat-title": "Oblika vsebine ni podprta",
        "editpage-notsupportedcontentformat-text": "Model vsebine $2 ne podpira oblike vsebine $1.",
        "content-model-wikitext": "wikibesedilo",
        "file-thumbnail-no": "Ime datoteke se začne z <strong>$1</strong>.\nIzgleda, da je to pomanjšana slika ''(thumbnail)''.\nČe imate sliko polne resolucije, jo naložite, drugače spremenite ime datoteke.",
        "fileexists-forbidden": "Datoteka s tem imenom že obstaja in je ni mogoče prepisati.\nČe še vedno želite naložiti vašo datoteko, se prosimo vrnite nazaj in uporabite novo ime.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Datoteka s tem imenom že obstaja v skupnem skladišču datotek.\nProsimo, vrnite se in naložite svojo datoteko pod drugim imenom.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Naložena datoteka je točen dvojnik trenutne različice <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Naložena datoteka je točen dvojnik {{PLURAL:$2|starejše različice|starejših različic}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ta datoteka je dvojnik {{PLURAL:$1|naslednje datoteke|naslednjih datotek}}:",
        "file-deleted-duplicate": "Datoteka je identična tej ([[:$1]]), ki je bila predhodno izbrisana.\nPreverite zgodovino brisanja datoteke, preden jo ponovno naložite.",
        "file-deleted-duplicate-notitle": "Datoteka, identična tej datoteki, je bila v preteklosti izbrisana in naslov je bil zatrt.\nPoprosite koga, ki ima možnost ogleda podatkov zatrtih datotek, da preveri položaj, preden nadaljujete s ponovnim nalaganjem.",
        "filerevert-submit": "Vrni",
        "filerevert-success": "Datoteka '''[[Media:$1|$1]]''' je bila vrnjena na [$4 različico $3, $2].",
        "filerevert-badversion": "Ne najdem preteklih lokalnih verzij datoteke s podanim časovnim žigom.",
+       "filerevert-identical": "Trenutna različica datoteke je že enaka izbrani.",
        "filedelete": "Izbriši $1",
        "filedelete-legend": "Brisanje datoteke",
        "filedelete-intro": "Brišete datoteko '''[[Media:$1|$1]]''' skupaj z njeno celotno zgodovino.",
        "rollbacklinkcount-morethan": "vrni več kot $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}",
        "rollbackfailed": "Vrnitev ni uspela",
        "rollback-missingparam": "Pri zahtevi manjkajo zahtevani parametri.",
+       "rollback-missingrevision": "Ne moremo naložiti podatkov o redakciji.",
        "cantrollback": "Urejanja ne morem vrniti; zadnji urejevalec je hkrati edini.",
        "alreadyrolled": "Zadnje spremembe [[:$1]] uporabnika [[User:$2|$2]] ([[User talk:$2|pogovor]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ne morem vrniti;\nstran je spremenil ali vrnil že nekdo drug.\n\nZadnji je stran urejal uporabnik [[User:$3|$3]] ([[User talk:$3|pogovor]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Povzetek urejanja je bil: <em>$1</em>.",
        "pageinfo-article-id": "ID strani",
        "pageinfo-language": "Jezik vsebine strani",
        "pageinfo-content-model": "Model vsebine strani",
+       "pageinfo-content-model-change": "spremeni",
        "pageinfo-robot-policy": "Robotsko indeksiranje",
        "pageinfo-robot-index": "Dovoljeno",
        "pageinfo-robot-noindex": "Nedovoljeno",
        "tag-filter": "Filter [[Special:Tags|oznak]]:",
        "tag-filter-submit": "Filtriraj",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Oznaka|Oznaki|Oznake}}]]: $2)",
+       "tag-mw-contentmodelchange": "sprememba modela vsebine",
+       "tag-mw-contentmodelchange-description": "Urejanja, ki [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel spremenijo model vsebine] strani",
        "tags-title": "Etikete",
        "tags-intro": "Ta stran navaja etikete, s katerimi lahko programje označi urejanja, in njihov pomen.",
        "tags-tag": "Ime oznake",
        "tags-actions-header": "Dejanja",
        "tags-active-yes": "Da",
        "tags-active-no": "Ne",
-       "tags-source-extension": "Opredeljuje jo razširitev",
+       "tags-source-extension": "Opredeljuje jo programje",
        "tags-source-manual": "Ročno jo uporabljajo uporabniki in boti",
        "tags-source-none": "Se ne uporablja več",
        "tags-edit": "uredi",
index 26b3ffd..b04c775 100644 (file)
        "yourpasswordagain": "Потврда лозинке:",
        "createacct-yourpasswordagain": "Потврдите лозинку",
        "createacct-yourpasswordagain-ph": "Унесите лозинку још једном",
-       "remembermypassword": "Запамти ме на овом прегледачу (најдуже $1 {{PLURAL:$1|дан|дана}})",
        "userlogin-remembermypassword": "Остави ме пријављеног/у",
        "userlogin-signwithsecure": "Користите сигурну конекцију",
        "yourdomainname": "Домен:",
        "file-thumbnail-no": "Датотека почиње са <strong>$1</strong>.\nИзгледа да се ради о умањеној слици ''(thumbnail)''.\nУколико имате ову слику у пуној величини, пошаљите је, а ако немате, промените назив датотеке.",
        "fileexists-forbidden": "Датотека с овим називом већ постоји и не може се заменити.\nАко и даље желите да пошаљете датотеку, вратите се и изаберите други назив.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Датотека с овим називом већ постоји у заједничкој остави.\nВратите се и пошаљите датотеку с другим називом.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Датотека је дупликат тренутне верзије <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Датотека је дупликат {{PLURAL:$2|старе верзије|старих верзија}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ово је дупликат {{PLURAL:$1|следеће датотеке|следећих датотека}}:",
        "file-deleted-duplicate": "Датотека истоветна овој ([[:$1]]) је претходно обрисана.\nПогледајте историју брисања пре поновног слања.",
        "file-deleted-duplicate-notitle": "Датотека идентична овој претходно је обрисана и име јој је сакривено.\nТребали бисте питати некога ко може видети податке скривених датотека да прегледа ситуацију пре него што поново отпремите датотеку.",
        "filerevert-submit": "Врати",
        "filerevert-success": "Датотека '''[[Media:$1|$1]]''' је враћена на [$4 издање од $2; $3].",
        "filerevert-badversion": "Не постоји раније локално издање датотеке с наведеним временским подацима.",
+       "filerevert-identical": "Тренутна верзија датотеке индентична је изабраној.",
        "filedelete": "Обриши $1",
        "filedelete-legend": "Обриши датотеку",
        "filedelete-intro": "Бришете датотеку '''[[Media:$1|$1]]''' заједно с њеном историјом.",
index 33c3522..3ea3fc4 100644 (file)
@@ -73,7 +73,8 @@
                        "Matma Rex",
                        "McDutchie",
                        "Larske",
-                       "Rockyfelle"
+                       "Rockyfelle",
+                       "Johan"
                ]
        },
        "tog-underline": "Stryk under länkar:",
        "tagline": "Från {{SITENAME}}",
        "help": "Hjälp",
        "search": "Sök",
-       "search-ignored-headings": "#<!-- lämna denna rad precis som den är --> <pre>\n# Rubriker som kommer att ignoreras av sökningen.\n# Ändringar till detta kommer att gälla så fort sidan med rubriken är indexerad.\n# Du kan tvinga sidan att indexeras om genom att göra en null-redigering.\n# Syntaxen är som följer:\n#  * Allt från ett \"#\" tecken till slutet av raden är en kommentar.\n#  * Varje icke-tom rad är den exakta titeln som ska ignoreras, shiftläge och allt.\nReferenser\nExterna länkar\nSe även\n #</pre> <!-- lämna denna rad precis som den är -->",
+       "search-ignored-headings": "#<!-- lämna denna rad precis som den är --> <pre>\n# Rubriker som kommer att ignoreras av sökningen.\n# Förändringar av detta kommer att gälla så fort sidan med rubriken är indexerad.\n# Du kan tvinga sidan att indexeras om genom att göra en null-redigering.\n# Syntaxen är som följer:\n#  * Allt från ett \"#\" tecken till slutet av raden är en kommentar.\n#  * Varje icke-tom rad är den exakta titeln som ska ignoreras, shiftläge och allt.\nReferenser\nExterna länkar\nSe även\n #</pre> <!-- lämna denna rad precis som den är -->",
        "searchbutton": "Sök",
        "go": "Gå till",
        "searcharticle": "Gå till",
        "actionthrottledtext": "Som skydd mot missbruk finns det en begränsning av hur många gånger du kan utföra den här åtgärden under en viss tid. Du har överskridit den gränsen.\nFörsök igen om några minuter.",
        "protectedpagetext": "Den här sidan har skrivskyddats för att förhindra redigering eller andra åtgärder.",
        "viewsourcetext": "Du kan se och kopiera denna sidas källtext.",
-       "viewyourtext": "Du kan se och kopiera källan för <strong>dina redigeringar</strong> av denna sida.",
+       "viewyourtext": "Du kan se och kopiera källtexten för <strong>dina redigeringar</strong> av denna sida.",
        "protectedinterface": "Denna sida innehåller text för mjukvarans gränssnitt på denna wiki, och är skrivskyddad för att förebygga missbruk.\nFör att lägga till eller ändra översättningar för alla wikis, var god använd [https://translatewiki.net/ translatewiki.net], lokaliseringsprojektet för MediaWiki.",
        "editinginterface": "<strong>Varning:</strong> Du redigerar en sida som används för texten i gränssnittet.\nÄndringar på denna sida kommer att påverka användargränssnittets utseende för andra användare på denna wiki.",
        "translateinterface": "För att lägga till eller ändra översättningar för alla wikis, använd [https://translatewiki.net/ translatewiki.net], lokaliseringsprojektet för MediaWiki.",
        "yourpasswordagain": "Upprepa lösenord",
        "createacct-yourpasswordagain": "Bekräfta lösenordet",
        "createacct-yourpasswordagain-ph": "Ange lösenordet igen",
-       "remembermypassword": "Spara min inloggning på den här datorn (i max $1 {{PLURAL:$1|dygn}})",
        "userlogin-remembermypassword": "Håll mig inloggad",
        "userlogin-signwithsecure": "Använd säker anslutning",
+       "cannotlogin-title": "Kan inte logga in",
+       "cannotlogin-text": "Det går inte att logga in.",
        "cannotloginnow-title": "Kan inte logga in nu",
        "cannotloginnow-text": "Det går inte att logga in med $1.",
+       "cannotcreateaccount-title": "Kan inte skapa konton",
+       "cannotcreateaccount-text": "Direkt kontoregistrering är inte aktiverat på denna wiki.",
        "yourdomainname": "Din domän",
        "password-change-forbidden": "Du kan inte ändra lösenord på denna wiki.",
        "externaldberror": "Antingen inträffade autentiseringsproblem med en extern databas, eller så får du inte uppdatera ditt externa konto.",
        "revdelete-selected-file": "{{PLURAL:$1|Vald filversion|Valda filversioner}} av [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Vald loggåtgärd|Valda loggåtgärder}}:",
        "revdelete-text-text": "Raderade sidversioner kommer fortfarande synas i sidans historik, men delar av innehållet kommer inte att vara tillgängligt offentligt.",
-       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filens historik, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
-       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
+       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filhistoriken, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
+       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
        "revdelete-text-others": "Andra administratörer kommer fortfarande att kunna komma åt det dolda innehållet och återställa det igen om inte ytterligare begränsningar används.",
        "revdelete-confirm": "Var god bekräfta att du vill göra detta, och att du förstår konsekvenserna, och att du gör så i enlighet med [[{{MediaWiki:Policy-url}}|policyn]].",
        "revdelete-suppress-text": "Undanhållande ska '''bara''' användas i följande fall:\n* Eventuell förolämpande information\n* Opassande personlig information\n*: ''hemadresser och telefonnummer, personnummer, etc.''",
        "search-relatedarticle": "Relaterad",
        "searchrelated": "relaterad",
        "searchall": "alla",
-       "showingresults": "Nedan visas upp till {{PLURAL:$1|'''1''' post|'''$1''' poster}} från och med nummer '''$2'''.",
+       "showingresults": "Nedan visas upp till {{PLURAL:$1|<strong>1</strong> resultat|<strong>$1</strong> resultat}} från och med nummer <strong>$2</strong>.",
        "showingresultsinrange": "Nedan visas upp till {{PLURAL:$3|<strong>1</strong> resultat|<strong>$1</strong> resultat}} mellan nummer <strong>$2</strong> och nummer <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultat <strong>$1</strong> av <strong>$3</strong>|Resultat <strong>$1 – $2</strong> av <strong>$3</strong>}}",
        "search-nonefound": "Inga resultat matchade frågan.",
        "email": "E-post",
        "prefs-help-realname": "Riktigt namn behöver inte anges.\nOm angivet, kan det komma att användas för att tillskriva dig ditt arbete.",
        "prefs-help-email": "Att ange e-postadress är valfritt, men gör det möjligt att få ditt lösenord mejlat till dig om du glömmer det.",
-       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. Din e-postadress avslöjas inte när andra användare kontaktar dig.",
+       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. \nDin e-postadress avslöjas inte när andra användare kontaktar dig.",
        "prefs-help-email-required": "E-postadress måste anges.",
        "prefs-info": "Grundläggande information",
        "prefs-i18n": "Internationalisering",
        "right-createpage": "Skapa sidor (som inte är diskussionssidor)",
        "right-createtalk": "Skapa diskussionssidor",
        "right-createaccount": "Skapa nya användarkonton",
-       "right-autocreateaccount": "Logga in automatiskt med en extern användarkonto",
+       "right-autocreateaccount": "Logga in automatiskt med ett externt användarkonto",
        "right-minoredit": "Markera redigeringar som mindre",
        "right-move": "Flytta sidor",
        "right-move-subpages": "Flytta sidor med deras undersidor",
        "filetype-mime-mismatch": "Filtillägget \".$1\" matchar inte med den identifierade MIME-typen för filen ($2).",
        "filetype-badmime": "Uppladdning av filer av MIME-typ \"$1\" är inte tillåtet.",
        "filetype-bad-ie-mime": "Kan inte ladda upp denna fil på grund av att Internet Explorer skulle upptäcka att den är \"$1\", vilket är en otillåten och möjligtvis farlig filtyp.",
-       "filetype-unwanted-type": "'''\".$1\"''' är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
-       "filetype-banned-type": "'''\".$1\"''' är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåtna filtyper|Tillåten filtyp}} är $2.",
+       "filetype-unwanted-type": "<strong>\".$1\"</strong> är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
+       "filetype-banned-type": "<strong>\".$1\"</strong> är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
        "filetype-missing": "Filnamnet saknar ändelse (t ex \".jpg\").",
        "empty-file": "Filen du skickade var tom.",
        "file-too-large": "Filen du skickade var för stor.",
        "filename-tooshort": "Filnamnet är för kort.",
        "filetype-banned": "Denna typ av fil är förbjuden.",
        "verification-error": "Denna fil klarade inte verifieringen.",
-       "hookaborted": "Ändringen du försökte göra avbröts av en extension hook.",
+       "hookaborted": "Ändringen du försökte göra avbröts av ett tillägg.",
        "illegal-filename": "Filnamnet är inte tillåtet.",
        "overwrite": "Det är inte tillåtet att skriva över en befintlig fil.",
        "unknown-error": "Ett okänt fel uppstod.",
        "fileexists": "Det finns redan en fil med detta namn. Titta på <strong>[[:$1]]</strong>, om {{GENDER:|du}} inte är säker på att {{GENDER:|du}} vill ändra den.\n[[$1|thumb]]",
        "filepageexists": "Beskrivningssidan för denna fil har redan skapats på <strong>[[:$1]]</strong>, men just nu finns ingen fil med detta namn.\nDen sammanfattning du skriver här kommer inte visas på beskrivningssidan.\nFör att din sammanfattning ska visas där, så måste du redigera beskrivningssidan manuellt.\n[[$1|thumb]]",
        "fileexists-extension": "En fil med ett liknande namn finns redan: [[$2|thumb]]\n* Namn på den fil du försöker ladda upp: <strong>[[:$1]]</strong>\n* Namn på filen som redan finns: <strong>[[:$2]]</strong>\nVill du möjligen välja ett mer distinkt namn?",
-       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek ''(miniatyrbild)''. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
+       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek <em>(miniatyrbild)</em>. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
        "file-thumbnail-no": "Filnamnet börjar med <strong>$1</strong>.\nDet verkar vara en bild med förminskad storlek ''(miniatyrbild)''.\nOm du har denna bild i full storlek, ladda då hellre upp den, annars var vänlig och ändra filens namn.",
-       "fileexists-forbidden": "En fil med detta namn existerar redan, och kan inte överskrivas.\nOm du fortfarande vill ladda upp din fil, var god gå tillbaka och välj ett nytt namn. [[File:$1|thumb|center|$1]]",
+       "fileexists-forbidden": "En fil med detta namn existerar redan, och kan inte skrivas över.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "En fil med detta namn finns redan bland de delade filerna.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Den uppladdade filen är en exakt kopia av den aktuella versionen av <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Den uppladdade versionen är en exakt kopia av {{PLURAL:$2|en äldre version|äldre versioner}} av <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Denna fil är en dubblett av följande {{PLURAL:$1|fil|filer}}:",
-       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. Du bör kontrollera den filens raderingshistorik innan du fortsätter att återuppladda den.",
-       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den.",
+       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. \nDu bör kontrollera den filens raderingshistorik innan du fortsätter att ladda upp den på nytt.",
+       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den på nytt.",
        "uploadwarning": "Uppladdningsvarning",
        "uploadwarning-text": "Var god och ändra filbeskrivningen nedanför och försök igen.",
        "savefile": "Spara fil",
        "filejournal-fail-dbquery": "Kunde inte uppdatera journaldatabasen för lagringssystemet \"$1\".",
        "lockmanager-notlocked": "Kunde inte låsa upp \"$1\"; den är inte låst.",
        "lockmanager-fail-closelock": "Kunde inte att stänga låsfilen för \"$1\".",
-       "lockmanager-fail-deletelock": "Kunde inte att radera låsfilen för \"$1\".",
-       "lockmanager-fail-acquirelock": "Kunde inte skaffa låset för \"$1\".",
-       "lockmanager-fail-openlock": "Kunde inte att öppna låsfilen för \"$1\".",
-       "lockmanager-fail-releaselock": "Kunde inte att frigöra låset för \"$1\".",
+       "lockmanager-fail-deletelock": "Kunde inte radera låsfilen för \"$1\".",
+       "lockmanager-fail-acquirelock": "Kunde inte skaffa lås för \"$1\".",
+       "lockmanager-fail-openlock": "Kunde inte öppna låsfilen för \"$1\".",
+       "lockmanager-fail-releaselock": "Kunde inte att frigöra lås för \"$1\".",
        "lockmanager-fail-db-bucket": "Kunde inte kontakta tillräckligt många låsdatabaser i hinken $1.",
        "lockmanager-fail-db-release": "Kunde inte frigöra låsen på databasen $1 .",
        "lockmanager-fail-svr-acquire": "Kunde inte erhålla lås på servern $1 .",
-       "lockmanager-fail-svr-release": "Kunde inte frigöra låsen på servern $1.",
+       "lockmanager-fail-svr-release": "Kunde inte frigöra lås på servern $1.",
        "zip-file-open-error": "Ett fel inträffade när filen öppnades för en ZIP-kontroll.",
        "zip-wrong-format": "Den angivna filen var inte en ZIP-fil.",
        "zip-bad": "Filen är en skadad eller annars oläsbar ZIP-fil.\nDen kan inte säkerhetskontrolleras ordentligt.",
        "filerevert-legend": "Återställ fil",
        "filerevert-intro": "Du återställer '''[[Media:$1|$1]]''' till [$4 versionen från $2 kl. $3].",
        "filerevert-comment": "Anledning:",
-       "filerevert-defaultcomment": "Återställer till versionen från $1 kl. $2 ($3)",
+       "filerevert-defaultcomment": "Återställd till versionen från $1, kl. $2 ($3)",
        "filerevert-submit": "Återställ",
        "filerevert-success": "'''[[Media:$1|$1]]''' har återställts till [$4 versionen från $2 kl. $3].",
        "filerevert-badversion": "Det finns ingen tidigare version av filen från den angivna tidpunkten.",
+       "filerevert-identical": "Den aktuella versionen av filen är redan identisk med den valda.",
        "filedelete": "Radera $1",
        "filedelete-legend": "Radera fil",
        "filedelete-intro": "Du håller på att radera filen '''[[Media:$1|$1]]''' tillsammans med hela dess historik.",
        "listgrouprights-namespaceprotection-header": "Namnrymdsbegränsningar",
        "listgrouprights-namespaceprotection-namespace": "Namnrymd",
        "listgrouprights-namespaceprotection-restrictedto": "Rättighet(er) som låter användare redigera",
-       "listgrants": "Beviljanden",
+       "listgrants": "Behörigheter",
        "listgrants-summary": "Följande är en lista över behörigheter med deras associerade tillgång till användarrättigheter. Användare kan tillåta applikationer att använda deras konto, men med begränsad åtkomst baserat på de behörigheter användaren gav applikationen. En applikation som agerar på uppdrag av en användare kan i praktiken inte använda rättigheter som den användaren saknar.\nDet kan finnas [[{{MediaWiki:Listgrouprights-helppage}}|ytterligare information]] om individuella rättigheter.",
        "listgrants-grant": "Behörighet",
        "listgrants-rights": "Rättigheter",
        "trackingcategories-nodesc": "Ingen beskrivning tillgänglig.",
        "trackingcategories-disabled": "Kategorin är inaktiverad",
        "mailnologin": "Ingen adress att skicka till",
-       "mailnologintext": "För att kunna skicka e-post till andra användare, måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
+       "mailnologintext": "För att kunna skicka e-post till andra användare måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
        "emailuser": "Skicka e-post till den här användaren",
        "emailuser-title-target": "Skicka e-post till denna {{GENDER:$1|användare}}",
        "emailuser-title-notarget": "E-postanvändare",
        "watchlistanontext": "Du måste logga in för att se eller redigera din bevakningslista.",
        "watchnologin": "Inte inloggad",
        "addwatch": "Lägg till i bevakningslistan",
-       "addedwatchtext": "\"[[:$1]]\" har lagts till i din [[Special:Watchlist|bevakningslista]].",
+       "addedwatchtext": "\"[[:$1]]\" och dess diskussionssida har lagts till i din [[Special:Watchlist|bevakningslista]].",
        "addedwatchtext-talk": "\"[[:$1]]\" och dess associerade sida har lagts till i din [[Special:Watchlist|bevakningslista]].",
        "addedwatchtext-short": "Sidan \"$1\" har lagts till i din bevakningslista.",
        "removewatch": "Ta bort från bevakningslistan",
        "watchlist-hide": "Dölj",
        "watchlist-submit": "Visa",
        "wlshowtime": "Tidsperiod att visa:",
-       "wlshowhideminor": "mindre redigering",
+       "wlshowhideminor": "mindre redigeringar",
        "wlshowhidebots": "robotar",
        "wlshowhideliu": "registrerade användare",
        "wlshowhideanons": "anonyma användare",
        "exbeforeblank": "innehåll före tömning var: \"$1\"",
        "delete-confirm": "Radera \"$1\"",
        "delete-legend": "Radera",
-       "historywarning": "<strong>Varning:</strong> Sidan du håller på att radera har en historik med ungefär $1 {{PLURAL:$1|version|versioner}}:",
+       "historywarning": "<strong>Varning:</strong> Sidan du håller på att radera har en historik med $1 {{PLURAL:$1|version|versioner}}:",
        "historyaction-submit": "Visa",
        "confirmdeletetext": "Du håller på att ta bort en sida med hela dess historik.\nBekräfta att du förstår vad du håller på med och vilka konsekvenser detta leder till, och att du följer [[{{MediaWiki:Policy-url}}|riktlinjerna]].",
        "actioncomplete": "Genomfört",
        "rollbacklinkcount-morethan": "rulla tillbaka mer än $1 {{PLURAL:$1|redigering|redigeringar}}",
        "rollbackfailed": "Tillbakarullning misslyckades",
        "rollback-missingparam": "Nödvändiga parametrar i begäran saknas.",
+       "rollback-missingrevision": "Kunde inte läsa in sidversionsdata.",
        "cantrollback": "Det gick inte att rulla tillbaka, då sidan endast redigerats av en användare.",
        "alreadyrolled": "Det gick inte att rulla tillbaka den senaste redigeringen av [[User:$2|$2]] ([[User talk:$2|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) på sidan [[:$1|$1]]. Någon annan har redan rullat tillbaka eller redigerat sidan.\n\nSidan ändrades senast av [[User:$3|$3]] ([[User talk:$3|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]).",
        "editcomment": "Redigeringskommentaren var: <em>$1</em>.",
        "revertpage": "Återställde redigeringar av  [[Special:Contributions/$2|$2]] ([[User talk:$2|användardiskussion]]) till senaste versionen av [[User:$1|$1]]",
        "revertpage-nouser": "Återställde redigeringar av en dold användare till den senaste versionen av {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2.",
-       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2. [$3 Visa ändringar]",
+       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste sidversion av $2. [$3 Visa ändringar]",
        "sessionfailure-title": "Sessionsfel",
        "sessionfailure": "Något med din session som inloggad är på tok. Din begärda åtgärd har avbrutits, för att förhindra att någon kapar din session. Klicka på \"Tillbaka\" i din webbläsare och ladda om den sida du kom ifrån. Försök sedan igen.",
        "changecontentmodel": "Ändra innehållsmodell för en sida",
        "logentry-contentmodel-change-revertlink": "återställ",
        "logentry-contentmodel-change-revert": "återställ",
        "protectlogpage": "Skrivskyddslogg",
-       "protectlogtext": "Detta är en lista över applicerande och borttagande av skrivskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för listan över aktiva sidskydd.",
+       "protectlogtext": "Nedan är en lista över ändringar av sidskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för en förteckning över de sidskydd som för närvarande är aktiva.",
        "protectedarticle": "skrivskyddade \"[[$1]]\"",
        "modifiedarticleprotection": "ändrade skyddsnivån för \"[[$1]]\"",
        "unprotectedarticle": "tog bort skrivskydd från \"[[$1]]\"",
        "protect-default": "Tillåt alla användare",
        "protect-fallback": "Kräv \"$1\"-behörighet",
        "protect-level-autoconfirmed": "Blockera nya och oregistrerade användare",
-       "protect-level-sysop": "Enbart administratörer",
+       "protect-level-sysop": "Tillåt endast administratörer",
        "protect-summary-cascade": "kaskaderande",
        "protect-expiring": "upphör den $1 (UTC)",
        "protect-expiring-local": "upphör $1",
        "protect-expiry-indefinite": "på obestämd tid",
        "protect-cascade": "Skydda sidor som är inkluderade i den här sidan (kaskaderande skydd)",
        "protect-cantedit": "Du kan inte ändra skrivskyddsnivån för den här sidan, eftersom du inte har behörighet att redigera den.",
-       "protect-othertime": "Annan tidsperiod:",
-       "protect-othertime-op": "annan tidsperiod",
+       "protect-othertime": "Annan tid:",
+       "protect-othertime-op": "annan tid",
        "protect-existing-expiry": "Gällande varaktighet: $2, kl. $3",
        "protect-existing-expiry-infinity": "Gällande varaktighet: oändlig",
        "protect-otherreason": "Annan/ytterligare anledning:",
        "protect-dropdown": "*Vanliga anledningar för skrivskydd\n** Upprepad vandalisering\n** Upprepad spam\n** Redigeringskrig\n** Sida med många besökare",
        "protect-edit-reasonlist": "Redigera skrivskyddsanledningar",
        "protect-expiry-options": "1 timme:1 hour,1 dygn:1 day,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
-       "restriction-type": "Typ av skydd:",
+       "restriction-type": "Behörighet:",
        "restriction-level": "Skyddsnivå:",
        "minimum-size": "Minsta storlek",
        "maximum-size": "Största storlek:",
        "undeletepagetext": "Följande {{PLURAL:$1|sida har blivit raderad|$1 sidor har blivit raderade}} men finns fortfarande i arkivet och kan återställas.\nArkivet kan ibland rensas ut.",
        "undelete-fieldset-title": "Återställ sidversioner",
        "undeleteextrahelp": "För att återställa sidans hela historik, lämna alla rutor oifyllda och klicka på '''''{{int:undeletebtn}}'''''.\nFör att göra en selektiv återställning, kryssa i de rutor som hör till de versioner som ska återställas, och klicka på '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|version|versioner}} raderade",
-       "undeletehistory": "Om du återställer sidan kommer alla tidigare versioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
+       "undeleterevisions": "$1 {{PLURAL:$1|sidversion|sidversioner}} raderade",
+       "undeletehistory": "Om du återställer sidan kommer alla tidigare sidversioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
        "undeleterevdel": "Återställningen kan inte utföras om den resulterar i att den senaste versionen är delvis borttagen.\nI sådana fall måste du se till att den senaste raderade versionen inte är ikryssad, eller att den inte är dold.",
        "undeletehistorynoadmin": "Den här sidan har blivit raderad. Anledningen till detta anges i sammanfattningen nedan, tillsammans med uppgifter om de användare som redigerat sidan innan den raderades. Enbart administratörerna har tillgång till den raderade texten.",
        "undelete-revision": "Raderad version av $1 (från den $4 kl. $5) av $3.",
        "undelete-search-prefix": "Sidor som börjar med:",
        "undelete-search-submit": "Sök",
        "undelete-no-results": "Inga sidor med sådan titel hittades i arkivet över raderade sidor.",
-       "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: filnamnet stämmer inte.",
-       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: filen saknades före radering.",
+       "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: Filnamnet stämmer inte.",
+       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: Filen saknades före radering.",
        "undelete-cleanup-error": "Fel vid radering av den oanvända arkivfilen \"$1\".",
        "undelete-missing-filearchive": "Filen med arkiv-ID $1 kunde inte återställas eftersom den inte finns i databasen. Filen kanske redan har återställts.",
        "undelete-error": "Kunde inte återställa sidan",
        "undelete-error-short": "Fel vid filåterställning: $1",
-       "undelete-error-long": "Fel inträffade när vid återställning av filen:\n\n$1",
-       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl $3?",
+       "undelete-error-long": "Fel inträffade vid återställning av filen:\n\n$1",
+       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl. $3?",
        "undelete-show-file-submit": "Ja",
        "namespace": "Namnrymd:",
        "invert": "Invertera val",
        "tooltip-namespace_association": "Markera denna ruta för att även inkludera diskussions- eller ämnesnamnrymden som är associerad med den valda namnrymden",
        "blanknamespace": "(Huvudnamnrymden)",
        "contributions": "{{GENDER:$1|Användarbidrag}}",
-       "contributions-title": "Bidrag av $1",
+       "contributions-title": "Användarbidrag av $1",
        "mycontris": "Bidrag",
        "anoncontribs": "Bidrag",
        "contribsub2": "För {{GENDER:$3|$1}} ($2)",
        "sp-contributions-logs": "loggar",
        "sp-contributions-talk": "diskussion",
        "sp-contributions-userrights": "hantering av användarrättigheter",
-       "sp-contributions-blocked-notice": "Användaren är blockerad.\nOrsaken till senaste blockeringen kan ses nedan:",
+       "sp-contributions-blocked-notice": "Användaren är blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-blocked-notice-anon": "Denna IP-adress är för närvarande blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-search": "Sök efter användarbidrag",
        "sp-contributions-username": "IP-adress eller användarnamn:",
        "sp-contributions-toponly": "Visa endast aktuella sidversioner",
        "sp-contributions-newonly": "Visa endast redigeringar där sidor skapas",
-       "sp-contributions-hideminor": "Dölj mindre ändringar",
+       "sp-contributions-hideminor": "Dölj mindre redigeringar",
        "sp-contributions-submit": "Sök",
        "whatlinkshere": "Vad som länkar hit",
        "whatlinkshere-title": "Sidor som länkar till \"$1\"",
        "whatlinkshere-page": "Sida:",
-       "linkshere": "Följande sidor länkar till '''[[:$1]]''':",
-       "nolinkshere": "Inga sidor länkar till '''[[:$1]]'''.",
-       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till '''[[:$1]]'''.",
+       "linkshere": "Följande sidor länkar till <strong>[[:$1]]</strong>:",
+       "nolinkshere": "Inga sidor länkar till <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till <strong>[[:$1]]</strong>.",
        "isredirect": "omdirigeringssida",
        "istemplate": "inkluderad som mall",
        "isimage": "fillänk",
        "ipbemailban": "Hindra användaren från att skicka e-post",
        "ipbenableautoblock": "Blockera automatiskt den IP-adress som användaren använde senast, samt alla adresser som användaren försöker redigera ifrån",
        "ipbsubmit": "Blockera användaren",
-       "ipbother": "Annan tidsperiod:",
+       "ipbother": "Annan tid:",
        "ipboptions": "2 timmar:2 hours,1 dygn:1 day,3 dygn:3 days,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
        "ipbhidename": "Dölj användarnamnet från redigeringar och listor",
        "ipbwatchuser": "Bevaka användarens användarsida och diskussionssida",
        "createaccountblock": "kontoregistrering blockerad",
        "emailblock": "e-post blockerad",
        "blocklist-nousertalk": "kan inte redigera sin egen diskussionssida",
-       "ipblocklist-empty": "Listan över blockerade IP-adresser är tom.",
+       "ipblocklist-empty": "Listan över blockeringar är tom.",
        "ipblocklist-no-results": "Den angivna IP-adressen eller användaren är inte blockerad.",
        "blocklink": "blockera",
        "unblocklink": "ta bort blockering",
        "blocklogpage": "Blockeringslogg",
        "blocklog-showlog": "Denna användare har blivit blockerad tidigare.\nBlockeringsloggen är tillgänglig nedan som referens:",
        "blocklog-showsuppresslog": "Denna användare har tidigare blivit blockerad och dold.\nUndanhållandeloggen visas nedan för referens:",
-       "blocklogentry": "blockerade [[$1]] med blockeringstid på $2 $3",
+       "blocklogentry": "blockerade [[$1]] med en varaktighet på $2 $3",
        "reblock-logentry": "ändrade blockeringsinställningar för [[$1]] med en varaktighet på $2 $3",
        "blocklogtext": "Detta är en logg över blockeringar och avblockeringar.\nAutomatiskt blockerade IP-adresser listas ej.\nSe [[Special:BlockList|blockeringslistan]] för en översikt av gällande blockeringar.",
-       "unblocklogentry": "tog bort blockering av \"$1\"",
+       "unblocklogentry": "tog bort blockering av $1",
        "block-log-flags-anononly": "bara oinloggade",
        "block-log-flags-nocreate": "hindrar kontoregistrering",
        "block-log-flags-noautoblock": "utan automatblockering",
        "block-log-flags-angry-autoblock": "utökad automatblockering aktiverad",
        "block-log-flags-hiddenname": "användarnamn dolt",
        "range_block_disabled": "Möjligheten för administratörer att blockera intervall av IP-adresser har stängts av.",
-       "ipb_expiry_invalid": "Ogiltig varaktighetstid.",
+       "ipb_expiry_invalid": "Ogiltig utgångstid.",
        "ipb_expiry_old": "Utgångstiden har redan passerat.",
        "ipb_expiry_temp": "För att dölja användarnamnet måste blockeringen vara permanent.",
        "ipb_hide_invalid": "Kan inte undanhålla detta konto; det har fler än {{PLURAL:$1|en redigering|$1 redigeringar}}.",
        "lockdbtext": "En låsning av databasen hindrar alla användare från att redigera sidor, ändra inställningar och andra saker som kräver ändringar i databasen.\nBekräfta att du verkligen vill göra detta, och att du kommer att låsa upp databasen när underhållet är utfört.",
        "unlockdbtext": "Om du låser upp databasen kommer alla användare att åter kunna redigera sidor, ändra sina inställningar och så vidare. Bekräfta att du vill göra detta.",
        "lockconfirm": "Ja, jag vill verkligen låsa databasen.",
-       "unlockconfirm": "Ja, jag vill låsa upp databasen.",
+       "unlockconfirm": "Ja, jag vill verkligen låsa upp databasen.",
        "lockbtn": "Lås databasen",
        "unlockbtn": "Lås upp databasen",
        "locknoconfirm": "Du har inte bekräftat låsningen.",
        "moveuserpage-warning": "'''Varning:''' Du håller på att flytta en användarsida. Observera att endast sidan kommer att flyttas och att användaren ''inte'' kommer att byta namn.",
        "movecategorypage-warning": "<strong>Varning:</strong> Du är på väg att flytta en kategorisida. Observera att endast sidan kommer att flyttas och eventuella sidor i den gamla kategorin kommer <em>inte</em> att kategoriseras om till den nya kategorin.",
        "movenologintext": "För att flytta en sida måste du vara registrerad användare och [[Special:UserLogin|inloggad]].",
-       "movenotallowed": "Du har inte behörighet att flytta sidor på den här wikin.",
+       "movenotallowed": "Du har inte behörighet att flytta sidor.",
        "movenotallowedfile": "Du har inte tillåtelse att flytta filer.",
        "cant-move-user-page": "Du har inte behörighet att flytta användarsidor (bortsett från undersidor).",
        "cant-move-to-user-page": "Du har inte behörighet att flytta en sida till en användarsida (förutom till en användarundersida).",
        "cant-move-category-page": "Du har inte behörighet att flytta kategorisidor.",
-       "cant-move-to-category-page": "Du har inte behörighet att en sida till en kategorisida.",
+       "cant-move-to-category-page": "Du har inte behörighet att flytta en sida till en kategorisida.",
        "newtitle": "Ny titel:",
        "move-watch": "Bevaka denna sida",
        "movepagebtn": "Flytta sidan",
        "immobile-source-page": "Denna sida är inte flyttbar.",
        "immobile-target-page": "Kan inte flytta till det målnamnet.",
        "bad-target-model": "Den önskade destinationen använder en annan innehållsmodell. Kan inte konvertera från $1 till $2.",
-       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden",
-       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden",
-       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen",
-       "imageinvalidfilename": "Önskat filnamn är ogiltigt",
+       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden.",
+       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden.",
+       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen.",
+       "imageinvalidfilename": "Önskat filnamn är ogiltigt.",
        "fix-double-redirects": "Uppdatera omdirigeringar som leder till den gamla titeln",
        "move-leave-redirect": "Lämna kvar en omdirigering",
        "protectedpagemovewarning": "'''Varning:''' Den här sidan har låsts så att endast användare med administratörsrättigheter kan flytta den.\nDen senaste loggposten tillhandahålls nedan som referens:",
        "exporttext": "Du kan exportera text och versionshistorik för en eller flera sidor i XML-format.\nFilen kan sedan importeras till en annan MediaWiki-wiki med hjälp av sidan [[Special:Import|importera]].\n\nExportera sidor genom att skriva in sidtitlarna i rutan här nedan.\nSkriv en titel per rad och välj om du du vill exportera alla versioner av texten med sidhistorik, eller om du enbart vill exportera den nuvarande versionen med information om den senaste redigeringen.\n\nI det senare fallet kan du även använda en länk, exempel [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] för sidan \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Exportera alla sidor",
        "exportcuronly": "Inkludera endast den nuvarande versionen, inte hela historiken",
-       "exportnohistory": "----\n'''OBS:''' export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
+       "exportnohistory": "----\n<strong>OBS:</strong> Export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
        "exportlistauthors": "Inkludera en fullständig lista över bidragsgivare för varje sida",
        "export-submit": "Exportera",
        "export-addcattext": "Lägg till sidor från kategori:",
        "export-addcat": "Lägg till",
        "export-addnstext": "Lägg till sidor från namnrymd:",
        "export-addns": "Lägg till",
-       "export-download": "Ladda ner som fil",
+       "export-download": "Spara som fil",
        "export-templates": "Inkludera mallar",
        "export-pagelinks": "Inkludera länkade sidor till ett djup på:",
        "export-manual": "Lägg till sidor manuellt:",
        "allmessagesname": "Namn",
        "allmessagesdefault": "Standardtext",
        "allmessagescurrent": "Nuvarande text",
-       "allmessagestext": "Detta är en lista över alla meddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
+       "allmessagestext": "Detta är en lista över alla systemmeddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
        "allmessagesnotsupportedDB": "Den här sidan kan inte användas eftersom '''$wgUseDatabaseMessages''' är avstängd.",
        "allmessages-filter-legend": "Filtrera",
        "allmessages-filter": "Filtrera efter anpassningsgrad:",
        "thumbnail_image-type": "Bildtypen stöds inte",
        "thumbnail_gd-library": "Inkomplett GD library konfigurering: saknar funktionen $1",
        "thumbnail_image-missing": "Fil verkar saknas: $1",
-       "thumbnail_image-failure-limit": "Det har nyligen förekommit alltför många misslyckade ($1 eller fler) försök skapa den här miniatyrbilden. Försök igen senare.",
+       "thumbnail_image-failure-limit": "Det har nyligen förekommit alltför många misslyckade försök ($1 eller fler) att skapa den här miniatyrbilden. Försök igen senare.",
        "import": "Importera sidor",
        "importinterwiki": "Importera från en annan wiki",
        "import-interwiki-text": "Välj en wiki och sidtitel att importera.\nVersionshistorikens datum och redigerare kommer att bevaras.\nAll importering från andra wikis listas i [[Special:Log/import|importloggen]].",
        "import-mapping-subpage": "Importera som undersidor till följande sida:",
        "import-upload-filename": "Filnamn:",
        "import-comment": "Kommentar:",
-       "importtext": "Var god exportera filen från ursprungs-wikin med hjälp av [[Special:Export|exporteringsverktyget]].\nSpara den på din dator och ladda upp den här.",
+       "importtext": "Var god exportera filen frånkällwikin med hjälp av [[Special:Export|exporteringsverktyget]].\nSpara den på din dator och ladda upp den här.",
        "importstart": "Importerar sidor....",
        "import-revision-count": "$1 {{PLURAL:$1|version|versioner}}",
        "importnopages": "Det finns inga sidor att importera.",
        "importuploaderrorpartial": "Uppladdningen av importfilen misslyckades. Bara en del av filen laddades upp.",
        "importuploaderrortemp": "Uppladdningen av importfilen misslyckades. En temporär katalog saknas.",
        "import-parse-failure": "Tolkningsfel vid XML-import",
-       "import-noarticle": "Inga sidor att importera!",
+       "import-noarticle": "Ingen sida att importera!",
        "import-nonewrevisions": "Inga sidversioner importerades (alla var antingen redan där eller hoppades över p.g.a. fel).",
        "xml-error-string": "$1 på rad $2, kolumn $3 (byte $4): $5",
        "import-upload": "Ladda upp XML-data",
        "javascripttest-pagetext-unknownaction": "Okänd handling \"$1\".",
        "javascripttest-qunit-intro": "Se [$1 testningsdokumentationen] på mediawiki.org.",
        "tooltip-pt-userpage": "{{GENDER:|Din användarsida}}",
-       "tooltip-pt-anonuserpage": "Användarsida för ip-numret du redigerar från",
+       "tooltip-pt-anonuserpage": "Användarsida för IP-numret du redigerar från",
        "tooltip-pt-mytalk": "{{GENDER:|Din}} diskussionssida",
-       "tooltip-pt-anontalk": "Diskussion om redigeringar från det här ip-numret",
+       "tooltip-pt-anontalk": "Diskussion om redigeringar från det här IP-numret",
        "tooltip-pt-preferences": "{{GENDER:|Dina}} inställningar",
        "tooltip-pt-watchlist": "Listan över sidor du bevakar för ändringar",
        "tooltip-pt-mycontris": "Lista över {{GENDER:|dina}} bidrag",
        "pageinfo-article-id": "Sid-ID",
        "pageinfo-language": "Språk för sidinnehåll",
        "pageinfo-content-model": "Sidinnehållsmodell",
+       "pageinfo-content-model-change": "ändra",
        "pageinfo-robot-policy": "Indexering av robotar",
        "pageinfo-robot-index": "Tillåten",
        "pageinfo-robot-noindex": "Inte tillåten",
        "pageinfo-few-watchers": "Färre än $1 {{PLURAL:$1|bevakare}}",
        "pageinfo-few-visiting-watchers": "Det kan finnas någon bevakande användare som granskar nyliga redigeringar",
        "pageinfo-redirects-name": "Antal omdirigeringar till denna sida",
-       "pageinfo-subpages-name": "Undersidor till denna sida",
+       "pageinfo-subpages-name": "Antal undersidor till denna sida",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|omdirigering|omdirigeringar}}; $3 {{PLURAL:$3|icke-omdirigering|icke-omdirigeringar}})",
        "pageinfo-firstuser": "Sidskapare",
        "pageinfo-firsttime": "Datum när sidan skapades",
        "markaspatrolledtext": "Märk den här sidan som patrullerad",
        "markaspatrolledtext-file": "Märk denna filversion som patrullerad",
        "markedaspatrolled": "Markerad som patrullerad",
-       "markedaspatrolledtext": "Den valda versionen av [[:$1]] har märkts som patrullerad.",
+       "markedaspatrolledtext": "Den valda versionen av [[:$1]] har markerats som patrullerad.",
        "rcpatroldisabled": "Patrullering av Senaste ändringar är avstängd.",
        "rcpatroldisabledtext": "Funktionen \"patrullering av Senaste ändringar\" är tillfälligt avstängd.",
        "markedaspatrollederror": "Kan inte markera som patrullerad",
        "file-info-size": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4",
        "file-info-size-pages": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4, $5 {{PLURAL:$5|sida|sidor}}",
        "file-nohires": "Det finns ingen version med högre upplösning.",
-       "svg-long-desc": "SVG-fil, grundstorlek: $1 × $2 pixlar, filstorlek: $3",
+       "svg-long-desc": "SVG-fil, standardstorlek: $1 × $2 pixlar, filstorlek: $3",
        "svg-long-desc-animated": "Animerad SVG-fil, standardstorlek $1 × $2 pixlar, filstorlek: $3",
        "svg-long-error": "Felaktig SVG-fil: $1",
        "show-big-image": "Originalfil",
        "exif-gpsspeed-m": "Miles i timmen",
        "exif-gpsspeed-n": "Knop",
        "exif-gpsdestdistance-k": "Kilometer",
-       "exif-gpsdestdistance-m": "Mil",
+       "exif-gpsdestdistance-m": "Miles",
        "exif-gpsdestdistance-n": "Nautiska mil",
        "exif-gpsdop-excellent": "Utmärkt ($1)",
        "exif-gpsdop-good": "Bra ($1)",
        "confirmemail": "Bekräfta e-postadress",
        "confirmemail_noemail": "Du har inte angivit någon giltig e-postadress i dina [[Special:Preferences|inställningar]].",
        "confirmemail_text": "Innan du kan använda {{SITENAME}}s funktioner för e-post måste du bekräfta din e-postadress. Aktivera knappen nedan för att skicka en bekräftelsekod till din e-postadress. Mailet kommer att innehålla en länk, som innehåller en kod. Genom att klicka på den länken eller kopiera den till din webbläsares fönster för webbadresser, bekräftar du att din e-postadress fungerar.",
-       "confirmemail_pending": "En bekräftelsekod har redan skickats till din epostadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
+       "confirmemail_pending": "En bekräftelsekod har redan skickats till din e-postadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
        "confirmemail_send": "Skicka bekräftelsekod",
        "confirmemail_sent": "E-post med bekräftelse skickat.",
        "confirmemail_oncreate": "En bekräftelsekod skickades till din epostadress. Koden behövs inte för att logga in, men du behöver koden för att få tillgång till de epostbaserade funktionerna på wikin.",
        "confirmemail_body": "Någon, troligen du, har från IP-adressen $1 registrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera funktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_changed": "Någon, troligen du, har från IP-adressen $1\nregistrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera\nfunktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk\nför att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_set": "Någon, förmodligen du, från IP-adressen $1,\nhar angivit e-postadressen till kontot \"$2\" till den här adressen på {{SITENAME}}.\n\nFör att bekräfta att kontot verkligen tillhör dig, bör du aktivera e-postfunktionerna på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm kontot *inte* tillhör dig, följ den här länken för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer att sluta fungera efter $4.",
-       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har ogiltigförklarats",
+       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har avbrutits",
        "invalidateemail": "Avbryt bekräftelse av e-postadress",
        "notificationemail_subject_changed": "Registrerad e-postadress på {{SITENAME}} har ändrats",
        "notificationemail_subject_removed": "Registrerad e-postadress på {{SITENAME}} har tagits bort",
        "table_pager_prev": "Föregående sida",
        "table_pager_first": "Första sidan",
        "table_pager_last": "Sista sidan",
-       "table_pager_limit": "Visa $1 poster per sida",
+       "table_pager_limit": "Visa $1 objekt per sida",
        "table_pager_limit_label": "Objekt per sida:",
        "table_pager_limit_submit": "Utför",
        "table_pager_empty": "Inga resultat",
        "lag-warn-high": "På grund av omfattande fördröjning i databasen visas kanske inte ändringar nyare än $1 {{PLURAL:$1|sekund|sekunder}} i den här listan.",
        "watchlistedit-normal-title": "Redigera bevakningslista",
        "watchlistedit-normal-legend": "Ta bort sidor från bevakningslistan",
-       "watchlistedit-normal-explain": "Titlar på din bevakningslista visas nedan.\nFör att ta bort en titel, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
+       "watchlistedit-normal-explain": "Sidor på din bevakningslista visas nedan.\nFör att ta bort en sida, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
        "watchlistedit-normal-submit": "Ta bort sidor",
        "watchlistedit-normal-done": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort från din bevakningslista:",
        "watchlistedit-raw-title": "Redigera bevakningslistan i råformat",
        "watchlistedit-raw-legend": "Redigera bevakningslistan i råformat",
-       "watchlistedit-raw-explain": "Titlar på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen titel per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
+       "watchlistedit-raw-explain": "Sidor på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen sida per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
        "watchlistedit-raw-titles": "Sidor:",
        "watchlistedit-raw-submit": "Uppdatera bevakningslistan",
        "watchlistedit-raw-done": "Din bevakningslista har uppdaterats.",
        "watchlistedit-clear-title": "Rensa bevakningslistan",
        "watchlistedit-clear-legend": "Rensa bevakningslistan",
        "watchlistedit-clear-explain": "Alla titlar kommer att tas bort från din bevakningslista",
-       "watchlistedit-clear-titles": "Titlar:",
+       "watchlistedit-clear-titles": "Sidor:",
        "watchlistedit-clear-submit": "Rensa bevakningslistan (Detta är permanent!)",
        "watchlistedit-clear-done": "Din bevakningslista har rensats.",
-       "watchlistedit-clear-removed": "{{PLURAL:$1|1 titel|$1 titlar}} togs bort:",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort:",
        "watchlistedit-too-many": "Det finns för många sidor att visa här.",
        "watchlisttools-clear": "Rensa bevakningslistan",
        "watchlisttools-view": "Visa relevanta ändringar",
        "tags-activate": "aktivera",
        "tags-deactivate": "inaktivera",
        "tags-hitcount": "$1 {{PLURAL:$1|ändring|ändringar}}",
-       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringstaggar.",
-       "tags-manage-blocked": "Du kan inte hantera ändringsmärken när du är blockerad.",
+       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringsmärken.",
+       "tags-manage-blocked": "Du kan inte hantera förändringsmärken när du är blockerad.",
        "tags-create-heading": "Skapa ett nytt märke",
-       "tags-create-explanation": "Som standard, kommer nyskapade taggar att bli tillgängliga för användning av användare och botar.",
+       "tags-create-explanation": "Som standard, kommer nyskapade märken att bli tillgängliga för användning av användare och botar.",
        "tags-create-tag-name": "Märkesnamn:",
        "tags-create-reason": "Anledning:",
        "tags-create-submit": "Skapa",
        "tags-deactivate-reason": "Anledning:",
        "tags-deactivate-not-allowed": "Det är inte möjligt att inaktivera märket \"$1\".",
        "tags-deactivate-submit": "Inaktivera",
-       "tags-apply-no-permission": "Du har inte behörighet att tillämpa märken på dina ändringar",
+       "tags-apply-no-permission": "Du har inte behörighet att tillämpa ändringsmärken på dina ändringar.",
        "tags-apply-blocked": "Du kan inte ange ändringsmärken med dina ändringar medans du är blockerad.",
        "tags-apply-not-allowed-one": "Märket \"$1\" kan inte läggas till manuellt.",
        "tags-apply-not-allowed-multi": "Följande {{PLURAL:$2|märke|märken}} kan inte läggas till manuellt: $1",
        "log-name-managetags": "Märkeshanteringslogg",
        "log-description-managetags": "Denna sida innehåller administrativa [[Special:Tags|märke]]srelaterade uppgifter. Loggen innehåller bara åtgärder som utförts manuellt av en administratör; märken kan skapas eller raderas av wikins mjukvara utan att en post registreras i loggen.",
        "logentry-managetags-create": "$1 {{GENDER:$2|skapade}} märket \"$4\"",
-       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|sidversion eller loggpost|sidversioner och/eller loggposter}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} märket \"$4\" för användning av användare och botar.",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} märket \"$4\" för användning av användare och botar.",
        "log-name-tag": "Märkeslogg",
        "log-description-tag": "Denna sida visar när användare har lagt till eller tagit bort [[Special:Tags|märken]] från individuella sidversioner eller loggposter. Loggen registrerar inte handlingar där märken hanteras i redigeringar, raderingar eller liknande handlingar.",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 för sidversionen $4 av sidan $3",
-       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för siden $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för sidan $3",
        "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från sidversionen $4 av sidan $3",
        "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från loggposten $5 för sidan $3",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|uppdaterade}} märken på sidversionen $4 för sidan $3 ({{PLURAL:$7|lade till}} $6; {{PLURAL:$9|tog bort}} $8)",
        "api-error-badaccess-groups": "Du får inte ladda upp filer till denna wiki.",
        "api-error-badtoken": "Internt fel: felaktig nyckel.",
        "api-error-blocked": "Du har blockerats från att redigera.",
-       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverad på den här servern.",
+       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverat på den här servern.",
        "api-error-duplicate": "Det finns redan {{PLURAL:$1|en annan fil|andra filer}} på webbplatsen med samma innehåll.",
        "api-error-duplicate-archive": "Det fanns redan {{PLURAL:$1|en annan fil|några andra filer}} på webbplatsen med samma innehåll, men {{PLURAL:$1|den har|de har}} raderats.",
        "api-error-empty-file": "Filen du skickade var tom.",
        "api-error-filename-tooshort": "Filnamnet är för kort.",
        "api-error-filetype-banned": "Denna typ av fil är förbjuden.",
        "api-error-filetype-banned-type": "$1 är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}. {{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
-       "api-error-filetype-missing": "Filen saknar en filändelse.",
+       "api-error-filetype-missing": "Filnamnet saknar en filändelse.",
        "api-error-hookaborted": "Ändringen du försökte göra avbröts av en extension hook.",
        "api-error-http": "Internt fel: Det gick inte att ansluta till servern.",
        "api-error-illegal-filename": "Filnamnet är inte tillåtet.",
-       "api-error-internal-error": "Internt fel: något gick fel med bearbetningen av din uppladdning på wikin.",
+       "api-error-internal-error": "Internt fel: Något gick fel med bearbetningen av din uppladdning på wikin.",
        "api-error-invalid-file-key": "Internt fel: filen hittades inte i tillfällig lagring.",
-       "api-error-missingparam": "Internt fel: det saknas parametrar i begäran.",
-       "api-error-missingresult": "Internt fel: kunde inte avgöra om kopieringen lyckades.",
+       "api-error-missingparam": "Internt fel: Det saknas parametrar i begäran.",
+       "api-error-missingresult": "Internt fel: Kunde inte avgöra om kopieringen lyckades.",
        "api-error-mustbeloggedin": "Du måste vara inloggad för att kunna ladda upp filer.",
        "api-error-mustbeposted": "Det finns en bugg i detta program, det använder inte rätt HTTP-metod.",
        "api-error-noimageinfo": "Uppladdningen lyckades, men servern gav oss inte någon information om filen.",
-       "api-error-nomodule": "Internt fel: ingen uppladdningsmodul uppsatt.",
+       "api-error-nomodule": "Internt fel: Ingen uppladdningsmodul uppsatt.",
        "api-error-ok-but-empty": "Internt fel: Inget svar från servern.",
        "api-error-overwrite": "Det är inte tillåtet att skriva över en befintlig fil.",
-       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kort tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
+       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kortare tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
        "api-error-stashfailed": "Internt fel: servern kunde inte lagra temporär fil.",
        "api-error-publishfailed": "Internt fel: Servern kunde inte publicera temporär fil.",
        "api-error-stasherror": "Ett fel uppstod under uppladdningen av filen till mellanlagringsfilen.",
        "api-error-stashwrongowner": "Filen du försöker komma åt i det temporära lagringsutrymmet tillhör inte dig.",
        "api-error-stashnosuchfilekey": "Filnyckeln som du försökte komma åt i den temporära lagringsytan existerar inte.",
        "api-error-timeout": "Servern svarade inte inom förväntad tid.",
-       "api-error-unclassified": "Ett okänt fel uppstod",
-       "api-error-unknown-code": "Okänt fel: \"$1\"",
+       "api-error-unclassified": "Ett okänt fel uppstod.",
+       "api-error-unknown-code": "Okänt fel: \"$1\".",
        "api-error-unknown-error": "Internt fel: något gick fel när vi försökte ladda upp din fil.",
-       "api-error-unknown-warning": "Okänd varning: $1",
+       "api-error-unknown-warning": "Okänd varning: \"$1\".",
        "api-error-unknownerror": "Okänt fel: \"$1\".",
        "api-error-uploaddisabled": "Uppladdning är inaktiverad på denna wiki.",
        "api-error-verification-error": "Denna fil kan vara skadad eller har fel filändelse.",
        "expand_templates_output": "Expanderad kod",
        "expand_templates_xml_output": "XML-kod",
        "expand_templates_html_output": "Rå HTML-utdata",
-       "expand_templates_ok": "Expandera",
+       "expand_templates_ok": "OK",
        "expand_templates_remove_comments": "Ta bort kommentarer",
        "expand_templates_remove_nowiki": "Undertryck <nowiki> taggar i resultatet",
        "expand_templates_generate_xml": "Visa parseträd som XML",
        "pagelang-use-default": "Använd standardspråk",
        "pagelang-select-lang": "Välj språk",
        "pagelang-submit": "Skicka",
-       "right-pagelang": "Ändra sidans språk",
+       "right-pagelang": "Ändra sidspråk",
        "action-pagelang": "ändra sidspråket",
        "log-name-pagelang": "Språkändringslogg",
        "log-description-pagelang": "Detta är en logg över ändringar i sidspråken.",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Symboler",
        "special-characters-group-greek": "Grekiska",
-       "special-characters-group-greekextended": "Grekiska utvidgad",
-       "special-characters-group-cyrillic": "Kyrilliskt",
+       "special-characters-group-greekextended": "Utökad grekiska",
+       "special-characters-group-cyrillic": "Kyrilliska",
        "special-characters-group-arabic": "Arabiska",
-       "special-characters-group-arabicextended": "Arabiska utökade",
+       "special-characters-group-arabicextended": "Utökad arabiska",
        "special-characters-group-persian": "Persiska",
        "special-characters-group-hebrew": "Hebreiska",
        "special-characters-group-bangla": "Bengali",
        "special-characters-group-gujarati": "Gujarati",
        "special-characters-group-devanagari": "Devenagari",
        "special-characters-group-thai": "Thai",
-       "special-characters-group-lao": "Laotisk",
+       "special-characters-group-lao": "Laotiska",
        "special-characters-group-khmer": "Khmer",
        "special-characters-title-endash": "tankstreck",
        "special-characters-title-emdash": "långt tankstreck",
        "log-action-filter-block": "Typ av blockering:",
        "log-action-filter-contentmodel": "Typ av innehållsmodellsändring:",
        "log-action-filter-delete": "Typ av radering:",
-       "log-action-filter-import": "Importeringstyp:",
+       "log-action-filter-import": "Typ av importering:",
        "log-action-filter-managetags": "Typ av märkeshanteringsåtgärd:",
-       "log-action-filter-move": "Flyttningstyp:",
+       "log-action-filter-move": "Typ av flyttning:",
        "log-action-filter-newusers": "Typ av kontoskapande:",
        "log-action-filter-patrol": "Typ av patrullering:",
        "log-action-filter-protect": "Typ av skydd:",
        "log-action-filter-rights": "Typ av rättighetsändring:",
-       "log-action-filter-suppress": "Censurtyp:",
+       "log-action-filter-suppress": "Typ av censur:",
        "log-action-filter-upload": "Typ av uppladdning:",
        "log-action-filter-all": "Alla",
        "log-action-filter-block-block": "Blockering",
        "log-action-filter-contentmodel-change": "Ändring av innehållsmodell",
        "log-action-filter-contentmodel-new": "Skapande av sida med icke-standardiserad innehållsmodell",
        "log-action-filter-delete-delete": "Radering av sida",
-       "log-action-filter-delete-restore": "Återställde sida",
+       "log-action-filter-delete-restore": "Återställning av sida",
        "log-action-filter-delete-event": "Radering av logg",
        "log-action-filter-delete-revision": "Radering av sidversion",
        "log-action-filter-import-interwiki": "Interwikiimport",
        "authmanager-email-label": "E-post",
        "authmanager-email-help": "E-postadress",
        "authmanager-realname-label": "Riktigt namn",
-       "authmanager-realname-help": "Användarens riktiga namnet",
+       "authmanager-realname-help": "Användarens riktiga namn",
        "authmanager-provider-password": "Lösenordsbaserad autentisering",
        "authmanager-provider-password-domain": "Lösenord- och domänbaserad autentisering",
        "authmanager-provider-temporarypassword": "Tillfälligt lösenord",
index b1bb11e..7c33f6a 100644 (file)
@@ -49,7 +49,8 @@
                        "தமிழ்க்குரிசில்",
                        "Nemo bis",
                        "JAaron95",
-                       "Info-farmer"
+                       "Info-farmer",
+                       "Rakeshonwiki"
                ]
        },
        "tog-underline": "அடிக்கோடிட்டத்தை இணை:",
        "yourpasswordagain": "கடவுச்சொல்லைத் திரும்ப தட்டச்சிடுக:",
        "createacct-yourpasswordagain": "கடவுச்சொல்லை உறுதிசெய்க",
        "createacct-yourpasswordagain-ph": "கடவுச்சொல்லை மீளவும் இடுக",
-       "remembermypassword": "எனது கடவுச்சொல்லை (கூடியது $1 {{PLURAL:$1|நாள்|நாட்கள்}}) அமர்வுகளிடையே நினைவில் வைத்திருக்கவும்.",
        "userlogin-remembermypassword": "இடுபதிந்தே இருக்கவிடவும்",
        "userlogin-signwithsecure": "பாதுகாப்பான தொடர்பை உபயோகிக்கவும்",
+       "cannotlogin-title": "புகுபதிகை இயலாது",
+       "cannotlogin-text": "புகுபதிகை இயலாது.",
        "cannotloginnow-title": "இப்பொழுது விடுபதிகை செய்ய இயலாது.",
        "cannotloginnow-text": "$1-ஐ பயன்படுத்தும் பொழுது விடுபதிகை சாத்தியம் அல்ல.",
+       "cannotcreateaccount-title": "கணக்கைத் தொடங்க முடியாது",
        "yourdomainname": "உங்கள் உரிமைப்பரப்பு:",
        "password-change-forbidden": "நீங்கள் விக்கிகளில் கடவுச் சொற்களை மாற்ற முடியாது",
        "externaldberror": "வெளி உறுதிப்படுத்தலில் ஏற்பட்ட தவறு காரணமாக உங்கள் வெளி கணக்கை இற்றைப்படுத்த முடியாது.",
        "botpasswords-updated-body": "\"$1\" தானியங்கி கடவுச்சொல் முழுமையாக புதிப்பிக்கப்பட்டது.",
        "botpasswords-deleted-title": "தானியங்கி கடவுச்சொல் நீக்கப்பட்டது",
        "botpasswords-deleted-body": "\"$1\"-க்கான தானியங்கி கடவுச்சொல் நீக்கப்பட்டது.",
-       "botpasswords-newpassword": "<strong>$1</strong>-இற்கு புகுபதிகை செய்வதற்கான புதிய கடவுச்சொல் <strong>$2</strong> ஆகும். <em>தயவு செய்து வருங்கால மேற்கோளுக்கு இதனை பதிக.</em>",
+       "botpasswords-newpassword": "<strong>$1</strong>-இற்கு புகுபதிகை செய்வதற்கான புதிய கடவுச்சொல் <strong>$2</strong> ஆகும். <em>தயவு செய்து வருங்கால மேற்கோளுக்கு இதனை பதிக.</em><br>(பழைய முகவர்கள்  பயனாளர் பெயரை உள்ளீட்டிற்கு பயன்படுத்தலாம், மேலும் நீங்கள் <strong>$3</strong> ஐ பயனாளர் பெயராகவும் <strong>$4</strong>  ஐ கடவுச்சொல்லாகவும் பயன்படுத்தலாம்.)",
        "botpasswords-no-provider": "தானியங்கிகடவுச்சொல்அமர்வுவழங்குநர் பயன்பாட்டில் இல்லை.",
        "botpasswords-restriction-failed": "தானியங்கி கடவுச்சொல் புகுபதிகை செய்ய தடுக்கிறது.",
        "botpasswords-invalid-name": "தானியங்கி கடவுச்சொல் பிரிப்பானை (\"$1\") குறிக்கப்பட்ட பயனர் பெயர் கொண்டிருக்கவில்லை.",
        "minoredit": "இது ஒரு சிறு தொகுப்பு",
        "watchthis": "இக்கட்டுரையைக் கவனிக்கவும்",
        "savearticle": "பக்கத்தைச் சேமி",
+       "savechanges": "மாற்றங்களைச் சேமி",
        "publishpage": "பக்கத்தைப் பதிப்பிடுக",
        "publishchanges": "மாற்றங்களைப் பதிப்பிடுக",
        "preview": "முன்தோற்றம்",
        "invalid-content-data": "செல்லாத உள்ளடக்கத் தரவு",
        "content-not-allowed-here": "\"$1\" உள்ளடக்கம் [[$2]] பக்கத்தில் அனுமதிக்கப்படவில்லை.",
        "editwarning-warning": "இந்த பக்கத்தை விட்டு செல்வது நீங்கள் ஏற்படுத்திய மாற்றங்களை இழக்க வழிவகுக்கும்.\nநீங்கள் புகுபதிந்திருந்தால், இந்த எச்சரிக்கையை உங்கள் விருப்பத்தேர்வில் உள்ள \"{{int:prefs-editing}}\" பகுதி மூலம் நீக்கலாம்.",
+       "editpage-invalidcontentmodel-title": "உள்ளடக்க அமைப்பை ஆதரிக்க இயலாது",
+       "editpage-invalidcontentmodel-text": "\"$1\" வகை உள்ளுறை ஏற்பதில்லை.",
        "editpage-notsupportedcontentformat-title": "உள்ளடக்க அமைப்பு ஆதரவில்லாதது",
        "editpage-notsupportedcontentformat-text": "உள்ளடக்க அமைப்பு $1 ஆனது உள்ளடக்க வகை $2 ஆல் ஆதரிக்கப்படாதது.",
        "content-model-wikitext": "விக்கிஉரை",
        "pageinfo-article-id": "பக்க அடையாள இலக்கம்",
        "pageinfo-language": "பக்க உள்ளடக்க மொழி",
        "pageinfo-content-model": "பக்கள உள்ளடக்க மாதிரி",
+       "pageinfo-content-model-change": "மாற்று",
        "pageinfo-robot-policy": "தானியங்கி மூலம் அட்டவணைப்படுத்தல்",
        "pageinfo-robot-index": "அனுமதிக்கப்படுகிறது",
        "pageinfo-robot-noindex": "அனுமதிக்கப்படாதது",
        "tag-filter": "[[Special:Tags|குறிச்சொல்]] வடிப்பான்:",
        "tag-filter-submit": "வடிகட்டி",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|அடையாளம்|அடையாளங்கள்}}]]: $2)",
+       "tag-mw-contentmodelchange": "உள்ளடக்க மாதிரி மாற்றம்",
+       "tag-mw-contentmodelchange-description": "திருத்து\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel change the content model]",
        "tags-title": "குறிச்சொற்கள்",
        "tags-intro": "இப்பக்கத்தின் மென்பொருள் ஒரு திருத்ததுடனான குறியீடு என்று குறிச்சொற்கள், மற்றும் அவற்றின் பொருளை பட்டியலிடுகிறது.",
        "tags-tag": "குறிச்சொல்",
        "tags-actions-header": "செயல்கள்",
        "tags-active-yes": "ஆம்",
        "tags-active-no": "இல்லை",
-       "tags-source-extension": "வà¯\86ளியிணà¯\88பà¯\8dபà¯\81 à®®à¯\82லமà¯\8d à®µà®°à¯\88யறà¯\81à®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\8dà®\9fதà¯\81",
+       "tags-source-extension": "à®®à¯\86னà¯\8dபà¯\8aà®°à¯\81ளà¯\8d à®®à¯\82லமà¯\8d à®µà®°à¯\88யறà¯\81à®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\8dà®\9fதà¯\81.",
        "tags-source-manual": "பயனர் மற்றும் தானியங்கியால் செயல்படுத்தப்படுவது",
        "tags-source-none": "பயன்பாட்டில் இல்லை",
        "tags-edit": "தொகு",
index d9a5592..1f7fa45 100644 (file)
@@ -9,42 +9,43 @@
                        "Bharathesha Alasandemajalu",
                        "Soundarya shetty s",
                        "రహ్మానుద్దీన్",
-                       "BHARATHESHA ALASANDEMAJALU"
+                       "BHARATHESHA ALASANDEMAJALU",
+                       "Lokesha kunchadka"
                ]
        },
-       "tog-underline": "ಲಿà²\82à²\95à³\8dâ\80\99ಲà³\86ದ à²¤à²¿à²°à³\8dತà³\8d à²\97à³\86ರà³\86(à²\85à²\82ಡರà³\8d à²²à³\88ನà³\8d) à²ªà²¾à²¡à³\8dâ\80\99ಲೆ",
-       "tog-hideminor": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²£ೆಲೆನ್ ದೆಂಗಾಲೆ",
-       "tog-hidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²¸à²\82ಪದನà³\86ಲà³\86ನà³\8d à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86ಡà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-newpageshidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²ªà³\8aಸ à²ªà³\81à²\9fà³\8aà²\95à³\81ಲà³\86 à²ªà²\9fà³\8dà²\9fಿಡà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-hidecategorization": "ವಿà²\82à²\97ಡಿತà³\8dâ\80\8dನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-extendwatchlist": "à²\95à³\87ವಲà³\8a à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86ಲತà³\8dತà²\82ದà³\86, à²¸à²\82ಬà²\82ದà³\8a à²\87ಪà³\8dಪà³\81ನ à²®à²¾à²¤ à²¬à²¦à²²à²¾à²µà²£ೆನ್ಲಾ ತೋಜುನಂಚನೆ ಪಟ್ಟಿನ್ ವಿಸ್ತರಿಸಲೆ",
-       "tog-usenewrc": "à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86 à²¬à³\8aà²\95à³\8dà²\95à³\8a à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\97à³\81à²\82ಪà³\81 à²ªà³\81à²\9fà³\8a à²¬à²¦à²²à²¾à²µà²£ೆ",
-       "tog-numberheadings": "ಹà³\86ಡà³\8dಡಿà²\82à²\97à³\8dâ\80\99ಲà³\86à²\97à³\8d à²¸à²\82à²\96à³\8dಯà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dಪಾಲà³\86",
-       "tog-showtoolbar": "ಸà²\82ಪಾದನà³\86ದ à²\89ಪà²\95ರಣೊ ಪಟ್ಟಿನ್ ತೋಜಾವು",
+       "tog-underline": "ಲಿà²\82à²\95à³\8dâ\80\8dಲà³\86ದ à²¤à²¿à²°à³\8dತà³\8d à²\97à³\86ರà³\86(à²\85à²\82ಡರà³\8d à²²à³\88ನà³\8d) à²ªà²¾à²¡à³\8dâ\80\8dಲೆ",
+       "tog-hideminor": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²¨ೆಲೆನ್ ದೆಂಗಾಲೆ",
+       "tog-hidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²¸à²\82ಪದನà³\86ಲà³\86ನà³\8d à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86ಡà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-newpageshidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²ªà³\8aಸ à²ªà³\81à²\9fà³\8aà²\95à³\81ಲà³\86 à²ªà²\9fà³\8dà²\9fಿಡà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-hidecategorization": "ವಿà²\82à²\97ಡಿತà³\8dâ\80\8dನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-extendwatchlist": "à²\95à³\87ವಲà³\8a à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86ಲತà³\8dತà²\82ದà³\86, à²¸à²\82ಬà²\82ದà³\8a à²\87ಪà³\8dಪà³\81ನ à²®à²¾à²¤ à²¬à²¦à²²à²¾à²µà²¨ೆನ್ಲಾ ತೋಜುನಂಚನೆ ಪಟ್ಟಿನ್ ವಿಸ್ತರಿಸಲೆ",
+       "tog-usenewrc": "à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86 à²¬à³\8aà²\95à³\8dà²\95à³\8a à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\97à³\81à²\82ಪà³\81 à²ªà³\81à²\9fà³\8a à²¬à²¦à²²à²¾à²µà²¨ೆ",
+       "tog-numberheadings": "ತರà³\86ಬರವà³\81ಲà³\86à²\97à³\8d à²\85à²\82à²\95à³\86ಲà³\86ನà³\8d à²¤à³\8bà²\9cಾವà³\81",
+       "tog-showtoolbar": "ಸà²\82ಪಾದನà³\86ದ à²\89ಪà²\95ರನೊ ಪಟ್ಟಿನ್ ತೋಜಾವು",
        "tog-editondblclick": "ರಡ್ಡ್ ಸರ್ತಿ ಒತ್ತ್‌ನಗ ಪುಟೊನು ಸಂಪೊಲಿಪುನಂಚ ಆವಡ್",
        "tog-editsectiononrightclick": "ಪುಟೊತ ವಿಬಾಗೊಲೆನ್ ಐತ ಸೀರ್ಸಿಕೆನ್ ರಡ್ಡ್ ಸರ್ತಿ ಒತ್ತ್‌ನಗ ಸಂಪೊಲಿಪುನಂಚ ಉಪ್ಪಡ್",
-       "tog-watchcreations": "ಯಾನà³\8d à²ುರು ಮಲ್ತಿನ ಲೇಕನೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
+       "tog-watchcreations": "ಯಾನà³\8d à²¸ುರು ಮಲ್ತಿನ ಲೇಕನೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
        "tog-watchdefault": "ಯಾನ್ ಸಂಪೊಲಿಪುನ ಪುಟೊಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
-       "tog-watchmoves": "ಯಾನ್ ಸ್ತಲಾಂತರಿಸುನ ಪುಟೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
+       "tog-watchmoves": "ಯಾನà³\8d à²¸à³\8dತಲಾà²\82ತರಿಸಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²\8eನà³\8dನ à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿà²\97à³\8d à²¸à³\87ರà³\8dಪಾಲà³\86",
        "tog-watchdeletion": "ಯಾನ್ ದೆತ್ತ್‌ ಪಾಡುನ ಪುಟೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
-       "tog-watchuploads": "ಎನ್ನ ಅಪ್ಲೋಡ್ ಪಟ್ಟಿಗ್  ಪೊಸ ಕಡತೊಲೆನ್ ಸೇರಲ",
+       "tog-watchuploads": "ಎನ್ನ ಅಪ್ಲೋಡ್ ಪಟ್ಟಿಗ್ ಪೊಸ ಕಡತೊಲೆನ್ ಸೇರಲ",
        "tog-watchrollback": "ಯಾನ್ ಪಿರ ದೆತೊನುನ ಪುಟೊಲೆನ್ ಎನ್ನ ಗುಮನೊಗು ಸೇರಲೆ",
-       "tog-minordefault": "ಪà³\82ರಾ à²¸à²\82ಪಾದನà³\86ನà³\8dಲಾ à²\8eಲà³\8dಯ à²ªà²\82ಡà³\8dâ\80\99ದ್ ಗುರ್ತ ಮಲ್ಪುಲೆ",
-       "tog-previewontop": "ಮà³\81ನà³\8dನà³\8bà²\9fನà³\8d à²¸à²\82ಪಾದನà³\86 à²\85à²\82à²\95ಣದ ಮಿತ್ತ್ ತೊಜ್ಪಾಲೆ",
-       "tog-previewonfirst": "ಶà³\81ರà³\81ತ à²¬à²¦à²²à²¾à²µà²£ೆದ ಬೊಕ್ಕ ಮನ್ನೋಟನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-minordefault": "ಪà³\82ರಾ à²¸à²\82ಪಾದನà³\86ನà³\8dಲಾ à²\8eಲà³\8dಯ à²ªà²\82ಡà³\8dâ\80\8dದ್ ಗುರ್ತ ಮಲ್ಪುಲೆ",
+       "tog-previewontop": "ಮà³\81ನà³\8dನà³\8bà²\9fನà³\8d à²¸à²\82ಪಾದನà³\86 à²\85à²\82à²\95ನà³\8aದ ಮಿತ್ತ್ ತೊಜ್ಪಾಲೆ",
+       "tog-previewonfirst": "ಸà³\81ತ à²¬à²¦à²²à²¾à²µà²¨ೆದ ಬೊಕ್ಕ ಮನ್ನೋಟನ್ ತೊಜ್ಪಾಲೆ",
        "tog-enotifwatchlistpages": "ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಉಪ್ಪುನಂಚಿನ ಒವಾಂಡಲ ಪುಟೊ ಬದಲಾನಗ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
        "tog-enotifusertalkpages": "ಎನ್ನ ಚರ್ಚೆ ಪುಟ ಬದಲಾಂಡ ಎಂಕ್ ಇ-ಮೇಲ್ ಕಡಪುಡ್ಲೆ",
-       "tog-enotifminoredits": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²£ೆ ಆಂಡಲ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
-       "tog-enotifrevealaddr": "ಪà³\8dರà²\95à²\9fಣà³\86 à²\87-ಮà³\87ಲà³\8dâ\80\99ಡ್ ಎನ್ನ ಇ-ಮೇಲ್ ವಿಳಾಸನ್ ತೊಜ್ಪಾಲೆ",
-       "tog-shownumberswatching": "ಪà³\81à²\9fà³\8aನà³\81 à²¤à³\82ವà³\8aà²\82ದà³\81ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\99ನ ಸಂಖ್ಯೆನ್ ತೊಜ್ಪಾಲೆ",
-       "tog-oldsig": "à²\87ತà³\8dತà³\86ದ à²¸à²¹à²¿",
+       "tog-enotifminoredits": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²¨ೆ ಆಂಡಲ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
+       "tog-enotifrevealaddr": "ಪà³\8dರà²\95à²\9fಣà³\86 à²\87-ಮà³\87ಲà³\8dâ\80\8dಡ್ ಎನ್ನ ಇ-ಮೇಲ್ ವಿಳಾಸನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-shownumberswatching": "ಪà³\81à²\9fà³\8aನà³\81 à²¤à³\82ವà³\8aà²\82ದà³\81ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\8dನ ಸಂಖ್ಯೆನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-oldsig": "à²\87ತà³\8dತà³\86ದ à²¸à³\88ನà³\8d.",
        "tog-fancysig": "ವಿಕಿಟೆಕ್ಸ್‌ಟ್‍ಗ್ ದಸ್ಕತ್ತ್‌ದ ಉಪಚಾರೊ(ಸ್ವಂತೊ ಚಾಲನೆದ ಕೊಂಡಿ ಇದ್ಯಂದಿಲೆಕ)",
        "tog-uselivepreview": "ನೇರೊ ಮುನ್ನೋಟೊನು ಉಪಯೋಗ ಮಲ್ಪುಲೆ",
-       "tog-forceeditsummary": "ಸà²\82ಪಾದನà³\86 à²¸à²¾à²°à²¾à²\82ಶà³\8aನà³\81 à²\96ಾಲಿ à²¬à³\81ಡà³\8dâ\80\99ನà³\8dಡ್ ಎಂಕ್ ನೆನಪು ಮಲ್ಪುಲೆ",
-       "tog-watchlisthideown": "ವà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\8eನà³\8dನ à²¸à²\82ಪಾದನà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dâ\80\99ಪಾವà³\8aಚಿ",
+       "tog-forceeditsummary": "ಸà²\82ಪಾದನà³\86 à²¸à²¾à²°à²¾à²\82ಸà³\8aನà³\81 à²\95ಾಲಿ à²¬à³\81ಡà³\8dâ\80\8dà²\82ದ್ ಎಂಕ್ ನೆನಪು ಮಲ್ಪುಲೆ",
+       "tog-watchlisthideown": "ವà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\8eನà³\8dನ à²¸à²\82ಪಾದನà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dâ\80\8dಪಾವà³\8aಡà³\8dಚಿ",
        "tog-watchlisthidebots": "ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಬಾಟ್ ಸಂಪಾದನೆಲೆನ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthideminor": "ಎಲ್ಯ ಬದಲಾವಣೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
-       "tog-watchlisthideliu": "ಲಾà²\97ಿನà³\8d à²\86ತಿನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\99ನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
+       "tog-watchlisthideliu": "ಲಾà²\97ಿನà³\8d à²\86ತಿನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\8dನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthideanons": "ಪುದರಿಜ್ಜಂದಿನ ಬಳಕೆದಾರನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthidepatrolled": "ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಬಾಟ್ ಸಂಪಾದನೆಲೆನ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthidecategorization": "ವಿಂಗಡಿತ್‍ನ ಪುಟೊಲೆನ್ ಅಡೆಂಗಲ",
        "yourpasswordagain": "ಪಾಸ್ವರ್ಡ್ ಪಿರ ಟೈಪ್ ಮಲ್ಪುಲೆ",
        "createacct-yourpasswordagain": "ಪ್ರವೇಸೊ ಪದೊನು ದೃಡೊ ಮಲ್ಪುಲೆ",
        "createacct-yourpasswordagain-ph": "ಪ್ರವೇಸೊ ಪದೊನು ನನ ಒರ ನಮೂದಿಸಲೆ",
-       "remembermypassword": "ಈ ಗಣಕಯಂತ್ರೊಡು ಎನ್ನ ಲಾಗಿನ್ ನೆಂಪು ದೀಡೊನ್ಲೆ(ಹೆಚ್ಚ್ $1 {{PLURAL:$1|ದಿನೊತ|ದಿನೊಕ್ಕುಲೆ}}ಮುಟ್ಟೊ)",
        "userlogin-remembermypassword": "ಎನನ್ ಲಾಗಿನ್ ಆತೇ ದೀಡ್ಲೆ",
        "userlogin-signwithsecure": "ರಕ್ಷಣೆದ ಕನೆಕ್ಷನ್ ಉಪಯೋಗಿಸಲೆ.",
+       "cannotlogin-title": "ಇತ್ತೆ ಉಲಾಯಿ ಪೋಯರ್ ಸಾದ್ಯೊ ಅವೊಂತಿಜ್ಜಿ",
        "cannotloginnow-title": "ಇತ್ತೆ ಉಲಾಯಿ ಪೋಯರ್ ಸಾದ್ಯೊ ಇದ್ದಿ",
+       "cannotcreateaccount-title": "ಕಾತೆ ನಿರ್ಮಾಣೊ ಮಲ್ಪೆರೆ ಆವೊಂತಿಜ್ಜಿ",
        "yourdomainname": "ಈರೆನ ಕಾರ್ಯಕ್ಷೇತ್ರ",
        "password-change-forbidden": "ಈರ್ ಈ ವಿಕಿಡ್ ಪ್ರರವೇಸ ಪದೊನು ಬದಲ್ಪೆರೆ ಸಾದ್ಯೊ ಇದ್ದಿ.",
        "login": "ಲಾಗಿನ್ ಆಲೆ",
        "noname": "ಈರ್ ಸರಿಯಾಯಿನ ಬಳಕೆದಾರ ಪುದರ್ ಕೊರ್ತಿಜ್ಜರ್.",
        "loginsuccesstitle": "ಲಾಗ್ ಇನ್ ಯಶಸ್ವಿಯಾತ್ಂಡ್",
        "loginsuccess": "ಲಾಗ್ ಇನ್ ಯಶಸ್ವಿಯಾತ್‘ಂಡ್\". {{SITENAME}}  \"$1\".'''",
-       "nosuchuser": "!!\"$1\"ಪà³\81ದರà³\8dâ\80\98ದ à²µà²¾ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\98ಲಾ à²\87à²\9cà³\8dà²\9cà³\86ರà³\8d, à²\85à²\95à³\8dಷರ à²¸à²°à²¿à²¯à²¾à²¦ ತೂಲೆ ಅಥವಾ  [[Special:CreateAccount|ಪೊಸ ಸದಸ್ಯತ್ವ  ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ]].",
+       "nosuchuser": "!!\"$1\"ಪà³\81ದರà³\8dâ\80\98ದ à²µà²¾ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\98ಲಾ à²\87à²\9cà³\8dà²\9cà³\86ರà³\8d, à²\85à²\95à³\8dಷರ à²¸à²°à²¿à²¯à²¾à²¤à³\8d ತೂಲೆ ಅಥವಾ  [[Special:CreateAccount|ಪೊಸ ಸದಸ್ಯತ್ವ  ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ]].",
        "nosuchusershort": "!!\"$1\"ಪುದರ್‘ದ ವಾ ಸದಸ್ಯೆರ್‘ಲಾ ಇಜ್ಜೆರ್, ಅಕ್ಷರ ಸರಿಯಾದ ತೂಲೆ.",
        "nouserspecified": "ಈರ್ ಒಂಜಿ ಸದಸ್ಯತ್ವದ ಪುದರ್ ಸೂಚನೆ ಮಲ್ಪೊಡು.",
        "login-userblocked": "ಈ ಸದಸ್ಯರೆನ ಖಾತೆನ್ ತಡೆ ಪತ್ತ್‘ದುಂಡು. ಲಾಗ್ ಇನ್ ಮಲ್ಪರೆ ಆಪುಜ್ಜಿ.",
        "passwordreset-username": "ಸದಸ್ಯೆರ್ನ ಪುದರ್:",
        "passwordreset-domain": "ಕ್ಷೇತ್ರೊ:",
        "passwordreset-email": "ಇ-ಅಂಚೆ ವಿಳಾಸೊ",
+       "passwordreset-invalideamil": "ಇಮೇಲ್ ಸರಿ ಇಜ್ಜಿ",
+       "changeemail-oldemail": "ಇತ್ತೆತಾ ಈಮೇಲ್ ವಿಳಾಸೊ:",
        "changeemail-newemail": "ಪೊಸ ಇ-ಅಂಚೆ ವಿಳಾಸೊ:",
        "changeemail-none": "ಒವ್ವುಲಾ ಇಜ್ಜಿ",
        "changeemail-submit": "ಇ-ಅಂಚೆ ವಿಳಾಸ ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ",
        "permissionserrors": "ಅನುಮತಿ ದೋಷ",
        "permissionserrorstext-withaction": "$2 ಗ್ ಇರೆಗ್ ಅನುಮತಿ ಇದ್ದಿ, ಅಯಿಕ್ {{PLURAL:$1|ಕಾರಣೊ|ಕಾರಣೊಲು}}:",
        "moveddeleted-notice": "ಈ ಪುಟೊ ಅಸ್ತಿತ್ವೊಡ್ ಇದ್ದಿ.\nಪುಟೊದ ಡಿಲೀಶನ್ ಅತ್ತ್ಂಡ್ ಕಡಪ್ಪುಡುನೆ ಲಾಗ್‍ನ್ ತೂಯರೆ ತಿರ್ತ್ ಕೊರ್ತ್ಂಡ್.",
+       "postedit-confirmation-created": "ಈ ಪುಟೋನು ಉಂಡು ಮಾನ್ತುಂಡು.",
+       "postedit-confirmation-saved": "ಇರೇನಾ ಸಂಪಾದನೆನ್ ಒರಿಪಾತುಂಡು.",
+       "edit-already-exists": "ಪೊಸ ಪುಟೋನು ಉಂಡು ಮಲ್ಪರೆ ಅಯಿಜಿ. ಅವ್ವು ದುಂಬೇ ಉಂಡು.",
        "content-model-wikitext": "ವಿಕಿ ಪಠ್ಯ",
        "viewpagelogs": "ಈ ಪುಟೊತ ದಾಕಲೆಲೆನ್ ತೂಲೆ",
        "nohistory": "ಈ ಪುಟಕ್ ಬದಲಾವಣೆದ ಇತಿಹಾಸ ಇಜ್ಜಿ",
        "page_last": "ಕಡೆತ",
        "history-fieldset-title": "ಇತಿಹಾಸಡ್ ನಾಡ್ಲೆ",
        "history-show-deleted": "ದೆತ್ತ್ ಪಾಡಿನ",
-       "histfirst": "ಬಾರಿ à²¦à³\81à²\82ಬà³\81ದ",
+       "histfirst": "ಪರ",
        "histlast": "ಪೊಸ",
        "historysize": "({{PLURAL:$1|೧ ಬೈಟ್|$1 ಬೈಟ್‍ಲು}})",
        "historyempty": "(ಖಾಲಿ)",
        "rev-showdeleted": "ತೊಜಾವು",
        "revisiondelete": "ಮಾಜಾಯಿನ/ಮಾಜಾವಂದಿನ ಬದಲಾವಣೆಲು",
        "revdelete-show-file-submit": "ಅಂದ್",
-       "revdelete-hide-text": "ಬದಲಾವಣà³\86ದ à²ªà² à³\8dಯನà³\8d à²¦à³\86à²\82à²\97ಾಲà³\86",
+       "revdelete-hide-text": "ಪರಿಷà³\8dà²\95ರಣà³\86 à²\86ಯಿನ à²ªà² à³\8dಯ",
        "revdelete-hide-image": "ಪೈಲ್‘ಡ್  ಇಪ್ಪುನ ಮಾಹಿತ್‘ನ್ ದೆಂಗಾಲೆ",
        "revdelete-hide-name": "ಕಾರ್ಯ ಬೊಕ್ಕ ಗುರಿನ್ ದೆಂಗಾಲ",
-       "revdelete-hide-comment": "ಸಂಪಾದನೆದ ವಿವರಣೆ ದೆಂಗಾಲೆ",
-       "revdelete-radio-set": "ಅಂದ್",
-       "revdelete-radio-unset": "ಇಜ್ಜಿ",
+       "revdelete-hide-comment": "ಸಾರಾಂಶ ಸಂಪೊಲಿಪುಲೆ",
+       "revdelete-radio-same": "(ಬದಲಾವಣೆ ಮಾಂಪಾಡ್ಚಿ)",
+       "revdelete-radio-set": "ದೆಂಗಾಲೆ",
+       "revdelete-radio-unset": "ತೋಜುಂಡು",
        "revdelete-log": "ಕಾರಣ",
        "revdel-restore": "ವಿಸಿಬಿಲಿಟಿನ್ ಬದಲ್ ಮಲ್ಪುಲೆ",
        "pagehist": "ಪುಟೊತ ಚರಿತ್ರೆ",
        "right-delete": "ಪುಟೊಕುಲೆನ್ ಮಾಜಾಲೆ",
        "right-undelete": "ಪುಟೊನ್ ಮಾಜಾವಡೆ",
        "grant-group-email": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
+       "grant-createaccount": "ಪೊಸ ಕಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
        "newuserlogpage": "ಸದಸ್ಯೆರೆ ಸ್ರಿಸ್ಟಿದ ದಾಕಲೆ",
        "rightslog": "ಸದಸ್ಯೆರ್ನ ಹಕ್ಕು ದಾಖಲೆ",
        "action-read": "ಈ ಪುಟೊನು ಓದುಲೆ",
        "action-edit": "ಈ ಪುಟೊನು ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
        "action-createpage": "ಈ ಪುಟೊನು ಸೃಷ್ಟಿಸಾಲೆ",
-       "action-createtalk": "ಚರ್ಚಾಪುಟೊಕ್‘ಲೆನ್ ಸೃಷ್ಟಿಸಾಲೆ",
+       "action-createtalk": "ಚರ್ಚಾ ಪುಟೊನ್ ಸೃಷ್ಟಿಸಾಲೆ",
        "action-createaccount": "ಈ ಸದಸ್ಯೆರನ ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ",
        "action-minoredit": "ಉದೊಂಜಿ ಎಲ್ಯ  ಬದಲಾವಣೆ",
        "action-move": "ಈ ಪೂಟೊನು ಮೂವ್(ಸ್ಥಳಾಂತರ) ಮಲ್ಪುಲೆ",
        "action-upload": "ಈ ಫೈಲ್‘ನ್ ಅಪ್‘ಲೋಡ್ ಮಲ್ಪುಲೆ",
        "action-delete": "ಈ ಪುಟೊನ್ ಮಾಜಾಲೆ",
        "action-deleterevision": "ಈ ಆವೃತ್ತಿನ್ ಮಾಜಾಲೆ",
+       "action-browsearchive": "ಮಜಾಯಿನಾ ಪುಟೋನ್ ನಡ್ಲೆ",
+       "action-undelete": "ಈ ಪುಟೊನ್ ಮಾಜಾಯಿನೆನ್ ರದ್ದ್ ಮಾನ್ಪುಲೇ",
        "action-sendemail": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
        "nchanges": "$1 {{PLURAL:$1|ಬದಲಾವಣೆ|ಬದಲಾವಣೆಲು}}",
        "enhancedrc-history": "ಇತಿಹಾಸೊ",
        "rc_categories_any": "ಒವ್ವೇ",
        "rc-change-size-new": "$1 {{PLURAL:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}ಬದಲಾವಣೆಡ್ದ್ ಬುಕ್ಕೊ",
        "newsectionsummary": "\n/* $1 */ಪೊಸ ವಿಭಾಗ",
-       "rc-enhanced-expand": "ವಿವರà³\8aಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dಪಾವà³\81 (à²\9cಾವ à²¸à³\8dà²\95à³\8dರಿಪà³\8dà²\9fà³\8d à²¬à³\8bಡಾಪà³\81à²\82ಡà³\81)",
+       "rc-enhanced-expand": "ವಿವರà³\8aಲà³\86ನà³\8d à²¤à³\8aà²\9cಾವà³\8d",
        "rc-enhanced-hide": "ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "recentchangeslinked": "ಸಂಬಂದೊ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "recentchangeslinked-feed": "ಸಂಬಂಧ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "imagelinks": "ಫೈಲ್‍ದ ಬಳಕೆ",
        "linkstoimage": "ಈ ತಿರ್ತ್‍ದ {{PLURAL:$1|ಪುಟ|$1 ಪುಟೊಲೆ ಕೊಂಡಿ}}ಈ ಫೈಲ್‍ಗ್ ಕೊರ್ಪುಂಡು.",
        "nolinkstoimage": "ಈ ಫೈಲ್‍ಗ್ ಸಂಪರ್ಕೊ ಉಪ್ಪುನ ವಾ ಪುಟೊಲಾ ಇದ್ದಿ.",
-       "sharedupload": "à²\88 à²«à³\88ಲà³\8dâ\80\99ನà³\8d à²®à²¸à³\8dತà³\8d à²\9cನ à²ªà²\9fà³\8dà²\9fà³\8dâ\80\99ದà³\81ಲà³\8dಲà³\86ರà³\8d à²\85à²\82à²\9aà³\86ನà³\86 à²\89à²\82ದà³\81 à²®à²¸à³\8dತà³\8d à²ªà³\8dರà³\8aà²\9cà³\86à²\95à³\8dà²\9fà³\8dâ\80\99ಲà³\86ಡà³\8d à²\89ಪಯà³\8bà²\97ಡà³\81ಪà³\8dಪà³\81.",
+       "sharedupload": "à²\88 à²«à³\88ಲà³\8dâ\80\99ನà³\8d à²®à²¸à³\8dತà³\8d à²\9cನ à²ªà²\9fà³\8dà²\9fà³\8dâ\80\99ದà³\81ಲà³\8dಲà³\86ರà³\8d à²\85à²\82à²\9aà³\86ನà³\86 à²\89à²\82ದà³\81 à²®à²¸à³\8dತà³\8d à²ªà³\8dರà³\8aà²\9cà³\86à²\95à³\8dà²\9fà³\8dâ\80\99ಲà³\86ಡà³\8d à²\89ಪಯà³\8bà²\97ಿಸà³\8aಲಿ",
        "sharedupload-desc-here": "ಈ ಪುಟೊ $1ಡ್ದ್ ಬೊಕ್ಕ ಬೇತೆ ಯೋಜನೆಡ್ದ್ ಗಳಸೊಲಿ.\nಈ ಪುಟೊತ ವಿವರೊ [$2 ಪುಟೊತ ವಿವರೊ] ತಿರ್ತ ಸಾಲ್‍ಡ್ ತೋಜಾದ್ಂಡ್",
        "upload-disallowed-here": "ಈರ್ ಈ ಫೈಲ್‍ನ್ ಕುಡೊರೊ ಬರೆವರೆ ಸಾದ್ಯೊ ಇದ್ದಿ.",
        "filerevert-comment": "ಕಾರಣ:",
        "filerevert-submit": "ದುಂಬುದ ಲೆಕ ಮಲ್ಪುಲೆ",
+       "filedelete": "$1 ನ್ ಮಾಜಾಲೆ",
        "filedelete-legend": "ಕಡತನ್ ಮಾಜಾಲೆ",
        "filedelete-comment": "ಕಾರಣ",
        "filedelete-submit": "ಮಾಜಾಲೆ",
        "protectedpages": "ಸಂರಕ್ಷಿತ ಪುಟೊ",
        "protectedpages-page": "ಪುಟೊ",
        "protectedpages-reason": "ಕಾರಣೊ",
+       "protectedpages-submit": "ಪ್ರದರ್ಶಿಶಿಸಾಯಿನ ಪುದರ್",
        "protectedpages-unknown-timestamp": "ಗೊತ್ತಿಜ್ಜಾಂದಿನ",
        "protectedpages-unknown-performer": "ಅಜ್ಞಾತ ಬಳಕೆದಾರೆ",
        "protectedtitles": "ಸಂರಕ್ಷಿತ ಶೀರ್ಷಿಕೆಲು",
        "tooltip-summary": "ಒಂಜಿ ಎಲ್ಯ ಸಾರಾಂಸೊ ಕೊರ್ಲೆ",
        "simpleantispam-label": "ಯಾಂಟಿ-ಸ್ಪಾಮ್ ಚೆಕ್.\nಮುಲ್ಪ <strong>ದಿಂಜಾವೊಡ್ಚಿ</strong>",
        "pageinfo-article-id": "ಪುಟೊದ ಐಡಿ",
+       "pageinfo-content-model-change": "ಬದಲಾವಣೆಲು",
        "pageinfo-toolboxlink": "ಪುಟೊದ ಮಾಹಿತಿ",
        "pageinfo-contentpage-yes": "ಅಂದ್",
        "pageinfo-protect-cascading-yes": "ಅಂದ್",
index 4377326..b49742e 100644 (file)
        "yourpasswordagain": "సంకేతపదాన్ని మళ్ళీ ఇవ్వండి:",
        "createacct-yourpasswordagain": "సంకేతపదాన్ని నిర్ధారించండి",
        "createacct-yourpasswordagain-ph": "సంకేతపదాన్ని మళ్ళీ ఇవ్వండి",
-       "remembermypassword": "ఈ కంప్యూటరులో నా ప్రవేశాన్ని గుర్తుంచుకో (గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజుల}}కి)",
        "userlogin-remembermypassword": "నన్ను లాగిన్ చేసే ఉంచు",
        "userlogin-signwithsecure": "సురక్షిత కనెక్షను వాడు",
        "cannotloginnow-title": "ఇప్పుడు లాగిన్ అవలేరు",
        "mergehistory-empty": "ఏ కూర్పులనూ విలీనం చెయ్యలేము.",
        "mergehistory-done": "$1 యొక్క $3 {{PLURAL:$3|కూర్పుని|కూర్పులను}} [[:$2]] లోనికి జయప్రదంగా విలీనం చేసాం.",
        "mergehistory-fail": "చరితాన్ని విలీనం చెయ్యలేకపోయాం. పేజీని, సమయాలను సరిచూసుకోండి.",
+       "mergehistory-fail-bad-timestamp": "కాలముద్ర చెల్లదు.",
        "mergehistory-no-source": "మూలం పేజీ, $1 లేదు.",
        "mergehistory-no-destination": "గమ్యం పేజీ, $1 లేదు.",
        "mergehistory-invalid-source": "మూలం పేజీకి సరైన పేరు ఉండాలి.",
        "grant-group-email": "ఈమెయిలు పంపించడం",
        "grant-group-administration": "నిర్వాహక చర్యలు చేపట్టడం",
        "grant-group-private-information": "మీ గోపనీయ డేటాను చూడడం",
+       "grant-basic": "ప్రాథమిక హక్కులు",
        "newuserlogpage": "కొత్త వాడుకరుల చిట్టా",
        "newuserlogpagetext": "ఇది వాడుకరి నమోదుల చిట్టా.",
        "rightslog": "వాడుకరుల హక్కుల మార్పుల చిట్టా",
        "statistics-pages": "పేజీలు",
        "statistics-pages-desc": "ఈ వికీలోని అన్ని పేజీలు (చర్చా పేజీలు, దారిమార్పులు, మొదలైనవన్నీ కలుపుకొని).",
        "statistics-files": "ఎక్కించిన దస్త్రాలు",
-       "statistics-edits": "{{SITENAME}}ని మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
+       "statistics-edits": "{{SITENAME}} మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
        "statistics-edits-average": "పేజీకి సగటు మార్పులు",
        "statistics-users": "నమోదైన [[Special:ListUsers|వాడుకరులు]]",
        "statistics-users-active": "క్రియాశీల వాడుకరులు",
        "apihelp-no-such-module": "\"$1\" మాడ్యూలు కనబడలేదు.",
        "apisandbox": "API ప్రయోగశాల",
        "apisandbox-api-disabled": "ఈ సైటులో API అచేతనమై ఉంది.",
+       "apisandbox-unfullscreen": "పేజీను చూపించు",
        "apisandbox-submit": "అభ్యర్ధించు",
        "apisandbox-reset": "తుడిచివేయి",
-       "apisandbox-examples": "ఉదాహరణ",
-       "apisandbox-results": "ఫలితం",
+       "apisandbox-retry": "మళ్ళీ ప్రయత్నించు",
+       "apisandbox-helpurls": "సహాయపు లంకెలు",
+       "apisandbox-examples": "ఉదాహరణలు",
+       "apisandbox-dynamic-parameters": "అదనపు పరామితులు",
+       "apisandbox-dynamic-parameters-add-label": "పరామితిని చేర్చు:",
+       "apisandbox-dynamic-parameters-add-placeholder": "పరామితి పేరు",
+       "apisandbox-dynamic-error-exists": "\"$1\" అనే పరామితి ఇప్పటికే ఉంది.",
+       "apisandbox-results": "ఫలితాలు",
        "apisandbox-request-url-label": "అభ్యర్థన URL:",
        "apisandbox-request-time": "అభ్యర్ధన సమయం: $1",
        "booksources": "పుస్తక మూలాలు",
        "log-title-wildcard": "ఈ పాఠ్యంతో మొదలయ్యే పుస్తకాల కొరకు వెతుకు",
        "showhideselectedlogentries": "ఎంచుకున్న చిట్టా పద్దులను చూపించు/దాచు",
        "log-edit-tags": "ఎంచుకున్న చిట్టా ప్రవేశాల ట్యాగులను సవరించు",
+       "checkbox-all": "అన్నీ",
+       "checkbox-none": "దేన్నీ వద్దు",
+       "checkbox-invert": "తిరగవెయ్యి",
        "allpages": "అన్ని పేజీలు",
        "nextpage": "తరువాతి పేజీ ($1)",
        "prevpage": "మునుపటి పేజీ ($1)",
        "activeusers-hidebots": "బాట్లను దాచు",
        "activeusers-hidesysops": "నిర్వాహకులను దాచు",
        "activeusers-noresult": "వాడుకరులెవరూ లేరు.",
+       "activeusers-submit": "చేతనంగా ఉన్న వాడుకరులను చూపించు",
        "listgrouprights": "వాడుకరి గుంపుల హక్కులు",
        "listgrouprights-summary": "కింది జాబితాలో ఈ వికీలో నిర్వచించిన వాడుకరి గుంపులు, వాటికి సంబంధించిన హక్కులు ఉన్నాయి.\nవిడివిడిగా హక్కులకు సంబంధించిన మరింత సమాచారం [[{{MediaWiki:Listgrouprights-helppage}}]] వద్ద లభించవచ్చు.",
        "listgrouprights-key": "సూచిక:\n* <span class=\"listgrouprights-granted\">ప్రసాదించిన హక్కు</span>\n* <span class=\"listgrouprights-revoked\">వెనక్కి తీసుకున్న హక్కు</span>",
        "listgrouprights-namespaceprotection-header": "పేరుబరి నిబంధనలు",
        "listgrouprights-namespaceprotection-namespace": "పేరుబరి",
        "listgrouprights-namespaceprotection-restrictedto": "వాడుకరి మార్పు చేయుటకు హక్కు(లు)",
+       "listgrants": "గ్రాంట్లు",
+       "listgrants-grant": "గ్రాంటు",
+       "listgrants-rights": "హక్కులు",
        "trackingcategories": "పహారా కాయు వర్గాలు",
        "trackingcategories-msg": "పహారా కార్యు వర్గము",
        "trackingcategories-name": "సందేశం పేరు",
        "watchlist-submit": "చూపించు",
        "wlshowtime": "చూపించాల్సిన కాలం:",
        "wlshowhideminor": "చిన్న మార్పులు",
-       "wlshowhidebots": "బాట్లు",
+       "wlshowhidebots": "బాట్లు",
        "wlshowhideliu": "నమోదైన వాడుకరులు",
        "wlshowhideanons": "అజ్ఞాత వాడుకరులు",
        "wlshowhidemine": "నా మార్పులు",
        "sessionfailure-title": "సెషను వైఫల్యం",
        "sessionfailure": "మీ ప్రవేశపు సెషనుతో ఏదో సమస్య ఉన్నట్లుంది;\nసెషను హైజాకు కాకుండా ఈ చర్యను రద్దు చేసాం.\n\"back\" కొట్టి, ఎక్కడి నుండి వచ్చారో ఆ పేజీని మళ్ళీ లోడు చేసి, తిరిగి ప్రయత్నించండి.",
        "changecontentmodel-reason-label": "కారణం:",
+       "changecontentmodel-submit": "మార్చు",
        "protectlogpage": "సంరక్షణల చిట్టా",
        "protectlogtext": "ఈ క్రింద ఉన్నది పేజీల సంరక్షణలకు జరిగిన మార్పుల జాబితా.\nప్రస్తుతం అమలులో ఉన్న సంరక్షణలకై [[Special:ProtectedPages|సంరక్షిత పేజీల జాబితా]]ను చూడండి.",
        "protectedarticle": "\"[[$1]]\" సంరక్షించబడింది.",
        "movepagetext-noredirectfixer": "కింది ఫారాన్ని వాడి, ఓ పేజీ పేరు మార్చవచ్చు. దాని చరిత్ర పూర్తిగా కొత్త పేరుకు తరలిపోతుంది. \nపాత శీర్షిక కొత్తదానికి దారిమార్పు పేజీగా మారిపోతుంది.\n[[Special:DoubleRedirects|double]] లేదా [[Special:BrokenRedirects|broken redirects]] లను చూడటం మరువకండి.\nలింకులు వెళ్ళాల్సిన చోటికి వెళ్తున్నాయని నిర్ధారించుకోవాల్సిన బాధ్యత మీదే.\nకొత్త పేరుతో ఈసరికే ఏదైనా పేజీ ఉంటే - అది ఖాళీగా ఉన్నా లేక మార్పుచేర్పుల చరిత్ర ఏమీ లేని దారిమార్పు పేజీ అయినా తప్ప- తరలింపు ’’’జరుగదు’’’ అని గమనించండి.\nఅంటే, ఏదైనా పొరపాటు జరిగితే పేరును తిరిగి పాత పేరుకే మార్చగలరు తప్ప, ఈపాటికే ఉన్న పేజీపై ఓవరరైటు చెయ్యలేరు.\n\n'''హెచ్చరిక!'''\nబహుళ వ్యాప్తి పొందిన ఓ పేజీలో ఈ మార్పు చాలా తీవ్రమైనది, ఊహించనిదీ అవుతుంది.\nదాని పర్యవసానాలు అర్థం చేసుకున్నాకే ముందుకు వెళ్ళండి.",
        "movepagetalktext": "దానితో పాటు సంబంధిత చర్చా పేజీ కూడా ఆటోమాటిక్‌‌గా తరలించబడుతుంది, '''కింది సందర్భాలలో తప్ప:'''\n*ఒక నేంస్పేసు నుండి ఇంకోదానికి తరలించేటపుడు,\n*కొత్త పేరుతో ఇప్పటికే ఒక చర్చా పేజీ ఉంటే,\n*కింది చెక్‌బాక్సులో టిక్కు పెట్టకపోతే.\n\nఆ సందర్భాలలో, మీరు చర్చా పేజీని కూడా పనిగట్టుకుని తరలించవలసి ఉంటుంది, లేదా ఏకీకృత పరచవలసి ఉంటుంది.",
        "moveuserpage-warning": "'''హెచ్చరిక:''' మీరు ఒక వాడుకరి పేజీని తరలించబోతున్నారు. పేజీ మాత్రమే తరలించబడుతుందనీ, వాడుకరి పేరుమార్పు జరగదనీ గమనించండి.",
+       "movecategorypage-warning": "<strong>హెచ్చరిక:</strong> మీరు ఓ వర్గం పేజీని తరలించబోతున్నారు. కేవలం పేజీ మాత్రమే తరలుతుందని, పాత వర్గంలో ఉన్న పేజీలేవీ కొత్త వర్గంలోకి <em>చేరవని</em>  గ్రహించండి.",
        "movenologintext": "పేజీని తరలించడానికి మీరు [[Special:UserLogin|లాగిన్‌]] అయిఉండాలి.",
        "movenotallowed": "పేజీలను తరలించడానికి మీకు అనుమతి లేదు.",
        "movenotallowedfile": "మీకు ఫైళ్ళను తరలించే అనుమతి లేదు.",
        "confirm-watch-top": "ఈ పుటను మీ వీక్షణ జాబితాలో చేర్చాలా?",
        "confirm-unwatch-button": "సరే",
        "confirm-unwatch-top": "ఈ పుటను మీ వీక్షణ జాబితా నుండి తొలగించాలా?",
+       "confirm-rollback-button": "సరే",
        "quotation-marks": "“$1”",
        "imgmultipageprev": "← మునుపటి పేజీ",
        "imgmultipagenext": "తరువాతి పేజీ →",
        "tags-edit-title": "ట్యాగులను సవరించు",
        "tags-edit-manage-link": "ట్యాగులను నిర్వహించండి",
        "tags-edit-existing-tags": "ప్రస్తుత ట్యాగులు:",
-       "tags-edit-existing-tags-none": "''ఏమీలేవు''",
+       "tags-edit-existing-tags-none": "<em>ఏమీలేవు</em>",
        "tags-edit-new-tags": "కొత్త ట్యాగులు:",
        "tags-edit-add": "ఈ ట్యాగులను చేర్చు:",
        "tags-edit-remove": "ఈ ట్యాగులను తొలగించు:",
        "special-characters-title-emdash": "ఎమ్ డాష్",
        "special-characters-title-minus": "మైనస్ గుర్తు",
        "mw-widgets-dateinput-no-date": "ఏ తేదీనీ ఎంచుకోలేదు",
-       "mw-widgets-titleinput-description-new-page": "పేజీ ఇంకా లేదు"
+       "mw-widgets-titleinput-description-new-page": "పేజీ ఇంకా లేదు",
+       "log-action-filter-all": "అన్నీ",
+       "authmanager-realname-label": "అసలు పేరు",
+       "authmanager-realname-help": "వాడుకరి అసలు పేరు",
+       "authmanager-provider-temporarypassword": "తాత్కాలిక సంకేతపదం",
+       "credentialsform-account": "ఖాతా పేరు:"
 }
index 464655a..656ea39 100644 (file)
@@ -25,7 +25,8 @@
                        "Macofe",
                        "Pilarbini",
                        "Matma Rex",
-                       "B20180"
+                       "B20180",
+                       "Pon44695"
                ]
        },
        "tog-underline": "การขีดเส้นใต้ลิงก์:",
        "whatlinkshere-prev": "{{PLURAL:$1|ก่อนหน้า|ก่อนหน้า $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|ถัดไป|ถัดไป $1}}",
        "whatlinkshere-links": "← ลิงก์",
-       "whatlinkshere-hideredirs": "$1การเปลี่ยนทาง",
+       "whatlinkshere-hideredirs": "$1 การเปลี่ยนทาง",
        "whatlinkshere-hidetrans": "$1 ถูกรวมอยู่",
        "whatlinkshere-hidelinks": "$1 ลิงก์",
        "whatlinkshere-hideimages": "$1ลิงก์ไฟล์",
index 66e7a0e..7e167ac 100644 (file)
@@ -86,7 +86,9 @@
                        "İnternion",
                        "Hbseren",
                        "Kumkumuk",
-                       "Basak"
+                       "Basak",
+                       "Ece Alpdeniz",
+                       "Superyetkin"
                ]
        },
        "tog-underline": "Bağlantıların altını çiz:",
        "yourpasswordagain": "Parolayı yeniden girin:",
        "createacct-yourpasswordagain": "Parolayı onayla",
        "createacct-yourpasswordagain-ph": "Parolayı yeniden girin",
-       "remembermypassword": "Girişimi bu tarayıcıda hatırla (en fazla $1 {{PLURAL:$1|gün|gün}} için)",
        "userlogin-remembermypassword": "Oturumumu sürekli açık tut",
        "userlogin-signwithsecure": "Güvenli bağlantı kullanın",
        "cannotloginnow-title": "Şu an oturum açılamıyor",
        "spam_blanking": "Tüm revizyonlar $1 sayfasına bağlantı içeriyor, boşaltılıyor",
        "spam_deleting": "Tüm revizyonlar $1 sayfasına bağlantı içeriyor, siliniyor",
        "simpleantispam-label": "Anti-spam denetimi.\nBunu <strong>doldurmayın</strong>!",
-       "pageinfo-title": "Bilgi için \"$1\"",
+       "pageinfo-title": "\"$1\" sayfa bilgisi",
        "pageinfo-not-current": "Üzgünüz, eski sürümler için bu bilgileri sağlamamız mümkün değildir.",
        "pageinfo-header-basic": "Temel bilgiler",
        "pageinfo-header-edits": "Düzenleme geçmişi",
index 2eaa98c..62dc255 100644 (file)
@@ -93,7 +93,7 @@
        "tog-enotifminoredits": "Надсилати мені електронного листа навіть при незначних редагуваннях сторінок та файлів",
        "tog-enotifrevealaddr": "Показувати мою поштову адресу в повідомленнях",
        "tog-shownumberswatching": "Показувати число користувачів, які додали сторінку до свого списку спостереження",
-       "tog-oldsig": "Ð\9fоточний підпис:",
+       "tog-oldsig": "Ð\92аÑ\88 Ð¿оточний підпис:",
        "tog-fancysig": "Сприймати підпис як вікітекст (без автоматичного посилання)",
        "tog-uselivepreview": "Використовувати швидкий попередній перегляд",
        "tog-forceeditsummary": "Попереджати, коли не зазначений короткий опис редагування",
        "tog-showhiddencats": "Показувати приховані категорії",
        "tog-norollbackdiff": "Не показувати різницю версій після виконання відкоту",
        "tog-useeditwarning": "Попереджати мене, якщо я залишаю сторінку редагування з незбереженими змінами",
-       "tog-prefershttps": "Завжди використовувати безпечне з'єднання при вході у систему",
+       "tog-prefershttps": "Завжди використовувати безпечне з'єднання при вході в систему",
        "underline-always": "Завжди",
        "underline-never": "Ніколи",
        "underline-default": "Використовувати налаштування браузера",
        "newwindow": "(відкривається в новому вікні)",
        "cancel": "Скасувати",
        "moredotdotdot": "Більше…",
-       "morenotlisted": "Цей Ñ\81пиÑ\81ок Ð½ÐµÐ¿Ð¾Ð²Ð½Ð¸Ð¹.",
+       "morenotlisted": "Цей Ñ\81пиÑ\81ок Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð½ÐµÐ¿Ð¾Ð²Ð½Ð¸Ð¼.",
        "mypage": "Сторінка",
        "mytalk": "Обговорення",
        "anontalk": "Обговорення",
        "yourpasswordagain": "Повторний набір пароля:",
        "createacct-yourpasswordagain": "Підтвердіть пароль",
        "createacct-yourpasswordagain-ph": "Введіть пароль знову",
-       "remembermypassword": "Запам'ятати мій обліковий запис на цьому комп'ютері (на строк не більше $1 {{PLURAL:$1|1=дня|днів}})",
        "userlogin-remembermypassword": "Запам'ятати мене",
        "userlogin-signwithsecure": "Захищене з'єднання",
+       "cannotlogin-title": "Не вдалося увійти",
+       "cannotlogin-text": "Вхід у систему неможливий.",
        "cannotloginnow-title": "Неможливо увійти прямо зараз",
        "cannotloginnow-text": "Неможливо увійти під-час використання $1.",
+       "cannotcreateaccount-title": "Створення облікових записів неможливе",
+       "cannotcreateaccount-text": "Пряме створення облікових записів не увімкнене у цій вікі.",
        "yourdomainname": "Ваш домен:",
        "password-change-forbidden": "Ви не можна змінити пароль на цій вікі.",
        "externaldberror": "Сталася помилка при автентифікації за допомогою зовнішньої бази даних, або у вас недостатньо прав для внесення змін до свого зовнішнього облікового запису.",
        "botpasswords-updated-body": "Пароль бота з ім'ям «$1» користувача «$2» було оновлено.",
        "botpasswords-deleted-title": "Пароль бота видалено",
        "botpasswords-deleted-body": "Пароль бота з ім'ям «$1» користувача «$2» було видалено",
-       "botpasswords-newpassword": "Новий пароль для входу під <strong>$1</strong> — <strong>$2</strong>. <em>Запишіть його для подальшого використання.</em>",
+       "botpasswords-newpassword": "Новий пароль для входу під <strong>$1</strong> — <strong>$2</strong>. <em>Запишіть його для подальшого використання.</em> <br> (Для старих ботів, які вимагають, щоб логін був такий же, як і ім'я користувача, Ви також можете використовувати <strong>$3</strong> як ім'я користувача і <strong>$4</strong> як пароль.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider не доступний.",
        "botpasswords-restriction-failed": "Вхід не було здійснено через обмеження для паролю бота.",
        "botpasswords-invalid-name": "Вказане ім'я користувача не містить роздільник для пароля бота («$1»).",
        "userpage-userdoesnotexist": "Користувач під назвою \"<nowiki>$1</nowiki>\" не зареєстрований. Переконайтеся, що ви хочете створити/редагувати цю сторінку.",
        "userpage-userdoesnotexist-view": "Обліковий запис користувача «$1» не зареєстровано.",
        "blocked-notice-logextract": "Цей користувач наразі заблокований.\nОстанній запис у журналі блокувань такий:",
-       "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Привітність & безпека → очистити дані браузера → кеш</em>",
+       "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Приватність & безпека → очистити дані браузера → кеш</em>",
        "usercssyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий css-файл перед збереженням.",
        "userjsyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий код JavaScript перед збереженням.",
        "usercsspreview": "'''Пам'ятайте, що це лише попередній перегляд вашого css-файлу.'''\n'''Його ще не збережено!'''",
        "invalid-content-data": "Неприпустимі дані",
        "content-not-allowed-here": "Вміст «$1» недопустимий на сторінці [[$2]]",
        "editwarning-warning": "Перехід на іншу сторінку призведе до втрати ваших змін.\nЯкщо ви ввійшли до системи, то ви можете відключити це попередження в розділі \"{{int:prefs-editing}}\" ваших налаштувань.",
+       "editpage-invalidcontentmodel-title": "Контентна модель не підтримується",
+       "editpage-invalidcontentmodel-text": "Контентна модель «$1» не підтримується.",
        "editpage-notsupportedcontentformat-title": "Формат вмісту не підтримується",
        "editpage-notsupportedcontentformat-text": "Формат вмісту $1 не підтримується моделлю вмісту $2.",
        "content-model-wikitext": "вікітекст",
        "file-thumbnail-no": "Назва файлу починається на <strong>$1</strong>.\nМожливо, це зменшена копія зображення ''(мініатюра)''.\nЯкщо у вас є це зображення в повному розмірі, завантажте його, інакше змініть назву файлу.",
        "fileexists-forbidden": "Файл з такою назвою вже існує і не може бути перезаписаний.\nЯкщо ви все одно хочете завантажити цей файл, будь ласка, поверніться назад і оберіть іншу назву.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл із такою назвою вже існує у спільному сховищі файлів.\nЯкщо ви все ж хочете завантажити цей файл, будь ласка, поверніться назад і змініть назву файлу. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Це завантаження є точною копією поточної версії <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Це завантаження є точною копією {{PLURAL:$2|старішої версії|старіших версій}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Цей файл є дублікатом {{PLURAL:$1|1=файлу|таких файлів}}:",
        "file-deleted-duplicate": "Такий самий файл ([[:$1]]) уже вилучався раніше. Будь ласка, ознайомтеся з історією вилучень файлу перед тим, як завантажити його знову.",
        "file-deleted-duplicate-notitle": "Файл, ідентичний до цього файлу, був раніше видалений, і назву було усунено.\nВам слід попросити кого-небудь з можливістю перегляду усуненого файлу даних, щоб проаналізувати ситуацію, перш ніж приступити до повторного завантаження.",
        "filerevert-submit": "Повернути",
        "filerevert-success": "'''[[Media:$1|$1]]''' був повернутий до [$4 версії від $3, $2].",
        "filerevert-badversion": "Немає локальної версії цього файлу з вказаною поміткою дати і часу.",
+       "filerevert-identical": "Поточна версія файлу вже ідентична обраній.",
        "filedelete": "Вилучення $1",
        "filedelete-legend": "Вилучити файл",
        "filedelete-intro": "Ви збираєтесь вилучити '''[[Media:$1|$1]]''' і всю його історію.",
        "rollbacklinkcount-morethan": "відкинути понад $1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "rollbackfailed": "Відкинути зміни не вдалося",
        "rollback-missingparam": "Відсутні обов'язкові параметри за запитом.",
+       "rollback-missingrevision": "Не вдалося завантажити дані версії.",
        "cantrollback": "Неможливо відкинути редагування, оскільки останній дописувач сторінки є її автором.",
        "alreadyrolled": "Неможливо відкинути останні редагування [[:$1]], зроблені [[User:$2|$2]] ([[User talk:$2|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), оскільки хтось інший уже змінив чи відкинув редагування цієї статті.\n\nОстанні редагування зроблено [[User:$3|$3]] ([[User talk:$3|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Пояснення редагування було: «<em>$1</em>.».",
        "pageinfo-article-id": "ID сторінки",
        "pageinfo-language": "Мова вмісту сторінки",
        "pageinfo-content-model": "Модель вмісту сторінки",
+       "pageinfo-content-model-change": "змінити",
        "pageinfo-robot-policy": "Індексація пошуковими системами",
        "pageinfo-robot-index": "Індексується",
        "pageinfo-robot-noindex": "Не індексується",
        "tag-filter": "Фільтр&nbsp;[[Special:Tags|міток]]:",
        "tag-filter-submit": "Відфільтрувати",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Мітка|Мітки|Міток}}]]: $2)",
+       "tag-mw-contentmodelchange": "зміна контентної моделі",
+       "tag-mw-contentmodelchange-description": "Редагування, якими була здійснена [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel зміна контентної моделі] сторінки",
        "tags-title": "Мітки",
        "tags-intro": "На цій сторінці наведений список міток, якими програмне забезпечення помічає редагування, а також значення цих міток.",
        "tags-tag": "Назва мітки",
        "tags-actions-header": "Дії",
        "tags-active-yes": "Так",
        "tags-active-no": "Ні",
-       "tags-source-extension": "Визначається розширенням",
+       "tags-source-extension": "Визначається програмним забезпеченням",
        "tags-source-manual": "Застосовується вручну користувачами і ботами",
        "tags-source-none": "Більше не використовується",
        "tags-edit": "редагувати",
index 5be33fa..3a16bb5 100644 (file)
@@ -35,8 +35,9 @@
        "tog-hideminor": "حالیہ تبدیلیوں میں معمولی ترامیم چھپائیں",
        "tog-hidepatrolled": "حالیہ تبدیلیوں میں گشتی ترامیم چھپائیں",
        "tog-newpageshidepatrolled": "جدید صفحات کی فہرست میں مراجعت شدہ صفحات چھپائیں",
-       "tog-extendwatchlist": "حالیہ ترین تبدیلیوں کے بجائے جملہ تبدیلیاں دیکھنے کے لیے زیر نظر فہرست کی توسیع کریں",
-       "tog-usenewrc": "حالیہ تبدیلیاں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کیجئے",
+       "tog-hidecategorization": "صفحات کی زمرہ بندی چھپائیں",
+       "tog-extendwatchlist": "حالیہ ترین تبدیلیوں کی بجائے تمام تبدیلیاں دیکھنے کے لیے زیر نظر فہرست کو وسیع کریں",
+       "tog-usenewrc": "حالیہ تبدیلیاں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کریں",
        "tog-numberheadings": "سرخیوں کو خودکار نمبر دیں",
        "tog-showtoolbar": "آلات ترمیم دکھائیں",
        "tog-editondblclick": "دو کلک پر صفحات کی ترمیم کریں",
@@ -45,6 +46,7 @@
        "tog-watchdefault": "میرے ترمیم شدہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
        "tog-watchmoves": "میرے منتقل کردہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
        "tog-watchdeletion": "میرے حذف کردہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
+       "tog-watchuploads": "میری اپلوڈ کردہ نئی فائلوں کو زیر نظر فہرست میں شامل کریں",
        "tog-watchrollback": "میرے استرجع کردہ صفحات کو میری زیر نظر فہرست میں شامل کریں",
        "tog-minordefault": "ہمیشہ میری تمام ترامیم کو معمولی ترمیم کے طور پر نشان زد کریں",
        "tog-previewontop": "خانہ ترمیم سے پہلے نمائش دکھائیں",
@@ -54,7 +56,7 @@
        "tog-enotifminoredits": "مجھے صفحات اور فائلوں میں کی جانے والی معمولی ترامیم کی خبر بھی بذریعہ برقی ڈاک بھیجیں",
        "tog-enotifrevealaddr": "اطلاعاتی برقی خطوط میں میرا برقی ڈاک پتہ ظاہر کریں",
        "tog-shownumberswatching": "دیکھنے والے صارفین کی تعداد دکھائیں",
-       "tog-oldsig": "موجودہ دستخط:",
+       "tog-oldsig": "آپ کے موجودہ دستخط:",
        "tog-fancysig": "سادہ دستخط (بلا خودکار ربط)",
        "tog-uselivepreview": "راست نمائش استعمال کریں",
        "tog-forceeditsummary": "خلاصہ ترمیم خالی چھوڑنے پر مجھے آگاہ کریں",
        "tog-watchlisthidebots": "زیرِنظر فہرست سے روبہ جاتی ترامیم چھپائیں",
        "tog-watchlisthideminor": "زیرِنظر فہرست سے معمولی ترامیم چھپائیں",
        "tog-watchlisthideliu": "زیرِنظر فہرست سے داخلِ نوشتہ شدہ صارفین کی ترامیم چھپائیں",
+       "tog-watchlistreloadautomatically": "کسی مقطار میں تبدیلی کے بعد زیر نظر فہرست کو خودکار طور پر تازہ کریں (جاوا اسکرپٹ درکار)",
        "tog-watchlisthideanons": "زیرِنظر فہرست سے نامعلوم صارفین کی ترامیم چھپائیں",
        "tog-watchlisthidepatrolled": "زیرِنظر فہرست سے مراجع شدہ ترامیم چھپائیں",
-       "tog-ccmeonemails": "دیگر صارفین کو ارسال کردہ برقی خطوط کی نقول مجھے بھی ارسال کریں۔",
-       "tog-diffonly": "فرق کے نیچے صفحے کے مشمولات نہ دکھائیں",
+       "tog-watchlisthidecategorization": "صفحات کی زمرہ بندی چھپائیں",
+       "tog-ccmeonemails": "دیگر صارفین کو ارسال کردہ برقی خطوط کی نقل مجھے بھی ارسال کریں۔",
+       "tog-diffonly": "فرق کے نیچے صفحے کے مندرجات نہ دکھائیں",
        "tog-showhiddencats": "پوشیدہ زمرہ جات دکھائیں",
-       "tog-norollbackdiff": "استرجع Ú©Û\8c Ø§Ù\86جاÙ\85 Ø¯Û\81Û\8c Ú©Û\92 Ø¨Ø¹Ø¯ Ù\81رÙ\82 ØªØ±Ú© Ú©Ø±یں",
+       "tog-norollbackdiff": "استرجع Ú©Û\92 Ø¨Ø¹Ø¯ Ù\81رÙ\82 Ù\86Û\81 Ø¯Ú©Ú¾Ø§Ø¦یں",
        "tog-useeditwarning": "غیر محفوظ تبدیلیاں چھوڑنے پر مجھے آگاہ کریں",
-       "tog-prefershttps": "لاگ ان رہنے کے دوران ہمیشہ محفوظ کنیکشن استعمال کریں",
+       "tog-prefershttps": "داخل رہنے کے دوران میں ہمیشہ محفوظ کنیکشن استعمال کریں",
        "underline-always": "ہمیشہ",
        "underline-never": "کبھی نہیں",
-       "underline-default": "جلد یا براؤزر کا ڈیفالٹ",
+       "underline-default": "پوشاک یا براؤزر کا طے شدہ",
        "editfont-style": "خانۂ ترمیم کا فانٹ:",
-       "editfont-default": "براؤزر کا ڈیفالٹ",
+       "editfont-default": "براؤزر کا طے شدہ",
        "editfont-monospace": "مونوسپیسڈ فونٹ",
        "editfont-sansserif": "سنس سیرف فونٹ",
        "editfont-serif": "سیرف فونٹ",
        "october-date": "$1 اکتوبر",
        "november-date": "$1 نومبر",
        "december-date": "$1 دسمبر",
+       "period-am": "صبح",
+       "period-pm": "شام",
        "pagecategories": "{{PLURAL:$1|زمرہ|زمرہ جات}}",
        "category_header": "زمرہ \"$1\" میں صفحات",
        "subcategories": "ذیلی زمرہ جات",
        "newwindow": "(نـئی ونـڈو میـں کھولیں)",
        "cancel": "منسوخ",
        "moredotdotdot": "مزید...",
-       "morenotlisted": "یہ فہرست مکمل نہیں ہے۔",
+       "morenotlisted": "شاید یہ فہرست مکمل نہیں۔",
        "mypage": "صفحہ",
        "mytalk": "تبادلۂ خیال",
        "anontalk": "اس آئی پی پتہ کا تبادلۂ خیال",
        "tagline": "{{SITENAME}} سے",
        "help": "معاونت",
        "search": "تلاش",
+       "search-ignored-headings": " #<!-- اس سطر کو ہو بہو اپنی حالت پر چھوڑ دیں --> <pre>\n# سرخیاں جو تلاش کے دوران میں نظر انداز کر دی جائیں گی۔\n# سرخی پر مشتمل صفحہ کی فہرست سازی مکمل ہوتے ہی تبدیلیاں نافذ ہو جائیں گی۔\n# ایک خالی ترمیم کر کے آپ صفحہ کی دوبارہ فہرست سازی کر سکتے ہیں۔\n# صیغہ حسب ذیل ہے:\n# * ہر چیز جو \"#\" علامت کے بعد آخری سطر تک ہو اسے تبصرہ سمجھا جائے گا۔\n#* ہر وہ سطر جو خالی نہ ہو عنوان ہوگا اور اسے نظر انداز کر دیا جائے گا (نیز جس طرح درج ہے اسی طرح استعمال کیا جائے گا)۔\nحوالہ جات\nبیرونی روابط\nمزید دیکھیے\n #<!-- اس سطر کو ہو بہو اپنی حالت پر چھوڑ دیں --> <pre>",
        "searchbutton": "تلاش",
        "go": "چلو",
        "searcharticle": "چلو",
        "jumptosearch": "تلاش",
        "view-pool-error": "معذرت کے ساتھ، تمام معیلات پر اِس وقت اِضافی بوجھ ہے.\nبہت زیادہ صارفین اِس وقت یہ صفحہ ملاحظہ کرنے کی کوشش کررہے ہیں.\nبرائے مہربانی! صفحہ دیکھنے کیلئے دوبارہ کوشش کرنے سے پہلے ذرا انتظار فرمالیجئے.\n\n$1",
        "generic-pool-error": "ہم معذرت خواہ ہیں! معیلات (سرورز) پر اِس وقت اِضافی بوجھ ہے.\nصارفین کی کثیر تعداد اِس وقت یہی صفحہ ملاحظہ کرنے کی کوشش کررہی ہے.\nبرائے مہربانی!دوبارہ کوشش کرنے سے پہلے ذرا انتظار فرمائیے.",
+       "pool-timeout": "مقفل کرنے کے لیے انتظار کی مہلت ختم",
+       "pool-queuefull": "قطار لگی ہوئی ہے",
        "pool-errorunknown": "نامعلوم خطا",
+       "pool-servererror": "پول شمار خدمت دستیاب نہیں ($1)۔",
        "poolcounter-usage-error": "استعمال میں خامی: $1",
-       "aboutsite": "تعارف {{SITENAME}}",
+       "aboutsite": "{{SITENAME}} کا تعارف",
        "aboutpage": "Project:تعارف",
        "copyright": "تمام مواد $1 کے تحت میسر ہے، جب تک کوئی دوسری وجہ نا ہو۔",
        "copyrightpage": "{{ns:project}}:حقوق تصانیف",
        "policy-url": "Project:حکمتِ عملی",
        "portal": "دیوان عام",
        "portal-url": "Project:دیوان عام",
-       "privacy": "اصÙ\88Ù\84 Ø¨Ø±Ø§Û\93 Ø§Ø®Ù\81ائÛ\92 Ø±Ø§Ø²",
+       "privacy": "اخÙ\81ائÛ\92 Ø±Ø§Ø² Ú©Û\92 Ø§ØµÙ\88Ù\84",
        "privacypage": "Project:اصولِ اخفائے راز",
        "badaccess": "خطائے اجازت",
        "badaccess-group0": "آپ متمنی عمل کا اجراء کرنے کے مُجاز نہیں۔",
        "pagetitle-view-mainpage": "{{SITENAME}}",
        "retrievedfrom": "‘‘$1’’ مستعادہ منجانب",
        "youhavenewmessages": "آپکے لیۓ ایک $1 ہے۔ ($2)",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|آپ کے لیے}} {{PLURAL:$3|کسی دوسرے صارف|$3 صارفین}} کی جانب سے $1 ($2)۔",
+       "youhavenewmessagesmanyusers": "آپ کے لیے متعدد صارفین کی جانب سے $1 ($2)۔",
        "newmessageslinkplural": "{{PLURAL:$1|نیا پیغام|999=نئے پیغاماتs}}",
        "newmessagesdifflinkplural": "آخری {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
        "youhavenewmessagesmulti": "ء$1 پر آپ کیلئے نئے پیغامات ہیں",
        "nosuchaction": "کوئی سا عمل نہیں",
        "nosuchactiontext": "URL کی جانب سے مختص کیا گیا عمل درست نہیں.\nآپ نے شاید URL غلط لکھا، یا کسی غیر صحیح ربط کی پیروی کی ہے.\n{{اِس سے SITENAME کے زیرِ استعمال مصنع لطیف میں کھٹمل کی نشاندہی کا بھی اندیشہ ہے}}.",
        "nosuchspecialpage": "کوئی ایسا خاص صفحہ نہیں",
-       "nospecialpagetext": "<strong>آپ نے ایک ناقص خاص صفحہ کی درخواست کی ہے.</strong>\n\n{{درست خاص صفحات کی ایک فہرست [[Special:SpecialPages|{{int:specialpages}}]] پر دیکھی جاسکتی ہے}}.",
+       "nospecialpagetext": "<strong>آپ نے ایک غیر موجود خصوصی صفحہ کی درخواست کی ہے۔</strong>\n\nدرست خاص صفحات کی ایک فہرست [[Special:SpecialPages|{{int:specialpages}}]] پر دیکھی جاسکتی ہے۔",
        "error": "خطاء",
        "databaseerror": "خطائے ڈیٹابیس",
        "databaseerror-text": "ڈیٹا بیس کیوری میں خامی پیدا ہوگئی ہے.\nیہ سافٹ ویئر میں ایک مسئلے (بگ) کی نشاندہی کر سکتے ہیں.",
        "databaseerror-query": "کیوری: $1",
        "databaseerror-function": "فنکشن: $ 1",
        "databaseerror-error": "خرابی: $ 1",
+       "transaction-duration-limit-exceeded": "زیادہ تاخیر سے بچنے کے لیے اس اقدام کو منسوخ کر دیا گیا ہے کیونکہ مدت تحریر ($1) اپنی حد $2 سیکنڈ سے تجاوز کر چکی ہے۔\nاگر آپ ایک ہی وقت میں کئی چیزیں تبدیل کر رہے ہیں تو بہتر ہوگا کہ اس تبدیلی کو متعدد قسطوں میں انجام دیں۔",
        "laggedslavemode": "انتباہ: ممکن ہے کہ صفحہ میں حالیہ بتاریخہ جات شامل نہ ہوں.\n\nWarning: Page may not contain recent updates.",
        "readonly": "ڈیٹابیس مقفل ہے",
        "enterlockreason": "قفل کیلئے کوئی وجہ درج کیجئے، بشمولِ تخمینہ کہ قفل کب کھولا جائے گا.",
        "missingarticle-rev": "(نظرثانی#: $1)",
        "missingarticle-diff": "(فرق: $1، $2)",
        "readonly_lag": "ڈیٹابیس خودکار طور پر مقفل ہوچکا ہے تاکہ ماتحت ڈیٹابیسی معیلات کا درجہ آقا کا ہوجائے.",
+       "nonwrite-api-promise-error": "ایچ ٹی ٹی پی سرنامہ 'Promise-Non-Write-API-Action' بھیجا گیا لیکن یہ ایک اے پی آئی نویس ماڈیول کو بھیجی گئی درخواست تھی۔",
        "internalerror": "خطائے اندرونی",
        "internalerror_info": "خطائے اندرونی: $1",
+       "internalerror-fatal-exception": "\"$1\" قسم کا تباہ کن استثنا",
        "filecopyerror": "\"$1\" مسل کو \"$2\" کی طرف نقل نہیں کیا جاسکا.",
        "filerenameerror": "مسل \"$1\" کو \"$2\" میں بازنام نہیں کیا جاسکا.",
        "filedeleteerror": "مسل \"$1\" کو حذف نہیں کیا جاسکا.",
        "cannotdelete": "صفحہ یا ملف $1 کو حذف نہیں کیا جاسکتا.\nہوسکتا ہے کہ اسے پہلے ہی کسی نے حذف کردیا ہو.",
        "cannotdelete-title": "صفحہ ھذف نہیں کیا جا سکتا \"$1\"",
        "delete-hook-aborted": "حذف شدگی روک دی گئی\nوضاحت نہیں کی گئی",
+       "no-null-revision": "صفحہ \"$1\" کے لیے نیا خالی نسخہ نہیں بنایا جا سکتا",
        "badtitle": "خراب عنوان",
        "badtitletext": "درخواست شدہ صفحہ کا عنوان ناقص، خالی، یا کوئی غلط ربط شدہ بین لسانی یا بین ویکی عنوان ہے.\nشاید اِس میں ایک یا زیادہ ایسے حروف موجود ہوں جو عنوانات میں استعمال نہیں ہوسکتے.",
+       "title-invalid-empty": "درخواست شدہ عنوان خالی ہے یا اس میں محض نام فضا کا نام ہے۔",
+       "title-invalid-utf8": "درخواست شدہ عنوان میں نادرست یونیکوڈ حروف موجود ہیں۔",
+       "title-invalid-interwiki": "درخواست شدہ عنوان میں ایسا بین الویکی ربط موجود ہے جسے عنوانات میں استعمال نہیں کیا جا سکتا۔",
+       "title-invalid-talk-namespace": "درخواست شدہ عنوان اس تبادلۂ خیال صفحہ کا ہے جو موجود نہیں۔",
+       "title-invalid-characters": "درخواست شدہ عنوان میں نادرست حروف «$1» موجود ہیں۔",
+       "title-invalid-relative": "عنوان میں علامت موجود ہے۔ علامتوں (./, ../) پر مشتمل عنوانات نادرست ہیں، اس لیے کہ یہ علامتیں صارف کے براؤزر کے لیے ناقابل رسائی ہوتی ہیں۔",
+       "title-invalid-magic-tilde": "درخواست شدہ عنوان میں نادرست جادوئی علامتیں مسلسل استعمال کی گئی ہیں (<nowiki>~~~</nowiki>)۔",
+       "title-invalid-too-long": "درخواست شدہ عنوان بے حد طویل ہے۔ عنوان کی طوالت یونیکوڈ کے $1 {{PLURAL:$1|بائٹ}} سے کم ہونی چاہیے۔",
+       "title-invalid-leading-colon": "درخواست شدہ عنوان کے شروع میں ایک نادرست رابطہ موجود ہے۔",
        "perfcached": "ذیلی ڈیٹا ابطن شدہ (cached) ہے اور اِس کے پُرانے ہونے کا امکان ہے. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.",
        "perfcachedts": "ذیلی ڈیٹا ابطن شدہ ہے (cached) اور آخری بار اِس کی بتاریخیت $1 کو ہوئی. A maximum of {{PLURAL:$4|one result is|$4 results are}} available in the cache.",
        "querypage-no-updates": "اِس صفحہ کیلئے بتاریخات فی الحال ناقابل بنائی گئی ہیں.\nیہاں کا ڈیٹا ابھی تازہ نہیں کیا جائے گا.",
        "protectedpagetext": "اس صفحہ کو تدوین سے محفوظ رکھنے کیلیے مقفل کر دیا گیا ہے۔",
        "viewsourcetext": "آپ صرف مسودہ دیکھ سکتے ہیں اور اسکی نقل اتار سکتے ہیں۔",
        "viewyourtext": "آپ اس مواد کو دیکھ سکتے ہیں اور اٹھا (کاپی) سکتے ہیں <strong>آپ کی ترامیم</strong> اس صفحہ پر۔",
-       "protectedinterface": "{{fmbox \n| type = editnotice\n| image = [[File:Namespace MediaWiki.svg|45px]]\n| text = '''اس [[mw:Manual:System message|انٹرفیس پیغام]] یا [[ویکیپیڈیا:جلد|جلد]] کی دستاویز [[mw:Manual:Interface/{{PAGENAME}}|میڈیاویکی ڈاٹ آرگ]] یا [[translatewiki:MediaWiki:{{PAGENAME}}/qqq|ٹرانسلیٹ ویکی ڈاٹ نیٹ]] پر بنائی جا سکتی ہے۔''' <br /> چونکہ یہ صفحہ میڈیاویکی سوفٹویئر کے لیے انٹرفیس متن فراہم کرتا ہے، اس لیے اس میں محض [[ویکیپیڈیا:منتظمین|منتظمین]] ہی ترمیم کر سکتے ہیں۔ البتہ اگر آپ کو اس میں کسی قسم کی ترمیم مطلوب ہو تو ذیل میں موجود بٹن پر کلک کرکے اس پیغام میں ترمیم کی درخواست کر سکتے ہیں۔\n<div class=\"center\" style=\"margin-top: 4px;\">{{درخواست ترمیم روانہ کریں|type=full}}</div>\n}}{{editnotice load\n| notice action = view \n}}",
+       "protectedinterface": "یہ صفحہ سافٹ ویئر کا انٹرفیس متن فراہم کرتا ہے اور غلط استعمال سے بچنے کے لیے اسے محفوظ رکھا گیا ہے۔\nتمام ویکیوں میں ترجمہ شامل کرنے یا اس میں تبدیلی کرنے کے لیے میڈیاویکی دار الترجمہ [https://translatewiki.net/ translatewiki.net]کو استعمال کریں۔",
        "editinginterface": "<strong>انتباہ: </strong> آپ ایک ایسے صفحے میں ترمیم کر رہے ہیں جو سوفٹ ویئر کے لیے انٹرفیس متن فراہم کرتا ہے۔ اس صفحہ میں کی جانے والی تبدیلی سے اس ویکی پر دیگر صارفین کے لیے انٹرفیس متاثر ہوگی۔",
        "translateinterface": "تمام ویکیوں میں تبدیلی یا شامل کرنے کے لیے، [https://translatewiki.net/ translatewiki.net]کو استعمال کریں ، میڈیا ویکی دارالترجمہ.",
+       "cascadeprotected": "درج ذیل محفوظ کردہ {{PLURAL:$1|صفحہ|صفحات}} کی «آبشاری» حفاظت میں شامل ہونے کی وجہ سے یہ صفحہ بھی محفوظ ہے:\n$2",
        "namespaceprotected": "آپ کو '''$1''' فضائے نام میں صفحات تدوین کرنے کی اِجازت نہیں ہے.",
+       "customcssprotected": "آپ کو اس سی ایس ایس میں ترمیم کرنے کی اجازت نہیں کیونکہ اس میں کسی دوسرے صارف کی ذاتی ترتیبات موجود ہیں۔",
+       "customjsprotected": "آپ کو اس جاوا اسکرپٹ میں ترمیم کرنے کی اجازت نہیں کیونکہ اس میں کسی دوسرے صارف کی ذاتی ترتیبات موجود ہیں۔",
        "mycustomcssprotected": "آپ اس سی ایس ایس (CSS) صفحہ میں ترمیم کرنے کا اختیار نہیں رکھتے۔",
        "mycustomjsprotected": "آپ اس جاوا اسکپرٹ (JavaScript) صفحہ میں ترمیم کرنے کا اختیار نہیں رکھتے۔",
        "myprivateinfoprotected": "آپ ان ذاتی معلوات (private information) میں ترمیم کرنے کا اختیار نہیں رکھتے۔",
        "mypreferencesprotected": "آپ اپنی ان ترجیحات (preferences) میں ترمیم کرنے کا اختیار نہیں رکھتے۔",
        "ns-specialprotected": "خاص صفحات کی تدوین نہیں کی جاسکتی.",
        "titleprotected": "اس عنوان کو [[User:$1|$1]] نے تخلیق سے محفوظ کیا ہے.\nوجہ یہ بتائی گئی ہے: <em>$2</em>.",
+       "filereadonlyerror": "فائل «$1» میں تبدیلی ممکن نہیں کیونکہ خزانہ فائل «$2» فقط خواندگی حالت میں ہے۔\n\nانتظامیہ کی جانب سے مقفل کرنے کی حسب ذیل توجیہ پیش کی گئی ہے:\n\n«$3»",
+       "invalidtitle-knownnamespace": "«$2» نام فضا میں «$3» متن پر مشتمل عنوان نادرست ہے",
+       "invalidtitle-unknownnamespace": "نامعلوم نام فضا عدد «$1» اور «$2» متن پر مشتمل عنوان نادرست ہے",
        "exception-nologin": "غیر داخل نوشتہ",
        "exception-nologin-text": "براہ مہربانی! اس صفحہ تک رسائی یا ترمیم کے لیے لاگ ان ہوں۔",
        "exception-nologin-text-manual": "$1 براہ مہربانی! اس صفحہ تک رسائی یا ترمیم کے لیے لاگ ان ہوں۔",
        "virus-scanfailed": "تفریس ناکام (رمز $1)",
        "virus-unknownscanner": "انجان ضدوائرس:",
        "logouttext": "<strong>اب آپ خارج ہوچکے ہیں۔</strong>\n\nیاد رکھیں! کچھ صفحات ایسے نظر آتے رہیں گے کہ جیسے ابھی آپ خارج نہیں ہوئے، جب تک آپ اپنے کیشے صاف نہیں کرتے۔",
+       "cannotlogoutnow-title": "ابھی خارج نہیں ہو سکتے",
+       "cannotlogoutnow-text": "$1 کے استعمال کے دوران میں خارج ہونا ممکن نہیں۔",
        "welcomeuser": "خوش آمدید، $1!",
+       "welcomecreation-msg": "آپ کا کھاتہ بن گیا۔\nاگر آپ چاہیں تو [[Special:Preferences|اپنی ترجیحات]] میں حسب منشا تبدیلی کر سکتے ہیں۔",
        "yourname": "اسمِ رکنیت",
        "userlogin-yourname": "صارف نام",
        "userlogin-yourname-ph": "اپنا صارف نام درج کریں",
        "createacct-another-username-ph": "صارف نام درج کریں",
-       "yourpassword": "کلمۂ شناخت",
+       "yourpassword": "پاس ورڈ:",
        "userlogin-yourpassword": "کلمۂ شناخت",
        "userlogin-yourpassword-ph": "اپنا کلمہ شناخت دیں",
        "createacct-yourpassword-ph": "ایک پاس ورڈ داخل کریں",
        "yourpasswordagain": "کلمۂ شناخت دوبارہ لکھیں",
        "createacct-yourpasswordagain": "کلمۂ اجازت تصدیق کریں",
        "createacct-yourpasswordagain-ph": "پاس ورڈ پھر داخل کریں",
-       "remembermypassword": "اِس متصفح پر میرے داخلِ نوشتگی معلومات یاد رکھو (زیادہ سے زیادہ $1 {{PLURAL:$1|دِن|ایام}} کیلئے)",
        "userlogin-remembermypassword": "مجھے داخل رکھے",
        "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کریں",
+       "cannotlogin-title": "داخل نہیں ہو سکتے",
+       "cannotlogin-text": "داخل ہونا ممکن نہیں۔",
+       "cannotloginnow-title": "ابھی داخل نہیں ہو سکتے",
+       "cannotloginnow-text": "$1 کے استعمال کے دوران میں داخل ہونا ممکن نہیں۔",
+       "cannotcreateaccount-title": "کھاتے نہیں بنائے جا سکتے",
+       "cannotcreateaccount-text": "اس ویکی پر براہ راست کھاتہ سازی فعال نہیں ہے۔",
        "yourdomainname": "آپکا ڈومین",
        "password-change-forbidden": "آپ اس ویکی پر پارلفظ (پاس روڈ) تبدیل نہیں کر سکتے",
        "externaldberror": "یا تو توثیقی ڈیٹابیس میں خطا واقع ہوئی اور یا آپ کو بیرونی کھاتہ بتاریخ کرنے کی اِجازت نہیں ہے.",
        "login": "داخل ہوں",
+       "login-security": "اپنی شناخت کی تصدیق کریں",
        "nav-login-createaccount": "کھاتہ کھولیں یا اندراج کریں",
        "userlogin": "کھاتہ کھولیں یا اندراج کریں",
        "userloginnocreate": "داخلِ نوشتہ ہوجائیے",
        "userlogin-resetpassword-link": "کلمہ شناخت بھول گئے؟",
        "userlogin-helplink2": "داخل نوشتگی میں معاونت درکار ہے؟",
        "userlogin-loggedin": "آپ ویکیپیڈیا میں بطور صارف {{GENDER:$1|$1}}  پہلے سے داخل نوشتہ (logged in) ہیں۔\nدوسرے کھاتہ سے داخل ہونے کے لیے درج ذیل خانے پر کریں۔",
+       "userlogin-reauth": "آپ {{GENDER:$1|$1}} ہیں، اس کی تصدیق کے لیے آپ کا داخل ہونا ناگزیر ہے۔",
        "userlogin-createanother": "دوسرا کھاتہ تخلیق کریں",
        "createacct-emailrequired": "ای میل پتہ",
        "createacct-emailoptional": "ای میل ایڈریس (اختیاری)",
        "createacct-email-ph": "اپنا برقی پتہ لکھیں",
        "createacct-another-email-ph": "برقی پتہ لکھیں",
        "createaccountmail": "عارضی پاسورڈ استعمال کریں اور اسے متعینہ برقی ڈاک پتہ پر ارسال کریں",
+       "createaccountmail-help": "پاس ورڈ معلوم کیے بغیر کسی دوسرے شخص کا کھاتہ بنانے کے لیے اسے استعمال کیا جا سکتا ہے۔",
        "createacct-realname": "اصلی نام (اختیاری)",
        "createaccountreason": "وجہ:",
        "createacct-reason": "وجہ",
        "createacct-reason-ph": "آپ دوسرا کھاتہ کیوں تخلیق کررہے ہیں",
+       "createacct-reason-help": "نوشتہ کھاتہ سازی میں نظر آنے والا پیغام",
        "createacct-submit": "آپ کا کھاتا بنائیں",
        "createacct-another-submit": "کھاتہ بنائیں",
+       "createacct-continue-submit": "کھاتہ سازی جاری رکھیں",
+       "createacct-another-continue-submit": "کھاتہ سازی جاری رکھیں",
        "createacct-benefit-heading": "{{SITENAME}} آپ جیسے لوگوں کی طرف سے بنایا گیا ہے ۔",
        "createacct-benefit-body1": "{{PLURAL:$1|ترمیم|ترامیم}}",
        "createacct-benefit-body2": "$1 {{PLURAL:$1|صفحہ|صفحات}}",
        "nocookiesnew": "کھاتۂ صارف بنادیا گیا ہے، لیکن آپ کا داخلہ نہیں ہوا.\nصارفین کے داخلہ کیلئے {{SITENAME}} کوکیز استعمال کرتا ہے.\nآپ کے ہاں کوکیز غیر فعال ہیں.\nبراہِ کرم، انہیں فعال کیجئے، اور پھر اپنے نئے اسمِ صارف اور کلمۂ شناخت کے ساتھ داخل ہوجائیے.",
        "nocookieslogin": "صارفین کے داخل ہونے کیلئے {{SITENAME}} کوکیز استعمال کرتا ہے.\nآپ کے ہاں کوکیز غیر فعال ہیں.\nانہیں فعال کرنے کے بعد پھر کوشش کیجئے.",
        "nocookiesfornew": "اس صارف نام کا کھاتہ نہیں بن سکا۔ہم اس بات کی وضاحت نہیں کر سکتے (کہ ایسا کیوں ہوا)، براہ مہربانی! آپ\nیقین کر لیں کہ آپ کی کوکیز فعال ہیں، صفحہ تازہ کریں اور پھر کوشش کریں۔",
+       "createacct-loginerror": "کھاتہ بن چکا ہے لیکن آپ اس میں خودکار طور پر داخل نہیں ہو سکے۔ براہ کرم [[Special:UserLogin|دستی طور پر داخل ہونے کی کوشش کریں]]۔",
        "noname": "آپ نے صحیح اسم صارف نہیں چنا.",
        "loginsuccesstitle": "داخلہ کامیاب",
        "loginsuccess": "'''اب آپ {{SITENAME}} میں بنام \"$1\" داخل ہوچکے ہیں۔'''",
-       "nosuchuser": "\"$1\" کے نام سے کوئی صارف موجود نہیں ہے.\nبرائے مہربانی! ہجوں کے درست اندراج کی تصدیق کرلیجئے.\nاگر آپ چاہیں تو [[Special:CreateAccount|نیا کھاتہ بھی بناسکتے ہیں]].",
+       "nosuchuser": "\"$1\" کے نام سے کوئی صارف موجود نہیں ہے۔\nبراہ کرم ہجوں کو جانچ لیں۔\nنیز اگر آپ چاہیں تو [[Special:CreateAccount|نیا کھاتہ بھی بنا سکتے ہیں]]۔",
        "nosuchusershort": "\"$1\" کے نام سے کوئی صارف موجود نہیں.\nاپنا ہجہ جانچئے.",
        "nouserspecified": "آپ کو ایک اسمِ صارف مخصوص کرنا ہے.",
        "login-userblocked": "اِس صارف پر پابندی ہے. داخلِ نوشتہ ہونے کی اجازت نہیں.",
        "noemail": "صارف \"$1\" کیلئے کوئی برقی پتہ درج نہیں کیا گیا.",
        "noemailcreate": "صحیح برقی پتہ مہیّا کریں",
        "passwordsent": "ایک نیا کلمۂ شناخت \"$1\" کے نام سے بننے والی برقی ڈاک کے پتے کیلیے بھیج دیا گیا ہے۔\nجب وہ موصول ہو جاۓ تو براہ کرم اسکے ذریعے دوبارہ داخل ہوں۔",
-       "blocked-mailpassword": "آپ کا آئی.پی پتہ تدوین سے روک لیا گیا ہے، سو، ناجائز استعمال کو روکنے کیلئے، آپ کے آئی.پی پتہ کو کلمۂ شناخت کی بحالی کا فعل استعمال کرنے کی اِجازت نہیں ہے.",
+       "blocked-mailpassword": "آپ کے آئی پی پتے کی ترمیم کاری پر پابندی لگا دی گئی ہے۔ غلط استعمال سے بچنے کے لیے اس آئی پی پتے سے پاس ورڈ کی بازیابی کی اجازت منسوخ کر دی گئی ہے۔",
        "eauthentsent": "ایک تصدیقی برقی خط نامزد کیے گئے برقی پتہ پر ارسال کردیا گیا ہے۔\nآپ کو موصول ہوئے برقی خط میں ہدایات پر عمل کرکے اس بات کی توثیق کرلیں کہ مذکورہ برقی پتہ آپ کا ہی ہے۔",
        "throttled-mailpassword": "گزشتہ {{PLURAL:$1|گھنٹے|$1 گھنٹوں}} کے دوران پہلے سے ہی پارلفظ (پاسورڈ) کی تبدیلی کے لیے برقی خط بھیجا گيا ہے۔\nناجائز استعمال کے سدّباب کیلئے، {{PLURAL:$1|گھنٹہ|$1 گھنٹوں}} کے دوران صرف ایک برقی خط بھیجا جاسکتا ہے۔",
        "mailerror": "مسلہ دوران ترسیل خط:$1",
        "acct_creation_throttle_hit": "آپکی آئی.پی کے ذریعے اِس ویکی پر آنے والے صارفین نے پچھلے ایک دِن میں {{PLURAL:$1|1 کھاتہ بنایا ہے|$1 کھاتے بنائے ہیں}}، جو کہ مذکورہ وقت میں کافی ہیں.\nلہٰذا، آپکی آئی.پی استعمال کرنے والے صارفین اِس وقت مزید کھاتے نہیں بناسکتے.",
-       "emailauthenticated": "آپکے برقی ڈاک پتہ کی تصدیق تاریخ $2 بوقت $3 بجے کو ہوئی۔",
+       "emailauthenticated": "آپ کے برقی ڈاک پتہ کی تصدیق مورخہ $2 بوقت $3 بجے ہوئی۔",
        "emailnotauthenticated": "آپ کے برقی پتہ کی ابھی تصدیق نہیں ہوئی ہے۔\nدرج ذیل میں سے کسی بھی چیز کیلئے آپ کے برقی پتہ پر برقی ڈاک ارسال نہیں کی جائے گی۔",
        "noemailprefs": "اِن خصائص کو کام میں لانے کیلئے اپنے ترجیحات میں برقی ڈاک کا پتہ متعین کیجئے.",
        "emailconfirmlink": "اپنے برقی پتہ کی تصدیق کیجئے",
        "createaccount-title": "کھاتہ سازی برائے {{SITENAME}}",
        "createaccount-text": "کسی نے {{SITENAME}} ($4) پر \"$2\" کے نام سے اور \"$3\" پارلفظ کے ساتھ آپ کا برقی پتہ استعمال کرتے ہوئے کھاتہ بنایا ہے.\nآپ کو چاہئے کہ ابھی داخلِ نوشتہ ہوکر اپنا پارلفظ تبدیل کردیں.\n\nاگر یہ کھاتہ غلطی سے بنا تھا تو آپ یہ پیغام نظرانداز کرسکتے ہیں.",
        "login-throttled": "آپ نے حال ہی میں متعدد مرتبہ لاگ ان ہونے کی کوشش کی ہے۔\nدوبارہ کوشش کرنے سے پہلے $1 انتظار فرمائیے۔",
-       "login-abort-generic": "آپ داخل (لاگ ان) نہیں ہو پائے - ساقط",
+       "login-abort-generic": "لاگ ان ناکام - منسوخ شد",
+       "login-migrated-generic": "آپ کا کھاتہ منتقل کر دیا گیا، اب اس ویکی پر آپ کا صارف نام موجود نہیں۔",
        "loginlanguagelabel": "زبان: $1",
+       "suspicious-userlogout": "کھاتے سے خارج ہونے کی درخواست رد کر دی گئی ہے کیونکہ ایسا معلوم ہوتا ہے یہ درخواست کسی شکستہ براؤزر یا کیشے کی حامل پراکسی سے بھیجی گئی تھی۔",
+       "createacct-another-realname-tip": "حقیقی نام اختیاری ہے۔\nاگر آپ اسے فراہم کریں تو آپ کے کاموں کو اس نام سے منسوب کرنے کے لیے استعمال کیا جائے گا۔",
        "pt-login": "داخل ہوجائیے",
        "pt-login-button": "داخل ہو",
+       "pt-login-continue-button": "داخل ہوں",
        "pt-createaccount": "کھاتا بنائیں",
        "pt-userlogout": "خارج ہوجائیں",
+       "php-mail-error-unknown": "پی ایچ پی کے mail() فنکشن میں نامعلوم نقص۔",
        "user-mail-no-addy": "برقی ڈاک بھیجنے کی کوشش بغیر برقی ڈاک پتہ",
        "user-mail-no-body": "خالی یا بہت ہی مختصر برقی خط بھیجنے کی کوشش",
        "changepassword": "کلمۂ شناخت تبدیل کریں",
        "newpassword": "نیا کلمۂ شناخت",
        "retypenew": "نیا کلمۂ شناخت دوبارہ درج کریں:",
        "resetpass_submit": "پارلفظ بناؤ اور داخل ہوجاؤ",
-       "changepassword-success": "آپ کا پارلفظ کامیابی سے تبدیل ہوگیا!",
+       "changepassword-success": "آپ کا پاس ورڈ تبدیل کر دیا گیا!",
+       "changepassword-throttled": "آپ نے حال ہی میں متعدد مرتبہ داخل ہونے کی کوشش کی ہے۔\nدوبارہ کوشش کرنے سے پہلے $1 انتظار کریں۔",
+       "botpasswords": "روبہ پاس ورڈ",
+       "botpasswords-disabled": "روبہ کے پاس ورڈ غیر فعال ہیں۔",
+       "botpasswords-no-central-id": "روبہ کے پاس ورڈ کو استعمال کرنے کے لیے آپ کا مرکزی کھاتے میں داخل رہنا ضروری ہے۔",
+       "botpasswords-existing": "روبہ کے موجودہ پاس ورڈ",
+       "botpasswords-createnew": "روبہ کا نیا پاس ورڈ بنائیں",
+       "botpasswords-editexisting": "روبہ کے موجودہ پاس ورڈ میں ترمیم کریں",
+       "botpasswords-label-appid": "روبہ کا نام:",
+       "botpasswords-label-create": "تخلیق کریں",
+       "botpasswords-label-update": "تجدید کریں",
+       "botpasswords-label-cancel": "منسوخ کریں",
+       "botpasswords-label-delete": "حذف کریں",
+       "botpasswords-label-resetpassword": "پاس ورڈ تبدیل کریں",
+       "botpasswords-label-grants": "قابل تطبیق عطیے:",
+       "botpasswords-label-restrictions": "استعمال کی پابندیاں:",
+       "botpasswords-label-grants-column": "دے دیا گیا",
+       "botpasswords-bad-appid": "روبہ نام \"$1\" درست نہیں۔",
+       "botpasswords-insert-failed": "روبہ نام \"$1\" کو شامل کرنے میں ناکامی۔ کیا اسے پہلے شامل کیا جا چکا ہے؟",
+       "botpasswords-update-failed": "روبہ نام \"$1\" کی تجدید میں ناکامی۔ کیا اسے حذف کر دیا گیا ہے؟",
+       "botpasswords-created-title": "روبہ کا پاس ورڈ تخلیق ہو چکا ہے",
+       "botpasswords-created-body": "صارف \"$2\" کے روبہ نام \"$1\" کا پاس ورڈ تخلیق ہو چکا ہے۔",
+       "botpasswords-updated-title": "روبہ کا پاس ورڈ تازہ کر دیا گیا",
+       "botpasswords-updated-body": "صارف \"$2\" کے روبہ نام \"$1\" کا پاس ورڈ تازہ کر دیا گیا۔",
+       "botpasswords-deleted-title": "روبہ کا پاس ورڈ حذف ہو چکا ہے",
+       "botpasswords-deleted-body": "صارف \"$2\" کے روبہ نام \"$1\" کا پاس ورڈ حذف کیا جا چکا ہے۔",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider دستیاب نہیں۔",
+       "botpasswords-restriction-failed": "روبہ کے پاس ورڈ کی پابندیاں اس لاگ ان سے مانع ہیں۔",
+       "botpasswords-invalid-name": "درج کردہ صارف نام میں روبہ کے پاس ورڈ کا فاصل لفظ موجود نہیں ہے (\"$1\")۔",
+       "botpasswords-not-exist": "صارف \"$1\" کے پاس \"$2\" کے نام سے روبہ کا پاس ورڈ نہیں ہے۔",
        "resetpass_forbidden": "پارلفظ تبدیل نہیں ہوسکتا",
+       "resetpass_forbidden-reason": "پاس ورڈ تبدیل نہیں کیا جا سکتا: $1",
        "resetpass-no-info": "اِس صفحہ تک براہِ راست رسائی کیلئے آپ کو داخلِ نوشتہ ہونا پڑے گا.",
        "resetpass-submit-loggedin": "پارلفظ کی تبدیلی",
        "resetpass-submit-cancel": "منسوخ",
-       "resetpass-wrong-oldpass": "عارضÛ\8c Û\8cا Ù\85Ù\88جÙ\88دÛ\81 Ù¾Ø§Ø±Ù\84Ù\81ظ Ù\86اÙ\82ص Û\81Û\92.\nآپ Û\8cا ØªÙ\88 Ù¾Û\81Ù\84Û\92 Û\81Û\8c Ø³Û\92 Ø¢Ù¾Ù\86ا Ù¾Ø§Ø±Ù\84Ù\81ظ Ú©Ø§Ù\85Û\8cابÛ\8c Ø³Û\92 ØªØ¨Ø¯Û\8cÙ\84 Ú©Ø±Ú\86Ú©Û\92 Û\81Û\8cÚº Ø§Ù\88ر Û\8cا Ø¢Ù¾ Ù\86Û\92 Ù\86ئÛ\92 Ø¹Ø§Ø±Ø¶Û\8c Ù¾Ø§Ø±Ù\84Ù\81ظ Ú©Û\8c Ø¯Ø±Ø®Ù\88است Ú©Û\8c Û\81Û\92.",
+       "resetpass-wrong-oldpass": "عارضÛ\8c Û\8cا Ù\85Ù\88جÙ\88دÛ\81 Ù¾Ø§Ø³ Ù\88رÚ\88 Ù\86ادرست Û\81Û\92Û\94\nشاÛ\8cد Ø¢Ù¾ Ù\86Û\92 Ù¾Û\81Ù\84Û\92 Û\81Û\8c Ø§Ù¾Ù\86ا Ù¾Ø§Ø³ Ù\88رÚ\88 ØªØ¨Ø¯Û\8cÙ\84 Ú©Ø± Ù\84Û\8cا Û\81Û\92 Û\8cا Ù\86ئÛ\92 Ø¹Ø§Ø±Ø¶Û\8c Ù¾Ø§Ø³ Ù\88رÚ\88 Ú©Û\8c Ø¯Ø±Ø®Ù\88است Ú©Ø± Ú\86Ú©Û\92 Û\81Û\8cÚºÛ\94",
        "resetpass-recycled": "براہ کرم اپنے موجودہ کلمۂ شناخت (پاسورڈ) سے مختلف کلمۂ شناخت سے بازترتیب (ری‌سیٹ) کریں",
        "resetpass-temp-emailed": "آپ عارضی برقی خط سے بھیجے گئے کوڈ سے لاگ ان ہیں\nمکمل طور پر لاگ ان ہونے کے لیے آپ کو نیا پاسورڈ سیٹ کرنا پڑے گا",
        "resetpass-temp-password": "عارضی پارلفظ:",
+       "resetpass-abort-generic": "کسی توسیع نے پاس ورڈ کی تبدیلی کو منسوخ کر دیا ہے۔",
+       "resetpass-expired": "آپ کے پاس ورد کی مدت ختم ہو چکی ہے۔ داخل ہونے کے لیے براہ کرم نیا پاس ورڈ بنائیں۔",
+       "resetpass-expired-soft": "آپ کے پاس ورڈ کی مدت ختم ہو چکی ہے، لہذا اسے دوبارہ بنانے کی ضرورت ہے۔\nبراہ کرم نیا پاس ورڈ بنائیں، تاہم اگر مستقبل میں اس کی ترتیب نو مقصود ہو تو «{{int:authprovider-resetpass-skip-label}}» پر کلک کریں۔",
+       "resetpass-validity-soft": "آپ کا پاس ورڈ درست نہیں: $1\n\nبراہ کرم نیا پاس ورڈ بنائیں، تاہم اگر مستقبل میں اس کی ترتیب نو مقصود ہو تو «{{int:authprovider-resetpass-skip-label}}» پر کلک کریں۔",
        "passwordreset": "پارلفظ کی بازتعینی",
+       "passwordreset-text-one": "برقی خط کے ذریعہ عارضی پاس ورڈ حاصل کرنے کے لیے اس فارم کو پُر کریں۔",
+       "passwordreset-text-many": "{{PLURAL:$1|برقی خط کے ذریعہ عارضی پاس ورڈ حاصل کرنے کے لیے کسی ایک خانے کو پُر کریں۔}}",
+       "passwordreset-disabled": "اس ویکی پر پاس ورڈ کی ترتیب نو کی سہولت فعال نہیں ہے۔",
+       "passwordreset-emaildisabled": "اس ویکی پر برقی خط کی سہولت غیر فعال ہیں۔",
        "passwordreset-username": "اسمِ صارف:",
        "passwordreset-domain": "ساحہ:",
+       "passwordreset-capture": "برقی خط کی حتمی شکل دیکھیں؟",
+       "passwordreset-capture-help": "اس خانے کو نشان زد کرنے کی صورت میں (عارضی پاس ورڈ کا حامل) برقی خط آپ کو بھی نظر آئے گا اور صارف کو بھی روانہ کیا جائے گا۔",
        "passwordreset-email": "برقی ڈاک پتہ:",
+       "passwordreset-emailtitle": "{{SITENAME}} کھاتہ کی تفصیلات",
+       "passwordreset-emailelement": "صارف نام:\n$1\n\nعارضی پاس ورڈ: \n$2",
+       "passwordreset-emailsentemail": "اگر یہ برقی ڈاک پتا آپ کے کھاتے سے منسلک ہے تو پاس ورڈ کی ترتیب نو کا برقی خط بھیج دیا جائے گا۔",
+       "passwordreset-emailsentusername": "اگر کوئی برقی ڈاک پتا آپ کے کھاتے سے منسلک ہے تو پاس ورڈ کی ترتیب نو کا برقی خط بھیج دیا جائے گا۔",
+       "passwordreset-emailsent-capture2": "پاس ورڈ کی ترتیب نو {{PLURAL:$1|کا برقی خط بھیج دیا گیا ہے|کے برقی خطوط بھیج دیے گئے ہیں}}۔ {{PLURAL:$1|صارف نام اور پاس ورڈ|صارف ناموں اور ان کے پاس ورڈ کی فہرست}} ذیل میں ملاحظہ فرمائیں۔",
+       "passwordreset-emailerror-capture2": "{{GENDER:$2|صارف}} کو برقی خط بھیجنے میں ناکامی: $1\n{{PLURAL:$3|صارف نام اور پاس ورڈ|صارف ناموں کی فہرست اور ان کے پاس ورڈ}} ذیل میں ملاحظہ فرمائیں۔",
+       "passwordreset-nocaller": "کالر کا فراہم کیا جانا لازمی ہے",
+       "passwordreset-nosuchcaller": "کالر موجود نہیں: $1",
+       "passwordreset-invalideamil": "نادرست برقی ڈاک پتا",
+       "passwordreset-nodata": "کوئی صارف نام اور نہ کوئی برقی ڈاک پتا فراہم کیا گیا",
+       "changeemail": "برقی ڈاک پتا تبدیل یا حذف کریں",
+       "changeemail-header": "اپنے برقی ڈاک پتے کو تبدیل کرنے کے لیے اس فارم کو پُر کریں۔ اگر آپ اپنے کھاتے سے منسلک کسی برقی ڈاک پتے کو ختم کرنا چاہتے ہیں تو فارم پُر کرنے کے دوران میں نئے برقی ڈاک پتے کا خانہ خالی چھوڑ دیں۔",
+       "changeemail-no-info": "اِس صفحہ تک براہِ راست رسائی کیلئے آپ کو داخل ہونا پڑے گا۔",
        "changeemail-oldemail": "حالیہ برقی ڈاک پتہ:",
        "changeemail-newemail": "نیا برقی ڈاک پتہ:",
+       "changeemail-newemail-help": "اگر آپ اپنا برقی ڈاک پتہ حذف کرنا چاہتے ہیں تو اس خانے کو خالی چھوڑ دیں۔ تاہم اس صورت میں آپ کو ویکی کے برقی خطوط موصول نہیں ہونگے نیز فراموش شدہ پاس ورڈ کی ترتیب نو کا اختیار بھی ختم کر دیا جائے گا۔",
        "changeemail-none": "(کوئی نہیں)",
+       "changeemail-password": "آپ کا {{SITENAME}} پاس ورڈ:",
        "changeemail-submit": "برقی ڈاک تبدیل کریں",
+       "changeemail-throttled": "آپ نے متعدد مرتبہ داخل ہونے کی کوشش کی ہے۔\nدوبارہ کوشش کرنے سے پہلے $1 انتظار فرمائیں۔",
+       "changeemail-nochange": "براہ کرم کوئی دوسرا برقی ڈاک پتہ درج کریں۔",
+       "resettokens": "ٹوکنوں کو دوبارہ ترتیب دیں",
+       "resettokens-no-tokens": "ترتیب نو کے لیے کوئی ٹوکن موجود نہیں۔",
        "resettokens-tokens": "ٹوکن:",
        "resettokens-token-label": "$1 (موجودہ قدر: $2)",
+       "resettokens-watchlist-token": "[[Special:Watchlist|آپ کی زیرنظر فہرست میں موجود صفحات کی تبدیلیوں]] کی ویب فیڈ (آٹوم/آر ایس ایس) کا ٹوکن",
+       "resettokens-done": "ٹوکنوں کی ترتیب نو مکمل۔",
+       "resettokens-resetbutton": "منتخب ٹوکنوں کو دوبارہ مرتب کریں",
        "bold_sample": "دبیز متن",
        "bold_tip": "دبیز متن",
        "italic_sample": "ترچھا متن",
        "minoredit": "معمولی ترمیم",
        "watchthis": "یہ صفحہ زیر نظر کیجیۓ",
        "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کریں",
        "publishpage": "شائع کریں",
        "publishchanges": "تبدیلیاں شائع کریں",
        "preview": "نمائش",
        "showpreview": "نمائش",
-       "showdiff": "تبدیلیاں دکھاؤ",
+       "showdiff": "تبدیلیاں دکھائیں",
+       "blankarticle": "<strong>انتباہ:</strong> آپ خالی صفحہ تخلیق کر رہے ہیں۔\nاگر آپ «{{int:savearticle}}» پر دوبارہ کلک کریں تو یہ صفحہ بغیر کسی مواد کے محفوظ ہو جائے گا۔",
        "anoneditwarning": "<strong>انتباہ:</strong> آپ ویکیپیڈیا میں داخل نہیں ہوئے ہیں۔ لہذا اگر آپ اس صفحہ میں کوئی ترمیم کرتے ہیں تو آپکا آئی پی ایڈریس (IP) اس صفحہ کے تاریخچہ ترمیم میں محفوظ ہوجائے گا۔ اگر آپ  <strong>[$1 لاگ ان]</strong> ہوتے ہیں یا کھاتہ نہ ہونے کی صورت میں <strong>[$2 کھاتہ بنا لیتے ہیں]</strong> تو تو آپ کی ترامیم آپ کے صارف نام سے محفوظ ہوگی، جنھیں آپ کسی بھی وقت ملاحظہ کر سکتے ہیں۔",
+       "anonpreviewwarning": "<em>آپ داخل نہیں ہیں، چنانچہ تبدیلیاں محفوظ کرنے کی صورت میں اس صفحہ کا تاریخچہ آپ کا آئی پی پتا محفوظ کر لے گا۔</em>",
        "missingsummary": "'''انتباہ:''' آپ نے ترمیمی خلاصہ مہیّا نہیں کیا.\nاگر آپ نے محفوظ کا بٹن دوبارہ دبایا تو آپ کی ترمیم بغیر کسی خلاصہ کے محفوظ ہوجائے گی.",
+       "selfredirect": "<strong>انتباہ:</strong> آپ پیش نظر صفحہ کو خود اسی کی جانب رجوع مکرر کر رہے ہیں۔\nشاید آپ نے رجوع مکرر کا غلط ہدف درج کیا ہے یا غلط صفحہ میں ترمیم کر رہے ہیں۔\nتاہم اگر آپ «{{int:savearticle}}» پر دوبارہ کلک کریں تو بہرصورت اس رجوع مکرر کو بنا دیا جائے گا۔",
        "missingcommenttext": "براہِ کرم! تبصرہ نیچے درج کیجئے.",
        "missingcommentheader": "<strong>یاددہانی:</strong>  آپ نے اِس تبصرہ کیلئے عنوان یا شہ سرخی مہیّا نہیں کی ہے۔\nاگر آپ نے \"{{int:savearticle}}\" کا بٹن دوبارہ دبایا تو آپ کا تبصرہ بغیر کسی عنوان کے محفوظ ہوجائے گا۔",
        "summary-preview": "نمائش خلاصہ:",
        "subject-preview": "عنوان/شہ سرخی کی نمائش:",
+       "previewerrortext": "آپ کی تبدیلیوں کی نمائش دکھانے کے دوران میں کوئی نقص واقع ہو گیا ہے۔",
        "blockedtitle": "صارف مسدود ہے",
        "blockedtext": "'''آپکا اسمِ صارف یا آئی پی پتہ پر پابندی ہے.'''\n\n$1 نے پابندی لگائی تھی.\nوجہ یہ بتائی گئی کہ ''$2''.\n\n* پابندی کی ابتداء : $8\n* پابندی کا اختتام : $6\n* Intended blockee: $7\n\nآپ $1 یا کسی دوسرے [[{{MediaWiki:Grouppage-sysop}}|منتظم]] سے رابطہ کرکے پابندی پر بات چیت کرسکتے ہیں.\nآپ ‘صارف کو برقی خط ارسال کریں’ کی خاصیت اُس وقت تک استعمال نہیں کرسکتے جب تک آپ اپنے [[Special:Preferences|کھاتہ کے ترجیحات]] میں صحیح برقی پتہ معیّن نہ کریں، اور آپ کو اِسے استعمال کرنے سے پابند نہیں کیا گیا ہے.\nآپکا موجودہ آئی پی پتہ $3 ہے، اور پابندی کی شناخت #$5 ہے.\nبراہِ مہربانی کسی بھی قسم کے استفسار میں درج بالا تمام تفاصیل شامل کریں.",
        "blockednoreason": "کوئی وجہ نہیں دی گئی",
        "anontalkpagetext": "----''یہ صفحہ ایک ایسے صارف کا ہے جنہوں نے یا تو اب تک اپنا کھاتا نہیں بنایا یا پھر وہ اسے استعمال نہیں کر رہے/ رہی ہیں۔ لہٰذا ہمیں انکی شناخت کے لئے ایک عددی آئی پی پتہ استعمال کرنا پڑرہا ہے۔ اس قسم کا آئی پی پتہ ایک سے زائد صارفین کے لئے مشترک بھی ہوسکتا ہے۔ اگر آپکی موجودہ حیثیت ایک گمنام صارف کی ہے اور آپ محسوس کریں کہ اس صفحہ پر آپکی جانب منسوب یہ بیان غیرضروری ہے تو براہ کرم [[Special:CreateAccount|کھاتہ بنائیں]] یا [[Special:UserLogin|داخلِ نوشتہ]] ہوجائیے تاکہ مستقبل میں آپکو گمنام صارفین میں شمار کرنے سے پرہیز کیا جاسکے۔\"",
        "noarticletext": "اِس صفحہ میں فی الحال کوئی متن موجود نہیں ہے۔\nآپ دیگر صفحات میں [[Special:Search/{{PAGENAME}}|اِس صفحہ کے عنوان کو تلاش کر سکتے ہیں]]، <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>",
+       "userpage-userdoesnotexist": "«$1» کے نام سے صارف کھاتہ موجود نہیں ہے۔\nاگر آپ اس صفحہ کو تخلیق یا اس میں ترمیم کرنا چاہتے ہیں تو براہ کرم پہلے جانچ لیں۔",
        "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" مندرج نہیں ہے۔",
+       "blocked-notice-logextract": "یہ صارف معطل ہے۔\nحوالہ کے لیے نوشتہ پابندی کا تازہ ترین اندراج ذیل میں دستیاب ہے:",
+       "clearyourcache": "<strong>یاددہانی:</strong> محفوظ کرنے کے بعد ان تبدیلیوں کو دیکھنے کے لیے آپ کو اپنے براؤزر کا کیشے صاف کرنا ہوگا۔\n* '''فائرفاکس/ سفاری:''' جب ''Reload'' پر کلک کریں تو ''Shift'' دباکر رکھیں، یا ''Ctrl-F5'' یا ''Ctrl-R'' دبائیں (Mac پر ''R-⌘'')\n* '''گوگل کروم:''' ''Ctrl-Shift-R'' دبائیں (Mac پر ''Shift-R-⌘'')\n* '''انٹرنیٹ ایکسپلورر:''' جب ''Refresh'' پر کلک کریں تو ''Ctrl'' یا ''Ctrl-F5'' دبائیں\n* '''اوپیرا:'''  ''Tools → Preferences'' میں جائیں اور کیشے (cache) صاف کریں",
+       "usercssyoucanpreview": "<strong>نکتہ:</strong> اپنی نئی سی ایس ایس کو جانچنے کے لیے اسے محفوظ کرنے سے قبل «{{int:showpreview}}» کی بٹن استعمال کریں۔",
+       "userjsyoucanpreview": "<strong>نکتہ:</strong> اپنی نئی جاوا اسکرپٹ کو جانچنے کے لیے اسے محفوظ کرنے سے قبل «{{int:showpreview}}» کی بٹن استعمال کریں۔",
+       "usercsspreview": "<strong>یاد رہے کہ اس وقت آپ اپنی سی ایس کی محض نمائش دیکھ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
+       "userjspreview": "<strong>یاد رہے کہ اس وقت آپ اپنی جاوا اسکرپٹ کی محض نمائش دیکھ/جانچ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
+       "sitecsspreview": "<strong>یاد رہے کہ اس وقت آپ اس سی ایس کی محض نمائش دیکھ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
+       "sitejspreview": "<strong>یاد رہے کہ اس وقت آپ اس جاوا اسکرپٹ کوڈ کی محض نمائش دیکھ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
+       "userinvalidcssjstitle": "<strong>انتباہ:</strong> یہاں «$1» نام سے کوئی پوشاک موجود نہیں۔ شخصی .css اور .js کے صفحات اپنے عنوان میں چھوٹے حروف استعمال کرتے ہیں، مثلاً {{ns:user}}:Foo/Vector.css کی بجائے {{ns:user}}:Foo/vector.css",
        "updated": "(اپ ڈیٹڈ)",
        "note": "'''نوٹ:'''",
        "previewnote": "'''یاد رکھیں، یہ صرف نمائش ہے ۔آپ کی ترامیم ابھی محفوظ نہیں کی گئیں۔'''",
+       "continue-editing": "خانہ ترمیم میں جائیں",
+       "previewconflict": "اس نمائش میں خانہ ترمیم کے اوپر موجود متن جس انداز میں ظاہر ہو رہا ہے، محفوظ کرنے کے بعد اسی طرح نظر آئے گا۔",
        "session_fail_preview": "معاف کیجئے! نشست کے مواد میں خامی کی وجہ سے آپکی  ترمیم پر عمل نہیں کیا جاسکا.\nبرائے مہربانی دوبارہ کوشش کیجئے.\nاگر آپکو پھر بھی مشکل پیش آرہی ہے تو [[Special:UserLogout|خارجِ نوشتہ]] ہوکر واپس داخلِ نوشتہ ہوجایئے.",
+       "edit_form_incomplete": "<strong>خانہ ترمیم سے کچھ حصے سرور تک نہیں پہنچ سکے ہیں؛ براہ کرم اپنی ترامیم کو دوبارہ جانچ لیں کہ آیا وہ برقرار ہیں یا نہیں اور دوبارہ کوشش کریں۔</strong>",
        "editing": "آپ \"$1\" میں ترمیم کر رہے ہیں۔",
        "creating": "زیر تخلیق $1",
        "editingsection": "$1 کے قطعہ کی تدوین",
        "editingold": "'''انتباہ: آپ اس صفحے کا ایک پرانا مسودہ مرتب کررہے ہیں۔ اگر آپ اسے محفوظ کرتے ہیں تو اس صفحے کے اس پرانے مسودے سے اب تک کی جانے والی تمام تدوین ضائع ہو جاۓ گی۔'''",
        "yourdiff": "تضادات",
        "copyrightwarning": "یہ یادآوری کرلیجیۓ کہ {{SITENAME}} میں تمام تحریری شراکت جی این یو آزاد مسوداتی اجازہ ($2)کے تحت تصور کی جاتی ہے (مزید تفصیل کیلیۓ $1 دیکھیۓ)۔ اگر آپ اس بات سے متفق نہیں کہ آپکی تحریر میں ترمیمات کری جائیں اور اسے آزادانہ (جیسے ضرورت ہو) استعمال کیا جاۓ تو براۓ کرم اپنی تصانیف یہاں داخل نہ کیجیۓ۔ اگر آپ یہاں اپنی تحریر جمع کراتے ہیں تو آپ اس بات کا بھی اقرار کر رہے ہیں کہ، اسے آپ نے خود تصنیف کیا ہے یا دائرہ ءعام (پبلک ڈومین) سے حاصل کیا ہے یا اس جیسے کسی اور آذاد وسیلہ سے۔'''بلااجازت ایسا کام داخل نہ کیجیۓ جسکا حق ِطبع و نشر محفوظ ہو!'''",
+       "copyrightwarning2": "براہ کرم اس بات کا خیال رکھیں کہ {{SITENAME}} میں آپ کی جانب سے کی جانے والی تمام ترمیموں میں دیگر صارفین بھی حذف و اضافہ کر سکتے ہیں۔\nاگر آپ اپنی تحریر کے ساتھ اس قسم کے سلوک کے روادار نہیں تو براہ کرم اسے یہاں شائع نہ کریں۔<br />\nنیز اس تحریر کو شائع کرتے وقت آپ ہم سے یہ وعدہ بھی کر رہے ہیں کہ اسے آپ نے خود لکھا ہے یا اسے دائرہ عام یا کسی آزاد ماخذ سے یہاں نقل کر رہے ہیں (تفصیلات کے لیے $1 ملاحظہ فرمائیں)۔\n<strong>براہ کرم اجازت کے بغیر کسی کاپی رائٹ شدہ مواد کو یہاں شائع نہ کریں۔</strong>",
+       "editpage-cannot-use-custom-model": "اس صفحہ کے مواد کے ماڈل کو تبدیل نہیں کیا جا سکتا۔",
+       "readonlywarning": "<strong>انتباہ: انتظامی نگہداشت کی خاطر ڈیٹابیس کو مقفل کر دیا گیا ہے، لہذا اس وقت آپ اپنی ترامیم کو محفوظ نہیں کر سکتے۔</strong>\nآپ اپنی تحریر کو کسی ٹیکسٹ فائل میں محفوظ کر سکتے ہیں تاکہ وہ ضائع نہ ہو اور آئندہ اسے استعمال کیا جا سکے۔\n\nانتظامیہ کی جانب سے مقفل کرنے کی حسب ذیل وجہ بیان کی گئی ہے:\n\n$1",
+       "protectedpagewarning": "<strong>انتباہ: اس صفحہ میں ترمیم کاری کو مقفل کر دیا گیا ہے اور محض انتظامی اختیارات کے حامل صارفین ہی اس میں ترمیم کر سکتے ہیں۔</strong>\nحوالہ کے لیے ذیل میں نوشتہ جاتی اندراج فراہم کیا گیا ہے:",
+       "semiprotectedpagewarning": "<strong>اطلاع:</strong> اس صفحہ کو یوں مقفل کیا جاچکا ہے کہ اس میں صرف اندراج شدہ صارفین ہی ترمیم کرسکتے ہیں۔\nحوالہ کے لیے ذیل میں تازہ ترین نوشتہ جاتی اندراج دیا گیا ہے:",
+       "cascadeprotectedwarning": "<strong>انتباہ:</strong> اس صفحہ میں ترمیم کاری کو مقفل کر دیا گیا ہے اور محض انتظامی اختیارات کے حامل صارفین ہی اس میں ترمیم کر سکتے ہیں۔ اسے مقفل کرنے کی وجہ یہ ہے کہ پیش نظر صفحہ درج ذیل محفوظ {{PLURAL:$1|صفحہ|صفحات}} کی آبشاری حفاظت میں شامل ہے:",
+       "titleprotectedwarning": "<strong>انتباہ: اس صفحہ کو محفوظ کر دیا گیا ہے، چنانچہ اسے تخلیق کرنے کے لیے [[Special:ListGroupRights|خصوصی اختیارات]] درکار ہونگے۔</strong>\nحوالہ کے لیے ذیل میں نوشتہ کا تازہ ترین اندراج موجود ہے:",
        "templatesused": "اِس صفحہ پر مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "templatesusedpreview": "اِس پیش منظر میں مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "templatesusedsection": "اِس قطعہ میں مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "template-protected": "(محفوظ شدہ)",
        "template-semiprotected": "(نیم محفوظ)",
        "hiddencategories": "یہ صفحہ {{PLURAL:$1|1 چُھپے زمرے|$1 چُھپے زمرہ جات}} میں شامل ہے:",
+       "nocreatetext": "{{SITENAME}} نے نئے صفحات تخلیق کرنے پر پابندی لگا رکھی ہے۔\nتاہم آپ پہلے سے موجود صفحات میں ترمیم کر سکتے ہیں یا [[Special:UserLogin|اپنے کھاتے ميں داخل ہوں یا کھاتہ بنائیں]]۔",
        "nocreate-loggedin": "آپ کو نئے صفحات تخلیق کرنے کی اجازت نہیں ہے.",
        "sectioneditnotsupported-title": "قطعہ کی تدوین حمایت شدہ نہیں ہے",
        "sectioneditnotsupported-text": "اِس صفحہ میں قطعہ کی تدوین حمایت شدہ نہیں ہے.",
        "permissionserrors": "خطائے اجازت",
        "permissionserrorstext": "درج ذیل {{PLURAL:$1|وجہ|وجوہات}} کی بناء پر آپ کو ایسا کرنے کی اجازت نہیں ہے:",
        "permissionserrorstext-withaction": "درج ذیل {{PLURAL:$1|وجہ|وجوہات}} کی بناء پر آپ کو $2 کرنے کی اجازت نہیں ہے:",
+       "contentmodelediterror": "آپ اس نسخے میں ترمیم نہیں کر سکتے کیونکہ اس کے مواد کا ماڈل ‌‌<code>$1</code> ہے جو اس صفحہ کے مواد کے موجودہ ماڈل <code>$2</code> سے مختلف ہے۔",
        "recreate-moveddeleted-warn": "''' انتباہ: آپ ایک گزشتہ حذف شدہ صفحہ دوبارہ تخلیق کررہے ہیں. '''\n\nآپ کو اِس بات پر غور کرنا چاہئے کہ آیا اِس صفحہ کی تدوین جاری رکھنا موزوں ہے یا نہیں.\nصفحہ کا نوشتۂ حذف شدگی و منتقلی یہاں سہولت کی خاطر مہیّا کیا جارہا ہے:",
        "moveddeleted-notice": "یہ ایک حذف شدہ صفحہ ہے.\nصفحہ کا نوشتۂ حذف شدگی و منتقلی ذیل میں بطورِ حوالہ دیا جارہا ہے.",
+       "moveddeleted-notice-recent": "معذرت، اس صفحہ کو حال ہی میں حذف کیا گیا ہے (گزشتہ چوبیس گھنٹوں میں)۔\nحوالہ کے لیے ذیل میں اس صفحہ کا نوشتہ حذف اور نوشتہ منتقلی موجود ہے۔",
        "log-fulllog": "پورا نوشتہ دیکھئے",
        "edit-gone-missing": "صفحہ تجدید نہیں کیا جاسکتا.\nلگتا ہے یہ حذف ہوچکا ہے.",
        "edit-conflict": "تنازعۂ تدوین.",
        "postedit-confirmation-saved": "آپ کی ترمیم محفوظ ہوگئی۔",
        "edit-already-exists": "نیا صفحہ تخلیق نہیں کیا جاسکتا.\nیہ پہلے سے موجود ہے.",
        "defaultmessagetext": "طے شدہ پیغام کا متن",
+       "content-failed-to-parse": "ماڈل $1 کے $2 مواد کے تجزیہ میں ناکامی: $3",
+       "invalid-content-data": "نادرست ڈیٹا مندرجات",
+       "content-not-allowed-here": "صفحہ [[$2]] پر \"$1\" مواد کی اجازت نہیں",
+       "editwarning-warning": "اس صفحہ کو چھوڑنے پر ممکن ہے جو تبدیلیاں آپ نے کی ہیں وہ سب ضائع ہو جائیں۔\nاگر آپ داخل ہیں تو اپنی ترجیحات کے خانہ «{{int:prefs-editing}}» سے اس انتباہ کو غیر فعال کر سکتے ہیں۔",
+       "editpage-invalidcontentmodel-title": "مواد کا ماڈل معاونت یافتہ نہیں",
+       "editpage-invalidcontentmodel-text": "مواد کا ماڈل \"$1\" معاونت یافتہ نہیں ہے۔",
+       "editpage-notsupportedcontentformat-title": "مواد کا فارمیٹ معاونت یافتہ نہیں",
+       "editpage-notsupportedcontentformat-text": "مواد کے ماڈل $2 کی جانب سے مواد کا فارمیٹ $1 معاونت یافتہ نہیں۔",
+       "content-model-wikitext": "ویکی متن",
        "content-model-text": "سادہ متن",
        "content-model-javascript": "جاوا اسکرپٹ",
+       "content-json-empty-object": "خالی آبجیکٹ",
+       "content-json-empty-array": "خالی ایرے",
+       "deprecated-self-close-category": "صفحات مع نادرست ایچ ٹی ایم ایل ٹیگ",
+       "deprecated-self-close-category-desc": "اس صفحہ میں ایچ ٹی ایم ایل کے نادرست ٹیگ مثلاً <code>&lt;b/></code> or <code>&lt;span/></code> استعمال کیے گئے ہیں۔ چونکہ ایچ ٹی ایم ایل 5 میں ان ٹیگوں کا رویہ تبدیل ہو جائے گا، لہذا ویکی متن میں ان کا استعمال متروک ہو چکا ہے۔",
+       "undo-nochange": "معلوم ہوتا ہے کہ اس ترمیم کو پہلے ہی واپس پھیر دیا گیا ہے۔",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|تبادلہ خیال]]) کی جانب سے کی گئی ترمیم $1 رد کردی گئی ہے۔",
+       "undo-summary-username-hidden": "پوشیدہ صارف کے نسخہ $1 کو واپس پھیریں",
+       "cantcreateaccount-text": "[[User:$3|$3]] نے اس آئی پی پتہ (<strong>$1</strong>) کی کھاتہ سازی پر پابندی لگا رکھی ہے۔\n\n$3 نے «<em>$2</em>» وجہ بیان کی ہے",
+       "cantcreateaccount-range-text": "[[User:$3|$3]] نے <strong>$1</strong> رینج کے آئی پی پتوں پر جس میں آپ کا آئی پی پتہ (<strong>$4</strong>) بھی موجود ہے پر پابندی لگا دی ہے۔\n\n$3 نے «<em>$2</em>» وجہ بیان کی ہے",
        "viewpagelogs": "اس صفحہ کیلیے نوشتہ جات دیکھیے",
        "nohistory": "اِس صفحہ کیلئے کوئی تدوینی تاریخچہ موجود نہیں ہے.",
        "currentrev": "حـالیـہ تـجدید",
-       "currentrev-asof": "حاÙ\84Û\8cÛ\81 Ù\86ظرثاÙ\86Û\8c بمطابق $1",
-       "revisionasof": "تجدید بمطابق $1",
+       "currentrev-asof": "حاÙ\84Û\8cÛ\81 Ù\86سخÛ\81 بمطابق $1",
+       "revisionasof": "نسخہ بمطابق $1",
        "revision-info": "نظرثانی بتاریخ $1 از {{GENDER:$6|$2}}$7",
        "previousrevision": "←پرانی تدوین",
        "nextrevision": "→اگلا اعادہ",
        "history-feed-description": "ویکی پر اِس صفحہ کا تاریخچۂ نظرثانی",
        "history-feed-item-nocomment": "بہ $2 $1",
        "history-feed-empty": "درخواست شدہ صفحہ موجود نہیں.\nیا تو یہ ویکی سے حذف کیا گیا ہے اور یا اِس کا نام تبدیل کردیا گیا ہے.\nآپ متعلقہ نئے صفحات کیلئے [[Special:Search|ویکی پر تلاش]] کرسکتے ہیں.",
+       "history-edit-tags": "منتخب نظرثانیوں کے ٹیگوں میں ترمیم کریں",
        "rev-deleted-comment": "(تبصرہ حذف کی گيا ہے)",
        "rev-deleted-user": "(صارف نام حذف کیا گيا ہے)",
+       "rev-deleted-event": "(نوشتہ کی تفصیلات ہٹا دی گئیں)",
+       "rev-deleted-user-contribs": "[صارف نام یا آئی پی پتہ ہٹا دیا گیا - شراکتوں سے ترمیم پوشیدہ ہو گئی]",
+       "rev-deleted-text-permission": "پیش نظر صفحہ کی یہ ترمیم <strong>حذف کر دی گئی ہے</strong>۔\nمزید تفصیلات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔",
+       "rev-suppressed-text-permission": "پیش نظر صفحہ کی یہ ترمیم <strong>پوشیدہ کر دی گئی ہے</strong>۔\nمزید تفصیلات [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} نوشتہ پوشیدگی] میں دیکھی جا سکتی ہیں۔",
+       "rev-deleted-text-unhide": "پیش نظر صفحہ کی یہ ترمیم <strong>حذف کر دی گئی ہے</strong>۔\nمزید تفصیلات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔\nتاہم اگر آپ چاہیں تو [$1 اس نسخے کو ابھی بھی دیکھ سکتے ہیں]۔",
+       "rev-suppressed-text-unhide": "پیش نظر صفحہ کی یہ ترمیم <strong>پوشیدہ کر دی گئی ہے</strong>۔\nمزید تفصیلات [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔\nتاہم اگر آپ چاہیں تو [$1 اس نسخے کو ابھی بھی دیکھ سکتے ہیں]۔",
+       "rev-deleted-text-view": "پیش نظر صفحہ کی یہ ترمیم <strong>حذف کر دی گئی ہے</strong>۔\nتاہم اسے آپ دیکھ سکتے ہیں، مزید تفصیلات [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} نوشتہ حذف شدگی] میں دیکھی جا سکتی ہیں۔",
+       "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-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}}}} نوشتہ پوشیدگی] میں دیکھی جا سکتی ہیں۔",
        "rev-delundel": "دکھاؤ/چھپاؤ",
        "rev-showdeleted": "دکھاؤ",
        "revisiondelete": "نظرثانی حذف کریں/واپس لائیں",
        "revdelete-nooldid-title": "ناقص مقصود نظرثانی",
+       "revdelete-nooldid-text": "اس فنکشن کو جس نسخے پر انجام دینا ہے اسے آپ نے منتخب نہیں کیا، یا منتخب کردہ نسخہ موجود نہیں، یا آپ موجودہ نسخہ کو پوشیدہ کرنے کی کوشش کر رہے ہیں۔",
+       "revdelete-no-file": "درج کردہ فائل موجود نہیں ہے۔",
        "revdelete-show-file-submit": "ہاں",
+       "revdelete-selected-text": "[[:$2]] {{PLURAL:$1|کا منتخب نسخہ|کے منتخب نسخے}}:",
+       "revdelete-selected-file": "[[:$2]] {{PLURAL:$1|کا منتخب فائل نسخہ|کے منتخب فائل نسخے}}:",
        "logdelete-selected": "{{PLURAL:$1|منتخب واقعۂ نوشتہ|منتخب واقعاتِ نوشتہ}}:",
+       "revdelete-text-text": "صفحہ کے تاریخچے میں حذف شدہ نسخے نظر آئی گے لیکن ان کا مواد عام صارفین کے لیے ناقابل رسائی ہوگا۔",
+       "revdelete-text-file": "فائل کے تاریخچے میں حذف شدہ نسخے نظر آئی گے لیکن ان کا مواد عام صارفین کے لیے ناقابل رسائی ہوگا۔",
+       "logdelete-text": "نوشتہ کے حذف شدہ اندراجات نوشتوں میں ظاہر ہوتے رہیں گے لیکن ان کا مواد عام صارفین کے لیے ناقابل رسائی ہوگا۔",
+       "revdelete-text-others": "جب تک اضافی پابندیاں نہیں لگائی جاتیں دیگر منتظمین کو اس پوشیدہ مواد تک رسائی اور اسے بحال کرنے کا اختیار حاصل ہوگا۔",
        "revdelete-confirm": "برائے مہربانی! یقین دِہانی کرلیجئے کہ آپ واقعی ایسا کرنا چاہتے ہیں، آپ اِس کے نتائج سے باخبر ہیں، اور آپ یہ [[{{MediaWiki:Policy-url}}|پالیسی]] کے مطابق کررہے ہیں.",
        "revdelete-legend": "رویتی پابندیاں لگائیں",
        "revdelete-hide-text": "نظرثانی متن چھپاؤ",
        "revdelete-hide-name": "ہدف اور پیرامیٹرز کو چھپائیں",
        "revdelete-hide-comment": "ترمیمی تبصرہ چھپاؤ",
        "revdelete-hide-user": "ترمیم کار کا اسمِ صارف / آئی.پی پتہ چُھپاؤ",
+       "revdelete-hide-restricted": "منتظمین اور دیگر صارفین سے معلومات کو پوشیدہ کریں",
        "revdelete-radio-same": "(تبدیل مت کرو)",
        "revdelete-radio-set": "پوشیدہ",
        "revdelete-radio-unset": "ظاہر",
+       "revdelete-suppress": "منتظمین اور دیگر صارفین سے معلومات کو پوشیدہ کریں",
        "revdelete-unsuppress": "بحال شدہ نظرثانیوں پر پابندیاں ہٹاؤ",
        "revdelete-log": "وجہ",
        "revdelete-success": "'''رؤیتِ نظرثانی کی تجدید کامیابی سے ہوئی.'''",
        "revdel-restore": "ظاہریت تبدیل کرو",
        "pagehist": "تاریخچۂ صفحہ",
        "deletedhist": "حذف شدہ تاریخچہ",
+       "revdelete-reason-dropdown": "* عمومی وجوہات حذف شدگی\n** کاپی رائٹ کی خلاف ورزی\n** نامناسب تبصرہ یا ذاتی معلومات\n** نامناسب صارف نام\n** ممکنہ طور پر افترا آمیر معلومات",
        "revdelete-otherreason": "دوسری/اضافی وجہ:",
        "revdelete-reasonotherlist": "کوئی اَور وجہ",
        "revdelete-edit-reasonlist": "تحذیفی وجوہات کی تدوین",
        "revdelete-offender": "نظرثانی مصنف:",
+       "suppressionlog": "نوشتہ پوشیدگی",
        "mergehistory": "تواریخِ صفحہ کا انضمام",
        "mergehistory-box": "دو صفحات کی نظرثانیوں کا انضمام:",
        "mergehistory-from": "مآخذ صفحہ:",
        "mergelog": "نوشتہ کا انضمام",
        "revertmerge": "غیر ضم",
        "history-title": "\"$1\" کا نظرثانی تاریخچہ",
-       "difference-title": "\"$1\" کے اعادوں کے درمیان فرق",
+       "difference-title": "\"$1\" کے نسخوں کے درمیان فرق",
        "difference-multipage": "(فرق مابین صفحات)",
        "lineno": "لکیر $1:",
        "compareselectedversions": "منتخب متـن کا موازنہ",
        "searchdisabled": "{{SITENAME}} تلاش غیرفعال.\nآپ فی الحال گوگل کے ذریعے تلاش کرسکتے ہیں.\nیاد رکھئے کہ اُن کے {{SITENAME}} اشاریے ممکناً پرانے ہوسکتے ہیں.",
        "preferences": "ترجیحات",
        "mypreferences": "ترجیحات",
-       "prefs-edits": "تدÙ\88Û\8cÙ\86ات Ú©Û\8c ØªØ¹Ø¯Ø§Ø¯:",
+       "prefs-edits": "تعداد ØªØ±Ø§Ù\85Û\8cÙ\85:",
        "prefs-skin": "جِلد",
        "skin-preview": "پیش منظر",
-       "datedefault": "کوئی ترجیحات نہیں",
+       "datedefault": "کوئی ترجیح نہیں",
        "prefs-user-pages": "صارف صفحات",
-       "prefs-personal": "Ù\86Ù\85اÛ\8cÛ\82 ØµØ§Ø±Ù\81",
+       "prefs-personal": "پرÙ\88Ù\81ائÙ\84",
        "prefs-rc": "حالیہ تبدیلیاں",
        "prefs-watchlist": "زیرِنظر فہرست",
+       "prefs-editwatchlist": "زیر نظر فہرست میں ترمیم کریں",
+       "prefs-editwatchlist-label": "اپنی زیر نظر فہرست کے مندرجات میں ترمیم کریں:",
+       "prefs-editwatchlist-edit": "اپنی زیر نظر فہرست میں عناویں دیکھیں اور حذف کریں",
+       "prefs-editwatchlist-raw": "زیر نظر خام فہرست میں ترمیم کریں",
        "prefs-editwatchlist-clear": "اپنی زیر نظر فہرست صاف کریں",
-       "prefs-watchlist-days": "زیرِنظر فہرست میں نظر آنے والے ایام:",
-       "prefs-watchlist-days-max": "زیادا سے زیادہ $1 {{PLURAL:$1|یوم|ایام}}",
-       "prefs-watchlist-edits": "عرÛ\8cض Ø²Û\8cرÙ\90Ù\86ظرفہرست میں نظر آنے والی تبدیلیوں کی زیادہ سے زیادہ تعداد:",
-       "prefs-watchlist-edits-max": "(زیادہ سے زیادہ تعداد: 1000)",
-       "prefs-watchlist-token": "کلید زیرنظر فہرست:",
+       "prefs-watchlist-days": "زیر نظر فہرست میں نظر آنے والے ایام:",
+       "prefs-watchlist-days-max": "زیادہ سے زیادہ $1 {{PLURAL:$1|دن}}",
+       "prefs-watchlist-edits": "تÙ\88سÛ\8cع Ø´Ø¯Û\81 Ø²Û\8cر Ù\86ظر فہرست میں نظر آنے والی تبدیلیوں کی زیادہ سے زیادہ تعداد:",
+       "prefs-watchlist-edits-max": "زیادہ سے زیادہ تعداد: 1000",
+       "prefs-watchlist-token": "زیر نظر فہرست کی کلید:",
        "prefs-misc": "دیگر",
-       "prefs-resetpass": "کلمۂ شناخت تبدیل کیجئے",
+       "prefs-resetpass": "پاس ورڈ تبدیل کریں",
        "prefs-changeemail": "برقی ڈاک پتہ (e-mail address) تبدیل کریں",
        "prefs-setemail": "برقی پتہ دیں",
-       "prefs-email": "اختÛ\8cاراتÙ\90 Ø¨Ø±Ù\82Û\8c Ú\88اک",
+       "prefs-email": "برÙ\82Û\8c Ø®Ø· Ú©Û\92 Ø§Ø®ØªÛ\8cارات",
        "prefs-rendering": "ظاہریت",
        "saveprefs": "محفوظ",
-       "restoreprefs": "تÙ\85اÙ\85 Ø¨Û\92Ù\86Ù\82ص ØªØ±ØªÛ\8cبات بحال کریں",
-       "prefs-editing": "تدÙ\88Û\8cÙ\86",
+       "restoreprefs": "تÙ\85اÙ\85 Ø§Ø¨ØªØ¯Ø§Ø¦Û\8c ØªØ±ØªÛ\8cبات Ú©Ù\88 بحال کریں",
+       "prefs-editing": "ترÙ\85Û\8cÙ\85 Ú©Ø§Ø±Û\8c",
        "rows": "صفیں:",
        "columns": "قطاریں:",
        "searchresultshead": "تلاش",
+       "stub-threshold": "نامکمل ربط کے فارمیٹ کی حد ($1):",
        "stub-threshold-sample-link": "نمونہ",
        "stub-threshold-disabled": "غیر فعال",
-       "recentchangesdays": "حاÙ\84Û\8cÛ\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ù\85Û\8cÚº Ø¯Ú©Ú¾Ø§Ø¦Û\8c جانے والے ایّام:",
-       "recentchangesdays-max": "(زیادہ سے زیادہ $1 {{PLURAL:$1|دن|ایام}})",
+       "recentchangesdays": "حاÙ\84Û\8cÛ\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ù\85Û\8cÚº Ø¯Ú©Ú¾Ø§Ø¦Û\92 جانے والے ایّام:",
+       "recentchangesdays-max": "زیادہ سے زیادہ $1 {{PLURAL:$1|دن}}",
        "recentchangescount": "دکھائی جانے والی ترامیم کی تعداد:",
-       "prefs-help-recentchangescount": "اِس میں حالیہ تبدیلیاں، تواریخِ صفحہ اور نوشتہ جات شامل ہیں.",
+       "prefs-help-recentchangescount": "اِس میں حالیہ تبدیلیاں، تاریخچے اور نوشتہ جات شامل ہیں۔",
+       "prefs-help-watchlist-token2": "یہ آپ کی زیر نظر فہرست کے ویب فیڈ کی خفیہ کلید ہے۔\nاسے خفیہ رکھیں، تاکہ کوئی دوسرا شخص آپ کی زیر نظر فہرست نہ دیکھ سکے۔\nاگر آپ کو کلید تبدیل کرنی ہو تو [[Special:ResetTokens|یہاں کلک کریں]]۔",
        "savedprefs": "آپ کی ترجیحات محفوظ ہوگئیں۔",
+       "savedrights": "{{GENDER:$1|$1}} کے اختیارات محفوظ ہو گئے۔",
        "timezonelegend": "منطقۂ وقت:",
        "localtime": "مقامی وقت:",
-       "servertime": "سرور وقت:",
+       "timezoneuseserverdefault": "ویکی کا طے شدہ استعمال کریں ($1)",
+       "timezoneuseoffset": "دیگر (فرق درج کریں)",
+       "servertime": "سرور کا وقت:",
+       "guesstimezone": "براؤزر کا وقت استعمال کریں",
        "timezoneregion-africa": "افریقہ",
        "timezoneregion-america": "امریکہ",
        "timezoneregion-antarctica": "انٹارکٹیکا",
        "prefs-searchoptions": "تلاش",
        "prefs-namespaces": "جائے نام",
        "default": "طے شدہ",
-       "prefs-files": "مسلات",
-       "prefs-custom-css": "خودساختہ CSS",
-       "prefs-custom-js": "خودساختہ JS",
-       "prefs-emailconfirm-label": "برقی پتہ کی تصدیق:",
-       "youremail": "٭ برقی خط",
+       "prefs-files": "فائلیں",
+       "prefs-custom-css": "شخصی سی ایس ایس",
+       "prefs-custom-js": "شخصی جاوا اسکرپٹ",
+       "prefs-common-css-js": "جملہ پوشاکوں کے لیے مشترکہ سی ایس ایس/جاوا اسکرپٹ:",
+       "prefs-reset-intro": "آپ اس صفحہ کے ذریعہ اپنی موجودہ ترجیحات کو سائٹ کی ابتدائی ترتیبات کے مطابق ڈھال سکتے ہیں۔\nلیکن اسے واپس نہیں پھیرا جا سکتا۔",
+       "prefs-emailconfirm-label": "برقی خط کی تصدیق:",
+       "youremail": "برقی خط:",
        "username": "صارف:",
-       "prefs-memberingroups": "{{PLURAL:$1|گروہ|گروہوں}} کا رُکن:",
+       "prefs-memberingroups": "{{PLURAL:$1|گروہ|گروہوں}} {{GENDER:$2|کا رکن|کی رکن}}:",
        "prefs-registration": "وقتِ اندراج:",
        "yourrealname": "* اصلی نام",
        "yourlanguage": "زبان:",
        "yourvariant": "متغیّر:",
-       "yournick": "دستخط",
+       "prefs-help-variant": "اس ویکی کے صفحات دکھانے کے لیے آپ کا پسندیدہ لہجہ یا املا۔",
+       "yournick": "شخصی دستخط:",
+       "prefs-help-signature": "تبادلۂ خیال صفحات پر تبصرہ تحریر کرنے کے بعد یہ \"<nowiki>~~~~</nowiki>\" علامتیں درج کرنی چاہئیں، یہ علامتیں از خود آپ کے دستخط اور وقت میں تبدیل ہو جائیں گی۔",
        "badsig": "ناقص خام دستخط.\nHTML tags جانچئے.",
        "badsiglength": "آپ کا دستخط کافی طویل ہے.\nیہ $1 {{PLURAL:$1|حرف|حروف}} سے زیادہ نہیں ہونا چاہئے.",
        "yourgender": "جنس:",
-       "gender-unknown": "آپ Ú©Û\92 ØªØ°Ú©Ø±Û\81 Ú©Û\92 Ù\88Ù\82تØ\8c Ø³Ù\88Ù\81Ù¹Ù\88Û\8cئر ØºÛ\8cر Ø¬Ø§Ù\86بدار Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\92 Ú¯Ø§ Ø§Ú¯Ø± Ù\85Ù\85Ú©Ù\86 Û\81Ù\88",
+       "gender-unknown": "اگر Ù\85Ù\85Ú©Ù\86 Û\81Ù\88 ØªÙ\88 Ø¢Ù¾ Ú©Û\92 ØªØ°Ú©Ø±Û\81 Ú©Û\92 Ù\88Ù\82ت Ø³Ø§Ù\81Ù¹ Ù\88Û\8cئر ØºÛ\8cر Ø¬Ø§Ù\86بدار Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\92 Ú¯Ø§",
        "gender-male": "مرد",
        "gender-female": "عورت",
-       "prefs-help-gender": "اختÛ\8cارÛ\8c: Ù\85صÙ\86عâ\80\8cÙ\84Ø·Û\8cÙ\81 Ú©Û\8c Ø·Ø±Ù\81 Ø³Û\92 ØµØ­Û\8cØ­â\80\8cاÙ\84جÙ\86س ØªØ®Ø§Ø·Ø¨ Ú©Û\8cÙ\84ئÛ\92 Ø§Ø³ØªØ¹Ù\85اÙ\84 Û\81Ù\88تا Û\81Û\92. Û\8cÛ\81 Ù\85عÙ\84Ù\88Ù\85ات Ø¹Ø§Ù\85 Û\81Ù\88Ú¯Û\8c.",
+       "prefs-help-gender": "اس ØªØ±Ø¬Û\8cØ­ Ú©Û\8c ØªØ±ØªÛ\8cب Ø§Ø®ØªÛ\8cارÛ\8c Û\81Û\92Û\94\nآپ Ø§Ù\88ر Ø¯Û\8cگر ØµØ§Ø±Ù\81Û\8cÙ\86 Ú©Û\92 Ù\84Û\8cÛ\92 Ø§Ø² Ø±Ù\88ئÛ\92 Ù\82Ù\88اعد Ù\85Ù\86اسب Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ú©Û\92 Ø§Ù\86تخاب Ú©Û\92 Ù\84Û\8cÛ\92 Ø³Ø§Ù\81Ù¹ Ù\88Û\8cئر Ø§Ø³ Ù\82در Ú©Ù\88 Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±ØªØ§ Û\81Û\92Û\94\nÛ\8cÛ\81 Ù\85عÙ\84Ù\88Ù\85ات Ø¹Ø§Ù\85 Û\81Ù\88Ú¯Û\8cÛ\94",
        "email": "برقی خط",
-       "prefs-help-realname": "Ø­Ù\82Û\8cÙ\82Û\8c Ù\86اÙ\85 Ø§Ø®ØªÛ\8cارÛ\8c Û\81Û\92Û\94\nاگر Ø¢Ù¾ Ø§Ø³Û\92 Ù\85Û\81Û\8cÙ\91ا Ú©Ø±ØªÛ\92 Û\81Û\8cÚºØ\8c ØªÙ\88 Ø§Ø³Û\92 Ø¢Ù¾ Ú©Û\92 Ú©Ø§Ù\85 Ú©Û\8cÙ\84ئÛ\92 Ø¢Ù¾ Ú©Ù\88 Ø§Ù\86تساب Ø¯Û\8cÙ\86Û\92 Ú©Û\8cÙ\84ئے استعمال کیا جائے گا۔",
-       "prefs-help-email": "برقی ڈاک کا پتہ اختیاری ہے، لیکن یہ اُس وقت مفید ثابت ہوسکتا ہے جب آپ اپنا پارلفظ بھول گئے ہوں.",
-       "prefs-help-email-others": "آپ یہ بھی منتخب کرسکتے ہیں کہ دوسرے صارفین آپ کے تبادلۂ خیال صفحہ پر ایک ربط کے ذریعے آپ کو برقی ڈاک بھیجیں.\nجب دوسرے صارفین آپ سے رابطہ کرتے ہیں تو آپ کا برقی ڈاک کا پتہ افشا نہیں کیا جاتا۔",
+       "prefs-help-realname": "Ø­Ù\82Û\8cÙ\82Û\8c Ù\86اÙ\85 Ø§Ø®ØªÛ\8cارÛ\8c Û\81Û\92Û\94\nاگر Ø¢Ù¾ Ø¯Ø±Ø¬ Ú©Ø±Û\8cÚº ØªÙ\88 Ø§Ø³Û\92 Ø¢Ù¾ Ú©Û\92 Ú©Ø§Ù\85Ù\88Úº Ú©Ù\88 Ø¢Ù¾ Ø³Û\92 Ù\85Ù\86سÙ\88ب Ú©Ø±Ù\86Û\92 Ú©Û\92 Ù\84Û\8cے استعمال کیا جائے گا۔",
+       "prefs-help-email": "برقی ڈاک پتے کا اندراج اختیاری ہے، عموماً اس کی ضرورت اس وقت پڑتی ہے جب آپ اپنا پاس ورڈ بھول چکے ہوں اور نیا پاس ورڈ رکھنا چاہتے ہوں۔",
+       "prefs-help-email-others": "یہ ممکن ہے کہ آپ دیگر صارفین کو اس بات کی اجازت دیں کہ وہ آپ کے صارف یا تبادلۂ خیال صفحہ پر موجود ربط کے ذریعہ آپ کو برقی خط بھیج سکیں۔\nجب صارفین اس طرح آپ سے رابطہ کریں گے تو انہیں آپ کا برقی ڈاک پتہ نظر نہیں آئے گا۔",
        "prefs-help-email-required": "برقی ڈاک پتہ چاہئے.",
        "prefs-info": "بنیادی معلومات",
        "prefs-i18n": "بین الاقوامیت",
        "prefs-signature": "دستخط",
-       "prefs-dateformat": "Ø´Ú©Ù\84بÙ\86دÙ\90 ØªØ§Ø±Û\8cØ®",
+       "prefs-dateformat": "تارÛ\8cØ® Ú©Û\8c ØªØ±ØªÛ\8cب",
        "prefs-timeoffset": "وقت کی ترتیب",
        "prefs-advancedediting": "اعلی اختیارات",
        "prefs-editor": "خانہ ترمیم",
        "prefs-advancedrendering": "اعلی اختیارات",
        "prefs-advancedsearchoptions": "اعلی اختیارات",
        "prefs-advancedwatchlist": "اعلی اختیارات",
+       "prefs-displayrc": "نمائش کے اختیارات",
+       "prefs-displaywatchlist": "نمائش کے اختیارات",
        "prefs-tokenwatchlist": "ٹوکن",
-       "prefs-diffs": "فروق",
+       "prefs-diffs": "فرق",
+       "prefs-help-prefershttps": "یہ ترجیح آپ کے اگلے لاگ ان پر اثر انداز ہوگی۔",
        "userrights": "حقوقِ صارف کی نظامت",
        "userrights-lookup-user": "گروہائے صارف کا انتظام",
        "userrights-user-editname": "کوئی اسم‌صارف داخل کیجئے:",
        "grouppage-bot": "{{ns:project}}:روبہ جات",
        "grouppage-sysop": "{{ns:project}}:منتظمین",
        "grouppage-bureaucrat": "{{ns:project}}:مامورین اداری",
+       "right-read": "مطالعہ صفحات",
+       "right-edit": "ترمیم صفحات",
+       "right-createpage": "تخلیق صفحات (تبادلہ خیال صفحات نہیں)",
+       "right-createtalk": "تخلیق تبادلہ خیال صفحات",
+       "right-createaccount": "کھاتہ سازی",
+       "right-autocreateaccount": "بیرونی صارف کھاتے کے ذریعہ خودکار لاگ ان",
+       "right-minoredit": "ترامیم کی بطور معمولی ترمیم نشان زدگی",
+       "right-move": "منتقلی صفحات",
+       "right-move-subpages": "منتقلی صفحات مع ذیلی صفحات",
        "right-upload": "ملفات زبراثقال (اپ لوڈ) کریں",
        "right-writeapi": "اے پی آئی لکھائی کا استعمال",
        "right-delete": "صفحات حذف کریں",
        "recentchanges-legend-heading": "<strong>اختیارات</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (نیز [[Special:NewPages|جدید صفحات کی فہرست]]) ملاحظہ فرمائیں",
        "recentchanges-submit": "دکھائیں",
-       "rcnotefrom": "ذیل میں <strong>$3, $4</strong> سے کی گئی {{PLURAL:$5|تبدیلی|تبدیلیاں}} <strong>$1</strong> تک دکھائی جا رہی ہیں۔",
-       "rclistfrom": "$3 $2 سےنئی تبدیلیاں دکھانا شروع کریں",
+       "rcnotefrom": "ذیل میں <strong>$2</strong> سے کی گئی {{PLURAL:$5|تبدیلی|تبدیلیاں}} <strong>$1</strong> تک دکھائی جا رہی ہیں۔",
+       "rclistfrom": "$2، $3ء سے ہونے والی نئی تبدیلیاں دکھائیں",
        "rcshowhideminor": "معمولی ترامیم $1",
        "rcshowhideminor-show": "دکھائیں",
        "rcshowhideminor-hide": "چھپائیں",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] اور {{PLURAL:$2|ایک صفحہ|$2 صفحات}} زمرہ میں شامل {{PLURAL:$2|کیا گیا|$2 کیے گئے}}",
        "recentchanges-page-removed-from-category": "[[:$1]] کو زمرہ سے ہٹایا",
        "autochange-username": "میڈیاویکی خودکار تبدیلیاں",
-       "upload": "فائل اثقال/اپلوڈ فائل",
+       "upload": "اپلوڈ",
        "uploadbtn": "زبراثقال ملف (اپ لوڈ فائل)",
        "reuploaddesc": "زبراثقال ورقہ (فارم) کیجانب واپس۔",
        "uploadnologin": "آپ داخل شدہ حالت میں نہیں",
        "randomincategory-category": "زمرہ:",
        "randomincategory-submit": "جانا",
        "statistics": "اعداد و شمار",
-       "statistics-header-pages": "احصائÛ\92 ØµÙ\81حات",
-       "statistics-header-edits": "احصائÛ\92 ØªØ¯Ù\88Û\8cÙ\86",
+       "statistics-header-pages": "صÙ\81حات Ú©Û\92 Ø§Ø¹Ø¯Ø§Ø¯ Ù\88 Ø´Ù\85ار",
+       "statistics-header-edits": "ترÙ\85Û\8cÙ\85Û\8c Ø§Ø¹Ø¯Ø§Ø¯ Ù\88 Ø´Ù\85ار",
        "statistics-header-users": "ارکان کے اعداد و شمار",
-       "statistics-header-hooks": "احصائÛ\92 Ø¯Û\8cÚ¯ر",
+       "statistics-header-hooks": "دÛ\8cگر Ø§Ø¹Ø¯Ø§Ø¯ Ù\88 Ø´Ù\85ار",
        "statistics-articles": "مندرج صفحات",
        "statistics-pages": "صفحات",
        "statistics-pages-desc": "(ویکی اقتباسات کے کل صفحات، بشمولِ تبادلۂ خیال، رجوع مکررات وغیرہ۔)",
-       "statistics-files": "زبراثÙ\82اÙ\84 Ø´Ø¯Û\81 Ù\85Ù\84Ù\81ات",
-       "statistics-edits": "ویکی اقتباسات کے آغاز سے کل صفحاتی ترمیم",
+       "statistics-files": "اپÙ\84Ù\88Ú\88 Ú©Ø±Ø¯Û\81 Ù\81ائÙ\84Û\8cÚº",
+       "statistics-edits": "{{SITENAME}} کے آغاز سے کل صفحاتی ترامیم",
        "statistics-edits-average": "فی صفحہ اوسط ترامیم",
        "statistics-users": "مندرج [[خاص:فہرست صارفین، صارف فہرست|صارفین]]",
        "statistics-users-active": "متحرک صارفین",
        "shortpages": "چھوٹے صفحات",
        "longpages": "طویل ترین صفحات",
        "deadendpages": "مردہ صفحات",
-       "protectedpages": "محفوظ کردہ صفحات",
+       "protectedpages": "محفوظ صفحات",
+       "protectedpages-summary": "ذیل میں ان صفحات کی فہرست موجود ہے جو ابھی محفوظ ہیں۔ محفوظ شدہ عنوانات جنہیں تخلیق نہیں کیا جا سکتا، ان کی فہرست کے لیے [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]] ملاحظہ فرمائیں۔",
        "protectedpages-noredirect": "رجوع مکررات چھپائیں",
        "protectedpages-timestamp": "وقت کی مہر",
        "protectedpages-page": "صفحہ",
        "protectedpages-expiry": "مدت محفوظ شدگی",
        "protectedpages-performer": "محفوظ کنندہ",
-       "protectedpages-params": "معیار حفاظت",
+       "protectedpages-params": "درجہ حفاظت",
        "protectedpages-reason": "وجہ",
        "protectedpages-submit": "صفحات دکھائیں",
        "protectedpages-unknown-timestamp": "نامعلوم",
        "protectedpages-unknown-performer": "نامعلوم صارف",
-       "protectedtitles": "Ù\85سدÙ\88د عنوانات",
-       "protectedtitles-summary": "یہ ان صفحات کی فہرست ہے جن کو تخلیق نہیں کیا جا سکتا۔ یہ عنوانات محفوظ شدہ ہیں، جن کو تخلیق نہیں کیا جا سکتا۔ دیکھیے [[{{#خاص:محفوظ صفحات}}|{{int:protectedpages}}]].",
+       "protectedtitles": "Ù\85Ø­Ù\81Ù\88ظ عنوانات",
+       "protectedtitles-summary": "ذیل میں ان عنوانات کی فہرست ہے جنہیں تخلیق نہیں کیا جا سکتا، یہ عنوانات محفوظ شدہ ہیں۔ ان صفحات کی فہرست کے لیے جو ابھی محفوظ ہیں [[{{#special:ProtectedPages}}|{{int:protectedpages}}]] ملاحظہ فرمائیں۔",
        "protectedtitles-submit": "دکھائیں",
        "listusers": "فہرست ارکان",
        "usereditcount": "$1 {{PLURAL:$1|ترمیم|ترامیم}}",
        "speciallogtitlelabel": "ہدف (عنوان یا {{ns:user}}:صارف نام برائے صارف):",
        "log": "نوشتہ جات",
        "logeventslist-submit": "دکھائیں",
+       "checkbox-select": "$1 کو منتخب کریں",
+       "checkbox-all": "سب",
+       "checkbox-none": "کچھ نہیں",
+       "checkbox-invert": "برعکس",
        "allpages": "تمام صفحات",
        "nextpage": "اگلا صفحہ ($1)",
        "prevpage": "پچھلا صفحہ ($1)",
        "deleteotherreason": "دوسری/اِضافی وجہ:",
        "deletereasonotherlist": "دوسری وجہ",
        "rollback": "ترمیمات سابقہ حالت پرواپس",
-       "rollbacklink": "واپس سابقہ حالت",
+       "rollbacklink": "استرجع کریں",
        "rollbacklinkcount": "استرجع $1 {{PLURAL:$1|ترمیم|ترامیم}}",
+       "rollbacklinkcount-morethan": "$1 {{PLURAL:$1|ترمیم|ترامیم}} سے زیادہ کا استرجع",
        "rollbackfailed": "سابقہ حالت پر واپسی ناکام",
        "cantrollback": "تدوین ثانی کا اعادہ نہیں کیا جاسکتا؛ کیونکہ اس میں آخری بار حصہ لینے والا ہی اس صفحہ کا واحد کاتب ہے۔",
        "changecontentmodel-title-label": "صفحہ کا عنوان",
        "changecontentmodel-reason-label": "وجہ:",
+       "log-name-contentmodel": "نوشتہ تبدیلی نمونہ مواد",
+       "logentry-contentmodel-change": "$1 نے صفحہ $3 کے مواد کی ساخت کو \"$4\" سے \"$5\" میں {{GENDER:$2|تبدیل کیا}}",
        "protectlogpage": "نوشتۂ محفوظ شدگی",
        "protectedarticle": "\"[[$1]]\" کومحفوظ کردیا",
        "unprotectedarticle": "\"[[$1]]\" کوغیر محفوظ کیا",
        "restriction-edit": "تحریر و ترمیم",
        "restriction-move": "منتقل",
        "restriction-create": "تخلیق",
-       "restriction-upload": "زبراثÙ\82اÙ\84",
+       "restriction-upload": "اپÙ\84Ù\88Ú\88",
        "restriction-level-sysop": "مکمل محفوظ",
        "restriction-level-autoconfirmed": "نیم محفوظ",
        "restriction-level-all": "کوئی بھی سطح",
        "undelete-search-submit": "تلاش",
        "undelete-no-results": "حذف شدہ صفحات میں ایسا کوئی صفحہ نہیں ملا",
        "undelete-show-file-submit": "ہاں",
-       "namespace": "Ù\81ضائÛ\92 Ù\86اÙ\85:",
+       "namespace": "Ù\86اÙ\85 Ù\81ضا:",
        "invert": "انتخاب بالعکس",
        "tooltip-invert": "منتخب شدہ فضائے نام (اور مُلحقہ فضائے نام) میں شامل صفحات کی تبدیلیوں کو چُھپانے کیلئے اِس خانہ کو ٹِک کریں۔",
-       "namespace_association": "متعلقہ فضا",
+       "namespace_association": "Ù\85تعÙ\84Ù\82Û\81 Ù\86اÙ\85 Ù\81ضا",
        "blanknamespace": "(مرکز)",
        "contributions": "{{GENDER:$1|صارف}} شراکتیں",
        "contributions-title": "مساہماتِ صارف برائے $1",
        "blocklink": "پابندی لگائیں",
        "unblocklink": "پابندی ختم",
        "change-blocklink": "پابندی میں تبدیلی",
-       "contribslink": "شراکت",
+       "contribslink": "شراکتیں",
        "blocklogpage": "نوشتۂ پابندی",
        "block-log-flags-nocreate": "کھاتے کی تخلیق غیرفعال",
        "move-page": "منتقلی $1",
        "delete_and_move_text": "==حذف شدگی لازم==\n\nمنتقلی کے سلسلے میں انتخاب کردہ مضمون \"[[:$1]]\" پہلے ہی موجود ہے۔ کیا آپ اسے حذف کرکے منتقلی کیلیۓ راستہ بنانا چاہتے ہیں؟",
        "delete_and_move_confirm": "ہاں، صفحہ حذف کر دیا جائے",
        "delete_and_move_reason": "[[$1]] سے منتقلی کے سلسلے میں حذف",
+       "protectedpagemovewarning": "<strong>انتباہ:</strong> اس صفحہ کو محفوظ کر دیا گیا ہے اور اب محض منتظمین ہی اسے منتقل کر سکتے ہیں۔\nحوالہ کے لیے نوشتہ کا جدید اندراج ذیل میں درج ہے:",
        "export": "برآمد صفحات",
        "allmessages": "نظامی پیغامات",
        "allmessagesname": "نام",
        "tooltip-rollback": "پچھلے صارف کی کی گئی اِس صفحے پر استرجع شدہ ترامیم کو ایک کلِک میں واپس کریں",
        "tooltip-undo": "''استرجع'' اس ترمیم کو پچھلی ترمیم کے جانب واپس کردیگا اور نمائشی انداز میں خانہ ترمیم کھول دے گا۔ آپ مختصراً سبب بیان کرنے کے بھی مجاز ہونگے۔",
        "tooltip-summary": "مختصر خلاصہ درج کریں",
+       "common.css": "body,\ntextarea {\n    font-family: Amiri;\n}",
        "anonymous": "{{SITENAME}} گمنام صارف",
        "others": "دیگر",
        "pageinfo-visiting-watchers": "تعداد ناظرین جنہوں نے حالیہ ترامیم کا مشاہدہ کیا",
        "pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
        "pageinfo-toolboxlink": "معلومات صفحہ",
+       "pageinfo-category-info": "زمرے کی معلومات",
+       "pageinfo-category-pages": "تعداد صفحات",
+       "pageinfo-category-subcats": "تعداد ذیلی زمرہ جات",
+       "pageinfo-category-files": "تعداد املاف",
        "markaspatrolledtext": "اس صفحہ کو بطور مراجعت شدہ نشان زد کریں",
        "markedaspatrollederrornotify": "بطور مراجعت نشان زد نہیں کیا جا سکا۔",
        "deletedrevision": "حذف شدہ پرانی ترمیم $1۔",
        "previousdiff": "← پُرانی تدوین",
        "nextdiff": "صفحہ کا نام:",
+       "imagemaxsize": "تصویر کی جسامت کی حد:<br /><em>(فائل کے توضیحی صفحات کے لیے)</em>",
+       "thumbsize": "تھمب نیل کی جسامت:",
        "file-info-size": "\n$1 × $2 عکصر (پکسلز)، حجم ملف: $3، MIME قسم: $4",
        "file-nohires": "اس سے بڑی تصمیم دستیاب نہیں۔",
        "show-big-image": "اصل ملف",
        "confirm_purge_button": "جی!",
        "confirm-rollback-button": "ٹھیک ہے",
        "semicolon-separator": "؛&#32;",
+       "comma-separator": "،&#32;",
        "imgmultipageprev": "← پچھلا",
        "imgmultipagenext": "اگلا →",
        "imgmultigo": "جائیں!",
        "autosumm-blank": "تمام مندرجات حذف",
        "autoredircomment": "[[$1]] سے رجوع مکرر",
        "autosumm-new": "نیا صفحہ: $1",
+       "size-bytes": "$1 بائٹ",
        "watchlisttools-view": "متعلقہ تبدیلیاں دیکھیں",
        "watchlisttools-edit": "زیرِنظرفہرست دیکھیں اور تدوین کریں",
        "watchlisttools-raw": "خام زیرِنظرفہرست تدوین کریں",
        "logentry-move-move": "$1 نے صفحہ $3 کو $4 کی جانب منتقل کیا",
        "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بنایا گیا}}",
        "logentry-protect-move_prot": "$1 نے ترتیب درجہ حفاظت $4 سے $3 کی طرف {{GENDER:$2|منتقل کی}}",
+       "logentry-protect-protect": "$1 نے $3 کو {{GENDER:$2|محفوظ کیا}}  $4",
        "logentry-protect-modify": "$1 نے $3 کا درجۂ حفاظت {{GENDER:$2|تبدیل کیا}} $4",
        "logentry-rights-rights": "$1 نے {{GENDER:$6|$3}} کی گروہی رکنیت از $4 تا $5 {{GENDER:$2|تبدیل کی}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|اپلوڈ}} $3",
index 2afd362..31b213f 100644 (file)
        "yourpasswordagain": "Gõ lại mật khẩu",
        "createacct-yourpasswordagain": "Xác nhận lại mật khẩu",
        "createacct-yourpasswordagain-ph": "Nhập mật khẩu lần nữa",
-       "remembermypassword": "Nhớ thông tin đăng nhập của tôi trên máy tính này (cho đến $1 ngày)",
        "userlogin-remembermypassword": "Giữ trạng thái đăng nhập",
        "userlogin-signwithsecure": "Sử dụng kết nối an toàn",
        "cannotloginnow-title": "Không thể đăng nhập lúc này",
        "file-thumbnail-no": "Tên tập tin bắt đầu bằng <strong>$1</strong>.\nCó vẻ đây là bản thu nhỏ của hình gốc ''(thumbnail)''.\nNếu bạn có hình ở độ phân giải tối đa, xin hãy tải bản đó lên, nếu không xin hãy đổi lại tên tập tin.",
        "fileexists-forbidden": "Đã có tập tin với tên gọi này, và nó không thể bị ghi đè.\nNếu bạn vẫn muốn tải tập tin của bạn lên, xin hãy quay lại và sử dụng một tên khác. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Một tập tin với tên này đã tồn tại ở kho tập tin dùng chung.\nNếu bạn vẫn muốn tải tập tin của bạn lên, xin hãy quay lại và dùng một tên khác. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Tập tin tải lên này là bản sao y hệt với phiên bản hiện tại của <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Tập tin tải lên này là bản sao y hệt với {{PLURAL:$2|một phiên bản trước đây|các phiên bản trước đây}} của <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Tập tin này có vẻ là bản sao của {{PLURAL:$1|tập tin|các  tập tin}} sau:",
        "file-deleted-duplicate": "Một tập tin giống hệt như tập tin này ([[:$1]]) đã từng bị xóa trước đây. Bạn nên xem lại lịch sử xóa tập tin trước khi tiếp tục tải nó lên lại.",
        "file-deleted-duplicate-notitle": "Một tập tin giống hệt như tập tin này đã từng bị xóa và tên bị xóa hẳn trước đây.\nBạn nên xin một người có quyền xem dữ liệu tập tin bị xóa hẳn xem lại trường hợp này trước khi tiếp tục tải nó lên lại.",
        "filerevert-submit": "Lùi lại",
        "filerevert-success": "'''[[Media:$1|$1]]''' đã được lùi về [$4 phiên bản lúc $3, $2].",
        "filerevert-badversion": "Không tồn tại phiên bản trước đó của tập tin tại thời điểm trên.",
+       "filerevert-identical": "Phiên bản hiện tại của tập tin đã y hệt với phiên bản được chọn.",
        "filedelete": "Xóa $1",
        "filedelete-legend": "Xóa tập tin",
        "filedelete-intro": "Bạn sắp xóa tập tin '''[[Media:$1|$1]]''' cùng với tất cả lịch sử của nó.",
        "rollbacklinkcount-morethan": "lùi tất cả hơn $1 sửa đổi",
        "rollbackfailed": "Lùi sửa đổi không thành công",
        "rollback-missingparam": "Yêu cầu thiếu những tham số bắt buộc.",
+       "rollback-missingrevision": "Không thể tải dữ liệu phiên bản.",
        "cantrollback": "Không lùi sửa đổi được;\nngười viết trang cuối cùng cũng là tác giả duy nhất của trang này.",
        "alreadyrolled": "Không thể lùi tất cả sửa đổi cuối của [[User:$2|$2]] ([[User talk:$2|thảo luận]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) tại [[:$1]]; ai đó đã thực hiện sửa đổi hoặc thực hiện lùi tất cả rồi.\n\nSửa đổi cuối cùng tại trang do [[User:$3|$3]] ([[User talk:$3|thảo luận]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) thực hiện.",
        "editcomment": "Tóm lược sửa đổi: <em>$1</em>.",
        "linkaccounts-submit": "Liên kết tài khoản",
        "unlinkaccounts": "Gỡ liên kết tài khoản",
        "unlinkaccounts-success": "Đã gỡ liên kết tài khoản.",
-       "authenticationdatachange-ignored": "Tác vụ thay đổi dữ liệu xác thực không được xử lý. Có lẽ nhà cung cấp chưa được cấu hình?"
+       "authenticationdatachange-ignored": "Tác vụ thay đổi dữ liệu xác thực không được xử lý. Có lẽ nhà cung cấp chưa được cấu hình?",
+       "userjsispublic": "Xin lưu ý: Các trang con JavaScript không nên chứa dữ liệu bí mật, vì những người dùng khác có thể xem các trang này.",
+       "usercssispublic": "Xin lưu ý: Các trang con CSS không nên chứa dữ liệu bí mật, vì những người dùng khác có thể xem các trang này."
 }
index fa7133a..71f37b9 100644 (file)
        "yourpasswordagain": "Klavolös dönu letavödi:",
        "createacct-yourpasswordagain": "Fümedolös letavödi",
        "createacct-yourpasswordagain-ph": "Penolös letavödi dönu",
-       "remembermypassword": "Dakipolöd ninädamanünis obik in nünöm at (muiko {{PLURAL:$1|del|dels}} $1)",
        "userlogin-remembermypassword": "Dakipön obi penunädöl",
        "yourdomainname": "Domen olik:",
        "password-change-forbidden": "No kanol votükön letavödis su el wiki at.",
        "minoredit": "Votükam pülik",
        "watchthis": "Galädolöd padi at",
        "savearticle": "Dakipolöd padi",
+       "publishpage": "Dabükön padi",
+       "publishchanges": "Dabükön votükamis",
        "preview": "Büologed",
        "showpreview": "Jonolöd padalogoti",
        "showdiff": "Jonolöd votükamis",
        "undo-norev": "No eplöpos ad sädunön redakami at, bi no dabinon u pämoükon.",
        "undo-summary": "Äsädunon votükami $1 fa [[Special:Contributions/$2|$2]] ([[User talk:$2|Bespikapad]])",
        "undo-summary-username-hidden": "Sädunön revidi: $1 fa geban peklenädöl",
-       "cantcreateaccounttitle": "Kal no kanon pajafön",
        "cantcreateaccount-text": "Kalijaf se ladet-IP at ('''$1''') peblokon fa geban: [[User:$3|$3]].\n\nKod blokama fa el $3 pegivöl binon ''$2''",
        "viewpagelogs": "Jonön jenotalisedis pada at",
        "nohistory": "Pad at no labon redakamajenotemi.",
index a977529..9937693 100644 (file)
        "yourpassword": "Vosse sicret",
        "userlogin-yourpassword": "Sicret",
        "yourpasswordagain": "Ritapez vosse sicret",
-       "remembermypassword": "Rimimbrer m' sicret inte les sessions (nén dpus ki po $1 {{PLURAL:$1|djoû|djoûs}})",
        "yourdomainname": "Vosse dominne",
        "login": "S' elodjî",
        "nav-login-createaccount": "Ahiver on conte, udon-bén s' elodjî",
        "minoredit": "Ci n' est k' ene tchitcheye",
        "watchthis": "Shuve cist årtike",
        "savearticle": "Schaper l' pådje",
+       "savechanges": "Schaper l' pådje",
        "preview": "Vey divant",
        "showpreview": "Vey divant",
        "showdiff": "Vey les candjmints",
        "editwarning-warning": "Cwiter cisse pådje ci vos frè piede tos les candjmints ki vos avoz fwait.\nSi vos estoz elodjî, vos ploz dismete cist adviertixhmint ci dins l' linwete «Boesse di tecse» di vos preferinces.",
        "post-expand-template-inclusion-warning": "'''Asteme:''' I gn a trop di modeles dins cisse pådje ci.\nSacwants di zels ni seront nén eployîs.",
        "post-expand-template-inclusion-category": "Pådjes ki l' inclusion d' modeles est foû limite",
-       "cantcreateaccounttitle": "Vos n' ploz nén ahiver-st on conte.",
        "viewpagelogs": "Vey les djournås po cisse pådje ci",
        "nohistory": "I n' a pont d' istwere des modêyes po cisse pådje chal.",
        "currentrev": "Modêye d' asteure",
        "duration-years": "$1 anêye{{PLURAL:$1||s}}",
        "duration-decades": "$1 dijhinne{{PLURAL:$1||s}} d' anêyes",
        "duration-centuries": "$1 sieke{{PLURAL:$1||s}}",
-       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}",
-       "api-error-blacklisted": "S' i vs plait, tchoezixhoz èn ôte tite, pus esplicant."
+       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}"
 }
index eb913d7..803c6a2 100644 (file)
        "yourpasswordagain": "ווידער אריינקלאפן פאסווארט",
        "createacct-yourpasswordagain": "באשטעטיקן פאסווארט",
        "createacct-yourpasswordagain-ph": "ארײַנגעבן פאסווארט נאכאמאל",
-       "remembermypassword": "געדענקען מיין אַריינלאָגירן אין דעם דאָזיקן בראַוזער (ביז $1 {{PLURAL:$1|טאָג|טעג}})",
        "userlogin-remembermypassword": "לאז מיך בלײַבן ארײַנלאגירט",
        "userlogin-signwithsecure": "ניצן זיכערן סארווער",
        "cannotloginnow-title": "קען נישט אריינלאגירן אצינד",
        "userlogin-resetpassword-link": "פֿאַרגעסן אײַער פאַסווארט?",
        "userlogin-helplink2": "הילף מיט ארײַנלאגירן",
        "userlogin-loggedin": "איר זענט שוין אריינלאגירט ווי {{GENDER:$1|$1}}.\nניצט די פארעם אונטן כדי אריינלאגירן ווי אן אנדער באניצער.",
+       "userlogin-reauth": "איר דארפט נאכאמאל אריינלאגירן צו באשטעטיקן אז איר זענט {{GENDER:$1|$1}}.",
        "userlogin-createanother": "שאפֿן נאך א קאנטע",
        "createacct-emailrequired": "בליצפּאָסט אַדרעס",
        "createacct-emailoptional": "בליצפאסט אדרעס (אפציאנאל)",
        "createaccountreason": "אורזאַך:",
        "createacct-reason": "אורזאך",
        "createacct-reason-ph": "פֿארוואס שאפֿט איר נאך א קאנטע",
+       "createacct-reason-help": "מעלדונג געוויזן אין קאנטע־שאפֿונג לאגבוך",
        "createacct-submit": "שאפֿט אײַער קאנטע",
        "createacct-another-submit": "שאַפֿן קאנטע",
        "createacct-continue-submit": "פֿארטזעצן שאפֿן קאנטע",
        "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן!",
        "changepassword-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
        "botpasswords": "באט פאסווערטער",
+       "botpasswords-disabled": "באט פאסווערטער זענען אומאקטיווירט.",
+       "botpasswords-existing": "עקזיסטירנדע באט פאסווערטער",
+       "botpasswords-createnew": "שאפֿן א ניי באט פאסווארט",
        "botpasswords-label-appid": "באט נאמען:",
        "botpasswords-label-create": "שאַפֿן",
        "botpasswords-label-update": "דערהײַנטיקן",
        "botpasswords-label-cancel": "אַנולירן",
        "botpasswords-label-delete": "אויסמעקן",
        "botpasswords-label-resetpassword": "ווידערשטעלן פאַסווארט",
+       "botpasswords-label-restrictions": "באניץ באגרענעצונגען:",
        "botpasswords-label-grants-column": "נאכגעגעבן",
+       "botpasswords-bad-appid": "דער באט נאמען \"$1\" איז אומגילטיק.",
        "botpasswords-created-title": "באט פאסווארט געשאפן",
        "botpasswords-created-body": "דאס באט פאסווארט פאר באט־נאמען \"$1\" פון באניצער \"$2\" איז געשאפן געווארן.",
        "botpasswords-updated-title": "באט פאסווארט דערהיינטיקט",
        "passwordreset-emailsentemail": "טאמער איז דער ע־פאסט אדרעס פארקניפט מיט אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-emailsentusername": "טאמער איז פאראן אן ע־פאסט אדרעס פארקניפט מיט דעם באניצער־נאמען, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-nocaller": "מען דארף פֿארזארגן א רופֿער",
+       "passwordreset-nosuchcaller": "רופֿער איז נישט פֿאראן: $1",
        "passwordreset-invalideamil": "אומגילטיקער ע־פאסט אדרעס",
        "changeemail": "ענדערן אדער אראפנעמען ע-פּאָסט אַדרעס",
        "changeemail-header": "דערגאַנצט די פֿאָרעם צו ענדערן אייער ע-פּאָסט אַדרעס .\nטאמער ווילט איר אראפנעמען די צוארדנונג פון איינעם פון אייערע ע־פאסט אדרעסן פו אייער קאנטע, לאזט ליידיג דעם נייעם ע־פאסט אדרעס ווען איר גיט איין די פֿארעם.",
        "minoredit": "דאס איז א מינערדיגע ענדערונג",
        "watchthis": "טוט אױפֿפּאַסן דעם בלאט",
        "savearticle": "אויפהיטן בלאַט",
+       "savechanges": "אויפֿהיטן ענדערונגען",
        "publishpage": "פובליקירן בלאַט",
        "publishchanges": "פובליקירן ענדערונגען",
        "preview": "פֿאראויסקוק",
        "right-passwordreset": "באַקוקן פאַסווארט צוריקשטעלן ע־בריוו",
        "right-managechangetags": "שאפן און (אומ)אקטיווירן [[Special:Tags|טאגן]]",
        "right-applychangetags": "אנווענדן [[Special:Tags|טאגן]] צוזאמען מיט ענדערונגען",
+       "grant-generic": "\"$1\" רעכטן־בינטל",
        "grant-group-page-interaction": "אינטעראגירן מיט בלעטער",
        "grant-group-file-interaction": "אינטעראגירן מיט מעדיע",
+       "grant-group-watchlist-interaction": "אינטעראגירן מיט אייער אויפֿפאסונג־ליסטע",
        "grant-group-email": "שיקן ע־פאסט",
        "grant-createaccount": "שאַפֿן קאנטעס",
        "grant-editmywatchlist": "רעדאקטירן אײַער אויפֿפאסונג ליסטע",
        "undeletedrevisions": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} צוריקגעשטעלט",
        "undeletedrevisions-files": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} און  {{PLURAL:$2|1 טעקע|$2 טעקעס}} צוריקגעשטעלט",
        "undeletedfiles": "{{PLURAL:$1|1 טעקע|$1 טעקעס}} צוריקגעשטעלט",
-       "cannotundelete": "צוריקשטעלונג איז דורכגעפאלן: $1",
+       "cannotundelete": "×\98×\99×\99×\9c ×\90×\93ער ×\92×\90רע ×¦×\95ר×\99קש×\98×¢×\9c×\95× ×\92 ×\90×\99×\96 ×\93×\95ר×\9b×\92עפ×\90×\9c×\9f: $1",
        "undeletedpage": "'''דער בלאט $1 איז געווארן צוריקגעשטעלט.'''\n\nזעט דעם [[Special:Log/delete| אויסמעקן לאג]] פֿאר א ליסטע פון די לעצטע אויסגעמעקטע און צוריקגעשטעלטע בלעטער.",
        "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט recently deleted pages.",
        "undelete-search-title": "זוכן אויסגעמעקטע בלעטער",
        "sp-contributions-newbies-sub": "פאר נייע קאנטעס",
        "sp-contributions-newbies-title": "ביישטייערונגען פון נייע באַניצער",
        "sp-contributions-blocklog": "בלאקירן לאג",
-       "sp-contributions-suppresslog": "אונטערדריקטע באַניצער בײַשטײַערונגען",
+       "sp-contributions-suppresslog": "אונטערדריקטע {{GENDER:$1|באַניצער}} בײַשטײַערונגען",
        "sp-contributions-deleted": "אויסגעמעקטע באַניצער בײַשטײַערונגען",
        "sp-contributions-uploads": "אַרויפֿלאָדונגען",
        "sp-contributions-logs": "לאגביכער",
index 5a2986f..84ec923 100644 (file)
        "yourpasswordagain": "再輸入密碼:",
        "createacct-yourpasswordagain": "確認密碼",
        "createacct-yourpasswordagain-ph": "入多次密碼",
-       "remembermypassword": "響呢個瀏覽器度記住我嘅登入資料 (最高維持$1{{PLURAL:$1|日|日}})",
        "userlogin-remembermypassword": "記住我有簽到",
        "userlogin-signwithsecure": "用安全連線",
        "yourdomainname": "你嘅網域:",
        "suppressionlog": "廢止日誌",
        "suppressionlogtext": "下面係刪除同埋由操作員牽涉到內容封鎖嘅一覽。\n睇吓[[Special:BlockList|封鎖一覽]]去睇現時進行緊嘅禁止同埋封鎖表。",
        "mergehistory": "合併頁歷史",
-       "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史。",
+       "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史連續性。",
        "mergehistory-box": "合併兩版嘅修訂:",
        "mergehistory-from": "來源頁:",
        "mergehistory-into": "目的頁:",
index 961ead6..45f0721 100644 (file)
        "tog-enotifminoredits": "当我的监视列表中的页面和文件有小编辑时也发送电子邮件通知我",
        "tog-enotifrevealaddr": "在通知电子邮件中显示我的电子邮件地址",
        "tog-shownumberswatching": "显示监视用户数",
-       "tog-oldsig": "当前签名:",
+       "tog-oldsig": "您现有的签名:",
        "tog-fancysig": "将签名视为维基文本(不自动生成链接)",
        "tog-uselivepreview": "使用实时预览",
        "tog-forceeditsummary": "未输入编辑摘要时提醒我",
        "tog-showhiddencats": "显示隐藏分类",
        "tog-norollbackdiff": "执行回退后不显示差异",
        "tog-useeditwarning": "当我离开编辑页面时,如果有尚未保存的更改,请提醒我",
-       "tog-prefershttps": "登录时始终使用安全连接",
+       "tog-prefershttps": "在登录时总是使用安全连接",
        "underline-always": "始终",
        "underline-never": "从不",
        "underline-default": "皮肤或浏览器默认设置",
        "newwindow": "(在新窗口中打开)",
        "cancel": "取消",
        "moredotdotdot": "更多...",
-       "morenotlisted": "æ\9c¬å\88\97表不完整。",
+       "morenotlisted": "æ­¤å\88\97表å\8f¯è\83½不完整。",
        "mypage": "页面",
        "mytalk": "讨论",
        "anontalk": "讨论",
        "createacct-another-username-ph": "请输入用户名",
        "yourpassword": "密码:",
        "userlogin-yourpassword": "密码",
-       "userlogin-yourpassword-ph": "请输入的密码",
+       "userlogin-yourpassword-ph": "请输入的密码",
        "createacct-yourpassword-ph": "请输入密码",
        "yourpasswordagain": "请再次输入密码:",
        "createacct-yourpasswordagain": "确认密码",
        "createacct-yourpasswordagain-ph": "请再次输入密码",
-       "remembermypassword": "在该浏览器记住我的登录状态(最长$1天)",
        "userlogin-remembermypassword": "记住我的登录状态",
        "userlogin-signwithsecure": "使用安全连接",
+       "cannotlogin-title": "不能登录",
+       "cannotlogin-text": "无法登录。",
        "cannotloginnow-title": "现在不能登录",
        "cannotloginnow-text": "当使用$1时无法登录。",
+       "cannotcreateaccount-title": "无法创建账户",
+       "cannotcreateaccount-text": "此wiki没有启用直接账户创建。",
        "yourdomainname": "您的域名:",
        "password-change-forbidden": "您不能在本wiki上更改密码。",
        "externaldberror": "验证数据库出错或您被禁止更新您的外部账号。",
        "botpasswords-updated-body": "用于用户“$2”的机器人名称“$1”的机器人密码已更新。",
        "botpasswords-deleted-title": "机器人密码已删除",
        "botpasswords-deleted-body": "用于用户“$2”的机器人名称“$1”的机器人密码已删除。",
-       "botpasswords-newpassword": "用于登录<strong>$1</strong>的新密码是<strong>$2</strong>。<em>请记住它以备今后参考。</em>",
+       "botpasswords-newpassword": "用于登录<strong>$1</strong>的新密码是<strong>$2</strong>。<em>请记住它以备今后参考。</em><br>(对于需要登录名与最终用户名相同的旧机器人,您也可以使用<strong>$3</strong>作为用户名,<strong>$4</strong>作为密码。)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider不可用。",
        "botpasswords-restriction-failed": "机器人密码限制阻止此次登录。",
        "botpasswords-invalid-name": "指定的用户名不包含机器人密码分隔符(“$1”)。",
        "changeemail-newemail": "新的电子邮件地址:",
        "changeemail-newemail-help": "此字段应留空,如果您希望移除您的电子邮件地址的话。如果电子邮件地址被移除,您将无法重置忘记的密码,并将不会接收来自此wiki的电子邮件。",
        "changeemail-none": "(无)",
-       "changeemail-password": "的{{SITENAME}}密码:",
+       "changeemail-password": "的{{SITENAME}}密码:",
        "changeemail-submit": "更改电子邮件地址",
        "changeemail-throttled": "您最近尝试了太多次登录。请等待$1后再试。",
        "changeemail-nochange": "请输入一个不同的新的电子邮件地址。",
        "invalid-content-data": "无效的内容数据",
        "content-not-allowed-here": "[[$2]]页面上不允许“$1”内容",
        "editwarning-warning": "离开本页面可能导致您失去任何你已经作出的更改。如果您处于登录状态,您可以在您的设置的“{{int:prefs-editing}}”部分停用该警告。",
+       "editpage-invalidcontentmodel-title": "内容模型不支持",
+       "editpage-invalidcontentmodel-text": "内容模型“$1”不被支持。",
        "editpage-notsupportedcontentformat-title": "内容格式尚不支持",
        "editpage-notsupportedcontentformat-text": "内容模型$2尚不支持内容格式$1。",
        "content-model-wikitext": "维基文字",
        "file-thumbnail-no": "文件名以<strong>$1</strong>开始。它似乎是缩小的图像<em>(缩略图)</em>。如果您有完整分辨率的该图像,请上传它,否则请更改文件名。",
        "fileexists-forbidden": "已存在相同名称的文件,且不能覆盖;请返回并用一个新的名称来上传此文件。[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "共享文件库中存在该名称的文件。如果您仍想上传你的文件,请返回使用其他名称。[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "上传的文件与<strong>[[:$1]]</strong>的当前版本完全相同。",
+       "fileexists-duplicate-version": "上传的文件与<strong>[[:$1]]</strong>的{{PLURAL:$2|旧版本}}完全相同。",
        "file-exists-duplicate": "本文件是以下{{PLURAL:$1|文件}}的副本:",
        "file-deleted-duplicate": "一个相同名称的文件 ([[:$1]]) 在先前删除过。您应该在重新上传之前检查一下该文件之删除纪录。",
        "file-deleted-duplicate-notitle": "之前有与此相同的文件被删除和取消标题。您应该询问查看过改文件数据的任何人以复查重新上传时的诸多问题。",
        "filerevert-submit": "恢复",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong>已经恢复至[$4 $2 $3的版本]。",
        "filerevert-badversion": "文件并无所请求时间戳下的早期本地版本。",
+       "filerevert-identical": "文件的当前版本已与选择的版本相同。",
        "filedelete": "删除$1",
        "filedelete-legend": "删除文件",
        "filedelete-intro": "您将要删除文件<strong>[[Media:$1|$1]]</strong>及其全部历史。",
        "rollbacklinkcount-morethan": "回退超过$1次的编辑",
        "rollbackfailed": "回退失败",
        "rollback-missingparam": "请求中缺少必需参数。",
+       "rollback-missingrevision": "无法加载修订版本数据。",
        "cantrollback": "无法恢复编辑,最后贡献者是该页面的唯一作者。",
        "alreadyrolled": "无法回退[[User:$2|$2]]([[User talk:$2|讨论]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])对[[:$1]]的编辑,其他人已经编辑或者回退了该页。\n\n本页最后的编辑者是[[User:$3|$3]]([[User talk:$3|讨论]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。",
        "editcomment": "编辑摘要:<em>$1</em>。",
        "tooltip-ca-nstab-help": "查看帮助页面",
        "tooltip-ca-nstab-category": "查看分类页面",
        "tooltip-minoredit": "标记本编辑为小编辑",
-       "tooltip-save": "保存的更改",
+       "tooltip-save": "保存的更改",
        "tooltip-publish": "发布您的更改",
        "tooltip-preview": "预览您的更改。请在保存前使用此功能。",
        "tooltip-diff": "显示您对该文字所做的更改",
        "pageinfo-article-id": "页面ID",
        "pageinfo-language": "页面内容语言",
        "pageinfo-content-model": "页面内容类型",
+       "pageinfo-content-model-change": "更改",
        "pageinfo-robot-policy": "爬虫索引",
        "pageinfo-robot-index": "允许",
        "pageinfo-robot-noindex": "不允许",
        "tag-filter": "[[Special:Tags|标签]]过滤器:",
        "tag-filter-submit": "过滤器",
        "tag-list-wrapper": "([[Special:Tags|$1个标签]]:$2)",
+       "tag-mw-contentmodelchange": "内容模型更改",
+       "tag-mw-contentmodelchange-description": "更改页面[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel 内容模型]的编辑",
        "tags-title": "标签",
        "tags-intro": "本页面列出了软件可能用于标记编辑的标签和它们的含义。",
        "tags-tag": "标签名称",
        "tags-actions-header": "操作",
        "tags-active-yes": "是",
        "tags-active-no": "否",
-       "tags-source-extension": "由一个扩展定义",
+       "tags-source-extension": "由软件定义",
        "tags-source-manual": "可被用户和机器人手动应用",
        "tags-source-none": "不再被使用",
        "tags-edit": "编辑",
        "logentry-delete-revision": "$1{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
        "logentry-delete-event-legacy": "$1{{GENDER:$2|更改}}$3的日志事件的可见性",
        "logentry-delete-revision-legacy": "$1{{GENDER:$2|更改}}页面$3的版本的可见性",
-       "logentry-suppress-delete": "$1{{GENDER:$2|已隐藏}}页面$3",
+       "logentry-suppress-delete": "$1{{GENDER:$2|已屏蔽}}页面$3",
        "logentry-suppress-event": "$1秘密地{{GENDER:$2|更改}}$3的{{PLURAL:$5|$5个日志事件}}的可见性:$4",
        "logentry-suppress-revision": "$1秘密地{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
        "logentry-suppress-event-legacy": "$1秘密地{{GENDER:$2|更改}}$3的日志事件的可见性",
index 0b01858..37d164b 100644 (file)
@@ -74,7 +74,8 @@
                        "Reke",
                        "Kly",
                        "Cosine02",
-                       "一個正常人"
+                       "一個正常人",
+                       "Wehwei"
                ]
        },
        "tog-underline": "底線標示連結:",
        "yourpasswordagain": "再輸入密碼一次:",
        "createacct-yourpasswordagain": "確認密碼",
        "createacct-yourpasswordagain-ph": "再次輸入密碼",
-       "remembermypassword": "在瀏覽器上記住我的登入資訊 (上限 $1 {{PLURAL:$1|天}})",
        "userlogin-remembermypassword": "記住我的登入狀態",
        "userlogin-signwithsecure": "使用安全連線",
+       "cannotlogin-title": "無法登入",
+       "cannotlogin-text": "無法登入",
        "cannotloginnow-title": "現在無法登入",
        "cannotloginnow-text": "使用 $1 時無法登入。",
+       "cannotcreateaccount-title": "無法建立帳號",
        "yourdomainname": "您的網域:",
        "password-change-forbidden": "您不可變更此 Wiki 上的密碼。",
        "externaldberror": "這可能是由於資料庫驗證錯誤,或是不允許您更新外部帳號。",
        "suppressionlog": "禁止顯示日誌",
        "suppressionlogtext": "以下清單為管理員透過刪除或封鎖所隱藏的內容。\n請至 [[Special:BlockList|封鎖清單]] 取得目前已封鎖的清單。",
        "mergehistory": "合併頁面歷史",
-       "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改會繼續保留該頁面先前的歷史版本。",
+       "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改能夠繼續保留該頁面先前歷史版本的連續性。",
        "mergehistory-box": "合併兩個頁面的修訂:",
        "mergehistory-from": "來源頁面:",
        "mergehistory-into": "目標頁面:",
        "grant-group-high-volume": "執行大量活動",
        "grant-group-customization": "自訂與偏好設定",
        "grant-group-administration": "執行管理操作",
+       "grant-group-private-information": "存取關於您的隱私資料",
        "grant-group-other": "其他活動",
        "grant-blockusers": "封鎖與解除封鎖使用者",
        "grant-createaccount": "建立帳號",
        "grant-highvolume": "大量編輯",
        "grant-oversight": "隱藏使用者和禁止顯示修訂",
        "grant-patrol": "巡邏頁面的變更",
+       "grant-privateinfo": "存取隱私資訊",
        "grant-protect": "保護與取消保護頁面",
        "grant-rollback": "還原頁面的變更",
        "grant-sendemail": "傳送電子郵件聯絡其他使用者",
        "file-thumbnail-no": "檔案名稱以 <strong>$1</strong> 為開頭。\n似乎已為縮小的圖片 <em>(縮圖)</em>。\n若您有原始大小的圖片,應上傳原始圖片,否則請變更檔名稱。",
        "fileexists-forbidden": "已存在相同名稱的檔案,且無法覆蓋。\n若您仍要上傳此檔案,請返回上一頁並使用其他名稱。\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "共用檔案庫中已存在此名稱的檔案。\n若您仍要上傳此檔案,請返回上一頁並使用其他名稱。\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "上傳的檔案與目前版本的 <strong>[[:$1]]</strong> 完全相同。",
+       "fileexists-duplicate-version": "上傳的檔案與{{PLURAL:$2|較舊版本|較舊版本}}的 <strong>[[:$1]]</strong> 完全相同。",
        "file-exists-duplicate": "此檔案與下列{{PLURAL:$1|一|多}}個檔案重複:",
        "file-deleted-duplicate": "與此檔案完全相同的檔案 ([[:$1]]) 在先前已被刪除。\n您應在重新上傳之前確認該檔案的刪除日誌。",
        "file-deleted-duplicate-notitle": "與此檔案完全相同的檔案在先前已被刪除,且禁止顯示該標題。\n您在重新上傳前,應請求有權力檢視隱藏檔案的使用者重新審查。",
        "filerevert-submit": "還原",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> 已經還原到 [$4 於 $2 $3 的版本]。",
        "filerevert-badversion": "查無此檔案先前於指定時間的本地版本。",
+       "filerevert-identical": "目前版本的檔案與選擇的版本完全相同。",
        "filedelete": "刪除 $1",
        "filedelete-legend": "刪除檔案",
        "filedelete-intro": "您現正要刪除檔案 <strong>[[Media:$1|$1]]</strong> 與其所有歷史版本。",
        "nrevisions": "$1 次修訂",
        "nimagelinks": "被 $1 個頁面使用",
        "ntransclusions": "被 $1 個頁面使用",
-       "specialpage-empty": "此報表查無任何結果。",
+       "specialpage-empty": "此報表查無任何結果。",
        "lonelypages": "孤立頁面",
        "lonelypagestext": "下列頁面尚未被 {{SITENAME}} 中的其它頁面連結或引用。",
        "uncategorizedpages": "未分類的頁面",
        "watchnologin": "尚未登入",
        "addwatch": "新增至監視清單",
        "addedwatchtext": "已於[[Special:Watchlist|您的監視清單]]新增頁面 \"[[:$1]]\" 及其討論頁面。\n未來對此頁面及其關聯的對話頁面的變更將會在此清單中列出。",
+       "addedwatchtext-talk": "\"[[:$1]]\" 及相關的頁面已加入至您的 [[Special:Watchlist|監視清單]]。",
        "addedwatchtext-short": "已於您的監視清單新增頁面 \"$1\"。",
        "removewatch": "從監視清單中移除",
        "removedwatchtext": "已於[[Special:Watchlist|您的監視清單]]移除頁面 \"[[:$1]]\" 及其討論頁面。",
+       "removedwatchtext-talk": "已自您的 [[Special:Watchlist|監視清單]] 移除 \"[[:$1]]\" 及相關的頁面。",
        "removedwatchtext-short": "已於您的監視清單移除頁面 \"$1\"。",
        "watch": "監視",
        "watchthispage": "監視此頁面",
        "rollbacklinkcount-morethan": "還原超過 $1 次{{PLURAL:$1|編輯}}",
        "rollbackfailed": "還原失敗",
        "rollback-missingparam": "請求缺少必要參數。",
+       "rollback-missingrevision": "無法載入修訂資料。",
        "cantrollback": "無法還原編輯;\n此頁面的最後貢獻者是唯一的作者。",
        "alreadyrolled": "無法還原由 [[User:$2|$2]] ([[User talk:$2|對話]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]] 所作的最後一次編輯 [[:$1]],已有其他人編輯或還原了該頁面。\n\n最後一次編輯該頁面的使用者是 [[User:$3|$3]] ([[User talk:$3|對話]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。",
        "editcomment": "編輯摘要為:<em>$1</em>。",
        "undeletehistorynoadmin": "已刪除此頁面。\n以下摘要顯示刪除原因與刪除前所有編輯過此頁面的使用者詳細資料。\n這些已刪除的實際文字修訂僅對管理員可用。",
        "undelete-revision": "被 $3 刪除的 $1 (於 $4 $5) 修訂:",
        "undeleterevision-missing": "無效或遺失的修訂。\n您可能使用了錯誤的連結,或該修訂已從封存中還原或刪除。",
+       "undeleterevision-duplicate-revid": "無法還原 {{PLURAL:$1|1 個修訂|$1 修訂}},因{{PLURAL:$1|修訂的|修訂的}} <code>rev_id</code> 已在使用中。",
        "undelete-nodiff": "查無先前的修訂。",
        "undeletebtn": "還原",
        "undeletelink": "檢視/還原",
        "undeletedrevisions": "{{PLURAL:$1|$1 個修訂}}已還原",
        "undeletedrevisions-files": "{{PLURAL:$1|$1 個修訂}}與 {{PLURAL:$2|$2 個檔案}}已還原",
        "undeletedfiles": "{{PLURAL:$1|$1}} 個檔案已還原",
-       "cannotundelete": "取消刪除失敗:\n$1",
+       "cannotundelete": "部份或全部的取消刪除失敗:\n$1",
        "undeletedpage": "<strong>$1 已還原</strong>\n\n請參考 [[Special:Log/delete|刪除日誌]] 以查詢最近刪除及還原的記錄。",
        "undelete-header": "請參考 [[Special:Log/delete|刪除日誌]] 查詢最近刪除的頁面。",
        "undelete-search-title": "搜尋已刪除頁面",
        "pageinfo-article-id": "頁面 ID",
        "pageinfo-language": "頁面內容語言",
        "pageinfo-content-model": "頁面內容模型",
+       "pageinfo-content-model-change": "變更",
        "pageinfo-robot-policy": "由機器人建立索引",
        "pageinfo-robot-index": "允許",
        "pageinfo-robot-noindex": "不允許",
        "tags-actions-header": "操作",
        "tags-active-yes": "是",
        "tags-active-no": "否",
-       "tags-source-extension": "由擴充套件定義",
+       "tags-source-extension": "由軟體定義",
        "tags-source-manual": "由使用者與機器人手動套用",
        "tags-source-none": "不再使用",
        "tags-edit": "編輯",
        "linkaccounts-submit": "連結帳號",
        "unlinkaccounts": "取消連結帳號",
        "unlinkaccounts-success": "已取消連結帳號。",
-       "authenticationdatachange-ignored": "認証資料變更未被處理,可能未設定提供者?"
+       "authenticationdatachange-ignored": "認証資料變更未被處理,可能未設定提供者?",
+       "userjsispublic": "請注意:JavaScript 子頁面可被其他使用者檢視,不應包含憑証資料。",
+       "usercssispublic": "請注意:CSS 子頁面可被其他使用者檢視,不應包含憑証資料。"
 }
index 6f91f1e..d9ef8bc 100644 (file)
@@ -18,6 +18,8 @@
  * @author Yanteng3
  */
 
+$fallback = 'zh-hant'; // T125373
+
 $specialPageAliases = [
        'Activeusers'               => [ '躍簿' ],
        'Allmessages'               => [ '官話' ],
index f9da1ed..d5449bf 100644 (file)
@@ -29,14 +29,14 @@ $fallback8bitEncoding = 'windows-1256';
 $rtl = true;
 
 $namespaceNames = [
-       NS_MEDIA            => 'Ù\88سÛ\8cØ·',
+       NS_MEDIA            => 'Ù\85Û\8cÚ\88Û\8cا',
        NS_SPECIAL          => 'خاص',
        NS_TALK             => 'تبادلۂ_خیال',
        NS_USER             => 'صارف',
        NS_USER_TALK        => 'تبادلۂ_خیال_صارف',
        NS_PROJECT_TALK     => 'تبادلۂ_خیال_$1',
-       NS_FILE             => 'Ù\85Ù\84Ù\81',
-       NS_FILE_TALK        => 'تبادÙ\84Û\82_Ø®Û\8cاÙ\84\85Ù\84Ù\81',
+       NS_FILE             => 'Ù\81ائÙ\84',
+       NS_FILE_TALK        => 'تبادÙ\84Û\82_Ø®Û\8cاÙ\84\81ائÙ\84',
        NS_MEDIAWIKI        => 'میڈیاویکی',
        NS_MEDIAWIKI_TALK   => 'تبادلۂ_خیال_میڈیاویکی',
        NS_TEMPLATE         => 'سانچہ',
@@ -48,9 +48,12 @@ $namespaceNames = [
 ];
 
 $namespaceAliases = [
+       'وسیط'            => NS_MEDIA,
        'زریعہ'            => NS_MEDIA,
        'تصویر'            => NS_FILE,
        'تبادلۂ_خیال_تصویر'   => NS_FILE_TALK,
+       'ملف'            => NS_FILE,
+       'تبادلۂ_خیال_ملف'   => NS_FILE_TALK,
        'میڈیاوکی'          => NS_MEDIAWIKI,
        'تبادلۂ_خیال_میڈیاوکی' => NS_MEDIAWIKI_TALK,
 ];
@@ -62,7 +65,7 @@ $specialPageAliases = [
        'Ancientpages'              => [ 'قدیم_صفحات' ],
        'Badtitle'                  => [ 'خراب_عنوان' ],
        'Blankpage'                 => [ 'خالی_صفحہ' ],
-       'Block'                     => [ 'پابندی', 'آئی_پی_پتہ_پابندی،_پابندی_بر_صارف' ],
+       'Block'                     => [ 'پابندی', 'آئی_پی_پتہ_پابندی', 'پابندی_بر_صارف' ],
        'Booksources'               => [ 'کتابی_وسائل' ],
        'BrokenRedirects'           => [ 'شکستہ_رجوع_مکررات' ],
        'Categories'                => [ 'زمرہ_جات' ],
@@ -77,31 +80,31 @@ $specialPageAliases = [
        'DoubleRedirects'           => [ 'دوہرے_رجوع_مکررات' ],
        'EditWatchlist'             => [ 'ترمیم_زیر_نظر' ],
        'Emailuser'                 => [ 'صارف_ڈاک' ],
-       'Export'                    => [ 'برآمدگی' ],
+       'Export'                    => [ 'برآمد', 'برآمدگی' ],
        'Fewestrevisions'           => [ 'کم_نظر_ثانی_شدہ' ],
-       'FileDuplicateSearch'       => [ 'دہری_ملف_تلاش' ],
-       'Filepath'                  => [ 'راہ_ملف' ],
-       'Import'                    => [ 'درآمدگی' ],
+       'FileDuplicateSearch'       => [ 'تÙ\84اش_دÙ\88Û\81رÛ\8c\81ائÙ\84', 'دÛ\81رÛ\8c\85Ù\84Ù\81_تÙ\84اش' ],
+       'Filepath'                  => [ 'راÛ\81\81ائÙ\84', 'راÛ\81\85Ù\84Ù\81' ],
+       'Import'                    => [ 'درآمد', 'درآمدگی' ],
        'Invalidateemail'           => [ 'ڈاک_تصدیق_منسوخ' ],
        'JavaScriptTest'            => [ 'تجربہ_جاوا_اسکرپٹ' ],
        'BlockList'                 => [ 'فہرست_ممنوع', 'فہرست_دستور_شبکی_ممنوع' ],
        'LinkSearch'                => [ 'تلاش_روابط' ],
        'Listadmins'                => [ 'فہرست_منتظمین' ],
        'Listbots'                  => [ 'فہرست_روبہ_جات' ],
-       'Listfiles'                 => [ 'فہرست_املاف', 'فہرست_تصاویر' ],
+       'Listfiles'                 => [ 'فائلوں_کی_فہرست', 'فہرست_تصاویر' ],
        'Listgrouprights'           => [ 'فہرست_اختیارات_گروہ', 'صارفی_گروہ_اختیارات' ],
        'Listredirects'             => [ 'فہرست_رجوع_مکررات' ],
-       'Listusers'                 => [ 'فہرست_صارفین،_صارف_فہرست' ],
+       'Listusers'                 => [ 'فہرست_صارفین' ],
        'Log'                       => [ 'نوشتہ', 'نوشتہ_جات' ],
        'Lonelypages'               => [ 'یتیم_صفحات' ],
        'Longpages'                 => [ 'طویل_صفحات' ],
        'MergeHistory'              => [ 'ضم_تاریخچہ' ],
        'Movepage'                  => [ 'منتقلی_صفحہ' ],
-       'Mycontributions'           => [ 'میرا_حصہ' ],
+       'Mycontributions'           => [ 'میری_شراکتیں', 'میرا_حصہ' ],
        'Mypage'                    => [ 'میرا_صفحہ' ],
        'Mytalk'                    => [ 'میری_گفتگو' ],
-       'Myuploads'                 => [ 'میرے_زبراثقالات' ],
-       'Newimages'                 => [ 'جدید_املاف', 'جدید_تصاویر' ],
+       'Myuploads'                 => [ 'Ù\85Û\8cرÛ\92§Ù¾Ù\84Ù\88Ú\88', 'Ù\85Û\8cرÛ\92²Ø¨Ø±Ø§Ø«Ù\82اÙ\84ات' ],
+       'Newimages'                 => [ 'جدید_فائلیں', 'جدید_املاف', 'جدید_تصاویر' ],
        'Newpages'                  => [ 'جدید_صفحات' ],
        'PermanentLink'             => [ 'مستقل_ربط' ],
        'Preferences'               => [ 'ترجیحات' ],
@@ -112,33 +115,33 @@ $specialPageAliases = [
        'Randomredirect'            => [ 'تصادفی_رجوع_مکرر' ],
        'Recentchanges'             => [ 'حالیہ_تبدیلیاں' ],
        'Recentchangeslinked'       => [ 'متعلقہ_تبدیلیاں' ],
-       'Revisiondelete'            => [ 'حذف_اعادہ' ],
+       'Revisiondelete'            => [ 'حذف_نظر_ثانی', 'حذف_اعادہ' ],
        'Search'                    => [ 'تلاش' ],
        'Shortpages'                => [ 'مختصر_صفحات' ],
        'Specialpages'              => [ 'خصوصی_صفحات' ],
        'Statistics'                => [ 'شماریات' ],
-       'Tags'                      => [ 'ٹیگز' ],
+       'Tags'                      => [ 'ٹیگ', 'ٹیگز' ],
        'Unblock'                   => [ 'پابندی_ختم' ],
        'Uncategorizedcategories'   => [ 'غیر_زمرہ_بند_زمرہ_جات' ],
-       'Uncategorizedimages'       => [ 'غیر_زمرہ_بند_املاف', 'غیر_زمرہ_بند_تصاویر' ],
+       'Uncategorizedimages'       => [ 'غیر_زمرہ_بند_فائلیں', 'غیر_زمرہ_بند_املاف', 'غیر_زمرہ_بند_تصاویر' ],
        'Uncategorizedpages'        => [ 'غیر_زمرہ_بند_صفحات' ],
        'Uncategorizedtemplates'    => [ 'غیر_زمرہ_بند_سانچے' ],
        'Undelete'                  => [ 'بحال' ],
        'Unusedcategories'          => [ 'غیر_مستعمل_زمرہ_جات' ],
-       'Unusedimages'              => [ 'غیر_مستعمل_املاف', 'غیر_مستعمل_تصاویر' ],
+       'Unusedimages'              => [ 'غیر_مستعمل_فائلیں', 'غیر_مستعمل_املاف', 'غیر_مستعمل_تصاویر' ],
        'Unusedtemplates'           => [ 'غیر_مستعمل_سانچے' ],
        'Unwatchedpages'            => [ 'نادیدہ_صفحات' ],
-       'Upload'                    => [ 'زبراثقال' ],
+       'Upload'                    => [ 'اپÙ\84Ù\88Ú\88', 'زبراثÙ\82اÙ\84' ],
        'Userlogin'                 => [ 'داخل_نوشتگی' ],
        'Userlogout'                => [ 'خارج_نوشتگی' ],
        'Userrights'                => [ 'صارفی_اختیارات' ],
-       'Version'                   => [ 'اخراجہ' ],
+       'Version'                   => [ 'نسخہ', 'اخراجہ' ],
        'Wantedcategories'          => [ 'مطلوبہ_زمرہ_جات' ],
-       'Wantedfiles'               => [ 'مطلوبہ_املاف' ],
+       'Wantedfiles'               => [ 'مطلوبہ_فائلیں', 'مطلوبہ_املاف' ],
        'Wantedpages'               => [ 'مطلوبہ_صفحات', 'شکستہ_روابط' ],
        'Wantedtemplates'           => [ 'مطلوبہ_سانچے' ],
        'Watchlist'                 => [ 'زیر_نظر_فہرست' ],
-       'Whatlinkshere'             => [ 'یہاں_کس_کا_رابطہ' ],
+       'Whatlinkshere'             => [ 'مربوط_صفحات', 'یہاں_کس_کا_رابطہ' ],
        'Withoutinterwiki'          => [ 'بدون_بین_الویکی' ],
 ];
 
index ab316c0..7e0fb45 100644 (file)
@@ -37,6 +37,7 @@ define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
 $maintClass = false;
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Abstract maintenance class for quickly writing and churning out
@@ -108,7 +109,7 @@ abstract class Maintenance {
        private $mDb = null;
 
        /** @var float UNIX timestamp */
-       private $lastSlaveWait = 0.0;
+       private $lastReplicationWait = 0.0;
 
        /**
         * Used when creating separate schema files.
@@ -548,6 +549,57 @@ abstract class Maintenance {
 
        }
 
+       /**
+        * Set triggers like when to try to run deferred updates
+        * @since 1.28
+        */
+       public function setAgentAndTriggers() {
+               if ( function_exists( 'posix_getpwuid' ) ) {
+                       $agent = posix_getpwuid( posix_geteuid() )['name'];
+               } else {
+                       $agent = 'sysadmin';
+               }
+               $agent .= '@' . wfHostname();
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               // Add a comment for easy SHOW PROCESSLIST interpretation
+               $lbFactory->setAgentName(
+                       mb_strlen( $agent ) > 15 ? mb_substr( $agent, 0, 15 ) . '...' : $agent
+               );
+               self::setLBFactoryTriggers( $lbFactory );
+       }
+
+       /**
+        * @param LBFactory $LBFactory
+        * @since 1.28
+        */
+       public static function setLBFactoryTriggers( LBFactory $LBFactory ) {
+               // Hook into period lag checks which often happen in long-running scripts
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->setWaitForReplicationListener(
+                       __METHOD__,
+                       function () {
+                               global $wgCommandLineMode;
+                               // Check config in case of JobRunner and unit tests
+                               if ( $wgCommandLineMode ) {
+                                       DeferredUpdates::tryOpportunisticExecute( 'run' );
+                               }
+                       }
+               );
+               // Check for other windows to run them. A script may read or do a few writes
+               // to the master but mostly be writing to something else, like a file store.
+               $lbFactory->getMainLB()->setTransactionListener(
+                       __METHOD__,
+                       function ( $trigger ) {
+                               global $wgCommandLineMode;
+                               // Check config in case of JobRunner and unit tests
+                               if ( $wgCommandLineMode && $trigger === IDatabase::TRIGGER_COMMIT ) {
+                                       DeferredUpdates::tryOpportunisticExecute( 'run' );
+                               }
+                       }
+               );
+       }
+
        /**
         * Run a child maintenance script. Pass all of the current arguments
         * to it.
@@ -1050,7 +1102,7 @@ abstract class Maintenance {
                                $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
                                $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
                        }
-                       LBFactory::destroyInstance();
+                       MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
                }
 
                // Per-script profiling; useful for debugging
@@ -1186,7 +1238,7 @@ abstract class Maintenance {
         * If not set, wfGetDB() will be used.
         * This function has the same parameters as wfGetDB()
         *
-        * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+        * @param integer $db DB index (DB_REPLICA/DB_MASTER)
         * @param array $groups; default: empty array
         * @param string|bool $wiki; default: current wiki
         * @return IDatabase
@@ -1223,23 +1275,29 @@ abstract class Maintenance {
        }
 
        /**
-        * Commit the transcation on a DB handle and wait for slaves to catch up
+        * Commit the transcation on a DB handle and wait for replica DBs to catch up
         *
         * This method makes it clear that commit() is called from a maintenance script,
         * which has outermost scope. This is safe, unlike $dbw->commit() called in other places.
         *
         * @param IDatabase $dbw
         * @param string $fname Caller name
-        * @return bool Whether the slave wait succeeded
+        * @return bool Whether the replica DB wait succeeded
         * @since 1.27
         */
        protected function commitTransaction( IDatabase $dbw, $fname ) {
                $dbw->commit( $fname );
+               try {
+                       $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                       $lbFactory->waitForReplication(
+                               [ 'timeout' => 30, 'ifWritesSince' => $this->lastReplicationWait ]
+                       );
+                       $this->lastReplicationWait = microtime( true );
 
-               $ok = wfWaitForSlaves( $this->lastSlaveWait, false, '*', 30 );
-               $this->lastSlaveWait = microtime( true );
-
-               return $ok;
+                       return true;
+               } catch ( DBReplicationWaitError $e ) {
+                       return false;
+               }
        }
 
        /**
@@ -1441,6 +1499,14 @@ abstract class Maintenance {
 
                return fgets( STDIN, 1024 );
        }
+
+       /**
+        * Call this to set up the autoloader to allow classes to be used from the
+        * tests directory.
+        */
+       public static function requireTestsAutoloader() {
+               require_once __DIR__ . '/../tests/common/TestsAutoLoader.php';
+       }
 }
 
 /**
index 2555475..a348e85 100644 (file)
@@ -4,7 +4,7 @@ help:
        @echo "Run 'make man' to run the doxygen generation with man pages."
 
 test:
-       php tests/parserTests.php --quiet
+       php tests/parser/parserTests.php --quiet
 
 doc:
        php mwdocgen.php --all
diff --git a/maintenance/archives/patch-pl-tl-il-nonunique.sql b/maintenance/archives/patch-pl-tl-il-nonunique.sql
new file mode 100644 (file)
index 0000000..8e1715b
--- /dev/null
@@ -0,0 +1,11 @@
+-- Make reorderings of UNIQUE indices non-UNIQUE
+-- Since 1.24, these indices have been non-UNIQUE in tables.sql.
+-- However, an earlier update from 1.15 that made the indices
+-- UNIQUE was not removed until 1.28 (T78513).
+
+DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
+CREATE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
+DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
+CREATE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
+DROP INDEX /*i*/il_to ON /*_*/imagelinks;
+CREATE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
diff --git a/maintenance/archives/patch-pl-tl-il-unique.sql b/maintenance/archives/patch-pl-tl-il-unique.sql
deleted file mode 100644 (file)
index a356670..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---
--- patch-pl-tl-il-unique-index.sql
---
--- Make reorderings of UNIQUE indices UNIQUE as well
-
-DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
-CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
-DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
-CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
-DROP INDEX /*i*/il_to ON /*_*/imagelinks;
-CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
index 4947cb1..a39ac0a 100644 (file)
@@ -1,7 +1,7 @@
 -- Split user table into two parts:
 --   user
 --   user_rights
--- The later contains only the permissions of the user. This way,
+-- The latter contains only the permissions of the user. This way,
 -- you can store the accounts for several wikis in one central
 -- database but keep user rights local to the wiki.
 
index db3af92..38daf64 100644 (file)
@@ -302,7 +302,7 @@ class BackupDumper extends Maintenance {
 
                $dbr = $this->forcedDb;
                if ( $this->forcedDb === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                }
                $this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ );
                $this->startTime = microtime( true );
@@ -322,7 +322,7 @@ class BackupDumper extends Maintenance {
                }
 
                $this->lb = wfGetLBFactory()->newMainLB();
-               $db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+               $db = $this->lb->getConnection( DB_REPLICA, 'dump' );
 
                // Discourage the server from disconnecting us if it takes a long time
                // to read out the big ol' batch query.
index 0a89bfa..1753250 100644 (file)
@@ -24,6 +24,8 @@
 
 require __DIR__ . '/../Maintenance.php';
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * Maintenance script to benchmark how long it takes to parse a given title at an optionally
  * specified timestamp
@@ -34,6 +36,13 @@ class BenchmarkParse extends Maintenance {
        /** @var string MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) */
        private $templateTimestamp = null;
 
+       private $clearLinkCache = false;
+
+       /**
+        * @var LinkCache
+        */
+       private $linkCache;
+
        /** @var array Cache that maps a Title DB key to revision ID for the requested timestamp */
        private $idCache = [];
 
@@ -52,6 +61,8 @@ class BenchmarkParse extends Maintenance {
                        'Use templates which were current at the given time (except that moves and ' .
                        'deletes are not handled properly)',
                        false, true );
+               $this->addOption( 'reset-linkcache', 'Reset the LinkCache after every parse.',
+                       false, false );
        }
 
        function execute() {
@@ -60,6 +71,10 @@ class BenchmarkParse extends Maintenance {
                        Hooks::register( 'BeforeParserFetchTemplateAndtitle', [ $this, 'onFetchTemplate' ] );
                }
 
+               $this->clearLinkCache = $this->hasOption( 'reset-linkcache' );
+               // Set as a member variable to avoid function calls when we're timing the parse
+               $this->linkCache = MediaWikiServices::getInstance()->getLinkCache();
+
                $title = Title::newFromText( $this->getArg() );
                if ( !$title ) {
                        $this->error( "Invalid title" );
@@ -118,7 +133,7 @@ class BenchmarkParse extends Maintenance {
         * @return bool|string Revision ID, or false if not found or error
         */
        function getRevIdForTime( Title $title, $timestamp ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $id = $dbr->selectField(
                        [ 'revision', 'page' ],
@@ -144,6 +159,9 @@ class BenchmarkParse extends Maintenance {
        function runParser( Revision $revision ) {
                $content = $revision->getContent();
                $content->getParserOutput( $revision->getTitle(), $revision->getId() );
+               if ( $this->clearLinkCache ) {
+                       $this->linkCache->clear();
+               }
        }
 
        /**
index 097ad1f..6eafc96 100644 (file)
@@ -36,7 +36,7 @@ class CheckBadRedirects extends Maintenance {
 
        public function execute() {
                $this->output( "Fetching redirects...\n" );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select(
                        [ 'page' ],
                        [ 'page_namespace', 'page_title', 'page_latest' ],
index f05d15c..3e57393 100644 (file)
@@ -37,7 +37,7 @@ class CheckImages extends Maintenance {
 
        public function execute() {
                $start = '';
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $numImages = 0;
                $numGood = 0;
index eeec9d1..8416c8a 100644 (file)
@@ -40,7 +40,7 @@ class CheckLess extends Maintenance {
                // NOTE (phuedx, 2014-03-26) wgAutoloadClasses isn't set up
                // by either of the dependencies at the top of the file, so
                // require it here.
-               require_once __DIR__ . '/../tests/TestsAutoLoader.php';
+               self::requireTestsAutoloader();
 
                // If phpunit isn't available by autoloader try pulling it in
                if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
index 6c66da4..e6d9547 100644 (file)
@@ -40,7 +40,7 @@ class CheckUsernames extends Maintenance {
        }
 
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $maxUserId = 0;
                do {
index 2f3f013..4e47cfb 100644 (file)
@@ -64,7 +64,7 @@ class CleanupSpam extends Maintenance {
                        $this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
                        $found = false;
                        foreach ( $wgLocalDatabases as $wikiID ) {
-                               $dbr = $this->getDB( DB_SLAVE, [], $wikiID );
+                               $dbr = $this->getDB( DB_REPLICA, [], $wikiID );
 
                                $count = $dbr->selectField( 'externallinks', 'COUNT(*)',
                                        [ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
@@ -83,7 +83,7 @@ class CleanupSpam extends Maintenance {
                } else {
                        // Clean up spam on this wiki
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $res = $dbr->select( 'externallinks', [ 'DISTINCT el_from' ],
                                [ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
                        $count = $dbr->numRows( $res );
index a18b81e..3ace09c 100644 (file)
@@ -106,7 +106,7 @@ class TableCleanup extends Maintenance {
         * @throws MWException
         */
        public function runTable( $params ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                if ( array_diff( array_keys( $params ),
                        [ 'table', 'conds', 'index', 'callback' ] )
index 4f23ef0..650fae0 100644 (file)
@@ -78,7 +78,7 @@ class TitleCleanup extends TableCleanup {
        protected function fileExists( $name ) {
                // XXX: Doesn't actually check for file existence, just presence of image record.
                // This is reasonable, since cleanupImages.php only iterates over the image table.
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $row = $dbr->selectRow( 'image', [ 'img_name' ], [ 'img_name' => $name ], __METHOD__ );
 
                return $row !== false;
index 13b239f..ce19974 100644 (file)
@@ -37,7 +37,7 @@ class ClearInterwikiCache extends Maintenance {
 
        public function execute() {
                global $wgLocalDatabases, $wgMemc;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( 'interwiki', [ 'iw_prefix' ], false );
                $prefixes = [];
                foreach ( $res as $row ) {
index 245b613..8bd060f 100644 (file)
@@ -35,7 +35,7 @@ class CompareParserCache extends Maintenance {
        public function execute() {
                $pages = $this->getOption( 'maxpages' );
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $totalsec = 0.0;
                $scanned = 0;
index 35a7ca7..69f4f89 100644 (file)
@@ -41,7 +41,7 @@ class DeleteDefaultMessages extends Maintenance {
                global $wgUser;
 
                $this->output( "Checking existence of old default messages..." );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( [ 'page', 'revision' ],
                        [ 'page_namespace', 'page_title' ],
                        [
index 1272ca2..60b24a2 100644 (file)
@@ -102,6 +102,10 @@ $maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main'
 // Sanity-check required extensions are installed
 $maintenance->checkRequiredExtensions();
 
+// A good time when no DBs have writes pending is around lag checks.
+// This avoids having long running scripts just OOM and lose all the updates.
+$maintenance->setAgentAndTriggers();
+
 // Do the work
 $maintenance->execute();
 
@@ -117,4 +121,4 @@ wfLogProfilingData();
 // Commit and close up!
 $factory = wfGetLBFactory();
 $factory->commitMasterChanges( 'doMaintenance' );
-$factory->shutdown();
+$factory->shutdown( $factory::SHUTDOWN_NO_CHRONPROT );
index 1faeb8a..ff4e894 100644 (file)
@@ -44,7 +44,7 @@ class DumpLinks extends Maintenance {
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select( [ 'pagelinks', 'page' ],
                        [
                                'page_id',
index cfb59c6..d0bda4e 100644 (file)
@@ -222,7 +222,7 @@ TEXT
 
                // 2. The Connection, through the load balancer.
                try {
-                       $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+                       $this->db = $this->lb->getConnection( DB_REPLICA, 'dump' );
                } catch ( Exception $e ) {
                        throw new MWException( __METHOD__
                                . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
index 5b446d8..8d63fe5 100644 (file)
@@ -76,7 +76,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchUsed( $shared ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $image = $dbr->tableName( 'image' );
                $imagelinks = $dbr->tableName( 'imagelinks' );
 
@@ -97,7 +97,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchLocal( $shared ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select( 'image',
                        [ 'img_name' ],
                        '',
index 7e1b527..2ed1efa 100644 (file)
@@ -49,7 +49,7 @@ class FetchText extends Maintenance {
         * note that the text string itself is *not* followed by newline
         */
        public function execute() {
-               $db = $this->getDB( DB_SLAVE );
+               $db = $this->getDB( DB_REPLICA );
                $stdin = $this->getStdin();
                while ( !feof( $stdin ) ) {
                        $line = fgets( $stdin );
index 232151b..460b553 100644 (file)
@@ -47,7 +47,7 @@ class FixDefaultJsonContentPages extends LoggedUpdateMaintenance {
                        return true;
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $namespaces = [
                        NS_MEDIAWIKI => $dbr->buildLike( $dbr->anyString(), '.json' ),
                        NS_USER => $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString(), '.json' ),
index 0592d2d..1d6f31d 100644 (file)
@@ -54,7 +54,7 @@ class FixDoubleRedirects extends Maintenance {
                        $title = null;
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                // See also SpecialDoubleRedirects
                $tables = [
index f4674cb..37fd44f 100644 (file)
@@ -80,7 +80,7 @@ class FixUserRegistration extends Maintenance {
                                        $this->output( "Could not find registration for #$id NULL\n" );
                                }
                        }
-                       $this->output( "Waiting for slaves..." );
+                       $this->output( "Waiting for replica DBs..." );
                        wfWaitForSlaves();
                        $this->output( " done.\n" );
                } while ( $res->numRows() >= $this->mBatchSize );
index f5cc6f6..87af5b8 100644 (file)
@@ -113,7 +113,7 @@ class GenerateSitemap extends Maintenance {
        public $timestamp;
 
        /**
-        * A database slave object
+        * A database replica DB object
         *
         * @var object
         */
@@ -196,7 +196,7 @@ class GenerateSitemap extends Maintenance {
                $this->identifier = $this->getOption( 'identifier', wfWikiID() );
                $this->compress = $this->getOption( 'compress', 'yes' ) !== 'no';
                $this->skipRedirects = $this->getOption( 'skip-redirects', false ) !== false;
-               $this->dbr = $this->getDB( DB_SLAVE );
+               $this->dbr = $this->getDB( DB_REPLICA );
                $this->generateNamespaces();
                $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
                $this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' );
diff --git a/maintenance/getReplicaServer.php b/maintenance/getReplicaServer.php
new file mode 100644 (file)
index 0000000..6e0a1fe
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Reports the hostname of a replica DB server.
+ *
+ * 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 Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Maintenance script that reports the hostname of a replica DB server.
+ *
+ * @ingroup Maintenance
+ */
+class GetSlaveServer extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addOption( "group", "Query group to check specifically" );
+               $this->addDescription( 'Report the hostname of a replica DB server' );
+       }
+
+       public function execute() {
+               global $wgAllDBsAreLocalhost;
+               if ( $wgAllDBsAreLocalhost ) {
+                       $host = 'localhost';
+               } elseif ( $this->hasOption( 'group' ) ) {
+                       $db = $this->getDB( DB_REPLICA, $this->getOption( 'group' ) );
+                       $host = $db->getServer();
+               } else {
+                       $lb = wfGetLB();
+                       $i = $lb->getReaderIndex();
+                       $host = $lb->getServerName( $i );
+               }
+               $this->output( "$host\n" );
+       }
+}
+
+$maintClass = "GetSlaveServer";
+require_once RUN_MAINTENANCE_IF_MAIN;
index 81228cc..2160928 100644 (file)
@@ -1,55 +1,3 @@
 <?php
-/**
- * Reports the hostname of a slave server.
- *
- * 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 Maintenance
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script that reports the hostname of a slave server.
- *
- * @ingroup Maintenance
- */
-class GetSlaveServer extends Maintenance {
-       public function __construct() {
-               parent::__construct();
-               $this->addOption( "group", "Query group to check specifically" );
-               $this->addDescription( 'Report the hostname of a slave server' );
-       }
-
-       public function execute() {
-               global $wgAllDBsAreLocalhost;
-               if ( $wgAllDBsAreLocalhost ) {
-                       $host = 'localhost';
-               } elseif ( $this->hasOption( 'group' ) ) {
-                       $db = $this->getDB( DB_SLAVE, $this->getOption( 'group' ) );
-                       $host = $db->getServer();
-               } else {
-                       $lb = wfGetLB();
-                       $i = $lb->getReaderIndex();
-                       $host = $lb->getServerName( $i );
-               }
-               $this->output( "$host\n" );
-       }
-}
-
-$maintClass = "GetSlaveServer";
-require_once RUN_MAINTENANCE_IF_MAIN;
+// B/C alias
+require_once ( __DIR__ . '/getReplicaServer.php' );
index c653a5f..ac700ef 100644 (file)
@@ -297,8 +297,8 @@ if ( $count > 0 ) {
 
                        if ( $doProtect ) {
                                # Protect the file
-                               echo "\nWaiting for slaves...\n";
-                               // Wait for slaves.
+                               echo "\nWaiting for replica DBs...\n";
+                               // Wait for replica DBs.
                                sleep( 2.0 ); # Why this sleep?
                                wfWaitForSlaves();
 
index 50a4018..6b06da7 100644 (file)
@@ -29,8 +29,8 @@ class InitEditCount extends Maintenance {
                parent::__construct();
                $this->addOption( 'quick', 'Force the update to be done in a single query' );
                $this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but
-               avoids locking tables or lagging slaves with large updates;
-               calculates counts on a slave if possible.
+               avoids locking tables or lagging replica DBs with large updates;
+               calculates counts on a replica DB if possible.
 
 Background mode will be automatically used if multiple servers are listed
 in the load balancer, usually indicating a replication environment.' );
@@ -56,7 +56,7 @@ in the load balancer, usually indicating a replication environment.' );
                if ( $backgroundMode ) {
                        $this->output( "Using replication-friendly background mode...\n" );
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $chunkSize = 100;
                        $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ );
 
index 5ec6e34..31e02c2 100644 (file)
 一地里      一地裏
 一年里      一年裏
 中文里      中文裏
+英文里      英文裏
+古文里      古文裏
+经文里      經文裏
+论文里      論文裏
+譯文里      譯文裏
+原文里      原文裏
+正文里      正文裏
+下文里      下文裏
+条文里      條文裏
+画里 畫裏
 事里 事裏
 井里 井裏
 作品里      作品裏
 会里 會裏
 村里的      村裏的
 村里有      村裏有
+区里的      區裏的
+区里有      區裏有
 森林里      森林裏
 棺材里      棺材裏
 树林里      樹林裏
index 59219ae..075deab 100644 (file)
 划着独木舟        划著獨木舟
 着眼于      著眼於
 桃金娘      桃金孃
+粘膜 黏膜
 缺省 預設
 以太网      乙太網
 光盘 光碟
 撒切尔      柴契爾
 戴卓爾      柴契爾
 摩根士丹利        摩根史坦利
-拉普兰      拉布蘭
 戴克里先   戴克里先
 戈爾巴喬夫        戈巴契夫
 戈尔巴乔夫        戈巴契夫
index 8fb4198..3062c1e 100644 (file)
 冬冬鼓      鼕鼕鼓
 苧麻 苧麻
 张柏芝      張栢芝
+杜琪峰      杜琪峯
index 463c126..dd38a30 100644 (file)
 皺彆
 一彆頭
 并州
+幽并
 併力
 ,並力
 ,并力討
 扁擬穀盜蟲
 不穀
 辟穀
-米穀
-田穀
 脫穀機
 年穀
 礱穀
 月球曆表
 伊爾汗曆表
 延曆
+萬曆
+永曆
+聖人曆
+羅馬曆
+羅馬歷史
+羅馬歷代
+曆數書
+曆局
+授時曆
+顓頊曆
 共和歷史
 厤物之意
 爰定祥厤
 磨麵
 莜麵
 雲吞麵
+拌麵
+乾拌麵
 冷面相
 糞穢衊面
 僕僕
 松山庄
 香山庄
 中庄子
+新庄子
 田庄英雄
 本庄
 庄司
 鬼谷子
 谷子敬
 洪谷子
-西米谷
-世田谷
 聖馬爾谷日
 澀谷區
 開山闢谷
 鬥敗
 鬥戰
 窩裡鬥
+亂鬥
 石樑
 木樑
 藏歷史
 裡面
 這裡
 中文裡
+英文裡
+古文裡
+經文裡
+論文裡
+譯文裡
+原文裡
+正文裡
+下文裡
+條文裡
+畫裡
 洞裡
 洞里薩
 界裡
 村裡的
 村裡有
 鎮裡
+區裡的
+區裡有
+實驗裡
+註裡
 裏白 #植物常用名
 烏蘇里 #分詞用
 首發
 涂醒哲
 涂善妮
 涂敏恆
+涂爾幹
 故云
 強制作用
 鬱南
 卜云吉
 黎吉雲
 代表
°´ç\84¡æ\80\9cå¥\88
+怜奈
 于冠華
 于雲鶴
 于忠肅集
 不太準
 非常準
 很準
-萬曆
-永曆
 囓蟲
 勳勞
 勳績
 煙臺
 太醜
 御製
-聖人曆
 電影後
 封為后
 皮托管
 白面包青天
 天神之后
-羅馬曆
-羅馬歷史
-羅馬歷代
-曆數書
-曆局
 你誇
 誇你
 誇我
 控制
 限制
 強制
+改制成
+考試制度
 體徵
 綜合徵
 价川
 琺瑯
 菜餚
 梁啓超
-改制成
 王添灯
 腌臢
 風颳
index d6a9ba8..506bc9c 100644 (file)
@@ -607,8 +607,8 @@ class NamespaceConflictChecker extends Maintenance {
                 * accidentally introduce an assumption of title validity to the code we
                 * are calling.
                 */
-               $updates = [ new LinksDeletionUpdate( $wikiPage ) ];
-               DataUpdate::runUpdates( $updates );
+               DeferredUpdates::addUpdate( new LinksDeletionUpdate( $wikiPage ) );
+               DeferredUpdates::doUpdates();
 
                return true;
        }
index 942f3f2..7557a42 100644 (file)
@@ -91,7 +91,7 @@ class PopulateFilearchiveSha1 extends LoggedUpdateMaintenance {
                                break;
                        }
 
-                       // print status and let slaves catch up
+                       // print status and let replica DBs catch up
                        $this->output( sprintf(
                                "id %d done (up to %d), %5.3f%%  \r", $lastId, $endId, $lastId / $endId * 100 ) );
                        wfWaitForSlaves();
index dcb9933..5e44faf 100644 (file)
@@ -73,7 +73,7 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
         * @return int
         */
        protected function doLenUpdates( $table, $idCol, $prefix, $fields ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
                $start = $dbw->selectField( $table, "MIN($idCol)", false, __METHOD__ );
                $end = $dbw->selectField( $table, "MAX($idCol)", false, __METHOD__ );
index 70a26cb..615d1fe 100644 (file)
@@ -106,7 +106,7 @@ class PurgeChangedFiles extends Maintenance {
                }
 
                // Validate the timestamps
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $this->startTimestamp = $dbr->timestamp( $this->getOption( 'starttime' ) );
                $this->endTimestamp = $dbr->timestamp( $this->getOption( 'endtime' ) );
 
@@ -137,7 +137,7 @@ class PurgeChangedFiles extends Maintenance {
         */
        protected function purgeFromLogType( $type ) {
                $repo = RepoGroup::singleton()->getLocalRepo();
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                foreach ( self::$typeMappings[$type] as $logType => $logActions ) {
                        $this->verbose( "Scanning for {$logType}/" . implode( ',', $logActions ) . "\n" );
index 58a4640..b354399 100644 (file)
@@ -65,7 +65,7 @@ class PurgeChangedPages extends Maintenance {
                        }
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $minTime = $dbr->timestamp( $this->getOption( 'starttime' ) );
                $maxTime = $dbr->timestamp( $this->getOption( 'endtime' ) );
 
index 21d1169..5ca7918 100644 (file)
@@ -86,7 +86,7 @@ class PurgeList extends Maintenance {
         * @param int|bool $namespace
         */
        private function purgeNamespace( $namespace = false ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $startId = 0;
                if ( $namespace === false ) {
                        $conds = [];
index 38556ed..b278e98 100644 (file)
@@ -70,14 +70,14 @@ class RebuildFileCache extends Maintenance {
 
                $this->output( "Building content page file cache from page {$start}!\n" );
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $overwrite = $this->getOption( 'overwrite', false );
                $start = ( $start > 0 )
                        ? $start
-                       : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ );
+                       : $dbr->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
                $end = ( $end > 0 )
                        ? $end
-                       : $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ );
+                       : $dbr->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
                if ( !$start ) {
                        $this->error( "Nothing to do.", true );
                }
@@ -93,9 +93,11 @@ class RebuildFileCache extends Maintenance {
                // Go through each page and save the output
                while ( $blockEnd <= $end ) {
                        // Get the pages
-                       $res = $dbr->select( 'page', [ 'page_namespace', 'page_title', 'page_id' ],
+                       $res = $dbr->select( 'page',
+                               [ 'page_namespace', 'page_title', 'page_id' ],
                                [ 'page_namespace' => MWNamespace::getContentNamespaces(),
                                        "page_id BETWEEN $blockStart AND $blockEnd" ],
+                               __METHOD__,
                                [ 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ]
                        );
 
@@ -119,7 +121,7 @@ class RebuildFileCache extends Maintenance {
 
                                // If the article is cacheable, then load it
                                if ( $article->isFileCacheable() ) {
-                                       $cache = HTMLFileCache::newFromTitle( $title, 'view' );
+                                       $cache = new HTMLFileCache( $title, 'view' );
                                        if ( $cache->isCacheGood() ) {
                                                if ( $overwrite ) {
                                                        $rebuilt = true;
index f67739b..3157186 100644 (file)
@@ -127,7 +127,7 @@ class ImageBuilder extends Maintenance {
                $this->init( $count, $table );
                $this->output( "Processing $table...\n" );
 
-               $result = $this->getDB( DB_SLAVE )->select( $table, '*', [], __METHOD__ );
+               $result = $this->getDB( DB_REPLICA )->select( $table, '*', [], __METHOD__ );
 
                foreach ( $result as $row ) {
                        $update = call_user_func( $callback, $row, null );
index d2ee6fc..95822ca 100644 (file)
@@ -41,7 +41,7 @@ class RebuildAll extends Maintenance {
 
        public function execute() {
                // Rebuild the text index
-               if ( $this->getDB( DB_SLAVE )->getType() != 'postgres' ) {
+               if ( $this->getDB( DB_REPLICA )->getType() != 'postgres' ) {
                        $this->output( "** Rebuilding fulltext search index (if you abort "
                                . "this will break searching; run this script again to fix):\n" );
                        $rebuildText = $this->runChild( 'RebuildTextIndex', 'rebuildtextindex.php' );
index ea5b6f9..e075501 100644 (file)
@@ -47,7 +47,7 @@ class RefreshFileHeaders extends Maintenance {
                $end = str_replace( ' ', '_', $this->getOption( 'end', '' ) ); // page on img_name
 
                $count = 0;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                do {
                        $conds = [ "img_name > {$dbr->addQuotes( $start )}" ];
                        if ( strlen( $end ) ) {
index e91a3d3..106be1f 100644 (file)
@@ -29,6 +29,9 @@ require_once __DIR__ . '/Maintenance.php';
  * @ingroup Maintenance
  */
 class RefreshLinks extends Maintenance {
+       /** @var int|bool */
+       protected $namespace = false;
+
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Refresh link tables' );
@@ -39,6 +42,7 @@ class RefreshLinks extends Maintenance {
                $this->addOption( 'e', 'Last page id to refresh', false, true );
                $this->addOption( 'dfn-chunk-size', 'Maximum number of existent IDs to check per ' .
                        'query, default 100000', false, true );
+               $this->addOption( 'namespace', 'Only fix pages in this namespace', false, true );
                $this->addArg( 'start', 'Page_id to start from, default 1', false );
                $this->setBatchSize( 100 );
        }
@@ -51,6 +55,12 @@ class RefreshLinks extends Maintenance {
                $start = (int)$this->getArg( 0 ) ?: null;
                $end = (int)$this->getOption( 'e' ) ?: null;
                $dfnChunkSize = (int)$this->getOption( 'dfn-chunk-size', 100000 );
+               $ns = $this->getOption( 'namespace' );
+               if ( $ns === null ) {
+                       $this->namespace = false;
+               } else {
+                       $this->namespace = (int)$ns;
+               }
                if ( !$this->hasOption( 'dfn-only' ) ) {
                        $new = $this->getOption( 'new-only', false );
                        $redir = $this->getOption( 'redirects-only', false );
@@ -62,6 +72,12 @@ class RefreshLinks extends Maintenance {
                }
        }
 
+       private function namespaceCond() {
+               return $this->namespace !== false
+                       ? [ 'page_namespace' => $this->namespace ]
+                       : [];
+       }
+
        /**
         * Do the actual link refreshing.
         * @param int|null $start Page_id to start from
@@ -74,7 +90,7 @@ class RefreshLinks extends Maintenance {
                $end = null, $redirectsOnly = false, $oldRedirectsOnly = false
        ) {
                $reportingInterval = 100;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                if ( $start === null ) {
                        $start = 1;
@@ -92,7 +108,7 @@ class RefreshLinks extends Maintenance {
                                "page_is_redirect=1",
                                "rd_from IS NULL",
                                self::intervalCond( $dbr, 'page_id', $start, $end ),
-                       ];
+                       ] + $this->namespaceCond();
 
                        $res = $dbr->select(
                                [ 'page', 'redirect' ],
@@ -121,7 +137,7 @@ class RefreshLinks extends Maintenance {
                                [
                                        'page_is_new' => 1,
                                        self::intervalCond( $dbr, 'page_id', $start, $end ),
-                               ],
+                               ] + $this->namespaceCond(),
                                __METHOD__
                        );
                        $num = $res->numRows();
@@ -136,7 +152,7 @@ class RefreshLinks extends Maintenance {
                                if ( $redirectsOnly ) {
                                        $this->fixRedirect( $row->page_id );
                                } else {
-                                       self::fixLinksFromArticle( $row->page_id );
+                                       self::fixLinksFromArticle( $row->page_id, $this->namespace );
                                }
                        }
                } else {
@@ -167,7 +183,7 @@ class RefreshLinks extends Maintenance {
                                                $this->output( "$id\n" );
                                                wfWaitForSlaves();
                                        }
-                                       self::fixLinksFromArticle( $id );
+                                       self::fixLinksFromArticle( $id, $this->namespace );
                                }
                        }
                }
@@ -195,6 +211,10 @@ class RefreshLinks extends Maintenance {
                        $dbw->delete( 'redirect', [ 'rd_from' => $id ],
                                __METHOD__ );
 
+                       return;
+               } elseif ( $this->namespace !== false
+                       && !$page->getTitle()->inNamespace( $this->namespace )
+               ) {
                        return;
                }
 
@@ -222,14 +242,18 @@ class RefreshLinks extends Maintenance {
        /**
         * Run LinksUpdate for all links on a given page_id
         * @param int $id The page_id
+        * @param int|bool $ns Only fix links if it is in this namespace
         */
-       public static function fixLinksFromArticle( $id ) {
+       public static function fixLinksFromArticle( $id, $ns = false ) {
                $page = WikiPage::newFromID( $id );
 
                LinkCache::singleton()->clear();
 
                if ( $page === null ) {
                        return;
+               } elseif ( $ns !== false
+                       && !$page->getTitle()->inNamespace( $ns ) ) {
+                       return;
                }
 
                $content = $page->getContent( Revision::RAW );
@@ -237,8 +261,9 @@ class RefreshLinks extends Maintenance {
                        return;
                }
 
-               $updates = $content->getSecondaryDataUpdates( $page->getTitle() );
-               DataUpdate::runUpdates( $updates );
+               foreach ( $content->getSecondaryDataUpdates( $page->getTitle() ) as $update ) {
+                       DeferredUpdates::addUpdate( $update );
+               }
        }
 
        /**
@@ -257,14 +282,15 @@ class RefreshLinks extends Maintenance {
        ) {
                wfWaitForSlaves();
                $this->output( "Deleting illegal entries from the links tables...\n" );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                do {
                        // Find the start of the next chunk. This is based only
                        // on existent page_ids.
                        $nextStart = $dbr->selectField(
                                'page',
                                'page_id',
-                               self::intervalCond( $dbr, 'page_id', $start, $end ),
+                               [ self::intervalCond( $dbr, 'page_id', $start, $end ) ]
+                               + $this->namespaceCond(),
                                __METHOD__,
                                [ 'ORDER BY' => 'page_id', 'OFFSET' => $chunkSize ]
                        );
@@ -298,7 +324,7 @@ class RefreshLinks extends Maintenance {
         */
        private function dfnCheckInterval( $start = null, $end = null, $batchSize = 100 ) {
                $dbw = $this->getDB( DB_MASTER );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $linksTables = [ // table name => page_id field
                        'pagelinks' => 'pl_from',
index 0f67317..1034005 100644 (file)
@@ -23,7 +23,7 @@ class RemoveInvalidEmails extends Maintenance {
        }
        public function execute() {
                $this->commit = $this->hasOption( 'commit' );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
                $lastId = 0;
                do {
index e3c99ed..ec8fcfe 100644 (file)
@@ -45,7 +45,7 @@ class RemoveUnusedAccounts extends Maintenance {
                # Do an initial scan for inactive accounts and report the result
                $this->output( "Checking for unused user accounts...\n" );
                $del = [];
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
                if ( $this->hasOption( 'ignore-groups' ) ) {
                        $excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
@@ -107,7 +107,7 @@ class RemoveUnusedAccounts extends Maintenance {
         * @return bool
         */
        private function isInactiveAccount( $id, $master = false ) {
-               $dbo = $this->getDB( $master ? DB_MASTER : DB_SLAVE );
+               $dbo = $this->getDB( $master ? DB_MASTER : DB_REPLICA );
                $checks = [
                        'revision' => 'rev',
                        'archive' => 'ar',
index 131a569..481da98 100644 (file)
@@ -67,8 +67,9 @@ class ResetUserTokens extends Maintenance {
                        wfCountDown( 5 );
                }
 
+               // We list user by user_id from one of the replica DBs
                // We list user by user_id from one of the slave database
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $where = [];
                if ( $this->nullsOnly ) {
index 10c107e..5ad7d4e 100644 (file)
@@ -95,7 +95,7 @@ class RollbackEdits extends Maintenance {
         * @return array
         */
        private function getRollbackTitles( $user ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $titles = [];
                $results = $dbr->select(
                        [ 'page', 'revision' ],
index 2feae02..a5e7a2f 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Run a database query in batches and wait for slaves. This is used on large
+ * Run a database query in batches and wait for replica DBs. This is used on large
  * wikis to prevent replication lag from going through the roof when executing
  * large write queries.
  *
@@ -26,7 +26,7 @@
 require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Maintenance script to run a database query in batches and wait for slaves.
+ * Maintenance script to run a database query in batches and wait for replica DBs.
  *
  * @ingroup Maintenance
  */
@@ -34,7 +34,7 @@ class BatchedQueryRunner extends Maintenance {
        public function __construct() {
                parent::__construct();
                $this->addDescription(
-                       "Run a query repeatedly until it affects 0 rows, and wait for slaves in between.\n" .
+                       "Run a query repeatedly until it affects 0 rows, and wait for replica DBs in between.\n" .
                                "NOTE: You need to set a LIMIT clause yourself." );
        }
 
index c3c2391..2e011fe 100644 (file)
@@ -53,8 +53,6 @@ class RunJobs extends Maintenance {
        }
 
        public function execute() {
-               global $wgCommandLineMode;
-
                if ( $this->hasOption( 'procs' ) ) {
                        $procs = intval( $this->getOption( 'procs' ) );
                        if ( $procs < 1 || $procs > 1000 ) {
@@ -70,10 +68,6 @@ class RunJobs extends Maintenance {
                $outputJSON = ( $this->getOption( 'result' ) === 'json' );
                $wait = $this->hasOption( 'wait' );
 
-               // Enable DBO_TRX for atomicity; JobRunner manages transactions
-               // and works well in web server mode already (@TODO: this is a hack)
-               $wgCommandLineMode = false;
-
                $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
                if ( !$outputJSON ) {
                        $runner->setDebugHandler( [ $this, 'debugInternal' ] );
@@ -111,8 +105,6 @@ class RunJobs extends Maintenance {
 
                        sleep( 1 );
                }
-
-               $wgCommandLineMode = true;
        }
 
        /**
index 1d3e43a..5a15165 100644 (file)
@@ -52,8 +52,8 @@ class ShowSiteStats extends Maintenance {
                        'ss_images' => 'Number of images',
                ];
 
-               // Get cached stats from slave database
-               $dbr = $this->getDB( DB_SLAVE );
+               // Get cached stats from a replica DB
+               $dbr = $this->getDB( DB_REPLICA );
                $stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
 
                // Get maximum size for each column
index 1aceace..e6a30a3 100644 (file)
@@ -34,10 +34,13 @@ class MwSql extends Maintenance {
                parent::__construct();
                $this->addDescription( 'Send SQL queries to a MediaWiki database. ' .
                        'Takes a file name containing SQL as argument or runs interactively.' );
-               $this->addOption( 'query', 'Run a single query instead of running interactively', false, true );
+               $this->addOption( 'query',
+                       'Run a single query instead of running interactively', false, true );
                $this->addOption( 'cluster', 'Use an external cluster by name', false, true );
-               $this->addOption( 'wikidb', 'The database wiki ID to use if not the current one', false, true );
-               $this->addOption( 'slave', 'Use a slave server (either "any" or by name)', false, true );
+               $this->addOption( 'wikidb',
+                       'The database wiki ID to use if not the current one', false, true );
+               $this->addOption( 'replicadb',
+                       'Replica DB server to use instead of the master DB (can be "any")', false, true );
        }
 
        public function execute() {
@@ -50,30 +53,28 @@ class MwSql extends Maintenance {
                        $lb = wfGetLB( $wiki );
                }
                // Figure out which server to use
-               if ( $this->hasOption( 'slave' ) ) {
-                       $server = $this->getOption( 'slave' );
-                       if ( $server === 'any' ) {
-                               $index = DB_SLAVE;
-                       } else {
-                               $index = null;
-                               $serverCount = $lb->getServerCount();
-                               for ( $i = 0; $i < $serverCount; ++$i ) {
-                                       if ( $lb->getServerName( $i ) === $server ) {
-                                               $index = $i;
-                                               break;
-                                       }
-                               }
-                               if ( $index === null ) {
-                                       $this->error( "No slave server configured with the name '$server'.", 1 );
+               $replicaDB = $this->getOption( 'replicadb', $this->getOption( 'slave', '' ) );
+               if ( $replicaDB === 'any' ) {
+                       $index = DB_REPLICA;
+               } elseif ( $replicaDB != '' ) {
+                       $index = null;
+                       $serverCount = $lb->getServerCount();
+                       for ( $i = 0; $i < $serverCount; ++$i ) {
+                               if ( $lb->getServerName( $i ) === $replicaDB ) {
+                                       $index = $i;
+                                       break;
                                }
                        }
+                       if ( $index === null ) {
+                               $this->error( "No replica DB server configured with the name '$server'.", 1 );
+                       }
                } else {
                        $index = DB_MASTER;
                }
                // Get a DB handle (with this wiki's DB selected) from the appropriate load balancer
                $db = $lb->getConnection( $index, [], $wiki );
-               if ( $this->hasOption( 'slave' ) && $db->getLBInfo( 'master' ) !== null ) {
-                       $this->error( "The server selected ({$db->getServer()}) is not a slave.", 1 );
+               if ( $replicaDB != '' && $db->getLBInfo( 'master' ) !== null ) {
+                       $this->error( "The server selected ({$db->getServer()}) is not a replica DB.", 1 );
                }
 
                if ( $this->hasArg( 0 ) ) {
index d69e5b9..bb9f4ea 100644 (file)
@@ -57,7 +57,7 @@ class CheckStorage {
        ];
 
        function check( $fix = false, $xml = '' ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $fix ) {
                        print "Checking, will fix errors if possible...\n";
                } else {
@@ -462,7 +462,7 @@ class CheckStorage {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $dbw = wfGetDB( DB_MASTER );
                $dbr->ping();
                $dbw->ping();
@@ -507,7 +507,7 @@ class CheckStorage {
                }
 
                // Find text row again
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
                if ( !$oldId ) {
                        echo "Missing revision row for rev_id $id\n";
index ba924bf..289d22d 100644 (file)
@@ -237,7 +237,7 @@ class CompressOld extends Maintenance {
        ) {
                $loadStyle = self::LS_CHUNKED;
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
 
                # Set up external storage
index 39e06a3..437bfcd 100644 (file)
@@ -36,7 +36,7 @@ class DumpRev extends Maintenance {
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $row = $dbr->selectRow(
                        [ 'text', 'revision' ],
                        [ 'old_flags', 'old_text' ],
index 94335cf..b444f31 100644 (file)
@@ -42,7 +42,7 @@ class FixBug20757 extends Maintenance {
        }
 
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
 
                $dryRun = $this->getOption( 'dry-run' );
@@ -281,7 +281,7 @@ class FixBug20757 extends Maintenance {
                                unset( $this->mapCache[$key] );
                        }
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $map = [];
                        $res = $dbr->select( 'revision',
                                [ 'rev_id', 'rev_text_id' ],
index 80dc7f9..e117992 100644 (file)
@@ -51,7 +51,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 function moveToExternal( $cluster, $maxID, $minID = 1 ) {
        $fname = 'moveToExternal';
        $dbw = wfGetDB( DB_MASTER );
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
 
        $count = $maxID - $minID + 1;
        $blockSize = 1000;
index d775830..d7d0b84 100644 (file)
@@ -39,11 +39,11 @@ class OrphanStats extends Maintenance {
        protected function &getDB( $cluster, $groups = [], $wiki = false ) {
                $lb = wfGetLBFactory()->getExternalLB( $cluster );
 
-               return $lb->getConnection( DB_SLAVE );
+               return $lb->getConnection( DB_REPLICA );
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                if ( !$dbr->tableExists( 'blob_orphans' ) ) {
                        $this->error( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first", true );
                }
index 292a25d..a0efcb8 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\Logger\LegacyLogger;
+use MediaWiki\MediaWikiServices;
 
 $optionsWithArgs = RecompressTracked::getOptionsWithArgs();
 require __DIR__ . '/../commandLine.inc';
@@ -60,17 +61,17 @@ class RecompressTracked {
        public $numProcs = 1;
        public $numBatches = 0;
        public $pageBlobClass, $orphanBlobClass;
-       public $slavePipes, $slaveProcs, $prevSlaveId;
+       public $replicaPipes, $replicaProcs, $prevReplicaId;
        public $copyOnly = false;
        public $isChild = false;
-       public $slaveId = false;
+       public $replicaId = false;
        public $noCount = false;
        public $debugLog, $infoLog, $criticalLog;
        public $store;
 
        private static $optionsWithArgs = [
                'procs',
-               'slave-id',
+               'replica-id',
                'debug-log',
                'info-log',
                'critical-log'
@@ -81,7 +82,7 @@ class RecompressTracked {
                'procs' => 'numProcs',
                'copy-only' => 'copyOnly',
                'child' => 'isChild',
-               'slave-id' => 'slaveId',
+               'replica-id' => 'replicaId',
                'debug-log' => 'debugLog',
                'info-log' => 'infoLog',
                'critical-log' => 'criticalLog',
@@ -109,8 +110,8 @@ class RecompressTracked {
                $this->store = new ExternalStoreDB;
                if ( !$this->isChild ) {
                        $GLOBALS['wgDebugLogPrefix'] = "RCT M: ";
-               } elseif ( $this->slaveId !== false ) {
-                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->slaveId}: ";
+               } elseif ( $this->replicaId !== false ) {
+                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->replicaId}: ";
                }
                $this->pageBlobClass = function_exists( 'xdiff_string_bdiff' ) ?
                        'DiffHistoryBlob' : 'ConcatenatedGzipHistoryBlob';
@@ -140,21 +141,21 @@ class RecompressTracked {
 
        function logToFile( $msg, $file ) {
                $header = '[' . date( 'd\TH:i:s' ) . '] ' . wfHostname() . ' ' . posix_getpid();
-               if ( $this->slaveId !== false ) {
-                       $header .= "({$this->slaveId})";
+               if ( $this->replicaId !== false ) {
+                       $header .= "({$this->replicaId})";
                }
                $header .= ' ' . wfWikiID();
                LegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file );
        }
 
        /**
-        * Wait until the selected slave has caught up to the master.
-        * This allows us to use the slave for things that were committed in a
+        * Wait until the selected replica DB has caught up to the master.
+        * This allows us to use the replica DB for things that were committed in a
         * previous part of this batch process.
         */
        function syncDBs() {
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $pos = $dbw->getMasterPos();
                $dbr->masterPosWait( $pos, 100000 );
        }
@@ -179,10 +180,10 @@ class RecompressTracked {
                }
 
                $this->syncDBs();
-               $this->startSlaveProcs();
+               $this->startReplicaProcs();
                $this->doAllPages();
                $this->doAllOrphans();
-               $this->killSlaveProcs();
+               $this->killReplicaProcs();
        }
 
        /**
@@ -190,7 +191,7 @@ class RecompressTracked {
         * @return bool
         */
        function checkTrackingTable() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( !$dbr->tableExists( 'blob_tracking' ) ) {
                        $this->critical( "Error: blob_tracking table does not exist" );
 
@@ -212,10 +213,10 @@ class RecompressTracked {
         * This necessary because text recompression is slow: loading, compressing and
         * writing are all slow.
         */
-       function startSlaveProcs() {
+       function startReplicaProcs() {
                $cmd = 'php ' . wfEscapeShellArg( __FILE__ );
                foreach ( self::$cmdLineOptionMap as $cmdOption => $classOption ) {
-                       if ( $cmdOption == 'slave-id' ) {
+                       if ( $cmdOption == 'replica-id' ) {
                                continue;
                        } elseif ( in_array( $cmdOption, self::$optionsWithArgs ) && isset( $this->$classOption ) ) {
                                $cmd .= " --$cmdOption " . wfEscapeShellArg( $this->$classOption );
@@ -227,7 +228,7 @@ class RecompressTracked {
                        ' --wiki ' . wfEscapeShellArg( wfWikiID() ) .
                        ' ' . call_user_func_array( 'wfEscapeShellArg', $this->destClusters );
 
-               $this->slavePipes = $this->slaveProcs = [];
+               $this->replicaPipes = $this->replicaProcs = [];
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
                        $pipes = [];
                        $spec = [
@@ -236,28 +237,28 @@ class RecompressTracked {
                                [ 'file', 'php://stderr', 'w' ]
                        ];
                        MediaWiki\suppressWarnings();
-                       $proc = proc_open( "$cmd --slave-id $i", $spec, $pipes );
+                       $proc = proc_open( "$cmd --replica-id $i", $spec, $pipes );
                        MediaWiki\restoreWarnings();
                        if ( !$proc ) {
-                               $this->critical( "Error opening slave process: $cmd" );
+                               $this->critical( "Error opening replica DB process: $cmd" );
                                exit( 1 );
                        }
-                       $this->slaveProcs[$i] = $proc;
-                       $this->slavePipes[$i] = $pipes[0];
+                       $this->replicaProcs[$i] = $proc;
+                       $this->replicaPipes[$i] = $pipes[0];
                }
-               $this->prevSlaveId = -1;
+               $this->prevReplicaId = -1;
        }
 
        /**
         * Gracefully terminate the child processes
         */
-       function killSlaveProcs() {
-               $this->info( "Waiting for slave processes to finish..." );
+       function killReplicaProcs() {
+               $this->info( "Waiting for replica DB processes to finish..." );
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $this->dispatchToSlave( $i, 'quit' );
+                       $this->dispatchToReplica( $i, 'quit' );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $status = proc_close( $this->slaveProcs[$i] );
+                       $status = proc_close( $this->replicaProcs[$i] );
                        if ( $status ) {
                                $this->critical( "Warning: child #$i exited with status $status" );
                        }
@@ -266,22 +267,22 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to the next available slave.
-        * This may block until a slave finishes its work and becomes available.
+        * 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();
-               $pipes = $this->slavePipes;
+               $pipes = $this->replicaPipes;
                $numPipes = stream_select( $x = [], $pipes, $y = [], 3600 );
                if ( !$numPipes ) {
-                       $this->critical( "Error waiting to write to slaves. Aborting" );
+                       $this->critical( "Error waiting to write to replica DBs. Aborting" );
                        exit( 1 );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $slaveId = ( $i + $this->prevSlaveId + 1 ) % $this->numProcs;
-                       if ( isset( $pipes[$slaveId] ) ) {
-                               $this->prevSlaveId = $slaveId;
-                               $this->dispatchToSlave( $slaveId, $args );
+                       $replicaId = ( $i + $this->prevReplicaId + 1 ) % $this->numProcs;
+                       if ( isset( $pipes[$replicaId] ) ) {
+                               $this->prevReplicaId = $replicaId;
+                               $this->dispatchToReplica( $replicaId, $args );
 
                                return;
                        }
@@ -291,21 +292,21 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to a specified slave
-        * @param int $slaveId
+        * Dispatch a command to a specified replica DB
+        * @param int $replicaId
         * @param array|string $args
         */
-       function dispatchToSlave( $slaveId, $args ) {
+       function dispatchToReplica( $replicaId, $args ) {
                $args = (array)$args;
                $cmd = implode( ' ', $args );
-               fwrite( $this->slavePipes[$slaveId], "$cmd\n" );
+               fwrite( $this->replicaPipes[$replicaId], "$cmd\n" );
        }
 
        /**
         * Move all tracked pages to the new clusters
         */
        function doAllPages() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $i = 0;
                $startId = 0;
                if ( $this->noCount ) {
@@ -366,7 +367,7 @@ class RecompressTracked {
                if ( $current == $end || $this->numBatches >= $this->reportingInterval ) {
                        $this->numBatches = 0;
                        $this->info( "$label: $current / $end" );
-                       wfWaitForSlaves();
+                       MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
                }
        }
 
@@ -374,7 +375,7 @@ class RecompressTracked {
         * Move all orphan text to the new clusters
         */
        function doAllOrphans() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $startId = 0;
                $i = 0;
                if ( $this->noCount ) {
@@ -464,7 +465,7 @@ class RecompressTracked {
                                case 'quit':
                                        return;
                        }
-                       wfWaitForSlaves();
+                       MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
                }
        }
 
@@ -480,7 +481,7 @@ class RecompressTracked {
                } else {
                        $titleText = '[deleted]';
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Finish any incomplete transactions
                if ( !$this->copyOnly ) {
@@ -491,6 +492,7 @@ class RecompressTracked {
                $startId = 0;
                $trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
 
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                while ( true ) {
                        $res = $dbr->select(
                                [ 'blob_tracking', 'text' ],
@@ -532,7 +534,7 @@ class RecompressTracked {
                                        $this->debug( "$titleText: committing blob with " . $trx->getSize() . " items" );
                                        $trx->commit();
                                        $trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
-                                       wfWaitForSlaves();
+                                       $lbFactory->waitForReplication();
                                }
                        }
                }
@@ -590,7 +592,8 @@ class RecompressTracked {
         * @param array $conds
         */
        function finishIncompleteMoves( $conds ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
 
                $startId = 0;
                $conds = array_merge( $conds, [
@@ -615,7 +618,7 @@ class RecompressTracked {
                                $startId = $row->bt_text_id;
                                $this->moveTextRow( $row->bt_text_id, $row->bt_new_url );
                                if ( $row->bt_text_id % 10 == 0 ) {
-                                       wfWaitForSlaves();
+                                       $lbFactory->waitForReplication();
                                }
                        }
                }
@@ -659,7 +662,8 @@ class RecompressTracked {
 
                $trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
 
-               $res = wfGetDB( DB_SLAVE )->select(
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $res = wfGetDB( DB_REPLICA )->select(
                        [ 'text', 'blob_tracking' ],
                        [ 'old_id', 'old_text', 'old_flags' ],
                        [
@@ -682,7 +686,7 @@ class RecompressTracked {
                                $this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
                                $trx->commit();
                                $trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
-                               wfWaitForSlaves();
+                               $lbFactory->waitForReplication();
                        }
                }
                $this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
@@ -762,7 +766,7 @@ class CgzCopyTransaction {
 
                /* Check to see if the target text_ids have been moved already.
                 *
-                * We originally read from the slave, so this can happen when a single
+                * We originally read from the replica DB, so this can happen when a single
                 * text_id is shared between multiple pages. It's rare, but possible
                 * if a delete/move/undelete cycle splits up a null edit.
                 *
index 33a9f96..8ca8bb2 100644 (file)
@@ -37,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 function resolveStubs() {
        $fname = 'resolveStubs';
 
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
        $maxID = $dbr->selectField( 'text', 'MAX(old_id)', false, $fname );
        $blockSize = 10000;
        $numBlocks = intval( $maxID / $blockSize ) + 1;
@@ -73,7 +73,7 @@ function resolveStub( $id, $stubText, $flags ) {
        $stub = unserialize( $stubText );
        $flags = explode( ',', $flags );
 
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
        $dbw = wfGetDB( DB_MASTER );
 
        if ( strtolower( get_class( $stub ) ) !== 'historyblobstub' ) {
index 04d2557..c23f508 100644 (file)
@@ -23,7 +23,7 @@ require_once __DIR__ . '/../Maintenance.php';
 
 class StorageTypeStats extends Maintenance {
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
                if ( !$endId ) {
index 2692a07..90d8d03 100644 (file)
@@ -47,7 +47,7 @@ if ( isset( $options['limit'] ) ) {
 }
 $type = isset( $options['type'] ) ? $options['type'] : 'ConcatenatedGzipHistoryBlob';
 
-$dbr = $this->getDB( DB_SLAVE );
+$dbr = $this->getDB( DB_REPLICA );
 $res = $dbr->select(
        [ 'page', 'revision', 'text' ],
        '*',
index 214f5b1..a2dc376 100644 (file)
@@ -67,7 +67,7 @@ class TrackBlobs {
 
        function checkIntegrity() {
                echo "Doing integrity check...\n";
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Scan for HistoryBlobStub objects in the text table (bug 20757)
 
@@ -117,7 +117,7 @@ class TrackBlobs {
 
        function getTextClause() {
                if ( !$this->textClause ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $this->textClause = '';
                        foreach ( $this->clusters as $cluster ) {
                                if ( $this->textClause != '' ) {
@@ -147,7 +147,7 @@ class TrackBlobs {
         */
        function trackRevisions() {
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $textClause = $this->getTextClause();
                $startId = 0;
@@ -219,9 +219,9 @@ class TrackBlobs {
         * archive table counts as orphan for our purposes.
         */
        function trackOrphanText() {
-               # Wait until the blob_tracking table is available in the slave
+               # Wait until the blob_tracking table is available in the replica DB
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $pos = $dbw->getMasterPos();
                $dbr->masterPosWait( $pos, 100000 );
 
@@ -317,7 +317,7 @@ class TrackBlobs {
                        echo "Searching for orphan blobs in $cluster...\n";
                        $lb = wfGetLBFactory()->getExternalLB( $cluster );
                        try {
-                               $extDB = $lb->getConnection( DB_SLAVE );
+                               $extDB = $lb->getConnection( DB_REPLICA );
                        } catch ( DBConnectionError $e ) {
                                if ( strpos( $e->error, 'Unknown database' ) !== false ) {
                                        echo "No database on $cluster\n";
index f47e13c..9d7cc0e 100644 (file)
@@ -7,7 +7,7 @@ require_once __DIR__ . '/Maintenance.php';
 class TidyUpBug37714 extends Maintenance {
        public function execute() {
                // Search for all log entries which are about changing the visability of other log entries.
-               $result = $this->getDB( DB_SLAVE )->select(
+               $result = $this->getDB( DB_REPLICA )->select(
                        'logging',
                        [ 'log_id', 'log_params' ],
                        [
@@ -21,7 +21,7 @@ class TidyUpBug37714 extends Maintenance {
 
                foreach ( $result as $row ) {
                        $ids = explode( ',', explode( "\n", $row->log_params )[0] );
-                       $result = $this->getDB( DB_SLAVE )->select( // Work out what log entries were changed here.
+                       $result = $this->getDB( DB_REPLICA )->select( // Work out what log entries were changed here.
                                'logging',
                                'log_type',
                                [ 'log_id' => $ids ],
index dff5aec..213195d 100644 (file)
@@ -46,7 +46,7 @@ class UpdateArticleCount extends Maintenance {
                if ( $this->hasOption( 'use-master' ) ) {
                        $dbr = $this->getDB( DB_MASTER );
                } else {
-                       $dbr = $this->getDB( DB_SLAVE, 'vslow' );
+                       $dbr = $this->getDB( DB_REPLICA, 'vslow' );
                }
                $counter = new SiteStatsInit( $dbr );
                $result = $counter->articles();
index 922cc87..e754e3c 100644 (file)
@@ -34,7 +34,7 @@ require_once __DIR__ . '/Maintenance.php';
  */
 class UpdateCollation extends Maintenance {
        const BATCH_SIZE = 100; // Number of rows to process in one batch
-       const SYNC_INTERVAL = 5; // Wait for slaves after this many batches
+       const SYNC_INTERVAL = 5; // Wait for replica DBs after this many batches
 
        public $sizeHistogram = [];
 
@@ -70,7 +70,7 @@ TEXT
                global $wgCategoryCollation;
 
                $dbw = $this->getDB( DB_MASTER );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $force = $this->getOption( 'force' );
                $dryRun = $this->getOption( 'dry-run' );
                $verboseStats = $this->getOption( 'verbose-stats' );
@@ -101,7 +101,7 @@ TEXT
                        'STRAIGHT_JOIN' // per T58041
                ];
 
-               if ( $force || $dryRun ) {
+               if ( $force ) {
                        $collationConds = [];
                } else {
                        if ( $this->hasOption( 'previous-collation' ) ) {
@@ -132,7 +132,11 @@ TEXT
 
                                return;
                        }
-                       $this->output( "Fixing collation for $count rows.\n" );
+                       if ( $dryRun ) {
+                               $this->output( "$count rows would be updated.\n" );
+                       } else {
+                               $this->output( "Fixing collation for $count rows.\n" );
+                       }
                        wfWaitForSlaves();
                }
                $count = 0;
@@ -220,7 +224,7 @@ TEXT
                        $this->output( "$count done.\n" );
 
                        if ( !$dryRun && ++$batchCount % self::SYNC_INTERVAL == 0 ) {
-                               $this->output( "Waiting for slaves ... " );
+                               $this->output( "Waiting for replica DBs ... " );
                                wfWaitForSlaves();
                                $this->output( "done\n" );
                        }
index 8b24b90..5ea3828 100644 (file)
@@ -109,7 +109,7 @@ class UpdateSpecialPages extends Maintenance {
                                                } while ( !wfGetLB()->pingAll() );
                                                $this->output( "Reconnected\n\n" );
                                        }
-                                       # Wait for the slave to catch up
+                                       # Wait for the replica DB to catch up
                                        wfWaitForSlaves();
                                } else {
                                        $this->output( "cheap, skipped\n" );
@@ -153,7 +153,7 @@ class UpdateSpecialPages extends Maintenance {
                                        $this->output( $minutes . 'm ' );
                                }
                                $this->output( sprintf( "%.2fs\n", $seconds ) );
-                               # Wait for the slave to catch up
+                               # Wait for the replica DB to catch up
                                wfWaitForSlaves();
                        }
                }
index 4b0a817..c657c03 100644 (file)
@@ -140,8 +140,8 @@ class UserOptions {
                $ret = [];
                $defaultOptions = User::getDefaultOptions();
 
-               // We list user by user_id from one of the slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               // We list user by user_id from one of the replica DBs
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->select( 'user',
                        [ 'user_id' ],
                        [],
@@ -194,8 +194,8 @@ class UserOptions {
        private function CHANGER() {
                $this->warn();
 
-               // We list user by user_id from one of the slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               // We list user by user_id from one of the replica DBs
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->select( 'user',
                        [ 'user_id' ],
                        [],
index b6c3007..89168db 100644 (file)
@@ -160,9 +160,13 @@ return [
                'targets' => [ 'mobile', 'desktop' ],
        ],
        'jquery.appear' => [
+               'deprecated' => [
+                       'message' => 'Please use "mediawiki.viewport" instead.',
+               ],
                'scripts' => 'resources/lib/jquery/jquery.appear.js',
        ],
        'jquery.arrowSteps' => [
+               'deprecated' => true,
                'scripts' => 'resources/src/jquery/jquery.arrowSteps.js',
                'styles' => 'resources/src/jquery/jquery.arrowSteps.css',
                'targets' => [ 'desktop', 'mobile' ],
@@ -568,6 +572,7 @@ return [
        ],
        'jquery.ui.position' => [
                'deprecated' => true,
+               'targets' => [ 'mobile', 'desktop' ],
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.position.js',
                'group' => 'jquery.ui',
        ],
@@ -985,6 +990,16 @@ return [
                // must be loaded on the bottom
                'position' => 'bottom',
        ],
+       'mediawiki.diff.styles' => [
+               'position' => 'top',
+               'styles' => [
+                       'resources/src/mediawiki/mediawiki.diff.styles.css',
+                       'resources/src/mediawiki/mediawiki.diff.styles.print.css' => [
+                               'media' => 'print'
+                       ],
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.feedback' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.feedback.js',
                'styles' => 'resources/src/mediawiki/mediawiki.feedback.css',
@@ -1142,7 +1157,7 @@ return [
        'mediawiki.notification' => [
                'styles' => [
                        'resources/src/mediawiki/mediawiki.notification.common.css',
-                       'resources/src/mediawiki/mediawiki.notification.hideForPrint.css'
+                       'resources/src/mediawiki/mediawiki.notification.print.css'
                                => [ 'media' => 'print' ],
                ],
                'skinStyles' => [
@@ -1268,6 +1283,7 @@ return [
                ],
                'dependencies' => [
                        'oojs-ui-core',
+                       'oojs-ui-widgets',
                        'oojs-ui-windows',
                        'oojs-ui.styles.icons-content',
                        'oojs-ui.styles.icons-editing-advanced',
@@ -1480,7 +1496,7 @@ return [
                        'jquery.spinner',
                        'jquery.textSelection',
                        'mediawiki.api',
-                       'mediawiki.action.history.diff',
+                       'mediawiki.diff.styles',
                        'mediawiki.util',
                        'mediawiki.jqueryMsg',
                ],
@@ -1507,11 +1523,12 @@ return [
                'position' => 'top',
                'styles' => 'resources/src/mediawiki.action/mediawiki.action.history.styles.css',
        ],
+       // using this module is deprecated, for diff styles use mediawiki.diff.styles instead
        'mediawiki.action.history.diff' => [
                'position' => 'top',
                'styles' => [
-                       'resources/src/mediawiki.action/mediawiki.action.history.diff.css',
-                       'resources/src/mediawiki.action/mediawiki.action.history.diff.print.css' => [
+                       'resources/src/mediawiki/mediawiki.diff.styles.css',
+                       'resources/src/mediawiki/mediawiki.diff.styles.print.css' => [
                                'media' => 'print'
                        ],
                ],
@@ -1691,7 +1708,7 @@ return [
        ],
        'mediawiki.page.gallery.styles' => [
                'styles' => [
-                       'resources/src/mediawiki/page/gallery-print.css' => [ 'media' => 'print' ],
+                       'resources/src/mediawiki/page/gallery.print.css' => [ 'media' => 'print' ],
                        'resources/src/mediawiki/page/gallery.css',
                ],
                'position' => 'top',
index 2fb7adf..55a41bd 100644 (file)
@@ -3,13 +3,15 @@
 // author : Werner Mollentze : https://github.com/wernerm
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('af', {
         months : 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'),
index 7add172..871e41c 100644 (file)
@@ -4,13 +4,15 @@
 // author : Abdel Said : https://github.com/abdelsaid
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ar-ma', {
         months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'),
index ea7e2f6..11350cb 100644 (file)
@@ -3,13 +3,15 @@
 // author : Suhail Alkowaileet : https://github.com/xsoh
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '١',
index d645008..cbec753 100644 (file)
@@ -5,13 +5,15 @@
 // Native plural forms: forabi https://github.com/forabi
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '١',
index d4d1434..649d9f2 100644 (file)
@@ -3,13 +3,15 @@
 // author : topchiyev : https://github.com/topchiyev
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var suffixes = {
         1: '-inci',
index 68a6f37..67ae899 100644 (file)
@@ -5,13 +5,15 @@
 // Author : Menelion Elensúle : https://github.com/Oire
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function plural(word, num) {
         var forms = word.split('_');
index 540e17b..fe610ec 100644 (file)
@@ -3,13 +3,15 @@
 // author : Krasen Borisov : https://github.com/kraz
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('bg', {
         months : 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'),
index e9549d9..59d26ba 100644 (file)
@@ -3,13 +3,15 @@
 // author : Kaushik Gandhi : https://github.com/kaushikgandhi
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '১',
index cece8d1..346fbdf 100644 (file)
@@ -3,13 +3,15 @@
 // author : Thupten N. Chakrishar : https://github.com/vajradog
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '༡',
index 1f8dd61..575d644 100644 (file)
@@ -3,13 +3,15 @@
 // author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function relativeTimeWithMutation(number, withoutSuffix, key) {
         var format = {
index c59f46b..415b596 100644 (file)
@@ -4,13 +4,15 @@
 // based on (hr) translation by Bojan Marković
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function translate(number, withoutSuffix, key) {
         var result = number + ' ';
index 4f0d3fe..6c82db0 100644 (file)
@@ -3,13 +3,15 @@
 // author : Juan G. Hurtado : https://github.com/juanghurtado
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ca', {
         months : 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'),
index b61658d..bf3e60a 100644 (file)
@@ -3,13 +3,15 @@
 // author : petrbela : https://github.com/petrbela
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var months = 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_'),
         monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_');
index ea8e314..10d33a6 100644 (file)
@@ -3,13 +3,15 @@
 // author : Anatoly Mironov : https://github.com/mirontoli
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('cv', {
         months : 'кăрлач_нарăс_пуш_ака_май_çĕртме_утă_çурла_авăн_юпа_чӳк_раштав'.split('_'),
index 72b2f91..a14ed7d 100644 (file)
@@ -3,13 +3,15 @@
 // author : Robert Allen
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('cy', {
         months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split('_'),
index 686ce00..228ca02 100644 (file)
@@ -3,13 +3,15 @@
 // author : Ulrik Nielsen : https://github.com/mrbase
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('da', {
         months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'),
index c982638..af6e61c 100644 (file)
@@ -5,13 +5,15 @@
 // author : Martin Groller : https://github.com/MadMG
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function processRelativeTime(number, withoutSuffix, key, isFuture) {
         var format = {
index f6d89a9..1b50f0f 100644 (file)
@@ -4,13 +4,15 @@
 // author: Menelion Elensúle: https://github.com/Oire
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function processRelativeTime(number, withoutSuffix, key, isFuture) {
         var format = {
index 6dc769e..a0e1695 100644 (file)
@@ -3,13 +3,15 @@
 // author : Aggelos Karalias : https://github.com/mehiel
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('el', {
         monthsNominativeEl : 'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split('_'),
index a382b0a..9d7b537 100644 (file)
@@ -2,13 +2,15 @@
 // locale : australian english (en-au)
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('en-au', {
         months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
index 2dec8a6..45d3569 100644 (file)
@@ -3,13 +3,15 @@
 // author : Jonathan Abourbih : https://github.com/jonbca
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('en-ca', {
         months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
index 4ea2b29..669ce5e 100644 (file)
@@ -3,13 +3,15 @@
 // author : Chris Gedrim : https://github.com/chrisgedrim
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('en-gb', {
         months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
index 6a3d097..7f8c504 100644 (file)
@@ -5,13 +5,15 @@
 //          Se ne, bonvolu korekti kaj avizi min por ke mi povas lerni!
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('eo', {
         months : 'januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro'.split('_'),
index b6e30b1..5d352a4 100644 (file)
@@ -3,13 +3,15 @@
 // author : Julio Napurí : https://github.com/julionc
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'),
         monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_');
index 7dbcee7..fec168c 100644 (file)
@@ -4,13 +4,15 @@
 // improvements : Illimar Tambek : https://github.com/ragulka
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function processRelativeTime(number, withoutSuffix, key, isFuture) {
         var format = {
index c455c46..1401346 100644 (file)
@@ -3,13 +3,15 @@
 // author : Eneko Illarramendi : https://github.com/eillarra
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('eu', {
         months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'),
index ad2087a..0205adf 100644 (file)
@@ -3,13 +3,15 @@
 // author : Ebrahim Byagowi : https://github.com/ebraminio
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '۱',
index f884c3e..66cd1c0 100644 (file)
@@ -3,13 +3,15 @@
 // author : Tarmo Aidantausta : https://github.com/bleadof
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '),
         numbersFuture = [
index 6b940e8..710abf7 100644 (file)
@@ -3,13 +3,15 @@
 // author : Ragnar Johannesen : https://github.com/ragnar123
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('fo', {
         months : 'januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
index 6cac1b8..2bbeb80 100644 (file)
@@ -3,13 +3,15 @@
 // author : Jonathan Abourbih : https://github.com/jonbca
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('fr-ca', {
         months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
index 4a7cbcc..29a3239 100644 (file)
@@ -3,13 +3,15 @@
 // author : John Fischer : https://github.com/jfroffice
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('fr', {
         months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
index 5ff9e3f..b939f8a 100644 (file)
@@ -3,13 +3,15 @@
 // author : Juan G. Hurtado : https://github.com/juanghurtado
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('gl', {
         months : 'Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro'.split('_'),
index 9f9f470..05b0209 100644 (file)
@@ -5,13 +5,15 @@
 // author : Tal Ater : https://github.com/TalAter
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('he', {
         months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'),
index 73deba5..36ee3ff 100644 (file)
@@ -3,13 +3,15 @@
 // author : Mayank Singhal : https://github.com/mayanksinghal
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '१',
index 65264dc..be9d785 100644 (file)
@@ -5,13 +5,15 @@
 // based on (sl) translation by Robert Sedovšek
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function translate(number, withoutSuffix, key) {
         var result = number + ' ';
index 7eccd1d..3ed864e 100644 (file)
@@ -3,13 +3,15 @@
 // author : Adam Brunner : https://github.com/adambrunner
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' ');
 
index 053a845..1e6540b 100644 (file)
@@ -3,13 +3,15 @@
 // author : Armendarabyan : https://github.com/armendarabyan
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function monthsCaseReplace(m, format) {
         var months = {
index 36a841a..b8fc9ad 100644 (file)
@@ -4,13 +4,15 @@
 // reference: http://id.wikisource.org/wiki/Pedoman_Umum_Ejaan_Bahasa_Indonesia_yang_Disempurnakan
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('id', {
         months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember'.split('_'),
index 21888aa..6422b47 100644 (file)
@@ -3,13 +3,15 @@
 // author : Hinrik Örn Sigurðsson : https://github.com/hinrik
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function plural(n) {
         if (n % 100 === 11) {
index 9d14714..1330988 100644 (file)
@@ -4,13 +4,15 @@
 // author: Mattia Larentis: https://github.com/nostalgiaz
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('it', {
         months : 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'),
index 3f55bcf..c3e3ebf 100644 (file)
@@ -3,13 +3,15 @@
 // author : LI Long : https://github.com/baryon
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ja', {
         months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
index b56e18c..1437c71 100644 (file)
@@ -3,13 +3,15 @@
 // author : Irakli Janiashvili : https://github.com/irakli-janiashvili
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function monthsCaseReplace(m, format) {
         var months = {
index 8d7b9b8..6229791 100644 (file)
@@ -3,13 +3,15 @@
 // author : Kruy Vanna : https://github.com/kruyvanna
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('km', {
         months: 'មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'),
index 956345b..2638959 100644 (file)
@@ -6,13 +6,15 @@
 // - Kyungwook, Park : https://github.com/kyungw00k
 // - Jeeeyul Lee <jeeeyul@gmail.com>
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ko', {
         months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'),
index 2e84dab..fe6b34b 100644 (file)
@@ -7,13 +7,15 @@
 // and 'eifelerRegelAppliesToNumber' methods are meant for
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function processRelativeTime(number, withoutSuffix, key, isFuture) {
         var format = {
index 2d87e04..d9c8ae5 100644 (file)
@@ -3,13 +3,15 @@
 // author : Mindaugas Mozūras : https://github.com/mmozuras
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var units = {
         'm' : 'minutė_minutės_minutę',
index 47a0708..315f27f 100644 (file)
@@ -3,13 +3,15 @@
 // author : Kristaps Karlsons : https://github.com/skakri
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var units = {
         'mm': 'minūti_minūtes_minūte_minūtes',
index de36631..74fd5a1 100644 (file)
@@ -3,13 +3,15 @@
 // author : Borislav Mickov : https://github.com/B0k0
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('mk', {
         months : 'јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември'.split('_'),
index 3850914..f72b2c4 100644 (file)
@@ -3,13 +3,15 @@
 // author : Floyd Pink : https://github.com/floydpink
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ml', {
         months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split('_'),
index 45c200e..7c04715 100644 (file)
@@ -3,13 +3,15 @@
 // author : Harshad Kale : https://github.com/kalehv
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '१',
index 09ec280..7072c2f 100644 (file)
@@ -3,13 +3,15 @@
 // author : Weldan Jamili : https://github.com/weldan
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('ms-my', {
         months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'),
index 31f5c9e..daba17d 100644 (file)
@@ -3,13 +3,15 @@
 // author : Squar team, mysquar.com
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '၁',
index 4764b50..4fab78b 100644 (file)
@@ -4,13 +4,15 @@
 //           Sigurd Gartmann : https://github.com/sigurdga
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('nb', {
         months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
index ceb2834..ad10165 100644 (file)
@@ -3,13 +3,15 @@
 // author : suvash : https://github.com/suvash
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var symbolMap = {
         '1': '१',
index 9f4fdfe..871760d 100644 (file)
@@ -3,13 +3,15 @@
 // author : Joris Röling : https://github.com/jjupiter
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'),
         monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_');
index d7a8238..de8deff 100644 (file)
@@ -3,13 +3,15 @@
 // author : https://github.com/mechuwind
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('nn', {
         months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
index 418ca81..167b4b4 100644 (file)
@@ -3,13 +3,15 @@
 // author : Rafal Hirsz : https://github.com/evoL
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_'),
         monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_');
index 813c2de..0c5d2c2 100644 (file)
@@ -3,13 +3,15 @@
 // author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('pt-br', {
         months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'),
index 4afd564..ef9cb80 100644 (file)
@@ -3,13 +3,15 @@
 // author : Jefferson : https://github.com/jalex79
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('pt', {
         months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'),
index fcc7d07..7b2c93d 100644 (file)
@@ -4,13 +4,15 @@
 // author : Valentin Agachi : https://github.com/avaly
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function relativeTimeWithPlural(number, withoutSuffix, key) {
         var format = {
index 5adfa9a..65265da 100644 (file)
@@ -4,13 +4,15 @@
 // Author : Menelion Elensúle : https://github.com/Oire
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function plural(word, num) {
         var forms = word.split('_');
index f9d74c5..7eae2f4 100644 (file)
@@ -4,13 +4,15 @@
 // based on work of petrbela : https://github.com/petrbela
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var months = 'január_február_marec_apríl_máj_jún_júl_august_september_október_november_december'.split('_'),
         monthsShort = 'jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec'.split('_');
index 232695f..54fe37d 100644 (file)
@@ -3,13 +3,15 @@
 // author : Robert Sedovšek : https://github.com/sedovsek
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function translate(number, withoutSuffix, key) {
         var result = number + ' ';
index 415495a..89cd7ba 100644 (file)
@@ -5,13 +5,15 @@
 // author : Oerd Cukalla : https://github.com/oerd (fixes)
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('sq', {
         months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'),
index 57619b6..db1dea4 100644 (file)
@@ -3,13 +3,15 @@
 // author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var translator = {
         words: { //Different grammatical cases
index 6f14284..f732316 100644 (file)
@@ -3,13 +3,15 @@
 // author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var translator = {
         words: { //Different grammatical cases
index 6e14958..48c3dee 100644 (file)
@@ -3,13 +3,15 @@
 // author : Jens Alm : https://github.com/ulmus
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('sv', {
         months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'),
index d0356a3..7e73599 100644 (file)
@@ -3,13 +3,15 @@
 // author : Arjunkumar Krishnamoorthy : https://github.com/tk120404
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     /*var symbolMap = {
             '1': '௧',
index e3c5422..fc949cb 100644 (file)
@@ -3,13 +3,15 @@
 // author : Kridsada Thanabulpong : https://github.com/sirn
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('th', {
         months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'),
index 40dbb07..910c681 100644 (file)
@@ -3,13 +3,15 @@
 // author : Dan Hagman
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('tl-ph', {
         months : 'Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre'.split('_'),
index cd0a746..a2707bc 100644 (file)
@@ -4,13 +4,15 @@
 //           Burak Yiğit Kaya: https://github.com/BYK
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     var suffixes = {
         1: '\'inci',
index 34592b4..9eefde5 100644 (file)
@@ -3,13 +3,15 @@
 // author : Abdel Said : https://github.com/abdelsaid
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('tzm-latn', {
         months : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'),
index 9591521..0895298 100644 (file)
@@ -3,13 +3,15 @@
 // author : Abdel Said : https://github.com/abdelsaid
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('tzm', {
         months : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'),
index 3dce4bc..32c51de 100644 (file)
@@ -4,13 +4,15 @@
 // Author : Menelion Elensúle : https://github.com/Oire
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     function plural(word, num) {
         var forms = word.split('_');
index 139e4de..db4fc24 100644 (file)
@@ -3,13 +3,15 @@
 // author : Sardor Muminov : https://github.com/muminoff
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('uz', {
         months : 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'),
index 15ec7dd..127d064 100644 (file)
@@ -3,13 +3,15 @@
 // author : Bang Nguyen : https://github.com/bangnk
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('vi', {
         months : 'tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12'.split('_'),
index b8a0bd2..5b71928 100644 (file)
@@ -4,13 +4,15 @@
 // author : Zeno Zeng : https://github.com/zenozeng
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('zh-cn', {
         months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
index b3c4439..7883ede 100644 (file)
@@ -3,13 +3,15 @@
 // author : Ben : https://github.com/ben-lin
 
 (function (factory) {
-    if (typeof define === 'function' && define.amd) {
+    // Comment out broken wrapper, see T145382
+    /*if (typeof define === 'function' && define.amd) {
         define(['moment'], factory); // AMD
     } else if (typeof exports === 'object') {
         module.exports = factory(require('../moment')); // Node
     } else {
         factory((typeof global !== 'undefined' ? global : this).moment); // node or other global
-    }
+    }*/
+    factory(this.moment);
 }(function (moment) {
     return moment.defineLocale('zh-tw', {
         months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
diff --git a/resources/lib/oojs-ui/i18n/bho.json b/resources/lib/oojs-ui/i18n/bho.json
new file mode 100644 (file)
index 0000000..9697db0
--- /dev/null
@@ -0,0 +1,23 @@
+{
+       "@metadata": {
+               "authors": [
+                       "SatyamMishra"
+               ]
+       },
+       "ooui-outline-control-move-down": "आइटम नीचे घसकाईं",
+       "ooui-outline-control-move-up": "आइटम ऊपर घसकाईं",
+       "ooui-outline-control-remove": "आइटम हटाईं",
+       "ooui-toolbar-more": "अउरी",
+       "ooui-toolgroup-expand": "अउरी",
+       "ooui-toolgroup-collapse": "कम",
+       "ooui-dialog-message-accept": "ओके",
+       "ooui-dialog-message-reject": "कैंसिल",
+       "ooui-dialog-process-error": "कुछ गड़बड़ी हो गइल",
+       "ooui-dialog-process-dismiss": "रद्द",
+       "ooui-dialog-process-retry": "दोबारा कोसिस करीं",
+       "ooui-dialog-process-continue": "जारी राखीं",
+       "ooui-selectfile-button-select": "एगो फाइल चुनीं",
+       "ooui-selectfile-not-supported": "फाइल के चुनाव के सपोर्ट नइखे",
+       "ooui-selectfile-placeholder": "कौनों फाइल नइखे चुनल गइल",
+       "ooui-selectfile-dragdrop-placeholder": "फाइल इहाँ ड्रॉप करीं"
+}
index 8da8ef1..546cc47 100644 (file)
@@ -15,7 +15,7 @@
        "ooui-toolgroup-expand": "Liyané",
        "ooui-toolgroup-collapse": "Sacukupé",
        "ooui-dialog-message-accept": "Ha'a",
-       "ooui-dialog-message-reject": "Wurungaké",
+       "ooui-dialog-message-reject": "Wurung",
        "ooui-dialog-process-error": "Ana sing klèru",
        "ooui-dialog-process-dismiss": "Tutup",
        "ooui-dialog-process-retry": "Jajal manèh",
index 982a3cd..741cfb3 100644 (file)
@@ -4,7 +4,8 @@
                        "Vikassy",
                        "Nayvik",
                        "Omshivaprakash",
-                       "Pavanaja"
+                       "Pavanaja",
+                       "Yogesh"
                ]
        },
        "ooui-outline-control-move-down": "ವಸ್ತುವನ್ನು ಕೆಳಗೆ ಸರಿಸು",
@@ -18,5 +19,8 @@
        "ooui-dialog-process-error": "ಏನೋ ಎಡವಟ್ಟಾಗಿದೆ....",
        "ooui-dialog-process-dismiss": "ತೆಗೆದುಹಾಕು",
        "ooui-dialog-process-retry": "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ",
-       "ooui-dialog-process-continue": "ಮುಂದುವರೆಸು"
+       "ooui-dialog-process-continue": "ಮುಂದುವರೆಸು",
+       "ooui-selectfile-button-select": "ಕಡತವನ್ನು ಆಯ್ಕೆಮಾಡಿ",
+       "ooui-selectfile-placeholder": "ಕಡತವು ಆಯ್ಕೆಯಾಗಿಲ್ಲ",
+       "ooui-selectfile-dragdrop-placeholder": "ಇಲ್ಲಿ ಕಡತವನ್ನು ಬಿಡಿ"
 }
index bccd615..24a5966 100644 (file)
        "ooui-toolgroup-collapse": "Mens",
        "ooui-dialog-message-accept": "D'acòrdi",
        "ooui-dialog-message-reject": "Anullar",
+       "ooui-dialog-process-error": "Quicòm a trucat",
        "ooui-dialog-process-dismiss": "Regetar",
        "ooui-dialog-process-retry": "Ensajatz tornamai",
        "ooui-dialog-process-continue": "Contunhar",
-       "ooui-selectfile-placeholder": "Cap de fichièr pas seleccionat"
+       "ooui-selectfile-button-select": "Seleccionar un fichièr",
+       "ooui-selectfile-not-supported": "Lo tipe de fichièr es pas compatible",
+       "ooui-selectfile-placeholder": "Cap de fichièr pas seleccionat",
+       "ooui-selectfile-dragdrop-placeholder": "Depausar lo fichièr aicí"
 }
diff --git a/resources/lib/oojs-ui/i18n/shn.json b/resources/lib/oojs-ui/i18n/shn.json
new file mode 100644 (file)
index 0000000..a93e616
--- /dev/null
@@ -0,0 +1,23 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Saimawnkham"
+               ]
+       },
+       "ooui-outline-control-move-down": "ၶၢႆႉလူင်းၽၢႆႇတႂ်ႈ",
+       "ooui-outline-control-move-up": "ၶၢႆႉၶိုၼ်ႈၽၢႆႇၼိူဝ်",
+       "ooui-outline-control-remove": "ထွၼ်ပႅတ်ႈ ဢၼ်ၶဝ်ႈပႃး",
+       "ooui-toolbar-more": "ၼမ်ႉလိူဝ်",
+       "ooui-toolgroup-expand": "ၼမ်လိူဝ်",
+       "ooui-toolgroup-collapse": "ဢေႇလိူဝ်",
+       "ooui-dialog-message-accept": "ဢူဝ်ႇၶေႇ",
+       "ooui-dialog-message-reject": "ဢမ်ႇႁဵတ်း",
+       "ooui-dialog-process-error": "သေဢၼ်ဢၼ်ၽိတ်းပိူင်ႈဝႆႉ",
+       "ooui-dialog-process-dismiss": "လူတ်းၵၢၼ်",
+       "ooui-dialog-process-retry": "ၶတ်းၸႂ်ထႅင်ႈ",
+       "ooui-dialog-process-continue": "သိုပ်ႇၼႃႈ",
+       "ooui-selectfile-button-select": "လိူၵ်ႈၾၢႆႇ",
+       "ooui-selectfile-not-supported": "လွင်ႈလိူၵ်ႈၽၢႆႇၼႆႉ ဢမ်ႇၵမ်ႉထႅမ်ဝႆႉပၼ်",
+       "ooui-selectfile-placeholder": "ဢမ်ႇလႆႈလိူၵ်ႈ ၾၢႆႇသင်ဝႆႉ",
+       "ooui-selectfile-dragdrop-placeholder": "ဢဝ်ၾၢႆႇ သႂ်ႇတီႈၼႆႉ"
+}
index 957b0d0..e4b50d8 100644 (file)
@@ -7,9 +7,13 @@
                        "Shanmugamp7",
                        "Veeven",
                        "Visdaviva",
-                       "மதனாஹரன்"
+                       "மதனாஹரன்",
+                       "రహ్మానుద్దీన్"
                ]
        },
+       "ooui-outline-control-move-down": "అంశాన్ని కిందికి కదుపు",
+       "ooui-outline-control-move-up": "అంశాన్ని పైకి కదుపు",
+       "ooui-outline-control-remove": "అంశాన్ని తీసివేయి",
        "ooui-toolbar-more": "మరిన్ని",
        "ooui-toolgroup-expand": "మరిన్ని",
        "ooui-toolgroup-collapse": "కొన్ని",
@@ -18,5 +22,9 @@
        "ooui-dialog-process-error": "ఏదో పొరపాటు జరిగింది",
        "ooui-dialog-process-dismiss": "రద్దుచేయి",
        "ooui-dialog-process-retry": "మళ్ళీ ప్రయత్నించు",
-       "ooui-dialog-process-continue": "కొనసాగించు"
+       "ooui-dialog-process-continue": "కొనసాగించు",
+       "ooui-selectfile-button-select": "దస్త్రాన్ని ఎంచుకో",
+       "ooui-selectfile-not-supported": "దస్త్రపు ఎంపిక అందుబాటులో లేదు",
+       "ooui-selectfile-placeholder": "ఏ దస్త్రము ఎంపిక చేయలేదు",
+       "ooui-selectfile-dragdrop-placeholder": "దస్త్రాన్ని ఇక్కడ పడేయండి"
 }
diff --git a/resources/lib/oojs-ui/i18n/ur.json b/resources/lib/oojs-ui/i18n/ur.json
new file mode 100644 (file)
index 0000000..62a1765
--- /dev/null
@@ -0,0 +1,23 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Muhammad Shuaib"
+               ]
+       },
+       "ooui-outline-control-move-down": "آئیٹم نیچے کھسکائیں",
+       "ooui-outline-control-move-up": "آئیٹم اوپر بڑھائیں",
+       "ooui-outline-control-remove": "آئیٹم حذف کریں",
+       "ooui-toolbar-more": "مزید",
+       "ooui-toolgroup-expand": "مزید",
+       "ooui-toolgroup-collapse": "کم کریں",
+       "ooui-dialog-message-accept": "ٹھیک",
+       "ooui-dialog-message-reject": "منسوخ کریں",
+       "ooui-dialog-process-error": "کچھ غلط ہو گیا ہے",
+       "ooui-dialog-process-dismiss": "ختم کریں",
+       "ooui-dialog-process-retry": "دوبارہ کوشش کریں",
+       "ooui-dialog-process-continue": "جاری رکھیں",
+       "ooui-selectfile-button-select": "فائل منتخب کریں",
+       "ooui-selectfile-not-supported": "فائل کا انتخاب معاونت شدہ نہیں",
+       "ooui-selectfile-placeholder": "کوئی فائل منتخب نہیں",
+       "ooui-selectfile-dragdrop-placeholder": "فائل یہاں چھوڑیں"
+}
index b272331..594cea2 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
index c13ac7a..a4479f7 100644 (file)
@@ -1,17 +1,21 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
        /* stylelint-disable-line declaration-no-important */
 }
+.oo-ui-buttonElement {
+       display: inline-block;
+       vertical-align: middle;
+}
 .oo-ui-buttonElement > .oo-ui-buttonElement-button {
        cursor: pointer;
        display: inline-block;
@@ -52,7 +56,7 @@
        text-align: center;
 }
 .oo-ui-buttonElement > .oo-ui-buttonElement-button {
-       color: #333333;
+       color: #333;
 }
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        margin-left: 0;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #000000;
+       color: #000;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #333333;
+       color: #333;
 }
 .oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin-left: 0.25em;
 }
 .oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button {
        padding-left: 0.25em;
-       color: #333333;
+       color: #333;
 }
 .oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:focus {
-       color: #000000;
+       color: #000;
 }
 .oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #087ecc;
        opacity: 0.2;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
        padding: 0.2em 0.8em;
        -webkit-transition: border-color 100ms ease;
           -moz-transition: border-color 100ms ease;
                transition: border-color 100ms ease;
-       background-color: #eeeeee;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
+       background-color: #eee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #fff), color-stop(100%, #ddd));
+       background-image: -webkit-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:    -moz-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:         linear-gradient(to bottom, #fff 0, #ddd 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffffffff\', endColorstr=\'#ffdddddd\' )';
 }
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
-       border-color: #aaaaaa;
+       border-color: #aaa;
        outline: none;
 }
 .oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
-       color: #000000;
+       color: #000;
        border-color: #c9c9c9;
-       background-color: #eeeeee;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #dddddd), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #dddddd 0, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #dddddd 0, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #dddddd 0, #ffffff 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffdddddd', endColorstr='#ffffffff' )";
+       background-color: #eee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ddd), color-stop(100%, #fff));
+       background-image: -webkit-linear-gradient(top, #ddd 0, #fff 100%);
+       background-image:    -moz-linear-gradient(top, #ddd 0, #fff 100%);
+       background-image:         linear-gradient(to bottom, #ddd 0, #fff 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffdddddd\', endColorstr=\'#ffffffff\' )';
 }
 .oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        margin-left: -0.5em;
        background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
        background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
        background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffeaf4fa\', endColorstr=\'#ffb0d9ee\' )';
 }
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
        background-image: -webkit-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
        background-image:    -moz-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
        background-image:         linear-gradient(to bottom, #b0d9ee 0, #eaf4fa 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffb0d9ee', endColorstr='#ffeaf4fa' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffb0d9ee\', endColorstr=\'#ffeaf4fa\' )';
 }
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
        border: 1px solid #b8d892;
        background-image: -webkit-linear-gradient(top, #f0fbe1 0, #c3e59a 100%);
        background-image:    -moz-linear-gradient(top, #f0fbe1 0, #c3e59a 100%);
        background-image:         linear-gradient(to bottom, #f0fbe1 0, #c3e59a 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff0fbe1', endColorstr='#ffc3e59a' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#fff0fbe1\', endColorstr=\'#ffc3e59a\' )';
 }
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
        background-image: -webkit-linear-gradient(top, #c3e59a 0, #f0fbe1 100%);
        background-image:    -moz-linear-gradient(top, #c3e59a 0, #f0fbe1 100%);
        background-image:         linear-gradient(to bottom, #c3e59a 0, #f0fbe1 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffc3e59a', endColorstr='#fff0fbe1' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffc3e59a\', endColorstr=\'#fff0fbe1\' )';
 }
 .oo-ui-buttonElement-framed.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
        color: #d45353;
        opacity: 0.5;
        -webkit-transform: translate3d(0, 0, 0);
        box-shadow: none;
-       color: #333333;
-       background: #eeeeee;
-       border-color: #cccccc;
+       color: #333;
+       background: #eee;
+       border-color: #ccc;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus,
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:focus,
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:focus {
-       border-color: #cccccc;
+       border-color: #ccc;
        box-shadow: none;
 }
 .oo-ui-clippableElement-clippable {
 }
 .oo-ui-fieldLayout:before,
 .oo-ui-fieldLayout:after {
-       content: " ";
+       content: ' ';
        display: table;
 }
 .oo-ui-fieldLayout:after {
        margin-right: 0;
 }
 .oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-fieldLayout-messages {
        list-style: none none;
 }
 .oo-ui-fieldsetLayout {
        position: relative;
+       min-width: 0;
        margin: 0;
-       padding: 0;
        border: 0;
+       padding: 0.01px 0 0 0;
+}
+body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout {
+       display: table-cell;
 }
 .oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
        display: block;
        position: absolute;
 }
 .oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: inline-block;
+       color: inherit;
+       display: table;
+       box-sizing: border-box;
+       max-width: 100%;
+       padding: 0;
+       white-space: normal;
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
        float: right;
        background-color: #a7dcff;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-decoratedOptionWidget {
        padding: 0.5em 2em 0.5em 3em;
        opacity: 0.2;
 }
 .oo-ui-buttonWidget {
-       display: inline-block;
-       vertical-align: middle;
        margin-right: 0.5em;
 }
 .oo-ui-buttonWidget:last-child {
        overflow: hidden;
 }
 .oo-ui-popupWidget-popup {
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       border: 1px solid #ccc;
        border-radius: 0.25em;
        box-shadow: 0 0.15em 0.5em 0 rgba(0, 0, 0, 0.2);
 }
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       content: "";
+       content: '';
        position: absolute;
        width: 0;
        height: 0;
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
        bottom: -7px;
        left: -6px;
-       border-bottom-color: #aaaaaa;
+       border-bottom-color: #aaa;
        border-width: 7px;
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
        bottom: -7px;
        left: -5px;
-       border-bottom-color: #ffffff;
+       border-bottom-color: #fff;
        border-width: 6px;
 }
 .oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
 .oo-ui-inputWidget:last-child {
        margin-right: 0;
 }
-.oo-ui-buttonInputWidget {
-       display: inline-block;
-       vertical-align: middle;
-}
 .oo-ui-buttonInputWidget > button,
 .oo-ui-buttonInputWidget > input {
        border: 0;
                box-sizing: border-box;
 }
 .oo-ui-dropdownInputWidget select {
-       background-color: #ffffff;
+       background-color: #fff;
        height: 2.5em;
        padding: 0.5em;
        font-size: inherit;
        outline: none;
 }
 .oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
-       color: #cccccc;
-       border-color: #dddddd;
+       color: #ccc;
+       border-color: #ddd;
        background-color: #f3f3f3;
 }
 .oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
        overflow: auto;
        resize: none;
 }
-.oo-ui-textInputWidget [type="number"] {
+.oo-ui-textInputWidget [type='number'] {
        -moz-appearance: textfield;
 }
-.oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
-.oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
+.oo-ui-textInputWidget [type='number']::-webkit-outer-spin-button,
+.oo-ui-textInputWidget [type='number']::-webkit-inner-spin-button {
        -webkit-appearance: none;
        margin: 0;
 }
-.oo-ui-textInputWidget [type="search"] {
+.oo-ui-textInputWidget [type='search'] {
        -webkit-appearance: textfield;
 }
-.oo-ui-textInputWidget [type="search"]::-ms-clear {
+.oo-ui-textInputWidget [type='search']::-ms-clear {
        display: none;
 }
-.oo-ui-textInputWidget [type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button {
+.oo-ui-textInputWidget [type='search']::-webkit-search-decoration,
+.oo-ui-textInputWidget [type='search']::-webkit-search-cancel-button {
        display: none;
 }
 .oo-ui-textInputWidget > .oo-ui-iconElement-icon,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
+.oo-ui-textInputWidget.oo-ui-widget-disabled input,
+.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
 .oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
        display: block;
 }
        line-height: 1.275em;
        font-size: inherit;
        font-family: inherit;
-       background-color: #ffffff;
-       color: #000000;
-       border: 1px solid #cccccc;
-       box-shadow: 0 0 0 #ffffff, inset 0 0.1em 0.2em #dddddd;
+       background-color: #fff;
+       color: #000;
+       border: 1px solid #ccc;
+       box-shadow: 0 0 0 #fff, inset 0 0.1em 0.2em #ddd;
        border-radius: 0.25em;
        -webkit-transition: border-color 250ms ease, box-shadow 250ms ease;
           -moz-transition: border-color 250ms ease, box-shadow 250ms ease;
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
        outline: none;
        border-color: #a7dcff;
-       box-shadow: 0 0 0.3em #a7dcff, 0 0 0 #ffffff;
+       box-shadow: 0 0 0.3em #a7dcff, 0 0 0 #fff;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
-       color: #777777;
+       color: #777;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
-       background-color: #ffdddd;
+       background-color: #fdd;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled input,
 .oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
+       color: #ccc;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #ddd;
        background-color: #f3f3f3;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
        opacity: 0.2;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
-       color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
+       color: #ddd;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-textInputWidget.oo-ui-iconElement input,
 .oo-ui-textInputWidget.oo-ui-iconElement textarea {
 .oo-ui-textInputWidget > .oo-ui-labelElement-label {
        padding: 0.4em;
        line-height: 1.5em;
-       color: #888888;
+       color: #888;
 }
 .oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
        margin-right: 2.0875em;
        position: absolute;
        width: 100%;
        z-index: 4;
-       background-color: #ffffff;
+       background-color: #fff;
        margin-top: -1px;
-       border: 1px solid #cccccc;
+       border: 1px solid #ccc;
        border-radius: 0 0 0.25em 0.25em;
        box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
 }
 .oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: transparent;
-}
 .oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
-       color: #888888;
+       color: #888;
 }
 .oo-ui-dropdownWidget {
        display: inline-block;
        position: relative;
        width: 100%;
        max-width: 50em;
-       background-color: #ffffff;
+       background-color: #fff;
        margin-right: 0.5em;
 }
 .oo-ui-dropdownWidget-handle {
        margin: 0 0.5em;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
+       color: #ccc;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #ddd;
        background-color: #f3f3f3;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
 }
 .oo-ui-comboBoxInputWidget {
        display: inline-block;
-       position: relative;
        width: 100%;
        max-width: 50em;
        margin-right: 0.5em;
        line-height: 1.5em;
 }
 .oo-ui-multioptionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-checkboxMultioptionWidget {
        cursor: default;
 }
 .oo-ui-progressBarWidget {
        max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       border: 1px solid #ccc;
        border-radius: 0.25em;
        overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
        height: 1em;
-       border-right: 1px solid #cccccc;
+       border-right: 1px solid #ccc;
        -webkit-transition: width 250ms ease, margin-left 250ms ease;
           -moz-transition: width 250ms ease, margin-left 250ms ease;
                transition: width 250ms ease, margin-left 250ms ease;
        background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
        background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
        background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffeaf4fa\', endColorstr=\'#ffb0d9ee\' )';
 }
 .oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
        -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
index 4e8f65c..09e6cfc 100644 (file)
@@ -1,17 +1,21 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
        /* stylelint-disable-line declaration-no-important */
 }
+.oo-ui-buttonElement {
+       display: inline-block;
+       vertical-align: middle;
+}
 .oo-ui-buttonElement > .oo-ui-buttonElement-button {
        cursor: pointer;
        display: inline-block;
        margin-right: 0.25em;
        margin-left: 0.46875em;
 }
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       -webkit-transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
+          -moz-transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
+               transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
+}
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       opacity: 0.87;
+       -webkit-transition: opacity 100ms;
+          -moz-transition: opacity 100ms;
+               transition: opacity 100ms;
+}
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon.oo-ui-image-invert,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator.oo-ui-image-invert {
+       opacity: 1;
+}
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-indicatorElement-indicator {
+       opacity: 0.73;
+}
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-iconElement-icon.oo-ui-image-invert,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-indicatorElement-indicator.oo-ui-image-invert {
+       opacity: 1;
+}
+.oo-ui-buttonElement.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       opacity: 1;
+}
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
        margin-right: 0;
 }
        padding-left: 0.25em;
        padding-right: 0.25em;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #347bff, 0 0 0 1px #347bff;
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       color: #222;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #555555;
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+       color: #444;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #36c, 0 0 0 1px #36c;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #444444;
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:active {
+       color: #000;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #347bff;
+       color: #36c;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
-       color: #2962cc;
+       color: #447ff5;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #1f4999;
+       color: #2a4b8d;
        box-shadow: none;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #347bff;
+       color: #36c;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
-       color: #2962cc;
+       color: #447ff5;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #1f4999;
+       color: #2a4b8d;
        box-shadow: none;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #d11d13;
+       color: #c33;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
-       color: #8c130d;
+       color: #e53939;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #73100a;
+       color: #873636;
        box-shadow: none;
 }
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       opacity: 1;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button:hover > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button:hover > .oo-ui-indicatorElement-indicator {
+       opacity: 0.73;
+}
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
 .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button {
        min-width: 1em;
        border-radius: 2px;
        position: relative;
-       -webkit-transition: background 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
-          -moz-transition: background 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
-               transition: background 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
 }
 .oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        left: 0.2em;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       background-color: #dddddd;
-       color: #ffffff;
-       border: 1px solid #dddddd;
+       background-color: #c8ccd1;
+       color: #fff;
+       border: 1px solid #c8ccd1;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled + .oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       border-left-color: #ffffff;
+       border-left-color: #fff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       color: #555555;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #f8f9fa;
+       color: #222;
+       border: 1px solid #9aa0a7;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #ebebeb;
+       background-color: #fff;
+       color: #444;
+       border-color: #a2a9b1;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        background-color: #d9d9d9;
-       border-color: #d9d9d9;
+       color: #000;
+       border-color: #72777d;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-       border-color: #999999;
+       background-color: #2a4b8d;
+       color: #fff;
+       border-color: #2a4b8d;
        z-index: 3;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-       color: #347bff;
+       color: #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #ebf2ff;
+       background-color: #fff;
        border-color: #859dcc;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       background-color: #eff3fa;
+       color: #2a4b8d;
+       border-color: #2a4b8d;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
-       color: #347bff;
+       color: #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #ebf2ff;
+       background-color: #fff;
        border-color: #859dcc;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       background-color: #eff3fa;
+       color: #2a4b8d;
+       border-color: #2a4b8d;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-       color: #d11d13;
+       color: #c33;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #fbe8e7;
+       background-color: #fff;
        border-color: #b77c79;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #73100a;
-       border-color: #73100a;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       background-color: #fbf4f4;
+       color: #873636;
+       border-color: #873636;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #d11d13;
-       box-shadow: inset 0 0 0 1px #d11d13;
+       border-color: #c33;
+       box-shadow: inset 0 0 0 1px #c33;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #347bff;
-       border-color: #347bff;
+       color: #fff;
+       background-color: #36c;
+       border-color: #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #2962cc;
-       border-color: #2962cc;
+       background-color: #447ff5;
+       border-color: #447ff5;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       color: #fff;
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff, inset 0 0 0 2px #ffffff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #347bff;
-       border-color: #347bff;
+       color: #fff;
+       background-color: #36c;
+       border-color: #36c;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #2962cc;
-       border-color: #2962cc;
+       background-color: #447ff5;
+       border-color: #447ff5;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       color: #fff;
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff, inset 0 0 0 2px #ffffff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #d11d13;
-       border-color: #d11d13;
+       color: #fff;
+       background-color: #c33;
+       border-color: #c33;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #8c130d;
-       border-color: #8c130d;
+       background-color: #e53939;
+       border-color: #e53939;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #73100a;
-       border-color: #73100a;
-       box-shadow: none;
-}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
+       color: #fff;
+       background-color: #873636;
+       border-color: #873636;
+       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       border-color: #d11d13;
-       box-shadow: inset 0 0 0 1px #d11d13, inset 0 0 0 2px #ffffff;
+       border-color: #c33;
+       box-shadow: inset 0 0 0 1px #c33, inset 0 0 0 2px #fff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       opacity: 1;
 }
 .oo-ui-clippableElement-clippable {
        -webkit-box-sizing: border-box;
 }
 .oo-ui-fieldLayout:before,
 .oo-ui-fieldLayout:after {
-       content: " ";
+       content: ' ';
        display: table;
 }
 .oo-ui-fieldLayout:after {
        margin-right: 0;
 }
 .oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-fieldLayout-messages {
        list-style: none none;
 }
 .oo-ui-fieldsetLayout {
        position: relative;
+       min-width: 0;
        margin: 0;
-       padding: 0;
        border: 0;
+       padding: 0.01px 0 0 0;
+}
+body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout {
+       display: table-cell;
 }
 .oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
        display: block;
        position: absolute;
 }
 .oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: inline-block;
+       color: inherit;
+       display: table;
+       box-sizing: border-box;
+       max-width: 100%;
+       padding: 0;
+       white-space: normal;
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
        float: right;
        padding: 1.25em;
 }
 .oo-ui-panelLayout-framed {
-       border: 1px solid #aaaaaa;
+       border: 1px solid #a2a9b1;
        border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-optionWidget {
        position: relative;
        display: block;
-       padding: 0.25em 0.5em;
        border: 0;
+       padding: 0.25em 0.5em;
 }
 .oo-ui-optionWidget.oo-ui-widget-enabled {
        cursor: pointer;
        text-overflow: ellipsis;
        overflow: hidden;
 }
-.oo-ui-optionWidget-highlighted {
-       background-color: #eeeeee;
-}
 .oo-ui-optionWidget .oo-ui-labelElement-label {
        line-height: 1.5;
 }
-.oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
-       background-color: #d0d0d0;
+.oo-ui-optionWidget-selected .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       opacity: 1;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-decoratedOptionWidget {
        padding: 0.5em 2em 0.5em 3em;
 }
 .oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
 .oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-radioSelectWidget:focus {
        outline: 0;
 }
-.oo-ui-radioSelectWidget:focus .oo-ui-radioOptionWidget.oo-ui-optionWidget-selected .oo-ui-radioInputWidget [type="radio"] + span {
-       border-width: 2px;
+.oo-ui-radioSelectWidget:focus [type='radio']:checked + span:before {
+       border-color: #fff;
 }
 .oo-ui-radioOptionWidget {
        cursor: default;
        line-height: 2.5;
 }
 .oo-ui-iconWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-indicatorWidget {
        display: inline-block;
        margin: 0.46875em;
 }
 .oo-ui-indicatorWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-buttonWidget {
-       display: inline-block;
-       vertical-align: middle;
        margin-right: 0.5em;
 }
 .oo-ui-buttonWidget:last-child {
        border-top-right-radius: 2px;
 }
 .oo-ui-buttonGroupWidget.oo-ui-widget-enabled .oo-ui-buttonElement .oo-ui-buttonElement-button:focus {
-       border-color: #347bff;
+       border-color: #36c;
        z-index: 3;
 }
 .oo-ui-popupWidget {
        overflow: hidden;
 }
 .oo-ui-popupWidget-popup {
-       background-color: #ffffff;
-       border: 1px solid #aaaaaa;
+       background-color: #fff;
+       border: 1px solid #a2a9b1;
        border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       content: "";
+       content: '';
        position: absolute;
        width: 0;
        height: 0;
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
        bottom: -10px;
        left: -9px;
-       border-bottom-color: #888888;
+       border-bottom-color: #888;
        border-width: 10px;
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
        bottom: -10px;
        left: -8px;
-       border-bottom-color: #ffffff;
+       border-bottom-color: #fff;
        border-width: 9px;
 }
 .oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
 .oo-ui-inputWidget:last-child {
        margin-right: 0;
 }
-.oo-ui-buttonInputWidget {
-       display: inline-block;
-       vertical-align: middle;
-}
 .oo-ui-buttonInputWidget > button,
 .oo-ui-buttonInputWidget > input {
        border: 0;
        font: inherit;
        vertical-align: middle;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"] {
-       opacity: 0;
-       z-index: 1;
+.oo-ui-checkboxInputWidget [type='checkbox'] {
        position: relative;
-       cursor: pointer;
-       margin: 0;
+       max-width: none;
        width: 1.6em;
        height: 1.6em;
-       max-width: none;
+       margin: 0;
+       opacity: 0;
+       z-index: 1;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"] + span {
-       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+.oo-ui-checkboxInputWidget [type='checkbox'] + span {
+       background-color: #fff;
+       background-origin: border-box;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 0 0;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
        left: 0;
        width: 1.6em;
        height: 1.6em;
-       background-color: #ffffff;
-       background-image: url("themes/mediawiki/images/icons/check-constructive-deprecated.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive-deprecated.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive-deprecated.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-constructive-deprecated.png");
-       background-repeat: no-repeat;
-       background-position: center center;
-       background-origin: border-box;
-       background-size: 0 0;
-       border: 1px solid #767676;
+       border: 1px solid #72777d;
        border-radius: 2px;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:checked + span {
-       background-size: 100% 100%;
+.oo-ui-checkboxInputWidget [type='checkbox']:checked + span {
+       background-image: url('themes/mediawiki/images/icons/check-invert.png');
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url('themes/mediawiki/images/icons/check-invert.svg');
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url('themes/mediawiki/images/icons/check-invert.svg');
+       background-image:      -o-linear-gradient(transparent, transparent), url('themes/mediawiki/images/icons/check-invert.png');
+       background-size: 90% 90%;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:active + span {
-       background-color: #767676;
-       border-color: #767676;
+.oo-ui-checkboxInputWidget [type='checkbox']:disabled + span {
+       background-color: #c8ccd1;
+       border-color: #c8ccd1;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:focus + span {
-       border-width: 2px;
+.oo-ui-checkboxInputWidget [type='checkbox']:disabled:hover + span {
+       background-color: #c8ccd1;
+       border-color: #c8ccd1;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:focus:hover + span,
-.oo-ui-checkboxInputWidget [type="checkbox"]:hover + span {
-       border-bottom-width: 3px;
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox'] {
+       cursor: pointer;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:disabled {
-       cursor: default;
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox'] + span {
+       cursor: pointer;
+       -webkit-transition: background-color 100ms, background-size 100ms, border-color 100ms, box-shadow 100ms;
+          -moz-transition: background-color 100ms, background-size 100ms, border-color 100ms, box-shadow 100ms;
+               transition: background-color 100ms, background-size 100ms, border-color 100ms, box-shadow 100ms;
+}
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:hover + span,
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:focus:hover + span {
+       border-color: #36c;
+}
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:active + span {
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+}
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:focus + span {
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
+}
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked + span {
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:disabled + span {
-       background-color: #dddddd;
-       border-color: #dddddd;
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked:hover + span,
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked:focus:hover + span {
+       background-color: #36c;
+       border-color: #36c;
 }
-.oo-ui-checkboxInputWidget [type="checkbox"]:disabled:checked + span {
-       background-image: url("themes/mediawiki/images/icons/check-invert.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-invert.png");
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked:active + span,
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked:active:hover + span {
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+}
+.oo-ui-checkboxInputWidget.oo-ui-widget-enabled [type='checkbox']:checked:focus + span {
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+       box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
 }
 .oo-ui-checkboxMultiselectInputWidget .oo-ui-fieldLayout {
        margin-bottom: 0;
                box-sizing: border-box;
 }
 .oo-ui-dropdownInputWidget select {
-       background-color: #ffffff;
+       background-color: #fff;
        height: 2.275em;
        font-size: inherit;
        font-family: inherit;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       border: 1px solid #cccccc;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
        padding-left: 1em;
        vertical-align: middle;
 }
 .oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:hover,
 .oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:focus {
-       border-color: #aaaaaa;
+       border-color: #a2a9b1;
        outline: 0;
 }
 .oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
-       color: #cccccc;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
+       color: #72777d;
+       border-color: #c8ccd1;
+       background-color: #eaecf0;
 }
 .oo-ui-radioInputWidget {
        position: relative;
        font: inherit;
        vertical-align: middle;
 }
-.oo-ui-radioInputWidget [type="radio"] {
-       opacity: 0;
-       z-index: 1;
+.oo-ui-radioInputWidget [type='radio'] {
        position: relative;
-       cursor: pointer;
-       margin: 0;
+       max-width: none;
        width: 1.6em;
        height: 1.6em;
-       max-width: none;
+       margin: 0;
+       opacity: 0;
+       z-index: 1;
 }
-.oo-ui-radioInputWidget [type="radio"] + span {
-       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+.oo-ui-radioInputWidget [type='radio'] + span {
+       background-color: #fff;
+       position: absolute;
+       left: 0;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       position: absolute;
-       left: 0;
        width: 1.6em;
        height: 1.6em;
-       background-color: #ffffff;
-       background-image: url("themes/mediawiki/images/icons/circle-constructive-deprecated.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive-deprecated.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive-deprecated.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-constructive-deprecated.png");
-       background-repeat: no-repeat;
-       background-position: center center;
-       background-origin: border-box;
-       background-size: 0 0;
-       border: 1px solid #767676;
+       border: 1px solid #72777d;
        border-radius: 100%;
 }
-.oo-ui-radioInputWidget [type="radio"]:checked + span {
-       background-size: 100% 100%;
+.oo-ui-radioInputWidget [type='radio'] + span:before {
+       content: ' ';
+       position: absolute;
+       top: -4px;
+       left: -4px;
+       right: -4px;
+       bottom: -4px;
+       border: 1px solid transparent;
+       border-radius: 100%;
 }
-.oo-ui-radioInputWidget [type="radio"]:active + span {
-       background-color: #767676;
-       border-color: #767676;
+.oo-ui-radioInputWidget [type='radio']:checked + span {
+       border-width: 0.4em;
 }
-.oo-ui-radioInputWidget [type="radio"]:focus + span {
-       border-width: 2px;
+.oo-ui-radioInputWidget [type='radio']:checked:hover + span,
+.oo-ui-radioInputWidget [type='radio']:checked:focus:hover + span {
+       border-width: 0.4em;
 }
-.oo-ui-radioInputWidget [type="radio"]:focus:hover + span,
-.oo-ui-radioInputWidget [type="radio"]:hover + span {
-       border-bottom-width: 3px;
+.oo-ui-radioInputWidget [type='radio']:disabled + span {
+       background-color: #c8ccd1;
+       border-color: #c8ccd1;
 }
-.oo-ui-radioInputWidget [type="radio"]:disabled {
-       cursor: default;
+.oo-ui-radioInputWidget [type='radio']:disabled:checked + span {
+       background-color: #fff;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio'] {
+       cursor: pointer;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio'] + span {
+       cursor: pointer;
+       -webkit-transition: background-color 100ms, border-color 100ms, border-width 100ms;
+          -moz-transition: background-color 100ms, border-color 100ms, border-width 100ms;
+               transition: background-color 100ms, border-color 100ms, border-width 100ms;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:hover + span {
+       border-color: #36c;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:active + span {
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked + span {
+       border-color: #2a4b8d;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:hover + span {
+       border-color: #36c;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:hover:focus + span {
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
+}
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:active + span,
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:active:focus + span {
+       border-color: #2a4b8d;
+       box-shadow: inset 0 0 0 1px #2a4b8d;
 }
-.oo-ui-radioInputWidget [type="radio"]:disabled + span {
-       background-color: #dddddd;
-       border-color: #dddddd;
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:focus + span {
+       box-shadow: inset 0 0 0 1px #36c;
 }
-.oo-ui-radioInputWidget [type="radio"]:disabled:checked + span {
-       background-image: url("themes/mediawiki/images/icons/circle-invert.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-invert.png");
+.oo-ui-radioInputWidget.oo-ui-widget-enabled [type='radio']:checked:focus + span:before {
+       border-color: #fff;
+       top: -3px;
+       right: -3px;
+       bottom: -3px;
+       left: -3px;
 }
 .oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
        margin-bottom: 0;
        overflow: auto;
        resize: none;
 }
-.oo-ui-textInputWidget [type="number"] {
+.oo-ui-textInputWidget [type='number'] {
        -moz-appearance: textfield;
 }
-.oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
-.oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
+.oo-ui-textInputWidget [type='number']::-webkit-outer-spin-button,
+.oo-ui-textInputWidget [type='number']::-webkit-inner-spin-button {
        -webkit-appearance: none;
        margin: 0;
 }
-.oo-ui-textInputWidget [type="search"] {
+.oo-ui-textInputWidget [type='search'] {
        -webkit-appearance: textfield;
 }
-.oo-ui-textInputWidget [type="search"]::-ms-clear {
+.oo-ui-textInputWidget [type='search']::-ms-clear {
        display: none;
 }
-.oo-ui-textInputWidget [type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget [type="search"]::-webkit-search-cancel-button {
+.oo-ui-textInputWidget [type='search']::-webkit-search-decoration,
+.oo-ui-textInputWidget [type='search']::-webkit-search-cancel-button {
        display: none;
 }
 .oo-ui-textInputWidget > .oo-ui-iconElement-icon,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
+.oo-ui-textInputWidget.oo-ui-widget-disabled input,
+.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
 .oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
        display: block;
 }
        margin: 0;
        font-size: inherit;
        font-family: inherit;
-       background-color: #ffffff;
-       color: #000000;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       color: #000;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
 }
 .oo-ui-textInputWidget textarea {
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea {
-       box-shadow: inset 0 0 0 0.1em #ffffff;
-       -webkit-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-          -moz-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-               transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+       box-shadow: inset 0 0 0 0.1em #fff;
+       -webkit-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+          -moz-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+               transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input:hover,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea:hover {
-       border-color: #aaaaaa;
+       border-color: #72777d;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
        outline: 0;
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
-       color: #777777;
-       text-shadow: 0 1px 1px #ffffff;
+       color: #777;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:hover,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:hover {
-       border-color: #cccccc;
+       border-color: #ccc;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:focus {
-       border-color: #cccccc;
-       box-shadow: inset 0 0 0 0.1em #cccccc;
+       border-color: #ccc;
+       box-shadow: inset 0 0 0 0.1em #ccc;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled :-moz-placeholder {
-       color: #595959;
+       color: #54595d;
        opacity: 1;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled ::-moz-placeholder {
-       color: #595959;
+       color: #54595d;
        opacity: 1;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled :-ms-input-placeholder {
-       color: #595959;
+       color: #54595d;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled ::-webkit-input-placeholder {
-       color: #595959;
+       color: #54595d;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
-       border-color: #ff0000;
+       border-color: #f00;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:hover,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:hover {
-       border-color: #ff0000;
+       border-color: #f00;
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:focus {
-       border-color: #ff0000;
-       box-shadow: inset 0 0 0 0.1em #ff0000;
+       border-color: #f00;
+       box-shadow: inset 0 0 0 0.1em #f00;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled input,
 .oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
+       background-color: #eaecf0;
+       color: #72777d;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #c8ccd1;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
 .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
+       color: #72777d;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-textInputWidget.oo-ui-iconElement input,
 .oo-ui-textInputWidget.oo-ui-iconElement textarea {
 .oo-ui-textInputWidget > .oo-ui-labelElement-label {
        padding: 0.4em;
        line-height: 1.5;
-       color: #888888;
+       color: #888;
 }
 .oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
        margin-right: 2.0875em;
        position: absolute;
        width: 100%;
        z-index: 4;
-       background-color: #ffffff;
+       background-color: #fff;
        margin-top: -1px;
-       border: 1px solid #aaaaaa;
+       border: 1px solid #a2a9b1;
        border-radius: 0 0 2px 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-menuOptionWidget {
        position: relative;
        padding: 0.5em 1em;
+       -webkit-transition: background-color 100ms, color 100ms;
+          -moz-transition: background-color 100ms, color 100ms;
+               transition: background-color 100ms, color 100ms;
 }
 .oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: transparent;
-}
 .oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: block;
 }
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: #eaecf0;
+       color: #000;
+}
 .oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: #d8e6fe;
-       color: rgba(0, 0, 0, 0.8);
+       background-color: #eaf3ff;
+       color: #36c;
 }
 .oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
        display: none;
 }
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: #eeeeee;
-       color: #000000;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: #d8e6fe;
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted,
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-pressed.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: rgba(41, 98, 204, 0.1);
+       color: #36c;
 }
 .oo-ui-menuSectionOptionWidget {
        cursor: default;
        padding: 0.33em 0.75em;
-       color: #888888;
+       color: #888;
 }
 .oo-ui-dropdownWidget {
        display: inline-block;
        padding: 0.5em 0;
        height: 2.275em;
        line-height: 1.275;
-       border: 1px solid #cccccc;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
 }
 .oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        margin: 0 1em;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
-       background-color: #ffffff;
-       -webkit-transition: border-color 100ms;
-          -moz-transition: border-color 100ms;
-               transition: border-color 100ms;
+       background-color: #f8f9fa;
+       color: #222;
+       -webkit-transition: background-color 100ms, border-color 100ms, box-shadow 100ms;
+          -moz-transition: background-color 100ms, border-color 100ms, box-shadow 100ms;
+               transition: background-color 100ms, border-color 100ms, box-shadow 100ms;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle:hover {
+       background-color: #fff;
+       border-color: #a2a9b1;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle:hover .oo-ui-iconElement-icon,
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle:hover .oo-ui-indicatorElement-indicator {
+       opacity: 0.73;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle:focus {
+       border-color: #36c;
+       outline: 0;
+       box-shadow: inset 0 0 0 1px #36c;
 }
-.oo-ui-dropdownWidget.oo-ui-widget-enabled:hover .oo-ui-dropdownWidget-handle {
-       border-color: #aaaaaa;
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon,
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       opacity: 0.87;
+       -webkit-transition: opacity 100ms;
+          -moz-transition: opacity 100ms;
+               transition: opacity 100ms;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled.oo-ui-dropdownWidget-open .oo-ui-dropdownWidget-handle {
+       background-color: #fff;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled.oo-ui-dropdownWidget-open .oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon,
+.oo-ui-dropdownWidget.oo-ui-widget-enabled.oo-ui-dropdownWidget-open .oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       opacity: 1;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
+       color: #72777d;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #c8ccd1;
+       background-color: #eaecf0;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
        outline: 0;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.15;
 }
 .oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-left: 3em;
 }
 .oo-ui-comboBoxInputWidget {
        display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-       margin-right: 0.5em;
 }
 .oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 .oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
        pointer-events: none;
 }
-.oo-ui-comboBoxInputWidget:last-child {
-       margin-right: 0;
-}
 .oo-ui-comboBoxInputWidget input,
 .oo-ui-comboBoxInputWidget textarea {
        height: 2.35em;
 }
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled:hover input,
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled:hover textarea {
+       border-color: #a2a9b1;
+}
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled:hover input:focus,
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled:hover textarea:focus {
+       border-color: #36c;
+}
+.oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.15;
+}
 .oo-ui-multioptionWidget {
        position: relative;
        display: block;
        line-height: 1.5;
 }
 .oo-ui-multioptionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-checkboxMultioptionWidget {
        cursor: default;
 }
 .oo-ui-progressBarWidget {
        max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
        overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
-       background-color: #dddddd;
+       background-color: #ddd;
        height: 1em;
        -webkit-transition: width 200ms, margin-left 200ms;
           -moz-transition: width 200ms, margin-left 200ms;
index cd1a3de..c982010 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
@@ -747,9 +747,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
        // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
        state = cls.static.gatherPreInfuseState( $elem[ 0 ], data );
        // rebuild widget
-       // jscs:disable requireCapitalizedConstructors
        obj = new cls( data );
-       // jscs:enable requireCapitalizedConstructors
        // now replace old DOM with this new DOM.
        if ( top ) {
                // An efficient constructor might be able to reuse the entire DOM tree of the original element,
@@ -1584,12 +1582,8 @@ OO.ui.Widget.prototype.updateDisabled = function () {
  * @class
  *
  * @constructor
- * @param {Object} [config] Configuration options
  */
-OO.ui.Theme = function OoUiTheme( config ) {
-       // Configuration initialization
-       config = config || {};
-};
+OO.ui.Theme = function OoUiTheme() {};
 
 /* Setup */
 
@@ -1809,7 +1803,7 @@ OO.ui.mixin.ButtonElement = function OoUiMixinButtonElement( config ) {
        // Properties
        this.$button = null;
        this.framed = null;
-       this.active = false;
+       this.active = config.active !== undefined && config.active;
        this.onMouseUpHandler = this.onMouseUp.bind( this );
        this.onMouseDownHandler = this.onMouseDown.bind( this );
        this.onKeyDownHandler = this.onKeyDown.bind( this );
@@ -6367,7 +6361,10 @@ OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
                keypress: this.menu.onKeyPressHandler,
                blur: this.menu.clearKeyPressBuffer.bind( this.menu )
        } );
-       this.menu.connect( this, { select: 'onMenuSelect' } );
+       this.menu.connect( this, {
+               select: 'onMenuSelect',
+               toggle: 'onMenuToggle'
+       } );
 
        // Initialization
        this.$handle
@@ -6423,6 +6420,16 @@ OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
        this.setLabel( selectedLabel );
 };
 
+/**
+ * Handle menu toggle events.
+ *
+ * @private
+ * @param {boolean} isVisible Menu toggle event
+ */
+OO.ui.DropdownWidget.prototype.onMenuToggle = function ( isVisible ) {
+       this.$element.toggleClass( 'oo-ui-dropdownWidget-open', isVisible );
+};
+
 /**
  * Handle mouse click events.
  *
@@ -7168,7 +7175,6 @@ OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positionin
  */
 OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function ( $element, $container ) {
        var elemRect, contRect,
-               topEdgeInBounds = false,
                leftEdgeInBounds = false,
                bottomEdgeInBounds = false,
                rightEdgeInBounds = false;
@@ -7185,9 +7191,8 @@ OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function ( $element
                contRect = $container[ 0 ].getBoundingClientRect();
        }
 
-       if ( elemRect.top >= contRect.top && elemRect.top <= contRect.bottom ) {
-               topEdgeInBounds = true;
-       }
+       // For completeness, if we still cared about topEdgeInBounds, that'd be:
+       // elemRect.top >= contRect.top && elemRect.top <= contRect.bottom
        if ( elemRect.left >= contRect.left && elemRect.left <= contRect.right ) {
                leftEdgeInBounds = true;
        }
@@ -9808,12 +9813,12 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
        // Initialization
        this.$element
                .addClass( 'oo-ui-fieldLayout' )
+               .toggleClass( 'oo-ui-fieldLayout-disabled', this.fieldWidget.isDisabled() )
                .append( this.$help, this.$body );
        this.$body.addClass( 'oo-ui-fieldLayout-body' );
        this.$messages.addClass( 'oo-ui-fieldLayout-messages' );
        this.$field
                .addClass( 'oo-ui-fieldLayout-field' )
-               .toggleClass( 'oo-ui-fieldLayout-disable', this.fieldWidget.isDisabled() )
                .append( this.fieldWidget.$element );
 
        this.setErrors( config.errors || [] );
@@ -10095,7 +10100,7 @@ OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
 
        // Mixin constructors
        OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: $( '<legend>' ) } ) );
        OO.ui.mixin.GroupElement.call( this, config );
 
        if ( config.help ) {
@@ -10118,7 +10123,7 @@ OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
        // Initialization
        this.$element
                .addClass( 'oo-ui-fieldsetLayout' )
-               .prepend( this.$help, this.$icon, this.$label, this.$group );
+               .prepend( this.$label, this.$help, this.$icon, this.$group );
        if ( Array.isArray( config.items ) ) {
                this.addItems( config.items );
        }
@@ -10131,6 +10136,10 @@ OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.IconElement );
 OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.LabelElement );
 OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.GroupElement );
 
+/* Static Properties */
+
+OO.ui.FieldsetLayout.static.tagName = 'fieldset';
+
 /**
  * FormLayouts are used to wrap {@link OO.ui.FieldsetLayout FieldsetLayouts} when you intend to use browser-based
  * form submission for the fields instead of handling them in JavaScript. Form layouts can be configured with an
index ab1e9ea..343508c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
@@ -54,7 +54,7 @@ OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
                } else if ( !isFramed && element.isDisabled() ) {
                        // Frameless disabled button, always use black icon regardless of flags
                        variants.invert = false;
-               } else {
+               } else if ( !element.isDisabled() ) {
                        // Any other kind of button, use the right colored icon if available
                        variants.progressive = element.hasFlag( 'progressive' );
                        variants.constructive = element.hasFlag( 'constructive' );
index 75fd654..9c9954e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
@@ -69,7 +69,7 @@
        border-color: rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #000000;
+       color: #000;
 }
 .oo-ui-barToolGroup > .oo-ui-iconElement-icon,
 .oo-ui-barToolGroup > .oo-ui-labelElement-label {
        border-color: rgba(0, 0, 0, 0.2);
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background-color: #f8fbfd;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #fff 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#fff1f7fb\', endColorstr=\'#ffffffff\' )';
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
        border-left-color: rgba(0, 0, 0, 0.1);
        outline: 0;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.2;
        outline: 0;
 }
 .oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.2;
        border-bottom-right-radius: 0;
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background-color: #f8fbfd;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #fff 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#fff1f7fb\', endColorstr=\'#ffffffff\' )';
 }
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 2.5em;
        margin: 0 -1px;
-       border: 1px solid #cccccc;
-       background-color: #ffffff;
+       border: 1px solid #ccc;
+       background-color: #fff;
        box-shadow: 0 0.3125em 1.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link {
        line-height: 2em;
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #888888;
+       color: #888;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
        display: block;
        border-color: rgba(0, 0, 0, 0.1);
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background-color: #f8fbfd;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #fff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #fff 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#fff1f7fb\', endColorstr=\'#ffffffff\' )';
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
        border-top-color: rgba(0, 0, 0, 0.1);
        opacity: 1;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #dddddd;
+       color: #ddd;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.2;
 }
 .oo-ui-listToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
 .oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
        background-image: none;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: url("themes/apex/images/icons/check.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/apex/images/icons/check.png");
+       background-image: url('themes/apex/images/icons/check.png');
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url('themes/apex/images/icons/check.svg');
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url('themes/apex/images/icons/check.svg');
+       background-image:      -o-linear-gradient(transparent, transparent), url('themes/apex/images/icons/check.png');
        background-size: contain;
        background-position: center center;
        background-repeat: no-repeat;
        background-color: #e1f3ff;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.2;
 }
 .oo-ui-menuToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
+       color: #ccc;
        border-color: rgba(0, 0, 0, 0.05);
 }
 .oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
        line-height: 1;
        position: relative;
 }
-.oo-ui-toolbar-actions {
-       float: right;
-}
-.oo-ui-toolbar-actions .oo-ui-toolbar {
-       display: inline-block;
+.oo-ui-toolbar-tools,
+.oo-ui-toolbar-actions,
+.oo-ui-toolbar-shadow {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
 }
 .oo-ui-toolbar-tools {
        display: inline;
 .oo-ui-toolbar-tools .oo-ui-tool {
        white-space: normal;
 }
-.oo-ui-toolbar-tools,
-.oo-ui-toolbar-actions,
-.oo-ui-toolbar-shadow {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
+.oo-ui-toolbar-actions {
+       float: right;
+}
+.oo-ui-toolbar-actions .oo-ui-toolbar {
+       display: inline-block;
 }
 .oo-ui-toolbar-actions .oo-ui-popupWidget {
        -webkit-touch-callout: default;
        pointer-events: none;
 }
 .oo-ui-toolbar-bar {
-       border-bottom: 1px solid #cccccc;
+       border-bottom: 1px solid #ccc;
        background-color: #f8fbfd;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #f1f7fb));
-       background-image: -webkit-linear-gradient(top, #ffffff 0, #f1f7fb 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0, #f1f7fb 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0, #f1f7fb 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#fff1f7fb' )";
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #fff), color-stop(100%, #f1f7fb));
+       background-image: -webkit-linear-gradient(top, #fff 0, #f1f7fb 100%);
+       background-image:    -moz-linear-gradient(top, #fff 0, #f1f7fb 100%);
+       background-image:         linear-gradient(to bottom, #fff 0, #f1f7fb 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffffffff\', endColorstr=\'#fff1f7fb\' )';
 }
 .oo-ui-toolbar-bar .oo-ui-toolbar-bar {
        border: 0;
index 0b55308..a413005 100644 (file)
@@ -1,13 +1,23 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
+.oo-ui-tool.oo-ui-widget-enabled {
+       -webkit-transition: background-color 100ms;
+          -moz-transition: background-color 100ms;
+               transition: background-color 100ms;
+}
+.oo-ui-tool.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
+       -webkit-transition: color 100ms;
+          -moz-transition: color 100ms;
+               transition: color 100ms;
+}
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
        z-index: 4;
        /* @noflip */
        margin-left: 1.25em;
 }
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup {
-       border: 0;
-       border-radius: 0;
-       margin: 0;
-}
 .oo-ui-toolGroupTool > .oo-ui-toolGroup {
        border-right: 0;
 }
@@ -38,8 +43,7 @@
 .oo-ui-toolGroup {
        display: inline-block;
        vertical-align: middle;
-       border-right: 1px solid #cccccc;
-       border-radius: 0;
+       border-right: 1px solid #c8ccd1;
 }
 .oo-ui-toolGroup-empty {
        display: none;
        outline: 0;
        cursor: default;
 }
+.oo-ui-toolbar-actions .oo-ui-toolGroup {
+       border-right: 0;
+       border-left: 1px solid #9aa0a7;
+}
 .oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
        margin-left: 0;
 }
        line-height: 2.1;
        padding: 0 0.4em;
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #555555;
-}
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
+       background-color: #eaecf0;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #222;
+       -webkit-transition: color 100ms;
+          -moz-transition: color 100ms;
+               transition: color 100ms;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active {
-       background-color: #e5e5e5;
+       background-color: #eaf3ff;
        box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active:active {
-       background-color: #e5e5e5;
+       background-color: rgba(41, 98, 204, 0.1);
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.7;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.9;
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #36c;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title,
 .oo-ui-barToolGroup.oo-ui-widget-disabled .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon,
 .oo-ui-barToolGroup.oo-ui-widget-disabled .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
+       opacity: 0.3;
 }
 .oo-ui-popupToolGroup {
        position: relative;
 .oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
        margin-right: 1.75em;
 }
+.oo-ui-popupToolGroup-header {
+       line-height: 2.6;
+       margin: 0 0.6em;
+       font-weight: bold;
+}
 .oo-ui-popupToolGroup-handle {
        padding: 0.3125em;
        height: 2.5em;
 .oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0;
 }
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:active {
-       background-color: #e5e5e5;
-}
-.oo-ui-popupToolGroup-header {
-       line-height: 2.6;
-       margin: 0 0.6em;
-       font-weight: bold;
-}
-.oo-ui-popupToolGroup-active.oo-ui-widget-enabled {
-       border-bottom-left-radius: 0;
-       border-bottom-right-radius: 0;
-       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
-       background-color: #eeeeee;
-}
 .oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
        top: 3.125em;
        margin: 0 -1px;
-       border: 1px solid #cccccc;
-       background-color: #ffffff;
+       border: 1px solid #9aa0a7;
+       background-color: #fff;
        box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
        min-width: 16em;
 }
        width: 1.875em;
        min-width: 1.875em;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       padding-left: 0.5em;
-       color: #555555;
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
+       line-height: 2;
 }
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2;
+       padding-left: 0.5em;
+       color: #222;
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #888888;
+       color: #888;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled {
+       -webkit-transition: background-color 100ms, box-shadow 100ms;
+          -moz-transition: background-color 100ms, box-shadow 100ms;
+               transition: background-color 100ms, box-shadow 100ms;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled.oo-ui-popupToolGroup-active {
+       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+       background-color: #eaecf0;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled.oo-ui-popupToolGroup-active .oo-ui-tool-active.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #36c;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled-handle:hover {
+       background-color: #eaecf0;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled-handle:active {
+       background-color: #eaf3ff;
 }
 .oo-ui-listToolGroup .oo-ui-tool {
        display: block;
                box-sizing: border-box;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
+       background-color: #eaecf0;
 }
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.9;
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
+       background-color: #eaf3ff;
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:first-child {
        box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
-       background-color: #e5e5e5;
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
+       background-color: rgba(41, 98, 204, 0.1);
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #36c;
 }
 .oo-ui-listToolGroup.oo-ui-widget-disabled,
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-title,
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-accel {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
 .oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon,
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
+       opacity: 0.3;
 }
 .oo-ui-menuToolGroup .oo-ui-tool {
        display: block;
        background-image: none;
 }
 .oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: url("themes/mediawiki/images/icons/check.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check.png");
+       background-image: url('themes/mediawiki/images/icons/check-progressive.png');
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url('themes/mediawiki/images/icons/check-progressive.svg');
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url('themes/mediawiki/images/icons/check-progressive.svg');
+       background-image:      -o-linear-gradient(transparent, transparent), url('themes/mediawiki/images/icons/check-progressive.png');
        background-size: contain;
        background-position: center center;
        background-repeat: no-repeat;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
+       background-color: rgba(41, 98, 204, 0.1);
+}
+.oo-ui-menuToolGroup .oo-ui-tool-name-menuTool.oo-ui-tool-active {
+       background-color: #eaf3ff;
 }
 .oo-ui-menuToolGroup.oo-ui-widget-disabled,
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-title {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
 .oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon,
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
+       opacity: 0.3;
 }
 .oo-ui-toolbar {
        clear: both;
        line-height: 1;
        position: relative;
 }
-.oo-ui-toolbar-actions {
-       float: right;
-}
-.oo-ui-toolbar-actions .oo-ui-toolbar {
-       display: inline-block;
+.oo-ui-toolbar-tools,
+.oo-ui-toolbar-actions,
+.oo-ui-toolbar-shadow {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
 }
 .oo-ui-toolbar-tools {
        display: inline;
 .oo-ui-toolbar-tools .oo-ui-tool {
        white-space: normal;
 }
-.oo-ui-toolbar-tools,
-.oo-ui-toolbar-actions,
-.oo-ui-toolbar-shadow {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
+.oo-ui-toolbar-actions {
+       float: right;
+}
+.oo-ui-toolbar-actions .oo-ui-toolbar {
+       display: inline-block;
 }
 .oo-ui-toolbar-actions .oo-ui-popupWidget {
        -webkit-touch-callout: default;
        pointer-events: none;
 }
 .oo-ui-toolbar-bar {
-       border-bottom: 1px solid #cccccc;
-       background-color: #ffffff;
+       border-bottom: 1px solid #c8ccd1;
+       background-color: #fff;
        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
        font-weight: 500;
-       color: #555555;
+       color: #222;
 }
 .oo-ui-toolbar-bar .oo-ui-toolbar-bar {
-       border: 0;
-       background: none;
+       border-bottom: 0;
+       background-color: transparent;
        box-shadow: none;
 }
 .oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement {
 .oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button {
        border: 0;
        border-radius: 0;
-       margin: 0;
        padding: 0 0.3125em;
 }
 .oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin: 0 1em;
        line-height: 3.125em;
 }
+.oo-ui-toolbar-actions > .oo-ui-toolbar:not( :last-child ) {
+       border-right: 1px solid #9aa0a7;
+}
index 18fda57..ba959cf 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
@@ -781,8 +781,10 @@ OO.ui.Tool.prototype.setActive = function ( state ) {
        this.active = !!state;
        if ( this.active ) {
                this.$element.addClass( 'oo-ui-tool-active' );
+               this.setFlags( 'progressive' );
        } else {
                this.$element.removeClass( 'oo-ui-tool-active' );
+               this.clearFlags();
        }
 };
 
index b0e87af..bd8034d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
@@ -65,7 +65,7 @@
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
-       border-right: 1px solid #dddddd;
+       border-right: 1px solid #ddd;
 }
 .oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
        padding: 0;
        background-color: transparent;
 }
+.oo-ui-buttonOptionWidget.oo-ui-buttonElement-active .oo-ui-buttonElement-button {
+       cursor: default;
+}
 .oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
 .oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        position: static;
        background-color: transparent;
 }
 .oo-ui-toggleButtonWidget {
-       display: inline-block;
-       vertical-align: middle;
        margin-right: 0.5em;
 }
 .oo-ui-toggleButtonWidget:last-child {
        height: 2em;
        width: 4em;
        border-radius: 1em;
-       box-shadow: 0 0 0 #ffffff, inset 0 0.1em 0.2em #dddddd;
-       border: 1px solid #cccccc;
+       box-shadow: 0 0 0 #fff, inset 0 0.1em 0.2em #ddd;
+       border: 1px solid #ccc;
        margin-right: 0.5em;
-       background-color: #eeeeee;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #dddddd), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #dddddd 0, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #dddddd 0, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #dddddd 0, #ffffff 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffdddddd', endColorstr='#ffffffff' )";
+       background-color: #eee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ddd), color-stop(100%, #fff));
+       background-image: -webkit-linear-gradient(top, #ddd 0, #fff 100%);
+       background-image:    -moz-linear-gradient(top, #ddd 0, #fff 100%);
+       background-image:         linear-gradient(to bottom, #ddd 0, #fff 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffdddddd\', endColorstr=\'#ffffffff\' )';
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
        cursor: pointer;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover,
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover .oo-ui-toggleSwitchWidget-grip {
-       border-color: #aaaaaa;
+       border-color: #aaa;
 }
 .oo-ui-toggleSwitchWidget-grip {
        top: 0.25em;
        -webkit-transition: left 250ms ease, margin-left 250ms ease;
           -moz-transition: left 250ms ease, margin-left 250ms ease;
                transition: left 250ms ease, margin-left 250ms ease;
-       background-color: #eeeeee;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
+       background-color: #eee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #fff), color-stop(100%, #ddd));
+       background-image: -webkit-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:    -moz-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:         linear-gradient(to bottom, #fff 0, #ddd 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffffffff\', endColorstr=\'#ffdddddd\' )';
 }
 .oo-ui-toggleSwitchWidget-glow {
        position: absolute;
        background-image: -webkit-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
        background-image:    -moz-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
        background-image:         linear-gradient(to bottom, #b0d9ee 0, #eaf4fa 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffb0d9ee', endColorstr='#ffeaf4fa' )";
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffb0d9ee\', endColorstr=\'#ffeaf4fa\' )';
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
 }
 .oo-ui-selectFileWidget-selectButton {
        display: table-cell;
-       vertical-align: middle;
 }
 .oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
        position: relative;
        overflow: hidden;
 }
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > [type="file"] {
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > [type='file'] {
        position: absolute;
        top: 0;
        bottom: 0;
        cursor: pointer;
        padding-top: 100px;
 }
-.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > [type="file"] {
+.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > [type='file'] {
        display: none;
 }
 .oo-ui-selectFileWidget-info {
        right: 0;
        text-overflow: ellipsis;
 }
-.oo-ui-selectFileWidget-fileType {
-       display: none;
-}
 .oo-ui-selectFileWidget-clearButton {
        position: absolute;
        z-index: 2;
 }
 .oo-ui-selectFileWidget-info {
        height: 2.4em;
-       background-color: #ffffff;
+       background-color: #fff;
        border: 1px solid rgba(0, 0, 0, 0.1);
        border-radius: 0.25em 0 0 0.25em;
        border-width: 1px 0 1px 1px;
        white-space: nowrap;
        text-overflow: ellipsis;
 }
-.oo-ui-selectFileWidget-fileType {
-       color: #888888;
-       display: block;
-       margin-top: 0.25em;
-}
 .oo-ui-selectFileWidget-clearButton {
        top: 0;
        right: 0;
        height: 2.3em;
 }
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
-       color: #cccccc;
+       color: #ccc;
 }
 .oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-label {
        left: 2.475em;
        background-color: #e1f3ff;
 }
 .oo-ui-selectFileWidget-dropTarget {
-       background-color: #ffffff;
-       border: 1px solid #aaaaaa;
+       background-color: #fff;
+       border: 1px solid #aaa;
        vertical-align: middle;
        border-radius: 0.25em;
 }
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
 .oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
        background-color: #f3f3f3;
-       color: #cccccc;
-       border-color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
+       color: #ccc;
+       border-color: #ddd;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
 .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info {
        background-color: #f3f3f3;
-       color: #cccccc;
-       border-color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
+       color: #ccc;
+       border-color: #ddd;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
        opacity: 0.2;
 }
 .oo-ui-outlineOptionWidget {
-       position: relative;
-       cursor: pointer;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
        opacity: 0.5;
 }
 .oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
-       color: #777777;
+       color: #777;
 }
 .oo-ui-outlineControlsWidget {
        height: 3em;
-       background-color: #ffffff;
+       background-color: #fff;
 }
 .oo-ui-outlineControlsWidget-items,
 .oo-ui-outlineControlsWidget-movers {
        text-align: left;
        white-space: nowrap;
        overflow: hidden;
-       background-color: #eeeeee;
+       background-color: #eee;
        box-shadow: inset 0 -0.015em 0.1em rgba(0, 0, 0, 0.1);
 }
 .oo-ui-tabOptionWidget {
 }
 .oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
        background-color: rgba(255, 255, 255, 0.2);
-       border-color: #dddddd;
+       border-color: #ddd;
 }
 .oo-ui-tabOptionWidget.oo-ui-widget-enabled:active {
-       background-color: #ffffff;
-       border-color: #dddddd;
+       background-color: #fff;
+       border-color: #ddd;
 }
 .oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
 .oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
 .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
-       background-color: #ffffff;
-       border-color: #dddddd;
+       background-color: #fff;
+       border-color: #ddd;
 }
 .oo-ui-capsuleMultiselectWidget {
        display: inline-block;
        display: inline;
 }
 .oo-ui-capsuleMultiselectWidget-handle {
-       background-color: #ffffff;
+       background-color: #fff;
        cursor: text;
        min-height: 2.4em;
        margin-right: 0.5em;
        font-size: inherit;
        font-family: inherit;
        background-color: transparent;
-       color: #000000;
+       color: #000;
        vertical-align: middle;
 }
 .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input:focus {
        border-color: rgba(0, 0, 0, 0.2);
 }
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
+       color: #ccc;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #ddd;
        background-color: #f3f3f3;
        cursor: default;
 }
        margin: 0.1em;
        height: 1.7em;
        line-height: 1.7em;
-       background-color: #eeeeee;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
-       border: 1px solid #cccccc;
-       color: #555555;
+       background-color: #eee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #fff), color-stop(100%, #ddd));
+       background-image: -webkit-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:    -moz-linear-gradient(top, #fff 0, #ddd 100%);
+       background-image:         linear-gradient(to bottom, #fff 0, #ddd 100%);
+       -ms-filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#ffffffff\', endColorstr=\'#ffdddddd\' )';
+       border: 1px solid #ccc;
+       color: #555;
        border-radius: 0.25em;
 }
 .oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        opacity: 0.5;
        -webkit-transform: translate3d(0, 0, 0);
        box-shadow: none;
-       color: #333333;
-       background: #eeeeee;
-       border-color: #cccccc;
+       color: #333;
+       background: #eee;
+       border-color: #ccc;
 }
 .oo-ui-capsuleItemWidget > .oo-ui-buttonElement {
        margin-top: -1.25em;
        position: relative;
        max-width: 50em;
 }
-.oo-ui-numberInputWidget-field {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonWidget,
+.oo-ui-numberInputWidget-buttoned .oo-ui-textInputWidget {
        display: table-cell;
-       vertical-align: middle;
 }
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       white-space: nowrap;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonElement-button {
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-numberInputWidget-field {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
 .oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
        width: 2.25em;
 }
index 9632bac..126b591 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
@@ -65,7 +65,7 @@
        padding: 1.5em;
 }
 .oo-ui-bookletLayout-outlinePanel {
-       border-right: 1px solid #dddddd;
+       border-right: 1px solid #ddd;
 }
 .oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
        border-top-right-radius: 2px;
 }
 .oo-ui-buttonSelectWidget.oo-ui-widget-enabled:focus .oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected .oo-ui-buttonElement-button {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
 }
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 }
+.oo-ui-buttonOptionWidget.oo-ui-buttonElement-active .oo-ui-buttonElement-button {
+       cursor: default;
+}
 .oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
 .oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        position: static;
        background-color: transparent;
 }
 .oo-ui-toggleButtonWidget {
-       display: inline-block;
-       vertical-align: middle;
        margin-right: 0.5em;
 }
 .oo-ui-toggleButtonWidget:last-child {
           -moz-transform: translateZ(0);
            -ms-transform: translateZ(0);
                transform: translateZ(0);
+       background-color: #f8f9fa;
        width: 3.5em;
        min-height: 26px;
        height: 2em;
-       border: 1px solid #767676;
+       border: 1px solid #72777d;
        border-radius: 1em;
-       background-color: #ffffff;
        margin-right: 0.5em;
-       -webkit-transition: background-color 100ms, border-color 100ms;
-          -moz-transition: background-color 100ms, border-color 100ms;
-               transition: background-color 100ms, border-color 100ms;
+       -webkit-transition: background-color 250ms, border-color 250ms;
+          -moz-transition: background-color 250ms, border-color 250ms;
+               transition: background-color 250ms, border-color 250ms;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
        cursor: pointer;
        margin-right: 0;
 }
 .oo-ui-toggleSwitchWidget:before {
-       content: "";
+       content: '';
        display: block;
        position: absolute;
        top: 1px;
        border: 1px solid transparent;
        border-radius: 1em;
        z-index: 1;
-       -webkit-transition: border-color 100ms;
-          -moz-transition: border-color 100ms;
-               transition: border-color 100ms;
+       -webkit-transition: border-color 250ms;
+          -moz-transition: border-color 250ms;
+               transition: border-color 250ms;
 }
 .oo-ui-toggleSwitchWidget-grip {
        top: 0.3125em;
        min-height: 16px;
        height: 1.25em;
        border-radius: 1.25em;
-       -webkit-transition: left 100ms, margin-left 100ms;
-          -moz-transition: left 100ms, margin-left 100ms;
-               transition: left 100ms, margin-left 100ms;
+       -webkit-transition: background-color 250ms, left 100ms, margin-left 100ms;
+          -moz-transition: background-color 250ms, left 100ms, margin-left 100ms;
+               transition: background-color 250ms, left 100ms, margin-left 100ms;
 }
 .oo-ui-toggleSwitchWidget-glow {
        display: none;
        margin-left: -2px;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled .oo-ui-toggleSwitchWidget-grip {
-       border: 1px solid #767676;
+       background-color: #f8f9fa;
+       border: 1px solid #72777d;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
-       border-color: #2962cc;
+       background-color: #fff;
+       border-color: #447ff5;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover .oo-ui-toggleSwitchWidget-grip {
-       border-color: #2962cc;
+       background-color: #fff;
+       border-color: #447ff5;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
-       background-color: #767676;
-       border-color: #347bff;
+       background-color: #36c;
+       border-color: #2a4b8d;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
-       border-color: #ffffff;
+       background-color: #fff;
+       border-color: #fff;
        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
        outline: 0;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus .oo-ui-toggleSwitchWidget-grip {
-       border-color: #347bff;
+       border-color: #36c;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
-       background-color: #347bff;
-       border-color: #347bff;
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
-       border-color: #ffffff;
+       background-color: #fff;
+       border-color: #fff;
        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:hover {
-       background-color: #2962cc;
-       border-color: #2962cc;
+       background-color: #36c;
+       border-color: #36c;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:active,
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:active:hover {
-       background-color: #1f4999;
-       border-color: #1f4999;
+       background-color: #2a4b8d;
+       border-color: #2a4b8d;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:focus {
-       border-color: #347bff;
+       border-color: #36c;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:focus:before {
-       border-color: #ffffff;
+       border-color: #fff;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
-       background-color: #dddddd;
-       border-color: #dddddd;
+       background-color: #c8ccd1;
+       border-color: #c8ccd1;
        outline: 0;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
+.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
+       border: 1px solid #fff;
+       box-shadow: inset 0 0 0 1px #fff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
+       background-color: #fff;
 }
 .oo-ui-selectFileWidget {
        display: inline-block;
 }
 .oo-ui-selectFileWidget-selectButton {
        display: table-cell;
-       vertical-align: middle;
 }
 .oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
        position: relative;
        overflow: hidden;
 }
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > [type="file"] {
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > [type='file'] {
        position: absolute;
        top: 0;
        bottom: 0;
        cursor: pointer;
        padding-top: 100px;
 }
-.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > [type="file"] {
+.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > [type='file'] {
        display: none;
 }
 .oo-ui-selectFileWidget-info {
        right: 0;
        text-overflow: ellipsis;
 }
-.oo-ui-selectFileWidget-fileType {
-       display: none;
-}
 .oo-ui-selectFileWidget-clearButton {
        position: absolute;
        z-index: 2;
 }
 .oo-ui-selectFileWidget-info {
        height: 2.4em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       border: 1px solid #9aa0a7;
        border-radius: 2px 0 0 2px;
        border-width: 1px 0 1px 1px;
 }
        text-overflow: ellipsis;
        padding-left: 0.5em;
 }
-.oo-ui-selectFileWidget-fileType {
-       color: #888888;
-       display: block;
-       margin-top: 0.25em;
-}
 .oo-ui-selectFileWidget-clearButton {
        top: 0;
        right: 0;
        height: 2.3em;
 }
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
-       color: #cccccc;
+       color: #72777d;
 }
 .oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-label {
        left: 2.875em;
        right: 2em;
 }
 .oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop.oo-ui-selectFileWidget-dropTarget {
-       background-color: #ebf2ff;
+       background-color: #eaf3ff;
 }
 .oo-ui-selectFileWidget-dropTarget {
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
+       background-color: #fff;
+       border: 1px solid #9aa0a7;
        vertical-align: middle;
        overflow: hidden;
        border-radius: 2px;
        overflow: inherit;
        white-space: normal;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget {
-       background-color: #eeeeee;
+.oo-ui-selectFileWidget-empty.oo-ui-widget-enabled.oo-ui-selectFileWidget-dropTarget {
+       background-color: #eee;
        border-style: dashed;
 }
 .oo-ui-selectFileWidget.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
 .oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
-       background-color: #f3f3f3;
-       border-color: #dddddd;
+       background-color: #eaecf0;
+       border-color: #c8ccd1;
 }
 .oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
 .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info {
-       background-color: #f3f3f3;
-       color: #cccccc;
-       border-color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
+       background-color: #eaecf0;
+       color: #72777d;
+       border-color: #c8ccd1;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
 .oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
 .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.51;
 }
 .oo-ui-widget-disabled .oo-ui-selectFileWidget-dropLabel {
        display: none;
 }
 .oo-ui-outlineOptionWidget {
-       position: relative;
-       cursor: pointer;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
                user-select: none;
        font-size: 1.1em;
        padding: 0.75em;
+       -webkit-transition: background-color 100ms, color 100ms;
+          -moz-transition: background-color 100ms, color 100ms;
+               transition: background-color 100ms, color 100ms;
+}
+.oo-ui-outlineOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: #eaecf0;
+       color: #000;
+}
+.oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
+       background-color: #eaf3ff;
+       color: #36c;
+}
+.oo-ui-outlineOptionWidget.oo-ui-optionWidget-pressed {
+       background-color: rgba(41, 98, 204, 0.1);
+       color: #36c;
 }
 .oo-ui-outlineOptionWidget .oo-ui-iconElement-icon {
        font-size: 90.90909%;
 .oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
        left: 4em;
 }
-.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
-       background-color: #d0d0d0;
-       text-shadow: 0 1px 1px #ffffff;
-}
 .oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
        font-weight: bold;
 }
        opacity: 0.5;
 }
 .oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
-       color: #777777;
+       color: #777;
 }
 .oo-ui-outlineControlsWidget {
        height: 3em;
-       background-color: #ffffff;
+       background-color: #fff;
 }
 .oo-ui-outlineControlsWidget-items,
 .oo-ui-outlineControlsWidget-movers {
        text-align: left;
        white-space: nowrap;
        overflow: hidden;
-       background-color: #dddddd;
+       background-color: #ddd;
 }
 .oo-ui-tabOptionWidget {
        display: inline-block;
        border-bottom: 0;
        border-top-left-radius: 2px;
        border-top-right-radius: 2px;
-       color: #555555;
+       color: #222;
        font-weight: bold;
 }
 .oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
 .oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
 .oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
 .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
-       background-color: #ffffff;
-       color: #333333;
+       background-color: #fff;
+       color: #333;
 }
 .oo-ui-capsuleMultiselectWidget {
        display: inline-block;
        min-height: 2.4em;
        margin-right: 0.5em;
        padding: 0.15em 0.25em;
-       border: 1px solid #cccccc;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
 .oo-ui-capsuleMultiselectWidget-handle:last-child {
        margin-right: 0;
 }
+.oo-ui-capsuleMultiselectWidget-handle:hover {
+       border-color: #72777d;
+}
 .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-indicatorElement-indicator,
 .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-iconElement-icon {
        position: absolute;
        font-size: inherit;
        font-family: inherit;
        background-color: transparent;
-       color: #000000;
+       color: #000;
        vertical-align: middle;
 }
 .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input:focus {
        top: 0;
        margin: 0.3em;
 }
+.oo-ui-capsuleMultiselectWidget .oo-ui-popupWidget {
+       width: 100%;
+       margin-top: -1px;
+}
+.oo-ui-capsuleMultiselectWidget .oo-ui-popupWidget-popup {
+       min-width: 100%;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       border-width: 0 1px;
+       border-radius: 0 0 2px 2px;
+}
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-enabled .oo-ui-capsuleMultiselectWidget-handle {
-       background-color: #ffffff;
+       background-color: #fff;
        cursor: text;
-       -webkit-transition: border-color 100ms;
-          -moz-transition: border-color 100ms;
-               transition: border-color 100ms;
+       -webkit-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+          -moz-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+               transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
 }
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-enabled:hover .oo-ui-capsuleMultiselectWidget-handle {
-       border-color: #aaaaaa;
+       border-color: #a2a9b1;
+}
+.oo-ui-capsuleMultiselectWidget.oo-ui-widget-enabled.oo-ui-capsuleMultiselectWidget-open .oo-ui-capsuleMultiselectWidget-handle {
+       border-color: #36c;
+       outline: 0;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
+       color: #72777d;
+       text-shadow: 0 1px 1px #fff;
+       border-color: #c8ccd1;
+       background-color: #eaecf0;
+}
+.oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-iconElement-icon {
+       opacity: 0.51;
 }
-.oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-iconElement-icon,
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
+       opacity: 0.15;
 }
 .oo-ui-capsuleItemWidget {
        position: relative;
        vertical-align: middle;
        height: 1.7em;
        line-height: 1.7;
-       background-color: #eeeeee;
-       color: #555555;
+       background-color: #eee;
+       color: #222;
        margin: 0.1em;
-       border: 1px solid #cccccc;
+       border: 1px solid #9aa0a7;
        border-radius: 2px;
        padding: 0 0.4em;
 }
 }
 .oo-ui-capsuleItemWidget:focus {
        outline: 0;
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
+       border-color: #36c;
+       box-shadow: inset 0 0 0 1px #36c;
 }
 .oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
-       background-color: #f3f3f3;
-       color: #cccccc;
-       border-color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
+       background-color: #eaecf0;
+       color: #72777d;
+       border-color: #c8ccd1;
+       text-shadow: 0 1px 1px #fff;
 }
 .oo-ui-capsuleItemWidget > .oo-ui-buttonElement {
        display: none;
 .oo-ui-searchWidget-query {
        height: 4em;
        padding: 0 1em;
-       border-bottom: 1px solid #cccccc;
+       border-bottom: 1px solid #9aa0a7;
 }
 .oo-ui-searchWidget-query .oo-ui-textInputWidget {
        margin: 0.75em 0;
        position: relative;
        max-width: 50em;
 }
-.oo-ui-numberInputWidget-field {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonWidget,
+.oo-ui-numberInputWidget-buttoned .oo-ui-textInputWidget {
        display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       width: 100%;
 }
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       white-space: nowrap;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonElement-button {
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
+.oo-ui-numberInputWidget-field {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonWidget {
        width: 2.5em;
 }
+.oo-ui-numberInputWidget-buttoned .oo-ui-buttonElement-button {
+       display: block;
+       padding-left: 0;
+       padding-right: 0;
+}
+.oo-ui-numberInputWidget-buttoned .oo-ui-textInputWidget input {
+       border-radius: 0;
+}
 .oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
        border-top-right-radius: 0;
        border-bottom-right-radius: 0;
        border-bottom-left-radius: 0;
        border-left-width: 0;
 }
-.oo-ui-numberInputWidget-buttoned .oo-ui-textInputWidget input {
-       border-radius: 0;
-}
index 7a38633..62195df 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
@@ -2170,7 +2170,7 @@ OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () {
  *     };
  *
  *     var card1 = new CardOneLayout( 'one' ),
- *         card2 = new CardLayout( 'two', { label: 'Card two' } );
+ *         card2 = new OO.ui.CardLayout( 'two', { label: 'Card two' } );
  *
  *     card2.$element.append( '<p>Second card</p>' );
  *
@@ -2709,7 +2709,7 @@ OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) {
        OO.ui.ToggleButtonWidget.parent.call( this, config );
 
        // Mixin constructors
-       OO.ui.mixin.ButtonElement.call( this, config );
+       OO.ui.mixin.ButtonElement.call( this, $.extend( {}, config, { active: this.active } ) );
        OO.ui.mixin.IconElement.call( this, config );
        OO.ui.mixin.IndicatorElement.call( this, config );
        OO.ui.mixin.LabelElement.call( this, config );
@@ -3057,7 +3057,7 @@ OO.inheritClass( OO.ui.OutlineOptionWidget, OO.ui.DecoratedOptionWidget );
 
 /* Static Properties */
 
-OO.ui.OutlineOptionWidget.static.highlightable = false;
+OO.ui.OutlineOptionWidget.static.highlightable = true;
 
 OO.ui.OutlineOptionWidget.static.scrollIntoViewOnSelect = true;
 
@@ -3098,6 +3098,22 @@ OO.ui.OutlineOptionWidget.prototype.getLevel = function () {
        return this.level;
 };
 
+/**
+ * @inheritdoc
+ */
+OO.ui.OutlineOptionWidget.prototype.setPressed = function ( state ) {
+       OO.ui.OutlineOptionWidget.parent.prototype.setPressed.call( this, state );
+       if ( this.constructor.static.pressable ) {
+               this.pressed = !!state;
+               if ( this.pressed ) {
+                       this.setFlags( 'progressive' );
+               } else if ( !this.selected ) {
+                       this.clearFlags();
+               }
+       }
+       return this;
+};
+
 /**
  * Set movability.
  *
@@ -3126,6 +3142,22 @@ OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
        return this;
 };
 
+/**
+ * @inheritdoc
+ */
+OO.ui.OutlineOptionWidget.prototype.setSelected = function ( state ) {
+       OO.ui.OutlineOptionWidget.parent.prototype.setSelected.call( this, state );
+       if ( this.constructor.static.selectable ) {
+               this.selected = !!state;
+               if ( this.selected ) {
+                       this.setFlags( 'progressive' );
+               } else {
+                       this.clearFlags();
+               }
+       }
+       return this;
+};
+
 /**
  * Set indentation level.
  *
@@ -3629,6 +3661,7 @@ OO.ui.CapsuleMultiselectWidget = function OoUiCapsuleMultiselectWidget( config )
        }
        this.menu.connect( this, {
                choose: 'onMenuChoose',
+               toggle: 'onMenuToggle',
                add: 'onMenuItemsChange',
                remove: 'onMenuItemsChange'
        } );
@@ -4174,6 +4207,16 @@ OO.ui.CapsuleMultiselectWidget.prototype.onMenuChoose = function ( item ) {
        }
 };
 
+/**
+ * Handle menu toggle events.
+ *
+ * @private
+ * @param {boolean} isVisible Menu toggle event
+ */
+OO.ui.CapsuleMultiselectWidget.prototype.onMenuToggle = function ( isVisible ) {
+       this.$element.toggleClass( 'oo-ui-capsuleMultiselectWidget-open', isVisible );
+};
+
 /**
  * Handle menu item change events.
  *
@@ -4480,13 +4523,6 @@ OO.ui.SelectFileWidget.prototype.updateUI = function () {
                                        .addClass( 'oo-ui-selectFileWidget-fileName' )
                                        .text( this.currentFile.name )
                        );
-                       if ( this.currentFile.type !== '' ) {
-                               $label = $label.add(
-                                       $( '<span>' )
-                                               .addClass( 'oo-ui-selectFileWidget-fileType' )
-                                               .text( this.currentFile.type )
-                               );
-                       }
                        this.setLabel( $label );
 
                        if ( this.showDropTarget ) {
@@ -5166,11 +5202,11 @@ OO.ui.NumberInputWidget.prototype.validateNumber = function ( value ) {
                return false;
        }
 
-       /*jshint bitwise: false */
+       /* eslint-disable no-bitwise */
        if ( this.isInteger && ( n | 0 ) !== n ) {
                return false;
        }
-       /*jshint bitwise: true */
+       /* eslint-enable no-bitwise */
 
        if ( n < this.min || n > this.max ) {
                return false;
index 2f6c1a0..3cff8f7 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-actionWidget.oo-ui-pendingElement-pending {
        background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
 .oo-ui-messageDialog-title {
        font-size: 1.5em;
        line-height: 1em;
-       color: #000000;
+       color: #000;
 }
 .oo-ui-messageDialog-message {
        font-size: 0.9em;
        line-height: 1.25em;
-       color: #666666;
+       color: #666;
 }
 .oo-ui-messageDialog-message-verbose {
        font-size: 1.1em;
 }
 .oo-ui-processDialog-errors-title {
        font-size: 1.5em;
-       color: #000000;
+       color: #000;
        margin-bottom: 2em;
 }
 .oo-ui-processDialog-error {
                transition: opacity 250ms ease;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
-       background-color: #ffffff;
+       background-color: #fff;
        opacity: 0;
        -webkit-transform: scale(0.5);
           -moz-transform: scale(0.5);
        bottom: 1em;
        max-height: 100%;
        max-height: calc(100% - 2em);
-       border: 1px solid #cccccc;
+       border: 1px solid #ccc;
        border-radius: 0.5em;
        box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.3);
 }
index 465e17b..2c115f9 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:53Z
+ * Date: 2016-09-13T18:30:06Z
  */
 .oo-ui-window {
        background: transparent;
@@ -95,7 +95,7 @@
        white-space: nowrap;
 }
 .oo-ui-messageDialog-content > .oo-ui-window-foot {
-       outline: 1px solid #aaaaaa;
+       outline: 1px solid #aaa;
 }
 .oo-ui-messageDialog-title,
 .oo-ui-messageDialog-message {
 .oo-ui-messageDialog-title {
        font-size: 1.5em;
        line-height: 1;
-       color: #000000;
+       color: #000;
 }
 .oo-ui-messageDialog-message {
        font-size: 0.9em;
        line-height: 1.25;
-       color: #555555;
+       color: #222;
 }
 .oo-ui-messageDialog-message-verbose {
        font-size: 1.1em;
        text-align: left;
 }
 .oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       border-right: 1px solid #cccccc;
+       border-right: 1px solid #9aa0a7;
        margin: 0;
 }
 .oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
        border-right-width: 0;
 }
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       border-bottom: 1px solid #cccccc;
+       border-bottom: 1px solid #9aa0a7;
        margin: 0;
 }
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
 }
 .oo-ui-processDialog-errors-title {
        font-size: 1.5em;
-       color: #000000;
+       color: #000;
        margin-bottom: 2em;
 }
 .oo-ui-processDialog-error {
                transition: opacity 250ms;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
-       background-color: #ffffff;
+       background-color: #fff;
        opacity: 0;
        -webkit-transform: scale(0.5);
           -moz-transform: scale(0.5);
        bottom: 1em;
        max-height: 100%;
        max-height: calc(100% - 2em);
-       border: 1px solid #aaaaaa;
+       border: 1px solid #a2a9b1;
        border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
index 510399d..8ef5ea5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.8
+ * OOjs UI v0.17.9
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-08-16T21:13:48Z
+ * Date: 2016-09-13T18:30:02Z
  */
 ( function ( OO ) {
 
@@ -183,6 +183,7 @@ OO.ui.ActionWidget.prototype.toggle = function () {
        return this;
 };
 
+/* eslint-disable no-unused-vars */
 /**
  * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them.
  * Actions can be made available for specific contexts (modes) and circumstances
@@ -283,6 +284,7 @@ OO.ui.ActionSet = function OoUiActionSet( config ) {
        this.changing = false;
        this.changed = false;
 };
+/* eslint-enable no-unused-vars */
 
 /* Setup */
 
@@ -1545,7 +1547,7 @@ OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
                $body = $( this.getElementDocument().body ),
                // We could have multiple window managers open so only modify
                // the body css at the bottom of the stack
-               stackDepth = $body.data( 'windowManagerGlobalEvents' ) || 0 ;
+               stackDepth = $body.data( 'windowManagerGlobalEvents' ) || 0;
 
        on = on === undefined ? !!this.globalEvents : !!on;
 
index 7dc6987..bd048c4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.png differ
index 9a7ce13..3ebb113 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.png differ
index d9f5d75..0b4270d 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/info.png and b/resources/lib/oojs-ui/themes/apex/images/icons/info.png differ
index 5d3b9f9..d5bba7a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/listBullet-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/listBullet-rtl.png differ
index f9fcbba..2623b84 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png differ
index a4dad7f..4d0ce86 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png differ
index 2517166..ca07bcc 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr-invert.png and b/resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr-invert.png differ
index 0084ac9..a7f6717 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/printer-ltr.png differ
index 37a9e3d..95ef54a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/printer-rtl-invert.png and b/resources/lib/oojs-ui/themes/apex/images/icons/printer-rtl-invert.png differ
index 9254844..feff2ba 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png differ
index 64c2148..fe3eb18 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-ltr.png differ
index 650be0c..ddaafe9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-column-rtl.png differ
index 67d488d..31a2306 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-row-after.png and b/resources/lib/oojs-ui/themes/apex/images/icons/table-insert-row-after.png differ
index 3957e7a..7f14fca 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/trash.png and b/resources/lib/oojs-ui/themes/apex/images/icons/trash.png differ
index dce4a9f..87590fa 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-ltr.png differ
index f3801e4..dd35023 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/trashUndo-rtl.png differ
index 6894d6e..f5694a1 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index c04573f..651cddf 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 40516c0..6b9e490 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index d70f60e..11fcef7 100644 (file)
@@ -4,17 +4,18 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347bff"
+                       "color": "#36c",
+                       "global": true
                },
                "constructive": {
-                       "color": "#347bff"
+                       "color": "#36c"
                },
                "destructive": {
-                       "color": "#d11d13"
+                       "color": "#c33"
                },
                "warning": {
                        "color": "#ff5d00"
index 489d6ca..cd4087e 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 5c3d03a..d168364 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index c3263e2..7efe531 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index efad42e..765b8fe 100644 (file)
@@ -4,17 +4,18 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347bff"
+                       "color": "#36c",
+                       "global": true
                },
                "constructive": {
-                       "color": "#347bff"
+                       "color": "#36c"
                },
                "destructive": {
-                       "color": "#d11d13"
+                       "color": "#c33"
                },
                "warning": {
                        "color": "#ff5d00"
index c53946f..c844449 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 56da53c..3911956 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 65c7c5b..e98012f 100644 (file)
@@ -4,17 +4,18 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347bff"
+                       "color": "#36c",
+                       "global": true
                },
                "constructive": {
-                       "color": "#347bff"
+                       "color": "#36c"
                },
                "destructive": {
-                       "color": "#d11d13"
+                       "color": "#c33"
                },
                "warning": {
                        "color": "#ff5d00"
index c216c37..c545a49 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 1b9359a..39fdda5 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index cf19c6c..bac9768 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 047bc6b..6a7c565 100644 (file)
@@ -4,20 +4,18 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347bff"
+                       "color": "#36c",
+                       "global": true
                },
                "constructive": {
-                       "color": "#347bff"
-               },
-               "constructive-deprecated": {
-                       "color": "#00af89"
+                       "color": "#36c"
                },
                "destructive": {
-                       "color": "#d11d13"
+                       "color": "#c33"
                },
                "warning": {
                        "color": "#ff5d00"
@@ -28,8 +26,8 @@
                "advanced": { "file": "images/icons/advanced.svg" },
                "alert": { "file": "images/icons/alert.svg", "variants": [ "warning" ] },
                "cancel": { "file": "images/icons/cancel.svg", "variants": [ "destructive" ] },
-               "check": { "file": "images/icons/check.svg", "variants": [ "constructive-deprecated", "constructive", "progressive", "destructive" ] },
-               "circle": { "file": "images/icons/circle.svg", "variants": [ "constructive-deprecated", "constructive", "progressive" ] },
+               "check": { "file": "images/icons/check.svg", "variants": [ "constructive", "progressive", "destructive" ] },
+               "circle": { "file": "images/icons/circle.svg", "variants": [ "constructive", "progressive" ] },
                "close": { "file": {
                        "ltr": "images/icons/close-ltr.svg",
                        "rtl": "images/icons/close-rtl.svg"
@@ -68,7 +66,7 @@
                        "rtl": "images/icons/search-rtl.svg"
                } },
                "settings": { "file": "images/icons/settings.svg" },
-               "tag": { "file": "images/icons/tag.svg", "variants": [ "destructive", "warning", "constructive", "progressive" ] },
+               "tag": { "file": "images/icons/tag.svg", "variants": [ "destructive", "warning", "constructive" ] },
                "undo": { "file": {
                        "ltr": "images/icons/arched-arrow-rtl.svg",
                        "rtl": "images/icons/arched-arrow-ltr.svg"
index 02d4f12..42209e7 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png differ
index 34a4bba..94b9b7b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <g id="add">
         <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
index bedfe5a..55f0013 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="add">
         <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
index 02d4f12..42209e7 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-progressive.png differ
index 34a4bba..94b9b7b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <g id="add">
         <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
index 9cf7fab..8e5d7ac 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 13.44v-2.88l-1.8-.3c-.1-.397-.3-.794-.6-1.39l1.1-1.49-2.1-2.088-1.5 1.093c-.5-.298-1-.497-1.4-.596L13.5 4h-2.9l-.3 1.79c-.5.098-.9.297-1.4.595L7.4 5.292 5.3 7.38l1 1.49c-.3.496-.4.894-.6 1.39l-1.7.2v2.882l1.8.298c.1.497.3.894.6 1.39l-1 1.492 2.1 2.087 1.5-1c.4.2.9.395 1.4.594l.3 1.79h3l.3-1.79c.5-.1.9-.298 1.4-.596l1.5 1.092 2.1-2.08-1.1-1.49c.3-.496.5-.993.6-1.39l1.5-.3zm-8 1.492c-1.7 0-3-1.292-3-2.982 0-1.69 1.3-2.98 3-2.98s3 1.29 3 2.98-1.3 2.982-3 2.982z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.png
new file mode 100644 (file)
index 0000000..e64bd2a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-progressive.svg
new file mode 100644 (file)
index 0000000..b8d38f5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 13.44v-2.88l-1.8-.3c-.1-.397-.3-.794-.6-1.39l1.1-1.49-2.1-2.088-1.5 1.093c-.5-.298-1-.497-1.4-.596L13.5 4h-2.9l-.3 1.79c-.5.098-.9.297-1.4.595L7.4 5.292 5.3 7.38l1 1.49c-.3.496-.4.894-.6 1.39l-1.7.2v2.882l1.8.298c.1.497.3.894.6 1.39l-1 1.492 2.1 2.087 1.5-1c.4.2.9.395 1.4.594l.3 1.79h3l.3-1.79c.5-.1.9-.298 1.4-.596l1.5 1.092 2.1-2.08-1.1-1.49c.3-.496.5-.993.6-1.39l1.5-.3zm-8 1.492c-1.7 0-3-1.292-3-2.982 0-1.69 1.3-2.98 3-2.98s3 1.29 3 2.98-1.3 2.982-3 2.982z"/>
+</svg>
index 74bb91d..d72ac35 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="alert">
         <path id="point" d="M11 16h2v2h-2z"/>
         <path id="stroke" d="M13.516 10h-3L11 15h2z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.png
new file mode 100644 (file)
index 0000000..66973b8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-progressive.svg
new file mode 100644 (file)
index 0000000..ddbb983
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="alert">
+        <path id="point" d="M11 16h2v2h-2z"/>
+        <path id="stroke" d="M13.516 10h-3L11 15h2z"/>
+        <path id="triangle" d="M12.017 5.974L19.537 19H4.497l7.52-13.026m0-2.474c-.545 0-1.09.357-1.5 1.07L2.53 18.403C1.705 19.833 2.38 21 4.03 21H20c1.65 0 2.325-1.17 1.5-2.6L13.517 4.575c-.413-.715-.956-1.072-1.5-1.072z"/>
+    </g>
+</svg>
index 0343b3f..a3495eb 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-invert.png differ
index 197e037..dc3a39a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H9c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm-5.5 9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0-12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-center"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.png
new file mode 100644 (file)
index 0000000..9032e12
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-progressive.svg
new file mode 100644 (file)
index 0000000..a88b9fe
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H9c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm-5.5 9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0-12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-center"/>
+</svg>
index 6d3d375..e274be4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-invert.png differ
index 663ba94..770e354 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H4c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm9.5 0h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm-10-9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0 12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-float-left"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.png
new file mode 100644 (file)
index 0000000..d37112a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-progressive.svg
new file mode 100644 (file)
index 0000000..ff0257f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H4c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm9.5 0h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm-10-9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0 12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-float-left"/>
+</svg>
index 655d7cc..c6659fb 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-invert.png differ
index 41be069..014fb7e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 9h-6c-.554 0-1 .446-1 1v5c0 .554.446 1 1 1h6c.554 0 1-.446 1-1v-5c0-.554-.446-1-1-1zm-9.5 0h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm10-9h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1zm0 12h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1z" id="align-float-right"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.png
new file mode 100644 (file)
index 0000000..8d3f480
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-progressive.svg
new file mode 100644 (file)
index 0000000..37ec2b5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 9h-6c-.554 0-1 .446-1 1v5c0 .554.446 1 1 1h6c.554 0 1-.446 1-1v-5c0-.554-.446-1-1-1zm-9.5 0h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm10-9h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1zm0 12h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1z" id="align-float-right"/>
+</svg>
index 3dc5125..9e18fd2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M13.3 6.3l6.3 5.7-6.3 5.7v-3.8H12c-3.2 0-6.3 1.3-7.6 3.8 0-4.7 2.8-7.6 7.9-7.6h.9V6.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.png
new file mode 100644 (file)
index 0000000..28f8835
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..ee78b93
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M13.3 6.3l6.3 5.7-6.3 5.7v-3.8H12c-3.2 0-6.3 1.3-7.6 3.8 0-4.7 2.8-7.6 7.9-7.6h.9V6.3z"/>
+</svg>
index 0e2c2f3..5bbad61 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10.7 6.3L4.4 12l6.3 5.7v-3.8H12c3.2 0 6.3 1.3 7.6 3.8 0-4.7-2.8-7.6-7.9-7.6h-.9V6.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.png
new file mode 100644 (file)
index 0000000..11f0c84
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..d9ad31e
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10.7 6.3L4.4 12l6.3 5.7v-3.8H12c3.2 0 6.3 1.3 7.6 3.8 0-4.7-2.8-7.6-7.9-7.6h-.9V6.3z"/>
+</svg>
index 3c5c48b..7ec8b03 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 12H6c-1.7 0-3 1.3-3 3h13v3l5-4.5L16 9v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.png
new file mode 100644 (file)
index 0000000..119a8be
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..1317492
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 12H6c-1.7 0-3 1.3-3 3h13v3l5-4.5L16 9v3z"/>
+</svg>
index da0c6ec..38c0f88 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M8 12h10c1.7 0 3 1.3 3 3H8v3l-5-4.5L8 9v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.png
new file mode 100644 (file)
index 0000000..3192d35
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e56da3c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M8 12h10c1.7 0 3 1.3 3 3H8v3l-5-4.5L8 9v3z"/>
+</svg>
index 8c264dd..231cb1b 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-invert.png differ
index 80e7992..5caf7dd 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 10h4V5h-4v5zm-5 2h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zM5 3h13v16H8c-1.7 0-3-1.3-3-3V3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.png
new file mode 100644 (file)
index 0000000..db45541
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..cd35d36
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 10h4V5h-4v5zm-5 2h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zM5 3h13v16H8c-1.7 0-3-1.3-3-3V3z"/>
+</svg>
index ab20718..7b83a3a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-invert.png differ
index f0a4a6a..19a1d72 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M11 10H7V5h4v5zm5 2H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zm6-2H5v16h10c1.7 0 3-1.3 3-3V3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.png
new file mode 100644 (file)
index 0000000..aba3c0b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..b04ecae
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M11 10H7V5h4v5zm5 2H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zm6-2H5v16h10c1.7 0 3-1.3 3-3V3z"/>
+</svg>
index 77febef..1f30f59 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 11l-6 7-4-4-1 1 5 5 7-8z"/>
     <path d="M17 14V3H4v13c0 1.7 1.3 3 3 3h5l-3-3H6v-1h2.6l1-1H6v-1h9v1h-2l1 1h2l1-1zM6 5h4v1H6V5zm0 2h4v1H6V7zm0 2h4v1H6V9zm9 3H6v-1h9v1zm-4-2V5h4v5h-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.png
new file mode 100644 (file)
index 0000000..688ec05
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b554100
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 11l-6 7-4-4-1 1 5 5 7-8z"/>
+    <path d="M17 14V3H4v13c0 1.7 1.3 3 3 3h5l-3-3H6v-1h2.6l1-1H6v-1h9v1h-2l1 1h2l1-1zM6 5h4v1H6V5zm0 2h4v1H6V7zm0 2h4v1H6V9zm9 3H6v-1h9v1zm-4-2V5h4v5h-4z"/>
+</svg>
index c80c83f..f4dc6e9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M5 11l6 7 4-4 1 1-5 5-7-8z"/>
     <path d="M9 14V3h13v13c0 1.7-1.3 3-3 3h-5l3-3h3v-1h-2.6l-1-1H20v-1h-9v1h2l-1 1h-2l-1-1zm11-9h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm-9 3h9v-1h-9v1zm4-2V5h-4v5h4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.png
new file mode 100644 (file)
index 0000000..b271ac5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..7a47a0e
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M5 11l6 7 4-4 1 1-5 5-7-8z"/>
+    <path d="M9 14V3h13v13c0 1.7-1.3 3-3 3h-5l3-3h3v-1h-2.6l-1-1H20v-1h-9v1h2l-1 1h-2l-1-1zm11-9h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm-9 3h9v-1h-9v1zm4-2V5h-4v5h4z"/>
+</svg>
index 0ed1d31..8b566ba 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="article-redirect">
         <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
         <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7b22a84
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..49c1975
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="article-redirect">
+        <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+        <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
+    </g>
+</svg>
index 1a5b22a..9bce7f2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="article-redirect">
         <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
         <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156a7.097 7.097 0 0 1-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094a178.903 178.903 0 0 1-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.png
new file mode 100644 (file)
index 0000000..464c344
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..98a0d4c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="article-redirect">
+        <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
+        <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156a7.097 7.097 0 0 1-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094a178.903 178.903 0 0 1-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
+    </g>
+</svg>
index de091d9..1febaf2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19.1 18.5c.6-.7.9-1.5.9-2.5 0-2.2-1.8-4-4-4s-4 1.8-4 4 1.8 4 4 4c.7 0 1.3-.1 1.8-.4l2.7 2.7 1.1-1.1-2.5-2.7zm-3.1-.3c-1.2 0-2.2-1-2.2-2.3 0-1.2 1-2.2 2.2-2.2 1.2 0 2.3 1 2.3 2.2-.1 1.3-1.1 2.3-2.3 2.3zM11.8 13c.3-.4.6-.7 1-1H7v-1h9s1.2 0 2 .6V3H5v13c0 1.7 1.3 3 3 3h3.8c-.6-.8-1-1.9-1-3H7v-1h3.9l.3-1H7v-1h4.8zm.2-8h4v5h-4V5zM7 5h4v1H7V5zm0 2h4v1H7V7zm0 2h4v1H7V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.png
new file mode 100644 (file)
index 0000000..d22174e
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..394203a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19.1 18.5c.6-.7.9-1.5.9-2.5 0-2.2-1.8-4-4-4s-4 1.8-4 4 1.8 4 4 4c.7 0 1.3-.1 1.8-.4l2.7 2.7 1.1-1.1-2.5-2.7zm-3.1-.3c-1.2 0-2.2-1-2.2-2.3 0-1.2 1-2.2 2.2-2.2 1.2 0 2.3 1 2.3 2.2-.1 1.3-1.1 2.3-2.3 2.3zM11.8 13c.3-.4.6-.7 1-1H7v-1h9s1.2 0 2 .6V3H5v13c0 1.7 1.3 3 3 3h3.8c-.6-.8-1-1.9-1-3H7v-1h3.9l.3-1H7v-1h4.8zm.2-8h4v5h-4V5zM7 5h4v1H7V5zm0 2h4v1H7V7zm0 2h4v1H7V9z"/>
+</svg>
index abb7ce7..be69d2a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7.5 18.5c-.6-.7-.9-1.5-.9-2.5 0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4c-.7 0-1.3-.1-1.8-.4l-2.7 2.7L5 21.2l2.5-2.7zm3.1-.3c1.2 0 2.2-1 2.2-2.3 0-1.2-1-2.2-2.2-2.2-1.2 0-2.3 1-2.3 2.2.1 1.3 1.1 2.3 2.3 2.3zm4.2-5.2c-.3-.4-.6-.7-1-1h5.8v-1h-9s-1.2 0-2 .6V3h13v13c0 1.7-1.3 3-3 3h-3.8c.6-.8 1-1.9 1-3h3.8v-1h-3.9l-.3-1h4.2v-1h-4.8zm-.2-8h-4v5h4V5zm5 0h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.png
new file mode 100644 (file)
index 0000000..e83f1ee
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..47768e9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7.5 18.5c-.6-.7-.9-1.5-.9-2.5 0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4c-.7 0-1.3-.1-1.8-.4l-2.7 2.7L5 21.2l2.5-2.7zm3.1-.3c1.2 0 2.2-1 2.2-2.3 0-1.2-1-2.2-2.2-2.2-1.2 0-2.3 1-2.3 2.2.1 1.3 1.1 2.3 2.3 2.3zm4.2-5.2c-.3-.4-.6-.7-1-1h5.8v-1h-9s-1.2 0-2 .6V3h13v13c0 1.7-1.3 3-3 3h-3.8c.6-.8 1-1.9 1-3h3.8v-1h-3.9l-.3-1h4.2v-1h-4.8zm-.2-8h-4v5h4V5zm5 0h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9z"/>
+</svg>
index edfe406..3a418f4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.png differ
index 8397962..2ac7914 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M11 12h4V7h-4v5zm-5 2h9v-1H6v1zm0 2h9v-1H6v1zm0 2h9v-1H6v1zm4-9H6v1h4V9zm0 2H6v1h4v-1zm0-4H6v1h4V7zM4 5h13v16H7c-1.7 0-3-1.3-3-3V5z"/>
     <path d="M18 4v14h2V2H7v2" fill-rule="evenodd"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.png
new file mode 100644 (file)
index 0000000..af2b7f0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..678916b
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M11 12h4V7h-4v5zm-5 2h9v-1H6v1zm0 2h9v-1H6v1zm0 2h9v-1H6v1zm4-9H6v1h4V9zm0 2H6v1h4v-1zm0-4H6v1h4V7zM4 5h13v16H7c-1.7 0-3-1.3-3-3V5z"/>
+    <path d="M18 4v14h2V2H7v2" fill-rule="evenodd"/>
+</svg>
index 7dc6987..bd048c4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.png differ
index 153d534..fe548a1 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.png differ
index 12432a2..e45e450 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M13 12H9V7h4v5zm5 2H9v-1h9v1zm0 2H9v-1h9v1zm0 2H9v-1h9v1zm-4-9h4v1h-4V9zm0 2h4v1h-4v-1zm0-4h4v1h-4V7zm6-2H7v16h10c1.7 0 3-1.3 3-3V5z"/>
     <path d="M6 4v14H4V2h13v2" fill-rule="evenodd"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.png
new file mode 100644 (file)
index 0000000..839f135
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e4ca34b
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M13 12H9V7h4v5zm5 2H9v-1h9v1zm0 2H9v-1h9v1zm0 2H9v-1h9v1zm-4-9h4v1h-4V9zm0 2h4v1h-4v-1zm0-4h4v1h-4V7zm6-2H7v16h10c1.7 0 3-1.3 3-3V5z"/>
+    <path d="M6 4v14H4V2h13v2" fill-rule="evenodd"/>
+</svg>
index 9a7ce13..3ebb113 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.png differ
index 67c3ae9..8e7de03 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24"><style>* { fill: #fff }</style>
     <path d="M-274.3 390.9c-1.6-1.6-4.3-1.5-5.8.1l.2.2c.5.5 1.3.7 2.1.4.8-.3 1.7-.1 2.4.6 1 .9.9 2.4 0 3.4l-7.1 7.1c-.9 1-2.4.9-3.4 0s-.9-2.4 0-3.4l4.4-4.4c.3-.3.9-.5 1.3-.1s.2 1-.1 1.3l-3.4 3.4c-.6.6-.6 1.7.1 2.3l4.3-4.3c.8-.8 1.1-1.8.9-2.7-.2-.9-.9-1.6-1.7-1.9-.9-.2-1.9 0-2.6.7l-4.4 4.4c-1.6 1.6-1.6 4.3.1 5.8 1.5 1.6 4.3 1.5 5.8-.1l7-7c.8-.8 1.2-1.9 1.2-3s-.5-2.1-1.3-2.8c-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-1.5-1.6.8.7 0 0z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.png
new file mode 100644 (file)
index 0000000..9723ae9
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c062052
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24"><style>* { fill: #36c }</style>
+    <path d="M-274.3 390.9c-1.6-1.6-4.3-1.5-5.8.1l.2.2c.5.5 1.3.7 2.1.4.8-.3 1.7-.1 2.4.6 1 .9.9 2.4 0 3.4l-7.1 7.1c-.9 1-2.4.9-3.4 0s-.9-2.4 0-3.4l4.4-4.4c.3-.3.9-.5 1.3-.1s.2 1-.1 1.3l-3.4 3.4c-.6.6-.6 1.7.1 2.3l4.3-4.3c.8-.8 1.1-1.8.9-2.7-.2-.9-.9-1.6-1.7-1.9-.9-.2-1.9 0-2.6.7l-4.4 4.4c-1.6 1.6-1.6 4.3.1 5.8 1.5 1.6 4.3 1.5 5.8-.1l7-7c.8-.8 1.2-1.9 1.2-3s-.5-2.1-1.3-2.8c-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-1.5-1.6.8.7 0 0z"/>
+</svg>
index 61e11d0..91304a1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24"><style>* { fill: #fff }</style>
     <path d="M-113.3 75.6c-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7-1.3 1.7-1.3 2.8 0 1.1.4 2.2 1.2 3l7 7c1.5 1.6 4.3 1.7 5.8.1 1.7-1.5 1.7-4.2.1-5.8l-4.4-4.4c-.7-.7-1.7-.9-2.6-.7-.8.3-1.5 1-1.7 1.9-.2.9.1 1.9.9 2.7l4.3 4.3c.7-.6.7-1.7.1-2.3l-3.4-3.4c-.3-.3-.5-.9-.1-1.3s1-.2 1.3.1l4.4 4.4c.9 1 1 2.5 0 3.4s-2.5 1-3.4 0l-7.1-7.1c-.9-1-1-2.5 0-3.4.7-.7 1.6-.9 2.4-.6.8.3 1.6.1 2.1-.4l.2-.2c-1.5-1.6-4.2-1.8-5.8-.1-.8.7 1.5-1.7 0 0z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.png
new file mode 100644 (file)
index 0000000..8786e5a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6185c05
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24"><style>* { fill: #36c }</style>
+    <path d="M-113.3 75.6c-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7-1.3 1.7-1.3 2.8 0 1.1.4 2.2 1.2 3l7 7c1.5 1.6 4.3 1.7 5.8.1 1.7-1.5 1.7-4.2.1-5.8l-4.4-4.4c-.7-.7-1.7-.9-2.6-.7-.8.3-1.5 1-1.7 1.9-.2.9.1 1.9.9 2.7l4.3 4.3c.7-.6.7-1.7.1-2.3l-3.4-3.4c-.3-.3-.5-.9-.1-1.3s1-.2 1.3.1l4.4 4.4c.9 1 1 2.5 0 3.4s-2.5 1-3.4 0l-7.1-7.1c-.9-1-1-2.5 0-3.4.7-.7 1.6-.9 2.4-.6.8.3 1.6.1 2.1-.4l.2-.2c-1.5-1.6-4.2-1.8-5.8-.1-.8.7 1.5-1.7 0 0z"/>
+</svg>
index 645c9cc..3762e49 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17.5 13V8c0-3-2.3-5-5.5-5S6.5 5 6.5 8v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 19H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.png
new file mode 100644 (file)
index 0000000..81294ff
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-progressive.svg
new file mode 100644 (file)
index 0000000..7a6b31c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M17.5 13V8c0-3-2.3-5-5.5-5S6.5 5 6.5 8v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 19H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
+</svg>
index 07de130..6f76157 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17.8 13.7L19.5 9c1-2.8-.5-5.5-3.5-6.6-3-1.1-5.9 0-6.9 2.8L7.4 9.9c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 18.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.png
new file mode 100644 (file)
index 0000000..91641ac
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..0a3d93d
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M17.8 13.7L19.5 9c1-2.8-.5-5.5-3.5-6.6-3-1.1-5.9 0-6.9 2.8L7.4 9.9c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 18.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
+</svg>
index 30617cb..886a34c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6.21 13.7L4.51 9c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.png
new file mode 100644 (file)
index 0000000..eddcf73
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..2b0d7df
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6.21 13.7L4.51 9c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
+</svg>
index d463577..0f5bf22 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 12l-3-2-1 4-1-4-3 2 2-3-4-1 4-1-2-3 3 2 1-4 1 4 3-2-2 3 4 1-4 1 2 3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.png
new file mode 100644 (file)
index 0000000..4c67bac
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-progressive.svg
new file mode 100644 (file)
index 0000000..84be0ce
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 12l-3-2-1 4-1-4-3 2 2-3-4-1 4-1-2-3 3 2 1-4 1 4 3-2-2 3 4 1-4 1 2 3z"/>
+</svg>
index 2b8ee7a..21548d9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15.3 14.7C16.1 10.9 14.7 4 12 4c-2.7 0-4.2 6.7-3.4 10.5L7 18h2.7l.3 1h4c.2-.3.1-.5.3-1H17l-1.7-3.3zM12 10c-.8 0-1.5-.7-1.5-1.5S11.2 7 12 7s1.5.7 1.5 1.5S12.8 10 12 10zm2 10c0 1.1-2 2-2 2s-2-.9-2-2"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.png
new file mode 100644 (file)
index 0000000..6afb2bd
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-progressive.svg
new file mode 100644 (file)
index 0000000..359d032
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15.3 14.7C16.1 10.9 14.7 4 12 4c-2.7 0-4.2 6.7-3.4 10.5L7 18h2.7l.3 1h4c.2-.3.1-.5.3-1H17l-1.7-3.3zM12 10c-.8 0-1.5-.7-1.5-1.5S11.2 7 12 7s1.5.7 1.5 1.5S12.8 10 12 10zm2 10c0 1.1-2 2-2 2s-2-.9-2-2"/>
+</svg>
index 41d644a..26f9653 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
     <g id="up">
         <path id="arrow" d="M15.5 9h7L19 3z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.png
new file mode 100644 (file)
index 0000000..183aaeb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..fedf787
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
+    <g id="up">
+        <path id="arrow" d="M15.5 9h7L19 3z"/>
+    </g>
+</svg>
index 4101ddf..aeb562c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
     <g id="up">
         <path id="arrow" d="M1.5 9h7L5 3z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.png
new file mode 100644 (file)
index 0000000..47af038
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6c7b5fc
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
+    <g id="up">
+        <path id="arrow" d="M1.5 9h7L5 3z"/>
+    </g>
+</svg>
index 31f14c5..53460be 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-destructive.png differ
index 2cf60bc..abf656f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9H7v-2h10v2z"/>
 </svg>
index a2f916e..2807149 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9H7v-2h10v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.png
new file mode 100644 (file)
index 0000000..c82c3eb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/block-progressive.svg
new file mode 100644 (file)
index 0000000..abcc7e0
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9H7v-2h10v2z"/>
+</svg>
index 1d41a44..36fd719 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17 11v2h-2l3.6 3.6c.9-1.3 1.4-2.9 1.4-4.6 0-4.4-3.6-8-8-8-1.7 0-3.3.5-4.6 1.4L13 11h4zM4 4L3 5l2.4 2.4C4.5 8.7 4 10.3 4 12c0 4.4 3.6 8 8 8 1.7 0 3.3-.5 4.6-1.4L19 21l1-1L4 4zm3 9v-2h2l2 2H7z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.png
new file mode 100644 (file)
index 0000000..d797f3e
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..bc96e99
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M17 11v2h-2l3.6 3.6c.9-1.3 1.4-2.9 1.4-4.6 0-4.4-3.6-8-8-8-1.7 0-3.3.5-4.6 1.4L13 11h4zM4 4L3 5l2.4 2.4C4.5 8.7 4 10.3 4 12c0 4.4 3.6 8 8 8 1.7 0 3.3-.5 4.6-1.4L19 21l1-1L4 4zm3 9v-2h2l2 2H7z"/>
+</svg>
index ca592ed..b6f1610 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 11v2h2l-3.6 3.6C4.5 15.3 4 13.7 4 12c0-4.4 3.6-8 8-8 1.7 0 3.3.5 4.6 1.4L11 11H7zm13-7l1 1-2.4 2.4c.9 1.3 1.4 2.9 1.4 4.6 0 4.4-3.6 8-8 8-1.7 0-3.3-.5-4.6-1.4L5 21l-1-1L20 4zm-3 9v-2h-2l-2 2h4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.png
new file mode 100644 (file)
index 0000000..4d062cb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..dc03220
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 11v2h2l-3.6 3.6C4.5 15.3 4 13.7 4 12c0-4.4 3.6-8 8-8 1.7 0 3.3.5 4.6 1.4L11 11H7zm13-7l1 1-2.4 2.4c.9 1.3 1.4 2.9 1.4 4.6 0 4.4-3.6 8-8 8-1.7 0-3.3-.5-4.6-1.4L5 21l-1-1L20 4zm-3 9v-2h-2l-2 2h4z"/>
+</svg>
index 519f850..f14a008 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 18h3L14 6h-3L6 18h3l1.25-3h4.5L16 18zm-4.917-5L12.5 9.6l1.417 3.4h-2.834z" id="bold-a"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.png
new file mode 100644 (file)
index 0000000..36f6920
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-progressive.svg
new file mode 100644 (file)
index 0000000..6bf90f6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 18h3L14 6h-3L6 18h3l1.25-3h4.5L16 18zm-4.917-5L12.5 9.6l1.417 3.4h-2.834z" id="bold-a"/>
+</svg>
index e43c4b5..ed986c8 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-arab-ain">
         <path id="arab-ain" d="M9.337 13.616c0 1.35 1.386 2.1 4.16 2.258l2.186-.03.318.045c-.03.12-.25.34-.66.65l-.09.06c-1.233.93-2.42 1.394-3.56 1.394-1.14 0-2.043-.33-2.71-.99-.65-.66-.973-1.56-.973-2.7.006-1.353.567-2.572 1.685-3.657v-.044l-.606-.55a.952.952 0 0 1-.222-.63c0-.49.24-1.11.72-1.863.65-1.045 1.302-1.565 1.957-1.56.886.005 1.618.42 2.194 1.246.325.48-.03.55-1.064.22-.843-.33-1.528-.05-2.055.826l.016.074 1.125.866.05.005c1.405-.497 2.42-.74 3.044-.725-.06.116-.14.36-.244.732a27.75 27.75 0 0 1-.304.982l-.125.372-.386.05c-1.743.24-2.992.716-3.745 1.43-.465.463-.7.972-.704 1.524"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.png
new file mode 100644 (file)
index 0000000..0e9c1b0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-progressive.svg
new file mode 100644 (file)
index 0000000..081c571
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-arab-ain">
+        <path id="arab-ain" d="M9.337 13.616c0 1.35 1.386 2.1 4.16 2.258l2.186-.03.318.045c-.03.12-.25.34-.66.65l-.09.06c-1.233.93-2.42 1.394-3.56 1.394-1.14 0-2.043-.33-2.71-.99-.65-.66-.973-1.56-.973-2.7.006-1.353.567-2.572 1.685-3.657v-.044l-.606-.55a.952.952 0 0 1-.222-.63c0-.49.24-1.11.72-1.863.65-1.045 1.302-1.565 1.957-1.56.886.005 1.618.42 2.194 1.246.325.48-.03.55-1.064.22-.843-.33-1.528-.05-2.055.826l.016.074 1.125.866.05.005c1.405-.497 2.42-.74 3.044-.725-.06.116-.14.36-.244.732a27.75 27.75 0 0 1-.304.982l-.125.372-.386.05c-1.743.24-2.992.716-3.745 1.43-.465.463-.7.972-.704 1.524"/>
+    </g>
+</svg>
index 7f24cbc..d8ecaa0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-arab-dad">
         <path id="arab-dad" d="M16.41 8.232l-1.675-.665L15.43 6l1.687.64-.707 1.592m.775 3.078c-.51-.286-1-.427-1.476-.423-.478 0-.99.205-1.54.616l-.505.38.006.024c1.085.066 1.935.1 2.55.1h.315c.57-.022.994-.065 1.278-.132-.067-.17-.275-.36-.625-.566h-.006M10.38 14.6c-.016-.905-.33-1.87-.937-2.9l1.294-1.73.118.15c.267.337.504.925.713 1.767l.064.05c.496-.007.942-.17 1.338-.484v-.006l1.732-1.53c.68-.6 1.282-.9 1.807-.9.383.003.85.194 1.394.57.55.378.884.697 1 .96.063.15.094.385.094.71 0 .694-.11 1.227-.33 1.596-.193.31-.474.555-.845.734-.438.208-1.55.312-3.333.312-.8 0-1.794-.02-2.982-.064l-.142.43c-.254.67-.463 1.112-.625 1.323-.724.937-1.785 1.405-3.182 1.405-1.71-.006-2.56-.92-2.56-2.74.003-.94.278-1.814.824-2.618.15-.216.298-.367.444-.454.225-.133.288-.09.188.124-.396.862-.596 1.548-.6 2.058.008 1.177.752 1.768 2.232 1.772 1.038-.004 1.803-.182 2.295-.535"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.png
new file mode 100644 (file)
index 0000000..8158ca1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-progressive.svg
new file mode 100644 (file)
index 0000000..c314577
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-arab-dad">
+        <path id="arab-dad" d="M16.41 8.232l-1.675-.665L15.43 6l1.687.64-.707 1.592m.775 3.078c-.51-.286-1-.427-1.476-.423-.478 0-.99.205-1.54.616l-.505.38.006.024c1.085.066 1.935.1 2.55.1h.315c.57-.022.994-.065 1.278-.132-.067-.17-.275-.36-.625-.566h-.006M10.38 14.6c-.016-.905-.33-1.87-.937-2.9l1.294-1.73.118.15c.267.337.504.925.713 1.767l.064.05c.496-.007.942-.17 1.338-.484v-.006l1.732-1.53c.68-.6 1.282-.9 1.807-.9.383.003.85.194 1.394.57.55.378.884.697 1 .96.063.15.094.385.094.71 0 .694-.11 1.227-.33 1.596-.193.31-.474.555-.845.734-.438.208-1.55.312-3.333.312-.8 0-1.794-.02-2.982-.064l-.142.43c-.254.67-.463 1.112-.625 1.323-.724.937-1.785 1.405-3.182 1.405-1.71-.006-2.56-.92-2.56-2.74.003-.94.278-1.814.824-2.618.15-.216.298-.367.444-.454.225-.133.288-.09.188.124-.396.862-.596 1.548-.6 2.058.008 1.177.752 1.768 2.232 1.772 1.038-.004 1.803-.182 2.295-.535"/>
+    </g>
+</svg>
index 3ad81f5..c8467bb 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-armn-to">
         <path id="armn-to" d="M13.86 16.257c.124 0 .254-.026.39-.078.135-.06.257-.15.367-.28a1.43 1.43 0 0 0 .273-.517c.073-.214.11-.48.11-.798V13h-1.14c-.14 0-.284.026-.43.078a.905.905 0 0 0-.383.258c-.11.125-.2.294-.274.508-.067.213-.1.487-.1.82 0 .34.035.47.108.695.08.21.18.39.29.53.12.13.25.23.39.29.14.05.276.07.406.07m-2.97-7.84a2.67 2.67 0 0 0-.975.45 2.1 2.1 0 0 0-.672.813c-.16.342-.242.78-.242 1.31V18H6v-7.188c0-.776.15-1.455.453-2.04a4.227 4.227 0 0 1 1.234-1.467c.52-.39 1.13-.685 1.83-.883a8.114 8.114 0 0 1 2.225-.297c.526 0 1.04.044 1.54.133.504.088.98.22 1.43.398.447.172.858.388 1.233.65.375.26.698.564.97.913.275.34.49.73.64 1.17.15.43.226 1.09.226 1.61h1.36v2.04h-1.36v1.6c0 .58-.102 1.09-.31 1.54-.21.44-.49.81-.844 1.11-.35.302-.834.53-1.297.687-.465.15-.954.226-1.47.226-.51 0-.997-.08-1.46-.235a3.46 3.46 0 0 1-1.22-.703 3.452 3.452 0 0 1-.836-1.174c-.203-.472-.304-1.027-.304-1.662s.1-1.18.32-1.64c.21-.46.49-.684.85-.976.35-.297.76-.513 1.22-.648.452-.14.93-.21 1.43-.21h1.13c-.01-.49-.04-1.044-.24-1.36a2.26 2.26 0 0 0-.77-.767 3.234 3.234 0 0 0-.986-.427c-.375-.09-.578-.094-1.1-.094-.52 0-.64.02-1.01.102z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.png
new file mode 100644 (file)
index 0000000..66b22c5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-progressive.svg
new file mode 100644 (file)
index 0000000..10b5fdc
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-armn-to">
+        <path id="armn-to" d="M13.86 16.257c.124 0 .254-.026.39-.078.135-.06.257-.15.367-.28a1.43 1.43 0 0 0 .273-.517c.073-.214.11-.48.11-.798V13h-1.14c-.14 0-.284.026-.43.078a.905.905 0 0 0-.383.258c-.11.125-.2.294-.274.508-.067.213-.1.487-.1.82 0 .34.035.47.108.695.08.21.18.39.29.53.12.13.25.23.39.29.14.05.276.07.406.07m-2.97-7.84a2.67 2.67 0 0 0-.975.45 2.1 2.1 0 0 0-.672.813c-.16.342-.242.78-.242 1.31V18H6v-7.188c0-.776.15-1.455.453-2.04a4.227 4.227 0 0 1 1.234-1.467c.52-.39 1.13-.685 1.83-.883a8.114 8.114 0 0 1 2.225-.297c.526 0 1.04.044 1.54.133.504.088.98.22 1.43.398.447.172.858.388 1.233.65.375.26.698.564.97.913.275.34.49.73.64 1.17.15.43.226 1.09.226 1.61h1.36v2.04h-1.36v1.6c0 .58-.102 1.09-.31 1.54-.21.44-.49.81-.844 1.11-.35.302-.834.53-1.297.687-.465.15-.954.226-1.47.226-.51 0-.997-.08-1.46-.235a3.46 3.46 0 0 1-1.22-.703 3.452 3.452 0 0 1-.836-1.174c-.203-.472-.304-1.027-.304-1.662s.1-1.18.32-1.64c.21-.46.49-.684.85-.976.35-.297.76-.513 1.22-.648.452-.14.93-.21 1.43-.21h1.13c-.01-.49-.04-1.044-.24-1.36a2.26 2.26 0 0 0-.77-.767 3.234 3.234 0 0 0-.986-.427c-.375-.09-.578-.094-1.1-.094-.52 0-.64.02-1.01.102z"/>
+    </g>
+</svg>
index 2ba09bc..49236c3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-b">
         <path id="b" d="M7 18h6c2 0 4-1 4-3 0-1.064.01-1.975-1.99-3 2-.975 1.99-1.935 1.99-3 0-2-2-3-4-3H7v12zm7-8c0 1 0 1-2 1h-2V8h2c2 0 2 0 2 1v1zm-2 6h-2v-3h2c2 0 2 0 2 1v1s0 1-2 1z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.png
new file mode 100644 (file)
index 0000000..96f917a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-progressive.svg
new file mode 100644 (file)
index 0000000..4980f22
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-b">
+        <path id="b" d="M7 18h6c2 0 4-1 4-3 0-1.064.01-1.975-1.99-3 2-.975 1.99-1.935 1.99-3 0-2-2-3-4-3H7v12zm7-8c0 1 0 1-2 1h-2V8h2c2 0 2 0 2 1v1zm-2 6h-2v-3h2c2 0 2 0 2 1v1s0 1-2 1z"/>
+    </g>
+</svg>
index 3a6ec97..8cbe378 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-cyrl-be">
         <path id="cyrl-be" d="M7 6h9v2h-6v3h2.65c.892 0 1.632.11 2.22.327.587.218 1.087.622 1.5 1.21.42.59.63 1.188.63 1.98 0 .812-.21 1.397-.63 1.976-.418.578-.897.974-1.436 1.187-.533.213-1.295.32-2.286.32h-5.65m4.768-2c.75 0 1.28-.05 1.584-.12.305-.077.57-.247.792-.51.23-.26.343-.472.343-.854 0-.557-.2-.868-.596-1.12-.4-.255-1.07-.397-2.02-.397H10v3"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.png
new file mode 100644 (file)
index 0000000..4337f73
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-progressive.svg
new file mode 100644 (file)
index 0000000..9476ae8
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-cyrl-be">
+        <path id="cyrl-be" d="M7 6h9v2h-6v3h2.65c.892 0 1.632.11 2.22.327.587.218 1.087.622 1.5 1.21.42.59.63 1.188.63 1.98 0 .812-.21 1.397-.63 1.976-.418.578-.897.974-1.436 1.187-.533.213-1.295.32-2.286.32h-5.65m4.768-2c.75 0 1.28-.05 1.584-.12.305-.077.57-.247.792-.51.23-.26.343-.472.343-.854 0-.557-.2-.868-.596-1.12-.4-.255-1.07-.397-2.02-.397H10v3"/>
+    </g>
+</svg>
index 803d066..ce6f320 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-invert.png differ
index 490c615..dc7b0df 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-cyrl-te">
         <path id="te" d="M11 18V8H7V6h11v2h-4v10"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.png
new file mode 100644 (file)
index 0000000..5e2073a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-progressive.svg
new file mode 100644 (file)
index 0000000..6554299
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-cyrl-te">
+        <path id="te" d="M11 18V8H7V6h11v2h-4v10"/>
+    </g>
+</svg>
index 91f80dc..32980c1 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-cyrl-zhe">
         <path id="cyrl-zhe" d="M13 6v5.154c.328-.033.537-.18.705-.447.168-.266.4-.873.698-1.82.39-1.242.79-2.034 1.197-2.375.403-.336 1.075-.504 2.014-.504L18 6v1.78l-.386-.008c-.4 0-.69.062-.878.187-.186.112-.337.3-.452.55-.115.25-.286.76-.512 1.533-.12.41-.25.755-.392 1.032-.137.275-.383.536-.738.78.44.156.8.465 1.084.926.288.455.603 1.103.944 1.943L18 18h-2.314l-1.17-3.08-.113-.253-.24-.56c-.247-.57-.45-.933-.61-1.09A.726.726 0 0 0 13 12.78V18h-2v-5.22c-.226 0-.382.077-.546.23-.164.15-.368.517-.612 1.097l-.246.56-.113.253L8.313 18H6l1.33-3.267c.327-.808.635-1.447.923-1.92.293-.476.663-.793 1.11-.95-.355-.244-.603-.5-.745-.772a6.357 6.357 0 0 1-.392-1.04c-.222-.76-.39-1.26-.505-1.52-.11-.25-.26-.44-.45-.57-.18-.12-.49-.18-.912-.18H6V6l.386.008c.953 0 1.63.17 2.034.512.4.347.79 1.136 1.177 2.366.3.954.534 1.564.698 1.83.168.26.377.405.705.438V6.002"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.png
new file mode 100644 (file)
index 0000000..86de0d4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-progressive.svg
new file mode 100644 (file)
index 0000000..a6694e7
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-cyrl-zhe">
+        <path id="cyrl-zhe" d="M13 6v5.154c.328-.033.537-.18.705-.447.168-.266.4-.873.698-1.82.39-1.242.79-2.034 1.197-2.375.403-.336 1.075-.504 2.014-.504L18 6v1.78l-.386-.008c-.4 0-.69.062-.878.187-.186.112-.337.3-.452.55-.115.25-.286.76-.512 1.533-.12.41-.25.755-.392 1.032-.137.275-.383.536-.738.78.44.156.8.465 1.084.926.288.455.603 1.103.944 1.943L18 18h-2.314l-1.17-3.08-.113-.253-.24-.56c-.247-.57-.45-.933-.61-1.09A.726.726 0 0 0 13 12.78V18h-2v-5.22c-.226 0-.382.077-.546.23-.164.15-.368.517-.612 1.097l-.246.56-.113.253L8.313 18H6l1.33-3.267c.327-.808.635-1.447.923-1.92.293-.476.663-.793 1.11-.95-.355-.244-.603-.5-.745-.772a6.357 6.357 0 0 1-.392-1.04c-.222-.76-.39-1.26-.505-1.52-.11-.25-.26-.44-.45-.57-.18-.12-.49-.18-.912-.18H6V6l.386.008c.953 0 1.63.17 2.034.512.4.347.79 1.136 1.177 2.366.3.954.534 1.564.698 1.83.168.26.377.405.705.438V6.002"/>
+    </g>
+</svg>
index 9281a0d..ba2c976 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-invert.png differ
index 1954e93..a2fbced 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-f">
         <path id="f" d="M16 8V6H8v12h3v-5h4v-2h-4V8z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.png
new file mode 100644 (file)
index 0000000..e225017
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-progressive.svg
new file mode 100644 (file)
index 0000000..cbd80ed
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-f">
+        <path id="f" d="M16 8V6H8v12h3v-5h4v-2h-4V8z"/>
+    </g>
+</svg>
index 03f9519..5aeb9a4 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-g">
         <path id="g" d="M12 14v-2h5v4.203c-.497.475-1.22.894-2.166 1.26A7.994 7.994 0 0 1 11.97 18c-1.23 0-2.303-.253-3.217-.76a4.908 4.908 0 0 1-2.062-2.185A7.008 7.008 0 0 1 6 11.96c0-1.208.26-2.282.77-3.222.518-.94 1.27-1.66 2.26-2.16.754-.386 1.693-.58 2.816-.58 1.46 0 2.6.304 3.418.91.825.603 1.354 1.436 1.59 2.502l-2.36.435a2.433 2.433 0 0 0-.94-1.346c-.454-.34-1.022-.5-1.707-.5-1.038 0-1.864.32-2.48.97-.61.65-.914 1.61-.914 2.89 0 1.375.31 2.41.93 3.1.62.687 1.434 1.03 2.44 1.03.497 0 .995-.095 1.49-.285.505-.196 1.334-.57 1.69-.846v-.868"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.png
new file mode 100644 (file)
index 0000000..8f3febe
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-progressive.svg
new file mode 100644 (file)
index 0000000..77d543f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-g">
+        <path id="g" d="M12 14v-2h5v4.203c-.497.475-1.22.894-2.166 1.26A7.994 7.994 0 0 1 11.97 18c-1.23 0-2.303-.253-3.217-.76a4.908 4.908 0 0 1-2.062-2.185A7.008 7.008 0 0 1 6 11.96c0-1.208.26-2.282.77-3.222.518-.94 1.27-1.66 2.26-2.16.754-.386 1.693-.58 2.816-.58 1.46 0 2.6.304 3.418.91.825.603 1.354 1.436 1.59 2.502l-2.36.435a2.433 2.433 0 0 0-.94-1.346c-.454-.34-1.022-.5-1.707-.5-1.038 0-1.864.32-2.48.97-.61.65-.914 1.61-.914 2.89 0 1.375.31 2.41.93 3.1.62.687 1.434 1.03 2.44 1.03.497 0 .995-.095 1.49-.285.505-.196 1.334-.57 1.69-.846v-.868"/>
+    </g>
+</svg>
index 290fd4c..b8ba06f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-geor-man">
         <path id="geor-man" d="M13.832 14.06c0-1.714-.394-2.572-1.182-2.572-.868 0-1.302.78-1.302 2.338-.01 1.624.42 2.436 1.295 2.436.793 0 1.19-.734 1.19-2.2m2.167 0C16 16.686 14.884 18 12.65 18 10.218 18 9 16.614 9 13.84c0-2.737 1.217-4.105 3.65-4.105.842 0 1.183.63 1.183.63v-1.58c0-.788-.45-1.183-1.347-1.183-.572 0-.858.374-.858 1.123h-2.34C9.29 6.908 10.35 6 12.462 6 14.83 6 16.01 6.946 16 8.84"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.png
new file mode 100644 (file)
index 0000000..ad5c93f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-progressive.svg
new file mode 100644 (file)
index 0000000..04dc619
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-geor-man">
+        <path id="geor-man" d="M13.832 14.06c0-1.714-.394-2.572-1.182-2.572-.868 0-1.302.78-1.302 2.338-.01 1.624.42 2.436 1.295 2.436.793 0 1.19-.734 1.19-2.2m2.167 0C16 16.686 14.884 18 12.65 18 10.218 18 9 16.614 9 13.84c0-2.737 1.217-4.105 3.65-4.105.842 0 1.183.63 1.183.63v-1.58c0-.788-.45-1.183-1.347-1.183-.572 0-.858.374-.858 1.123h-2.34C9.29 6.908 10.35 6 12.462 6 14.83 6 16.01 6.946 16 8.84"/>
+    </g>
+</svg>
index 26c7018..0736aab 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-invert.png differ
index 356145b..8d92de4 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-l">
         <path id="l" d="M8 18V6h3v10h5v2"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.png
new file mode 100644 (file)
index 0000000..7a1c401
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-progressive.svg
new file mode 100644 (file)
index 0000000..7aa3a8e
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-l">
+        <path id="l" d="M8 18V6h3v10h5v2"/>
+    </g>
+</svg>
index 82e1c0b..023f0ad 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-n">
         <path id="n" d="M7 18V6h3l4 8V6h3v12h-3l-4-8v8H7"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.png
new file mode 100644 (file)
index 0000000..bb0299c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-progressive.svg
new file mode 100644 (file)
index 0000000..0ed4acd
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-n">
+        <path id="n" d="M7 18V6h3l4 8V6h3v12h-3l-4-8v8H7"/>
+    </g>
+</svg>
index 18aef7b..2b8c035 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="bold-v">
         <path id="v" d="M10.5 18L6 6h3l3 8 3-8h3l-4.5 12"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.png
new file mode 100644 (file)
index 0000000..d7c5eb0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-progressive.svg
new file mode 100644 (file)
index 0000000..a6d9a25
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="bold-v">
+        <path id="v" d="M10.5 18L6 6h3l3 8 3-8h3l-4.5 12"/>
+    </g>
+</svg>
index c383e61..061ad41 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 7c-1.7 0-3 1.3-3 3 0-1.7-1.3-3-3-3H3v13h6c1.7 0 3 1 3 2 0-1 1.3-2 3-2h6V7h-6zm5 12h-5c-1.7 0-2 .4-2 .4v-8.9C13 9.1 14.1 8 15.5 8H20v11z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.png
new file mode 100644 (file)
index 0000000..9a63c72
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..626a020
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 7c-1.7 0-3 1.3-3 3 0-1.7-1.3-3-3-3H3v13h6c1.7 0 3 1 3 2 0-1 1.3-2 3-2h6V7h-6zm5 12h-5c-1.7 0-2 .4-2 .4v-8.9C13 9.1 14.1 8 15.5 8H20v11z"/>
+</svg>
index 390902f..f9be70f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9 7c1.7 0 3 1.3 3 3 0-1.7 1.3-3 3-3h6v13h-6c-1.7 0-3 1-3 2 0-1-1.3-2-3-2H3V7h6zM4 19h5c1.7 0 2 .4 2 .4v-8.9C11 9.1 9.9 8 8.5 8H4v11z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.png
new file mode 100644 (file)
index 0000000..6ff5355
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..948472d
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9 7c1.7 0 3 1.3 3 3 0-1.7 1.3-3 3-3h6v13h-6c-1.7 0-3 1-3 2 0-1-1.3-2-3-2H3V7h6zM4 19h5c1.7 0 2 .4 2 .4v-8.9C11 9.1 9.9 8 8.5 8H4v11z"/>
+</svg>
index ea474cb..44a7710 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 5H8c-1.1 0-2 .9-2 2v3h3v11l4-3 4 3V7c0-1.1-.9-2-2-2zM9 9H7V7c0-.6.4-1 1-1h1v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.png
new file mode 100644 (file)
index 0000000..f748967
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d321cea
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 5H8c-1.1 0-2 .9-2 2v3h3v11l4-3 4 3V7c0-1.1-.9-2-2-2zM9 9H7V7c0-.6.4-1 1-1h1v3z"/>
+</svg>
index 9049881..c2cf8c7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M8 5h7c1.1 0 2 .9 2 2v3h-3v11l-4-3-4 3V7c0-1.1.9-2 2-2zm6 4h2V7c0-.6-.4-1-1-1h-1v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.png
new file mode 100644 (file)
index 0000000..366eedc
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..9c752a8
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M8 5h7c1.1 0 2 .9 2 2v3h-3v11l-4-3-4 3V7c0-1.1.9-2 2-2zm6 4h2V7c0-.6-.4-1-1-1h-1v3z"/>
+</svg>
index 85c7273..1f61e46 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 6v11c0 1.7 1.3 3 3 3h15V6H3zm2.5 1C6.3 7 7 7.7 7 8.5S6.3 10 5.5 10 4 9.3 4 8.5 4.7 7 5.5 7zM20 19H6c-1.1 0-2-.9-2-2v-6h16v8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.png
new file mode 100644 (file)
index 0000000..4ab9a1b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..14a79cb
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 6v11c0 1.7 1.3 3 3 3h15V6H3zm2.5 1C6.3 7 7 7.7 7 8.5S6.3 10 5.5 10 4 9.3 4 8.5 4.7 7 5.5 7zM20 19H6c-1.1 0-2-.9-2-2v-6h16v8z"/>
+</svg>
index 38778ec..1290997 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 6v11c0 1.7-1.3 3-3 3H3V6h18zm-2.5 1c-.8 0-1.5.7-1.5 1.5s.7 1.5 1.5 1.5S20 9.3 20 8.5 19.3 7 18.5 7zM4 19h14c1.1 0 2-.9 2-2v-6H4v8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.png
new file mode 100644 (file)
index 0000000..1f33d14
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..80839ad
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 6v11c0 1.7-1.3 3-3 3H3V6h18zm-2.5 1c-.8 0-1.5.7-1.5 1.5s.7 1.5 1.5 1.5S20 9.3 20 8.5 19.3 7 18.5 7zM4 19h14c1.1 0 2-.9 2-2v-6H4v8z"/>
+</svg>
index f820d7a..d65c8fe 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 5v10c0 1.7 1.3 3 3 3h14V8c0-1.7-1.3-3-3-3H4zm2 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM5 9h3v2H5V9zm4 0h3v2H9V9zm4 0h3v2h-3V9zm4 0h3v2h-3V9zM5 12h3v2H5v-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2zM5 15h3v2H7c-1.195 0-2-.805-2-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.png
new file mode 100644 (file)
index 0000000..5f6143b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..f28c0ad
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 5v10c0 1.7 1.3 3 3 3h14V8c0-1.7-1.3-3-3-3H4zm2 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM5 9h3v2H5V9zm4 0h3v2H9V9zm4 0h3v2h-3V9zm4 0h3v2h-3V9zM5 12h3v2H5v-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2zM5 15h3v2H7c-1.195 0-2-.805-2-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2z"/>
+</svg>
index e430f0b..4d1fd8b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 5v10c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3h14zm-2 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zM7 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm13 3h-3v2h3V9zm-4 0h-3v2h3V9zm-4 0H9v2h3V9zM8 9H5v2h3V9zm12 3h-3v2h3v-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2zm12 3h-3v2h1c1.195 0 2-.805 2-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.png
new file mode 100644 (file)
index 0000000..1864f52
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..0e530c0
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 5v10c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3h14zm-2 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zM7 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm13 3h-3v2h3V9zm-4 0h-3v2h3V9zm-4 0H9v2h3V9zM8 9H5v2h3V9zm12 3h-3v2h3v-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2zm12 3h-3v2h1c1.195 0 2-.805 2-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2z"/>
+</svg>
index 50c6acd..000e529 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-destructive.png differ
index 5970559..b2b0179 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <g id="cancel">
         <path id="circle-with-strike" d="M12 5.022a6.98 6.98 0 0 0-.003 13.956 6.98 6.98 0 0 0-.002-13.956zM6.885 12c0-1.092.572-3.25.93-2.93l7.113 7.114c.487.525-1.838.93-2.93.93A5.113 5.113 0 0 1 6.884 12zm9.298 2.93L9.07 7.815c-.445-.483 1.837-.93 2.93-.93a5.112 5.112 0 0 1 5.114 5.113c0 1.092-.364 3.542-.93 2.93z"/>
     </g>
index 553e9f6..80de8e0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="cancel">
         <path id="circle-with-strike" d="M12 5.022a6.98 6.98 0 0 0-.003 13.956 6.98 6.98 0 0 0-.002-13.956zM6.885 12c0-1.092.572-3.25.93-2.93l7.113 7.114c.487.525-1.838.93-2.93.93A5.113 5.113 0 0 1 6.884 12zm9.298 2.93L9.07 7.815c-.445-.483 1.837-.93 2.93-.93a5.112 5.112 0 0 1 5.114 5.113c0 1.092-.364 3.542-.93 2.93z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.png
new file mode 100644 (file)
index 0000000..04b8a08
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-progressive.svg
new file mode 100644 (file)
index 0000000..c36387f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="cancel">
+        <path id="circle-with-strike" d="M12 5.022a6.98 6.98 0 0 0-.003 13.956 6.98 6.98 0 0 0-.002-13.956zM6.885 12c0-1.092.572-3.25.93-2.93l7.113 7.114c.487.525-1.838.93-2.93.93A5.113 5.113 0 0 1 6.884 12zm9.298 2.93L9.07 7.815c-.445-.483 1.837-.93 2.93-.93a5.112 5.112 0 0 1 5.114 5.113c0 1.092-.364 3.542-.93 2.93z"/>
+    </g>
+</svg>
index 28490a3..228a503 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 13.1l8.9 8.9c.8-.8.8-2 0-2.8l-6.1-6.1 6-6.1c.8-.8.8-2 0-2.8L7 13.1z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.png
new file mode 100644 (file)
index 0000000..ddb2318
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..4b2594b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 13.1l8.9 8.9c.8-.8.8-2 0-2.8l-6.1-6.1 6-6.1c.8-.8.8-2 0-2.8L7 13.1z"/>
+</svg>
index 1a3447e..a24bc6c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16.5 13.1L7.6 22c-.8-.8-.8-2 0-2.8l6.1-6.1-6-6.1c-.8-.8-.8-2 0-2.8l8.8 8.9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.png
new file mode 100644 (file)
index 0000000..b5711f8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..0b5cb95
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16.5 13.1L7.6 22c-.8-.8-.8-2 0-2.8l6.1-6.1-6-6.1c-.8-.8-.8-2 0-2.8l8.8 8.9z"/>
+</svg>
index 04e6e5e..cf819c3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 16l8.9-8.9c-.8-.8-2-.8-2.8 0L12 13.2l-6.1-6c-.8-.8-2-.8-2.8 0L12 16z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.png
new file mode 100644 (file)
index 0000000..b7a6e5a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-progressive.svg
new file mode 100644 (file)
index 0000000..4239e3b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 16l8.9-8.9c-.8-.8-2-.8-2.8 0L12 13.2l-6.1-6c-.8-.8-2-.8-2.8 0L12 16z"/>
+</svg>
index 2cbec64..625df49 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 6.5l8.9 8.9c-.8.8-2 .8-2.8 0L12 9.3l-6.1 6c-.8.8-2 .8-2.8 0L12 6.5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.png
new file mode 100644 (file)
index 0000000..285aba6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-progressive.svg
new file mode 100644 (file)
index 0000000..d87a183
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 6.5l8.9 8.9c-.8.8-2 .8-2.8 0L12 9.3l-6.1 6c-.8.8-2 .8-2.8 0L12 6.5z"/>
+</svg>
index e39d780..96c111e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="regular-expression">
         <path id="upper-case" d="M7.53 7L4 17h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 7h-2.12zm1.064 1.53L9.938 13H7.25l1.344-4.47z"/>
         <path id="lower-case" d="M18.55 17l-.184-1.035h-.055c-.35.44-.71.747-1.08.92-.37.167-.85.25-1.44.25-.564 0-.955-.208-1.377-.625-.42-.418-.627-1.012-.627-1.784 0-.808.283-1.403.846-1.784.568-.386 1.193-.607 2.208-.64l1.322-.04v-.335c0-.772-.396-1.158-1.187-1.158-.61 0-1.325.18-2.147.55l-.688-1.4c.877-.46 1.85-.69 2.916-.69 1.024 0 1.59.22 2.134.662.545.445.818 1.12.818 2.03V17h-1.45m-.394-3.527l-.802.027c-.604.018-1.054.127-1.35.327-.294.2-.442.504-.442.912 0 .58.336.87 1.008.87.48 0 .865-.137 1.152-.414.29-.277.436-.645.436-1.103v-.627"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.png
new file mode 100644 (file)
index 0000000..cc6f665
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-progressive.svg
new file mode 100644 (file)
index 0000000..a81eb4c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="regular-expression">
+        <path id="upper-case" d="M7.53 7L4 17h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 7h-2.12zm1.064 1.53L9.938 13H7.25l1.344-4.47z"/>
+        <path id="lower-case" d="M18.55 17l-.184-1.035h-.055c-.35.44-.71.747-1.08.92-.37.167-.85.25-1.44.25-.564 0-.955-.208-1.377-.625-.42-.418-.627-1.012-.627-1.784 0-.808.283-1.403.846-1.784.568-.386 1.193-.607 2.208-.64l1.322-.04v-.335c0-.772-.396-1.158-1.187-1.158-.61 0-1.325.18-2.147.55l-.688-1.4c.877-.46 1.85-.69 2.916-.69 1.024 0 1.59.22 2.134.662.545.445.818 1.12.818 2.03V17h-1.45m-.394-3.527l-.802.027c-.604.018-1.054.127-1.35.327-.294.2-.442.504-.442.912 0 .58.336.87 1.008.87.48 0 .865-.137 1.152-.414.29-.277.436-.645.436-1.103v-.627"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.png
deleted file mode 100644 (file)
index a0f9871..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive-deprecated.svg
deleted file mode 100644 (file)
index 07a5614..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
-    <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
-</svg>
index eb72d14..1dc3ae4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive.png differ
index 3084e5a..63b425a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index 65e5e8d..0cc9169 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-destructive.png differ
index eb495e4..7e3dc53 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index 1c198c5..b7a1be0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index eb72d14..1dc3ae4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/check-progressive.png differ
index 3084e5a..63b425a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.png
deleted file mode 100644 (file)
index 43ed482..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive-deprecated.svg
deleted file mode 100644 (file)
index b96e771..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
-    <circle cx="12" cy="12" r="6"/>
-</svg>
index d2e71c6..99daf8c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive.png differ
index 136b43e..82e64cd 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <circle cx="12" cy="12" r="6"/>
 </svg>
index ddc7b85..96d39e8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <circle cx="12" cy="12" r="6"/>
 </svg>
index d2e71c6..99daf8c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-progressive.png differ
index 136b43e..82e64cd 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <circle cx="12" cy="12" r="6"/>
 </svg>
index c6a4751..442399a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-invert.png differ
index a6bf1ce..4c6db6f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 12h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zm5-2h2v16H8c-1.7 0-3-1.3-3-3V3h8v7l1.5-2 1.5 2V3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.png
new file mode 100644 (file)
index 0000000..e27df80
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..e4b3aaf
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 12h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zm5-2h2v16H8c-1.7 0-3-1.3-3-3V3h8v7l1.5-2 1.5 2V3z"/>
+</svg>
index bf131c5..ca0bdc4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr.png differ
index 7b90a83..0159650 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-invert.png differ
index 72472b6..86781cf 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 12H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zM7 3H5v16h10c1.7 0 3-1.3 3-3V3h-8v7L8.5 8 7 10V3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.png
new file mode 100644 (file)
index 0000000..7fb92a7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..c51cfc9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 12H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zM7 3H5v16h10c1.7 0 3-1.3 3-3V3h-8v7L8.5 8 7 10V3z"/>
+</svg>
index df0c204..3eae601 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl.png differ
index 7a6b1c0..bbe321a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="clear">
         <path id="circle-with-cross" d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 11l-1 1-3-3-3 3-1-1 3-3-3-3 1-1 3 3 3-3 1 1-3 3 3 3z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.png
new file mode 100644 (file)
index 0000000..fb0d269
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-progressive.svg
new file mode 100644 (file)
index 0000000..1cc7c1d
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="clear">
+        <path id="circle-with-cross" d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 11l-1 1-3-3-3 3-1-1 3-3-3-3 1-1 3 3 3-3 1 1-3 3 3 3z"/>
+    </g>
+</svg>
index 6eb6e0a..9ad3926 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 12l-4-3V8h2v5l1.7 1.2c1.3.9 1 1.9.3 2.8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.png
new file mode 100644 (file)
index 0000000..f2683e5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-progressive.svg
new file mode 100644 (file)
index 0000000..a1a307b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 12l-4-3V8h2v5l1.7 1.2c1.3.9 1 1.9.3 2.8z"/>
+</svg>
index b90f031..0ea5a9a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="close">
         <path id="cross" d="M17.4 8.1c.8-.8.8-2 0-2.8L12 10.8 7.4 6.2 6 7.6l4.6 4.6-4 4c-.8.8-.8 2 0 2.8l5.4-5.4 4.6 4.6 1.4-1.4-4.6-4.6z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.png
new file mode 100644 (file)
index 0000000..69f0787
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..dd053f1
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="close">
+        <path id="cross" d="M17.4 8.1c.8-.8.8-2 0-2.8L12 10.8 7.4 6.2 6 7.6l4.6 4.6-4 4c-.8.8-.8 2 0 2.8l5.4-5.4 4.6 4.6 1.4-1.4-4.6-4.6z"/>
+    </g>
+</svg>
index 34afc43..848a0e3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="close">
         <path id="cross" d="M6.6 8.1c-.8-.8-.8-2 0-2.8l5.4 5.5 4.6-4.6L18 7.6l-4.6 4.6 4 4c.8.8.8 2 0 2.8L12 13.6l-4.6 4.6L6 16.8l4.6-4.6z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.png
new file mode 100644 (file)
index 0000000..2c88596
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6d53d36
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="close">
+        <path id="cross" d="M6.6 8.1c-.8-.8-.8-2 0-2.8l5.4 5.5 4.6-4.6L18 7.6l-4.6 4.6 4 4c.8.8.8 2 0 2.8L12 13.6l-4.6 4.6L6 16.8l4.6-4.6z"/>
+    </g>
+</svg>
index 3b129c0..224f1e7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="code">
         <path id="left-bracket" d="M4 12v-1h1c1 0 1 0 1-1V7.614c0-.514.024-.896.073-1.142.054-.252.14-.463.257-.633.204-.28.473-.48.808-.59.335-.11.872-.25 1.835-.25H10v1h-.752c-.457 0-.77.19-.936.406-.167.216-.312.446-.312 1.07v1.856c0 .73-.04 1.18-.244 1.493-.2.307-.562.53-1.09.667.535.155.9.385 1.096.688.2.31.238.76.238 1.49v1.86c0 .62.145.85.312 1.06.166.22.48.41.936.41H10v1H8.973c-.963 0-1.5-.133-1.835-.248a1.578 1.578 0 0 1-.808-.59 1.68 1.68 0 0 1-.257-.626C6.023 16.283 6 15.9 6 15.386V13c0-1 0-1-1-1H4z"/>
         <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" width="24" height="24" xlink:href="#left-bracket"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.png
new file mode 100644 (file)
index 0000000..21656c4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/code-progressive.svg
new file mode 100644 (file)
index 0000000..d4f3de8
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="code">
+        <path id="left-bracket" d="M4 12v-1h1c1 0 1 0 1-1V7.614c0-.514.024-.896.073-1.142.054-.252.14-.463.257-.633.204-.28.473-.48.808-.59.335-.11.872-.25 1.835-.25H10v1h-.752c-.457 0-.77.19-.936.406-.167.216-.312.446-.312 1.07v1.856c0 .73-.04 1.18-.244 1.493-.2.307-.562.53-1.09.667.535.155.9.385 1.096.688.2.31.238.76.238 1.49v1.86c0 .62.145.85.312 1.06.166.22.48.41.936.41H10v1H8.973c-.963 0-1.5-.133-1.835-.248a1.578 1.578 0 0 1-.808-.59 1.68 1.68 0 0 1-.257-.626C6.023 16.283 6 15.9 6 15.386V13c0-1 0-1-1-1H4z"/>
+        <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" width="24" height="24" xlink:href="#left-bracket"/>
+    </g>
+</svg>
index 239a248..9cd2f78 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="collapse">
         <path id="arrow" d="M6.697 15.714L12 10.412l5.303 5.302 1.414-1.414L12 7.583 5.283 14.3z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.png
new file mode 100644 (file)
index 0000000..8b4d5c7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-progressive.svg
new file mode 100644 (file)
index 0000000..b0253f2
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="collapse">
+        <path id="arrow" d="M6.697 15.714L12 10.412l5.303 5.302 1.414-1.414L12 7.583 5.283 14.3z"/>
+    </g>
+</svg>
index c123fd7..2ddc29c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="comment">
         <path id="speech-bubble" d="M15 6H9a3 3 0 0 0-3 3v4a3 3 0 0 0 3 3v3l3-3h3a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.png
new file mode 100644 (file)
index 0000000..6bb0a73
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-progressive.svg
new file mode 100644 (file)
index 0000000..31087f3
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="comment">
+        <path id="speech-bubble" d="M15 6H9a3 3 0 0 0-3 3v4a3 3 0 0 0 3 3v3l3-3h3a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3z"/>
+    </g>
+</svg>
index 014f7fe..ea8beb9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-invert.png differ
index 92f19bf..cffcd94 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 5H4v12c0 1.6 1.3 3 3 3h12V8c0-1.7-1.4-3-3-3zM7.5 17c-.8 0-1.5-.7-1.5-1.5S6.7 14 7.5 14s1.5.7 1.5 1.5S8.3 17 7.5 17zm0-6C6.7 11 6 10.3 6 9.5S6.7 8 7.5 8 9 8.7 9 9.5 8.3 11 7.5 11zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm0-6c-.8 0-1.5-.7-1.5-1.5S14.7 8 15.5 8s1.5.7 1.5 1.5-.7 1.5-1.5 1.5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.png
new file mode 100644 (file)
index 0000000..8e7e83a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..55ee4a5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 5H4v12c0 1.6 1.3 3 3 3h12V8c0-1.7-1.4-3-3-3zM7.5 17c-.8 0-1.5-.7-1.5-1.5S6.7 14 7.5 14s1.5.7 1.5 1.5S8.3 17 7.5 17zm0-6C6.7 11 6 10.3 6 9.5S6.7 8 7.5 8 9 8.7 9 9.5 8.3 11 7.5 11zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm0-6c-.8 0-1.5-.7-1.5-1.5S14.7 8 15.5 8s1.5.7 1.5 1.5-.7 1.5-1.5 1.5z"/>
+</svg>
index 9848fcd..32bbeef 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-invert.png differ
index b7c1c5c..ab88fb0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 5h12v12c0 1.6-1.3 3-3 3H4V8c0-1.7 1.4-3 3-3zm8.5 12c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm0-6c.8 0 1.5-.7 1.5-1.5S16.3 8 15.5 8 14 8.7 14 9.5s.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5S8.3 14 7.5 14 6 14.7 6 15.5 6.7 17 7.5 17zm0-6c.8 0 1.5-.7 1.5-1.5S8.3 8 7.5 8 6 8.7 6 9.5 6.7 11 7.5 11z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.png
new file mode 100644 (file)
index 0000000..5edaf86
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..9e3a64e
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 5h12v12c0 1.6-1.3 3-3 3H4V8c0-1.7 1.4-3 3-3zm8.5 12c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm0-6c.8 0 1.5-.7 1.5-1.5S16.3 8 15.5 8 14 8.7 14 9.5s.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5S8.3 14 7.5 14 6 14.7 6 15.5 6.7 17 7.5 17zm0-6c.8 0 1.5-.7 1.5-1.5S8.3 8 7.5 8 6 8.7 6 9.5 6.7 11 7.5 11z"/>
+</svg>
index e4592ed..53fc581 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 18l8-10H4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.png
new file mode 100644 (file)
index 0000000..ebe6abf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-progressive.svg
new file mode 100644 (file)
index 0000000..6fe38de
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 18l8-10H4z"/>
+</svg>
index a1104a8..9c7573e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 11h-3V4c-1.7 0-3 1.3-3 3v4H7l4.5 5 4.5-5zm1 2v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.png
new file mode 100644 (file)
index 0000000..28812d2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..98a86bb
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 11h-3V4c-1.7 0-3 1.3-3 3v4H7l4.5 5 4.5-5zm1 2v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
+</svg>
index fb82869..a89fd52 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 11h3V4c1.7 0 3 1.3 3 3v4h3l-4.5 5L7 11zm-1 2v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.png
new file mode 100644 (file)
index 0000000..6ba5e29
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..c5cbee5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 11h3V4c1.7 0 3 1.3 3 3v4h3l-4.5 5L7 11zm-1 2v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
+</svg>
index 7c0fd75..1e9b853 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17 2L5 14l-1 5 5-1L21 6c0-2-2-4-4-4zM7.2 15.5c-.3-.3-.7-.6-1-.8C8.5 12.4 17.5 3.3 17.5 3.3c.4.1.7.3 1 .7L7.2 15.5z"/>
 </svg>
index 44ceb9c..92d625a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-progressive.png differ
index 6dbfe37..c7e6f15 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M17 2L5 14l-1 5 5-1L21 6c0-2-2-4-4-4zM7.2 15.5c-.3-.3-.7-.6-1-.8C8.5 12.4 17.5 3.3 17.5 3.3c.4.1.7.3 1 .7L7.2 15.5z"/>
 </svg>
index fdfbca5..36f6d48 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M8 2l12 12 1 5-5-1L4 6c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8C16.5 12.4 7.5 3.3 7.5 3.3c-.4.1-.7.3-1 .7l11.3 11.5z"/>
 </svg>
index 4d2e0a4..90653f2 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-progressive.png differ
index 213841d..a77a027 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M8 2l12 12 1 5-5-1L4 6c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8C16.5 12.4 7.5 3.3 7.5 3.3c-.4.1-.7.3-1 .7l11.3 11.5z"/>
 </svg>
index 2f9ab93..00a7670 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 4V3s0-3-3-3-3 3-3 3v1h-1v6h8V4zm-1.5 0h-3V3s0-1.5 1.5-1.5c1.48.06 1.5 1.5 1.5 1.5zM13 9.6l-6.8 6.9c-.3-.3-.7-.6-1-.8 1.4-1.4 5-5 7.8-7.9V6l-9 9-1 5 5-1 8-8h-3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.png
new file mode 100644 (file)
index 0000000..4b4c8b4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..335b9a5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 4V3s0-3-3-3-3 3-3 3v1h-1v6h8V4zm-1.5 0h-3V3s0-1.5 1.5-1.5c1.48.06 1.5 1.5 1.5 1.5zM13 9.6l-6.8 6.9c-.3-.3-.7-.6-1-.8 1.4-1.4 5-5 7.8-7.9V6l-9 9-1 5 5-1 8-8h-3z"/>
+</svg>
index 9d563fb..3d6c9f1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 4V3s0-3 3-3 3 3 3 3v1h1v6H3V4zm1.5 0h3V3s0-1.5-1.5-1.5C5.52 1.56 5.5 3 5.5 3zM12 9.6l6.8 6.9c.3-.3.7-.6 1-.8-1.4-1.4-5-5-7.8-7.9V6l9 9 1 5-5-1-8-8h3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.png
new file mode 100644 (file)
index 0000000..720a501
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..96ad98d
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 4V3s0-3 3-3 3 3 3 3v1h1v6H3V4zm1.5 0h3V3s0-1.5-1.5-1.5C5.52 1.56 5.5 3 5.5 3zM12 9.6l6.8 6.9c.3-.3.7-.6 1-.8-1.4-1.4-5-5-7.8-7.9V6l9 9 1 5-5-1-8-8h3z"/>
+</svg>
index bdcbb21..8b6022d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14.9 2.8c.9 0 1.8.2 2.7.6.9.4 1.6.9 1.9 1.6-2.8.1-5 1.1-6.6 3.1l1.3 2-6.7-.3L8 3l1.7 2c1.8-1.5 3.5-2.2 5.2-2.2z"/>
     <path d="M15.2 11.1l-2.6-.1-5.4 5.5c-.3-.3-.7-.6-1-.8.9-.9 2.8-2.8 4.7-4.8H9.1L5 15l-1 5 5-1 7.8-7.8-1.6-.1zM20.6 6c-1.7 0-3.2.5-4.4 1.4l-.9.9.8 1.3.9 1.4 4-4c0-.3-.1-.7-.2-1h-.2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7932aa5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d231819
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14.9 2.8c.9 0 1.8.2 2.7.6.9.4 1.6.9 1.9 1.6-2.8.1-5 1.1-6.6 3.1l1.3 2-6.7-.3L8 3l1.7 2c1.8-1.5 3.5-2.2 5.2-2.2z"/>
+    <path d="M15.2 11.1l-2.6-.1-5.4 5.5c-.3-.3-.7-.6-1-.8.9-.9 2.8-2.8 4.7-4.8H9.1L5 15l-1 5 5-1 7.8-7.8-1.6-.1zM20.6 6c-1.7 0-3.2.5-4.4 1.4l-.9.9.8 1.3.9 1.4 4-4c0-.3-.1-.7-.2-1h-.2z"/>
+</svg>
index dfe6877..ff45759 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10.1 2.8c-.9 0-1.8.2-2.7.6-.9.4-1.6.9-1.9 1.6 2.8.1 5 1.1 6.6 3.1l-1.3 2 6.7-.3L17 3l-1.7 2c-1.8-1.5-3.5-2.2-5.2-2.2z"/>
     <path d="M9.8 11.1l2.6-.1 5.4 5.5c.3-.3.7-.6 1-.8-.9-.9-2.8-2.8-4.7-4.8h1.8L20 15l1 5-5-1-7.8-7.8 1.6-.1zM4.4 6c1.7 0 3.2.5 4.4 1.4l.9.9-.8 1.3L8 11 4 7c0-.3.1-.7.2-1h.2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.png
new file mode 100644 (file)
index 0000000..f563ed8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..fbb984b
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10.1 2.8c-.9 0-1.8.2-2.7.6-.9.4-1.6.9-1.9 1.6 2.8.1 5 1.1 6.6 3.1l-1.3 2 6.7-.3L17 3l-1.7 2c-1.8-1.5-3.5-2.2-5.2-2.2z"/>
+    <path d="M9.8 11.1l2.6-.1 5.4 5.5c.3-.3.7-.6 1-.8-.9-.9-2.8-2.8-4.7-4.8h1.8L20 15l1 5-5-1-7.8-7.8 1.6-.1zM4.4 6c1.7 0 3.2.5 4.4 1.4l.9.9-.8 1.3L8 11 4 7c0-.3.1-.7.2-1h.2z"/>
+</svg>
index b512f82..a2a651d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M8 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM14 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM20 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.png
new file mode 100644 (file)
index 0000000..ae7d071
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-progressive.svg
new file mode 100644 (file)
index 0000000..a56ec14
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M8 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM14 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM20 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
+</svg>
index 28fb1ef..7c034f0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="expand">
         <path id="arrow" d="M17.303 8.283L12 13.586 6.697 8.283 5.283 9.697 12 16.414l6.717-6.717z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.png
new file mode 100644 (file)
index 0000000..4a183b5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-progressive.svg
new file mode 100644 (file)
index 0000000..13d3b24
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="expand">
+        <path id="arrow" d="M17.303 8.283L12 13.586 6.697 8.283 5.283 9.697 12 16.414l6.717-6.717z"/>
+    </g>
+</svg>
index 269d813..af20a07 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="external">
         <path id="box" d="M4 4h6v2H6v12h12v-4h2v6H4z"/>
         <path id="arrow" d="M12.42 4H20v7.58l-2.84-2.846L12.892 13 11 11.106l4.264-4.266z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.png
new file mode 100644 (file)
index 0000000..00fd178
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..6688713
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="external">
+        <path id="box" d="M4 4h6v2H6v12h12v-4h2v6H4z"/>
+        <path id="arrow" d="M12.42 4H20v7.58l-2.84-2.846L12.892 13 11 11.106l4.264-4.266z"/>
+    </g>
+</svg>
index cc06c3a..754374a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="external">
         <path id="box" d="M20 4h-6v2h4v12H6v-4H4v6h16z"/>
         <path id="arrow" d="M11.58 4H4v7.58l2.84-2.846L11.108 13 13 11.106 8.736 6.84z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.png
new file mode 100644 (file)
index 0000000..569492e
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..440fbd2
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="external">
+        <path id="box" d="M20 4h-6v2h4v12H6v-4H4v6h16z"/>
+        <path id="arrow" d="M11.58 4H4v7.58l2.84-2.846L11.108 13 13 11.106 8.736 6.84z"/>
+    </g>
+</svg>
index fb90b1e..3a63516 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
     <circle cx="12" cy="14" r="2"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.png
new file mode 100644 (file)
index 0000000..383c78e
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-progressive.svg
new file mode 100644 (file)
index 0000000..a9b1dde
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+    <circle cx="12" cy="14" r="2"/>
+</svg>
index 3ce3da3..e070981 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19.4 12.7c.7-.8 1.2-1.7 1.4-2.7h-1.6c-.9 2.5-3.9 4.4-7.7 4.6h-.1c-3.7-.2-6.8-2.1-7.7-4.6H2.2c.2 1 .8 1.9 1.4 2.7l-2 2 .7.7 2-2c.8.6 1.7 1.2 2.7 1.7l-1 2.8.9.3 1-2.8c1 .3 2 .6 3.1.6v3h1v-3c1.1-.1 2.2-.3 3.1-.6l1 2.8.9-.3-1-2.8c1-.4 1.9-1 2.6-1.7l2 2 .7-.7-1.9-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.png
new file mode 100644 (file)
index 0000000..9c22844
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-progressive.svg
new file mode 100644 (file)
index 0000000..4515bdb
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19.4 12.7c.7-.8 1.2-1.7 1.4-2.7h-1.6c-.9 2.5-3.9 4.4-7.7 4.6h-.1c-3.7-.2-6.8-2.1-7.7-4.6H2.2c.2 1 .8 1.9 1.4 2.7l-2 2 .7.7 2-2c.8.6 1.7 1.2 2.7 1.7l-1 2.8.9.3 1-2.8c1 .3 2 .6 3.1.6v3h1v-3c1.1-.1 2.2-.3 3.1-.6l1 2.8.9-.3-1-2.8c1-.4 1.9-1 2.6-1.7l2 2 .7-.7-1.9-2z"/>
+</svg>
index 55e6441..5df95e9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="find">
         <path id="magnifying-glass" d="M13.656 11c-1.92 0-3.5 1.548-3.5 3.47 0 1.92 1.58 3.5 3.5 3.5.75 0 1.432-.253 2-.657l.094.156 2.375 2.37c.19.19.534.15.78-.096s.315-.59.126-.78l-2.37-2.377-.185-.094a3.545 3.545 0 0 0 .655-2.03c0-1.92-1.55-3.47-3.47-3.47zm0 1.656a1.8 1.8 0 0 1 1.813 1.813 1.83 1.83 0 0 1-1.82 1.84c-1.01 0-1.844-.83-1.844-1.847s.832-1.814 1.844-1.814z"/>
         <path id="text" d="M6 5v2h10V5H6zm0 3v2h11V8H6zm0 3v2h3.53a4.443 4.443 0 0 1 1.44-2H6zm0 3v2h3.53c-.177-.48-.28-.99-.28-1.53 0-.16.046-.315.063-.47H6z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.png
new file mode 100644 (file)
index 0000000..0ca17c1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b13bef8
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="find">
+        <path id="magnifying-glass" d="M13.656 11c-1.92 0-3.5 1.548-3.5 3.47 0 1.92 1.58 3.5 3.5 3.5.75 0 1.432-.253 2-.657l.094.156 2.375 2.37c.19.19.534.15.78-.096s.315-.59.126-.78l-2.37-2.377-.185-.094a3.545 3.545 0 0 0 .655-2.03c0-1.92-1.55-3.47-3.47-3.47zm0 1.656a1.8 1.8 0 0 1 1.813 1.813 1.83 1.83 0 0 1-1.82 1.84c-1.01 0-1.844-.83-1.844-1.847s.832-1.814 1.844-1.814z"/>
+        <path id="text" d="M6 5v2h10V5H6zm0 3v2h11V8H6zm0 3v2h3.53a4.443 4.443 0 0 1 1.44-2H6zm0 3v2h3.53c-.177-.48-.28-.99-.28-1.53 0-.16.046-.315.063-.47H6z"/>
+    </g>
+</svg>
index e6c6bb5..6277016 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="find">
         <path id="magnifying-glass" d="M11.344 11c1.92 0 3.5 1.548 3.5 3.47 0 1.92-1.58 3.5-3.5 3.5-.75 0-1.432-.253-2-.657l-.094.156-2.375 2.37c-.19.19-.534.15-.78-.096s-.315-.59-.126-.78l2.37-2.377.185-.094a3.545 3.545 0 0 1-.655-2.03c0-1.92 1.55-3.47 3.47-3.47zm0 1.656A1.8 1.8 0 0 0 9.53 14.47c0 1.01.806 1.84 1.818 1.84 1.01 0 1.844-.83 1.844-1.845s-.832-1.814-1.844-1.814z"/>
         <path id="text" d="M19 5v2H9V5zm0 3v2H8V8zm0 3v2h-3.53a4.443 4.443 0 0 0-1.44-2zm0 3v2h-3.53c.177-.48.28-.99.28-1.53 0-.16-.046-.315-.063-.47z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.png
new file mode 100644 (file)
index 0000000..142dbe4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6e21dcc
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="find">
+        <path id="magnifying-glass" d="M11.344 11c1.92 0 3.5 1.548 3.5 3.47 0 1.92-1.58 3.5-3.5 3.5-.75 0-1.432-.253-2-.657l-.094.156-2.375 2.37c-.19.19-.534.15-.78-.096s-.315-.59-.126-.78l2.37-2.377.185-.094a3.545 3.545 0 0 1-.655-2.03c0-1.92 1.55-3.47 3.47-3.47zm0 1.656A1.8 1.8 0 0 0 9.53 14.47c0 1.01.806 1.84 1.818 1.84 1.01 0 1.844-.83 1.844-1.845s-.832-1.814-1.844-1.814z"/>
+        <path id="text" d="M19 5v2H9V5zm0 3v2H8V8zm0 3v2h-3.53a4.443 4.443 0 0 0-1.44-2zm0 3v2h-3.53c.177-.48.28-.99.28-1.53 0-.16-.046-.315-.063-.47z"/>
+    </g>
+</svg>
index 832c44f..659d524 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14 6.5V5c-1.4-1.5-5.2-1.2-6 0V4H7v15h1v-7c.8-.8 3.4-.9 5-.5V13c1.2 1.5 4.3 1.2 5 0V6c-.7.7-2.7.9-4 .5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.png
new file mode 100644 (file)
index 0000000..4d76692
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..0d94ea8
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14 6.5V5c-1.4-1.5-5.2-1.2-6 0V4H7v15h1v-7c.8-.8 3.4-.9 5-.5V13c1.2 1.5 4.3 1.2 5 0V6c-.7.7-2.7.9-4 .5z"/>
+</svg>
index dbc78c6..bc0df6c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
        <path d="M10.3 7.5V6c1.4-1.5 5.2-1.2 6 0V5h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V14c-1.2 1.5-4.3 1.2-5 0V7c.7.7 2.7.9 4 .5z"/>
 </svg>
\ No newline at end of file
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.png
new file mode 100644 (file)
index 0000000..e88e061
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..1c0e572
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+       <path d="M10.3 7.5V6c1.4-1.5 5.2-1.2 6 0V5h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V14c-1.2 1.5-4.3 1.2-5 0V7c.7.7 2.7.9 4 .5z"/>
+</svg>
\ No newline at end of file
index 0614672..c73d23f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
        <path d="M19.9 19.6l-16-16-1.1 1.1L6 7.9V20h1v-7c.6-.6 2-.8 3.4-.7l8.4 8.4 1.1-1.1zM17 14V7c-.7.7-2.7.9-4 .5V6c-1.2-1.3-3.9-1.3-5.4-.5l8.9 9c.3-.2.4-.3.5-.5z"/>
 </svg>
\ No newline at end of file
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.png
new file mode 100644 (file)
index 0000000..bf1a6e0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..16b6d3a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+       <path d="M19.9 19.6l-16-16-1.1 1.1L6 7.9V20h1v-7c.6-.6 2-.8 3.4-.7l8.4 8.4 1.1-1.1zM17 14V7c-.7.7-2.7.9-4 .5V6c-1.2-1.3-3.9-1.3-5.4-.5l8.9 9c.3-.2.4-.3.5-.5z"/>
+</svg>
\ No newline at end of file
index 66a0c05..440390e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
        <path d="M3.5 19.6l16-16 1.1 1.1-3.2 3.2V20h-1v-7c-.6-.6-2-.8-3.4-.7l-8.4 8.4-1.1-1.1zM6.3 14V7c.7.7 2.7.9 4 .5V6c1.2-1.3 3.9-1.3 5.4-.5l-8.9 9c-.3-.2-.4-.3-.5-.5z"/>
 </svg>
\ No newline at end of file
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.png
new file mode 100644 (file)
index 0000000..4abe9c8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..7f45734
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+       <path d="M3.5 19.6l16-16 1.1 1.1-3.2 3.2V20h-1v-7c-.6-.6-2-.8-3.4-.7l-8.4 8.4-1.1-1.1zM6.3 14V7c.7.7 2.7.9 4 .5V6c1.2-1.3 3.9-1.3 5.4-.5l-8.9 9c-.3-.2-.4-.3-.5-.5z"/>
+</svg>
\ No newline at end of file
index c66c281..641d37b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M2 5v15h20V5H2zm15 11H8c-.6 0-1-.4-1-1V9h3l2 1h5v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.png
new file mode 100644 (file)
index 0000000..39c58be
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..af577c6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M2 5v15h20V5H2zm15 11H8c-.6 0-1-.4-1-1V9h3l2 1h5v6z"/>
+</svg>
index f2d9fab..1f27fdb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M22 5v15H2V5h20zM7 16h9c.6 0 1-.4 1-1V9h-3l-2 1H7v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.png
new file mode 100644 (file)
index 0000000..a384c8d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..30e27f9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M22 5v15H2V5h20zM7 16h9c.6 0 1-.4 1-1V9h-3l-2 1H7v6z"/>
+</svg>
index e7aec3d..3012dd9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="arrow" d="M6 6v4l1.28-1.28 2 2 1.423-1.44-2-2L10 6z"/>
     <use transform="rotate(90 12 12)" xlink:href="#arrow"/>
     <use transform="rotate(180 12 12)" xlink:href="#arrow"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.png
new file mode 100644 (file)
index 0000000..8b86269
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/fullScreen-progressive.svg
new file mode 100644 (file)
index 0000000..53adc94
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="arrow" d="M6 6v4l1.28-1.28 2 2 1.423-1.44-2-2L10 6z"/>
+    <use transform="rotate(90 12 12)" xlink:href="#arrow"/>
+    <use transform="rotate(180 12 12)" xlink:href="#arrow"/>
+    <use transform="rotate(-90 12 12)" xlink:href="#arrow"/>
+</svg>
index 00d2695..82156c4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M11 13L5 6h15l-6 7v7c-1.7 0-3-1.3-3-3v-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.png
new file mode 100644 (file)
index 0000000..a6079a4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..646c6c2
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M11 13L5 6h15l-6 7v7c-1.7 0-3-1.3-3-3v-4z"/>
+</svg>
index bd2d7de..94f0385 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14 13l6-7H5l6 7v7c1.7 0 3-1.3 3-3v-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.png
new file mode 100644 (file)
index 0000000..5c57662
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..0e016df
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14 13l6-7H5l6 7v7c1.7 0 3-1.3 3-3v-4z"/>
+</svg>
index 7ace9e4..817b768 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 7c-2 0-3 2-3 2s-1-2-3-2c-2.5 0-4 2-4 4 0 4 5 5 7 8 2-3 7-4 7-8 0-2-1.5-4-4-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.png
new file mode 100644 (file)
index 0000000..8797321
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-progressive.svg
new file mode 100644 (file)
index 0000000..6fbe267
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 7c-2 0-3 2-3 2s-1-2-3-2c-2.5 0-4 2-4 4 0 4 5 5 7 8 2-3 7-4 7-8 0-2-1.5-4-4-4z"/>
+</svg>
index a43996c..385bb7b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="help">
         <path id="circle" d="M12 2.085c-5.477 0-9.915 4.438-9.915 9.916 0 5.48 4.438 9.92 9.916 9.92 5.48 0 9.92-4.44 9.92-9.913 0-5.477-4.44-9.915-9.913-9.915zm.002 18a8.084 8.084 0 1 1 0-16.168 8.084 8.084 0 0 1 0 16.168z"/>
         <g id="question-mark">
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.png
new file mode 100644 (file)
index 0000000..87236a8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..863d202
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="help">
+        <path id="circle" d="M12 2.085c-5.477 0-9.915 4.438-9.915 9.916 0 5.48 4.438 9.92 9.916 9.92 5.48 0 9.92-4.44 9.92-9.913 0-5.477-4.44-9.915-9.913-9.915zm.002 18a8.084 8.084 0 1 1 0-16.168 8.084 8.084 0 0 1 0 16.168z"/>
+        <g id="question-mark">
+            <path id="top" d="M11.766 6.688c-2.5 0-3.22 2.188-3.22 2.188l1.412.854s.298-.79.9-1.23c.517-.374 1.626-.624 2.22.126.7.885-.17 1.587-1.078 2.72C11.047 12.53 11 15 11 15h1.97s.134-2.318 1.04-3.38c.603-.708 1.443-1.34 1.443-2.495s-1.187-2.437-3.687-2.437z"/>
+            <path id="bottom" d="M11 16h2v2h-2z"/>
+        </g>
+    </g>
+</svg>
index 0c0368e..9c6c365 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="help">
         <path id="circle" d="M12 2.085c5.477 0 9.915 4.438 9.915 9.916 0 5.48-4.438 9.92-9.916 9.92-5.48 0-9.92-4.44-9.92-9.913 0-5.477 4.44-9.915 9.913-9.915zm-.002 18a8.084 8.084 0 1 0 0-16.168 8.084 8.084 0 0 0 0 16.168z"/>
         <g id="question-mark">
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.png
new file mode 100644 (file)
index 0000000..72f95c2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..9dd7edf
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="help">
+        <path id="circle" d="M12 2.085c5.477 0 9.915 4.438 9.915 9.916 0 5.48-4.438 9.92-9.916 9.92-5.48 0-9.92-4.44-9.92-9.913 0-5.477 4.44-9.915 9.913-9.915zm-.002 18a8.084 8.084 0 1 0 0-16.168 8.084 8.084 0 0 0 0 16.168z"/>
+        <g id="question-mark">
+            <path id="top" d="M12.234 6.688c2.5 0 3.22 2.188 3.22 2.188l-1.412.854s-.298-.79-.9-1.23c-.517-.374-1.626-.624-2.22.126-.7.885.17 1.587 1.078 2.72C12.953 12.53 13 15 13 15h-1.97s-.134-2.318-1.04-3.38c-.603-.708-1.443-1.34-1.443-2.495 0-1.156 1.187-2.437 3.687-2.437z"/>
+            <path id="bottom" d="M13 16h-2v2h2z"/>
+        </g>
+    </g>
+</svg>
index 6b46920..900a565 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="history">
         <path id="clock-hands" d="M17.26 15.076s-2.385-1.935-4.005-3.062c.72-2.397 1.702-6.56 1.702-6.56s-4.35 5.364-4.877 6.7c-.463 1.168 1.46 2.21 2.346 1.678 1.9.55 4.834 1.244 4.834 1.244z"/>
         <path id="arrow" d="M12.086 2.085C6.608 2.085 2.17 6.523 2.17 12a9.86 9.86 0 0 0 1.3 4.9l-2.22 2.04h5.688v-5.22L4.87 15.616A7.982 7.982 0 0 1 4.004 12a8.084 8.084 0 0 1 16.167.004 8.08 8.08 0 0 1-8.08 8.085 7.975 7.975 0 0 1-3.21-.68L8.05 21.04a9.81 9.81 0 0 0 4.045.874C17.563 21.914 22 17.476 22 12c0-5.477-4.438-9.915-9.914-9.915z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.png
new file mode 100644 (file)
index 0000000..ec635a1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/history-progressive.svg
new file mode 100644 (file)
index 0000000..29f0c89
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="history">
+        <path id="clock-hands" d="M17.26 15.076s-2.385-1.935-4.005-3.062c.72-2.397 1.702-6.56 1.702-6.56s-4.35 5.364-4.877 6.7c-.463 1.168 1.46 2.21 2.346 1.678 1.9.55 4.834 1.244 4.834 1.244z"/>
+        <path id="arrow" d="M12.086 2.085C6.608 2.085 2.17 6.523 2.17 12a9.86 9.86 0 0 0 1.3 4.9l-2.22 2.04h5.688v-5.22L4.87 15.616A7.982 7.982 0 0 1 4.004 12a8.084 8.084 0 0 1 16.167.004 8.08 8.08 0 0 1-8.08 8.085 7.975 7.975 0 0 1-3.21-.68L8.05 21.04a9.81 9.81 0 0 0 4.045.874C17.563 21.914 22 17.476 22 12c0-5.477-4.438-9.915-9.914-9.915z"/>
+    </g>
+</svg>
index aeb8984..784b183 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="image">
         <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-11v13H4V6z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.png
new file mode 100644 (file)
index 0000000..f5cefdf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..ed5c051
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="image">
+        <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-11v13H4V6z"/>
+    </g>
+</svg>
index 8b82c20..226690e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="image">
         <path id="mountains" d="M6 17l3-3 2 1 3-3 4 5zM4 6v13h16V6z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.png
new file mode 100644 (file)
index 0000000..eadff85
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e506ea5
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="image">
+        <path id="mountains" d="M6 17l3-3 2 1 3-3 4 5zM4 6v13h16V6z"/>
+    </g>
+</svg>
index 81b6783..3a883bc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M16 17l-3-3-2 1-3-3-4 5zm-1-8v4h3v6H2V6h9v3z"/>
         <path id="add" d="M22 6h-4V2h-2v4h-4v2h4v4h2V8h4z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.png
new file mode 100644 (file)
index 0000000..e64819c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..47878dc
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="imageAdd">
+        <path id="mountains" d="M16 17l-3-3-2 1-3-3-4 5zm-1-8v4h3v6H2V6h9v3z"/>
+        <path id="add" d="M22 6h-4V2h-2v4h-4v2h4v4h2V8h4z"/>
+    </g>
+</svg>
index f14a20a..e1afd33 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M8 17l3-3 2 1 3-3 4 5zm1-8v4H6v6h16V6h-9v3z"/>
         <path id="add" d="M2 6h4V2h2v4h4v2H8v4H6V8H2z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.png
new file mode 100644 (file)
index 0000000..dec9401
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..089da6a
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="imageAdd">
+        <path id="mountains" d="M8 17l3-3 2 1 3-3 4 5zm1-8v4H6v6h16V6h-9v3z"/>
+        <path id="add" d="M2 6h4V2h2v4h4v2H8v4H6V8H2z"/>
+    </g>
+</svg>
index 785bd49..e0022a5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M2 4v14h2V6h15V4H2zm3 3v13h16V7H5zm6 6l3 3 2-1 3 3H7l4-5z" id="imageGallery"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7e360aa
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d0bc4ad
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M2 4v14h2V6h15V4H2zm3 3v13h16V7H5zm6 6l3 3 2-1 3 3H7l4-5z" id="imageGallery"/>
+</svg>
index fe66e4f..8769498 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 4v14h-2V6H4V4h17zm-3 3v13H2V7h16zm-6 6l-3 3-2-1-3 3h12l-4-5z" id="imageGallery"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.png
new file mode 100644 (file)
index 0000000..3d014a5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..ccba6ea
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 4v14h-2V6H4V4h17zm-3 3v13H2V7h16zm-6 6l-3 3-2-1-3 3h12l-4-5z" id="imageGallery"/>
+</svg>
index 5932525..74f8e58 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-5v7H4V6h8v6z"/>
         <path id="lock" d="M18.5 5h-3V4s0-1.5 1.5-1.5c1.5.06 1.5 1.5 1.5 1.5zM20 5V4s0-3-3-3-3 3-3 3v1h-1v6h8V5z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.png
new file mode 100644 (file)
index 0000000..f5604bf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..635dd52
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="imageAdd">
+        <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-5v7H4V6h8v6z"/>
+        <path id="lock" d="M18.5 5h-3V4s0-1.5 1.5-1.5c1.5.06 1.5 1.5 1.5 1.5zM20 5V4s0-3-3-3-3 3-3 3v1h-1v6h8V5z"/>
+    </g>
+</svg>
index b92b212..faf34b9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M7 17l3-3 2 1 3-3 4 5zm-2-5v7h16V6h-8v6z"/>
         <path id="lock" d="M6.5 5h3V4s0-1.5-1.5-1.5C6.5 2.56 6.5 4 6.5 4zM5 5V4s0-3 3-3 3 3 3 3v1h1v6H4V5z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.png
new file mode 100644 (file)
index 0000000..817e599
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..dd65b62
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="imageAdd">
+        <path id="mountains" d="M7 17l3-3 2 1 3-3 4 5zm-2-5v7h16V6h-8v6z"/>
+        <path id="lock" d="M6.5 5h3V4s0-1.5-1.5-1.5C6.5 2.56 6.5 4 6.5 4zM5 5V4s0-3 3-3 3 3 3 3v1h1v6H4V5z"/>
+    </g>
+</svg>
index 718c2b0..589187a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 8v8l5-4-5-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.png
new file mode 100644 (file)
index 0000000..eec5149
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..f5e9b04
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 8v8l5-4-5-4z"/>
+</svg>
index e6abc95..544bbb3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zM21 8v8l-5-4 5-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.png
new file mode 100644 (file)
index 0000000..b6edc33
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..1509cd5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zM21 8v8l-5-4 5-4z"/>
+</svg>
index 2c4e028..78532de 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-invert.png differ
index e519d7e..c80da91 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="info">
         <path id="circled-i" d="M11.5 17a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11zm0-12a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zm.5 5v4h1v1h-3v-1h1v-3h-1v-1zm-1-2h1v1h-1z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.png
new file mode 100644 (file)
index 0000000..9a0f5d7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/info-progressive.svg
new file mode 100644 (file)
index 0000000..648386e
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="info">
+        <path id="circled-i" d="M11.5 17a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11zm0-12a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zm.5 5v4h1v1h-3v-1h1v-3h-1v-1zm-1-2h1v1h-1z"/>
+    </g>
+</svg>
index d9f5d75..0b4270d 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/info.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/info.png differ
index ac33a08..f274352 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-a">
         <path id="a" d="M14.667 6h-1.372l-7 12H8l2.333-4h4L15 18h1.667l-2-12zm-3.75 7l2.527-4.333.723 4.333h-3.25z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.png
new file mode 100644 (file)
index 0000000..f8c25a9
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-progressive.svg
new file mode 100644 (file)
index 0000000..3ac0de6
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-a">
+        <path id="a" d="M14.667 6h-1.372l-7 12H8l2.333-4h4L15 18h1.667l-2-12zm-3.75 7l2.527-4.333.723 4.333h-3.25z"/>
+    </g>
+</svg>
index 5ea6072..20bca5e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-arab-keheh-jeem">
         <path id="arab-keheh-jeem" d="M18.125 5.844c-1.695.555-3.297 1.162-4.594 1.938-.49.3-.77.712-.87 1.125a1.26 1.26 0 0 0 .065.78c.19.406.54.575.844.814l.093-.12.53.627c.14.165.345.514.47.94.138.462.08.724 0 1.124h-3.44c-.34 0-.592.007-.766-.02-.34-.053-.256-.21-.234-.34.33-.127.56-.173.934-.14.29-.495.593-.886.906-1.314-.98.037-1.877.015-2.687-.094-.346-.046-.698-.185-1.094-.155-.36.026-.77.24-1.03.72-.25.447-.436.838-.66 1.28l.75-.47c.23-.14.486-.226.72-.218.158.004.276.053.407.093-.234.204-.51.4-.72.56-.3.26-.704.69-.908 1-.403.617-.694 1.086-.875 1.78-.18.69.003 1.34.468 1.75.426.38.846.52 1.28.566.65.064 1.206.092 2-.19.658-.23 1.022-.552 1.5-.97-.882.11-1.816.09-2.53.033-.87-.07-1.268-.386-1.47-.596-.27-.283-.306-.64-.155-1.22a1.44 1.44 0 0 1 .25-.53c.17-.228.363-.435.593-.656.45-.436 1.01-.737 1.46-.94-.042.207-.104.444-.052.69.05.23.25.38.44.47.26.12.506.152.69.153 1.42.01 2.86 0 4.28 0 .246 0 .45-.163.593-.375.14-.21.25-.48.343-.845.13-.5.094-1.062-.094-1.625a4.812 4.812 0 0 0-.72-1.406c-.336-.444-.675-.83-1-1.22 1.256-.815 2.715-1.24 3.97-1.688.12-.452.222-.926.31-1.313zm-9.47 8.438c-.26.394-.583.69-.874 1 .38.286.75.556 1.1.813.336-.303.627-.674.876-.97-.39-.267-.77-.587-1.093-.843z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.png
new file mode 100644 (file)
index 0000000..3c3ed9f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-progressive.svg
new file mode 100644 (file)
index 0000000..eb04c40
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-arab-keheh-jeem">
+        <path id="arab-keheh-jeem" d="M18.125 5.844c-1.695.555-3.297 1.162-4.594 1.938-.49.3-.77.712-.87 1.125a1.26 1.26 0 0 0 .065.78c.19.406.54.575.844.814l.093-.12.53.627c.14.165.345.514.47.94.138.462.08.724 0 1.124h-3.44c-.34 0-.592.007-.766-.02-.34-.053-.256-.21-.234-.34.33-.127.56-.173.934-.14.29-.495.593-.886.906-1.314-.98.037-1.877.015-2.687-.094-.346-.046-.698-.185-1.094-.155-.36.026-.77.24-1.03.72-.25.447-.436.838-.66 1.28l.75-.47c.23-.14.486-.226.72-.218.158.004.276.053.407.093-.234.204-.51.4-.72.56-.3.26-.704.69-.908 1-.403.617-.694 1.086-.875 1.78-.18.69.003 1.34.468 1.75.426.38.846.52 1.28.566.65.064 1.206.092 2-.19.658-.23 1.022-.552 1.5-.97-.882.11-1.816.09-2.53.033-.87-.07-1.268-.386-1.47-.596-.27-.283-.306-.64-.155-1.22a1.44 1.44 0 0 1 .25-.53c.17-.228.363-.435.593-.656.45-.436 1.01-.737 1.46-.94-.042.207-.104.444-.052.69.05.23.25.38.44.47.26.12.506.152.69.153 1.42.01 2.86 0 4.28 0 .246 0 .45-.163.593-.375.14-.21.25-.48.343-.845.13-.5.094-1.062-.094-1.625a4.812 4.812 0 0 0-.72-1.406c-.336-.444-.675-.83-1-1.22 1.256-.815 2.715-1.24 3.97-1.688.12-.452.222-.926.31-1.313zm-9.47 8.438c-.26.394-.583.69-.874 1 .38.286.75.556 1.1.813.336-.303.627-.674.876-.97-.39-.267-.77-.587-1.093-.843z"/>
+    </g>
+</svg>
index 5b4cd21..ecc636f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-arab-meem">
         <path id="arab-meem" d="M16 9.73l-.93 2.19h-4.663c-.48 0-.857.12-1.135.366l-.06.11c-.185 2.016-.503 3.558-.956 4.627a8.31 8.31 0 0 1-1.082 1.833c-.177.226-.22.186-.126-.12l.142-.503.17-.67.234-.87.002-.008.202-1.045.258-1.41.353-1.907c.19-.312.42-.638.692-.98a24.1 24.1 0 0 1 .94-1.09c.13-.092.697-.18 1.705-.266 1.05-.086 1.64-.183 1.765-.293l.065-.128c.01-.11-.01-.24-.052-.394a2.403 2.403 0 0 0-.232-.522c-.22-.428-.438-.64-.654-.64-.294 0-.915.268-1.864.805-.36.208-.378.125-.05-.247 1.555-1.71 2.705-2.566 3.45-2.566.38 0 .67.13.86.394.134.195.25.6.343 1.21l.202 1.2c.105.586.24.895.408.925"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.png
new file mode 100644 (file)
index 0000000..7179cb6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-progressive.svg
new file mode 100644 (file)
index 0000000..5742238
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-arab-meem">
+        <path id="arab-meem" d="M16 9.73l-.93 2.19h-4.663c-.48 0-.857.12-1.135.366l-.06.11c-.185 2.016-.503 3.558-.956 4.627a8.31 8.31 0 0 1-1.082 1.833c-.177.226-.22.186-.126-.12l.142-.503.17-.67.234-.87.002-.008.202-1.045.258-1.41.353-1.907c.19-.312.42-.638.692-.98a24.1 24.1 0 0 1 .94-1.09c.13-.092.697-.18 1.705-.266 1.05-.086 1.64-.183 1.765-.293l.065-.128c.01-.11-.01-.24-.052-.394a2.403 2.403 0 0 0-.232-.522c-.22-.428-.438-.64-.654-.64-.294 0-.915.268-1.864.805-.36.208-.378.125-.05-.247 1.555-1.71 2.705-2.566 3.45-2.566.38 0 .67.13.86.394.134.195.25.6.343 1.21l.202 1.2c.105.586.24.895.408.925"/>
+    </g>
+</svg>
index 44753a9..38c45d5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-armn-sha">
         <path id="armn-sha" d="M11.564 7.678a3.073 3.073 0 0 0-.93-.268c-.35-.047-.75-.07-1.197-.07h-1.11L8.587 6h1.723c.558 0 1.042.032 1.45.095.416.063.794.173 1.136.33l4.483 2.033-.33 1.67-2.625-1.165a1.867 1.867 0 0 0-.433-.134 2.45 2.45 0 0 0-.576-.06 4.88 4.88 0 0 0-1.663.28c-.526.19-1 .46-1.427.812-.42.35-.776.78-1.07 1.283a5.48 5.48 0 0 0-.63 1.71c-.24 1.255-.15 2.21.27 2.87.424.65 1.19.976 2.292.976.55 0 1.044-.08 1.48-.236a3.488 3.488 0 0 0 1.135-.66c.325-.29.59-.634.795-1.034.21-.4.363-.84.458-1.322l.11-.56h1.6l-.12.59a5.925 5.925 0 0 1-.676 1.844 5.19 5.19 0 0 1-1.214 1.423c-.488.395-1.053.7-1.694.923a6.573 6.573 0 0 1-2.106.324c-.767 0-1.434-.114-2-.34-.568-.226-1.025-.554-1.372-.985-.347-.437-.573-.97-.678-1.608-.105-.64-.078-1.366.08-2.186.125-.66.346-1.274.66-1.836A6.332 6.332 0 0 1 8.792 9.54a5.955 5.955 0 0 1 1.496-1.072 5.87 5.87 0 0 1 1.732-.57l-.465-.23"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.png
new file mode 100644 (file)
index 0000000..db31afa
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-progressive.svg
new file mode 100644 (file)
index 0000000..c2d580e
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-armn-sha">
+        <path id="armn-sha" d="M11.564 7.678a3.073 3.073 0 0 0-.93-.268c-.35-.047-.75-.07-1.197-.07h-1.11L8.587 6h1.723c.558 0 1.042.032 1.45.095.416.063.794.173 1.136.33l4.483 2.033-.33 1.67-2.625-1.165a1.867 1.867 0 0 0-.433-.134 2.45 2.45 0 0 0-.576-.06 4.88 4.88 0 0 0-1.663.28c-.526.19-1 .46-1.427.812-.42.35-.776.78-1.07 1.283a5.48 5.48 0 0 0-.63 1.71c-.24 1.255-.15 2.21.27 2.87.424.65 1.19.976 2.292.976.55 0 1.044-.08 1.48-.236a3.488 3.488 0 0 0 1.135-.66c.325-.29.59-.634.795-1.034.21-.4.363-.84.458-1.322l.11-.56h1.6l-.12.59a5.925 5.925 0 0 1-.676 1.844 5.19 5.19 0 0 1-1.214 1.423c-.488.395-1.053.7-1.694.923a6.573 6.573 0 0 1-2.106.324c-.767 0-1.434-.114-2-.34-.568-.226-1.025-.554-1.372-.985-.347-.437-.573-.97-.678-1.608-.105-.64-.078-1.366.08-2.186.125-.66.346-1.274.66-1.836A6.332 6.332 0 0 1 8.792 9.54a5.955 5.955 0 0 1 1.496-1.072 5.87 5.87 0 0 1 1.732-.57l-.465-.23"/>
+    </g>
+</svg>
index 467d12d..882af04 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-c">
         <path id="c" d="M15.008 13.718l1.48.214c-.467 1.34-1.15 2.354-2.045 3.04a4.835 4.835 0 0 1-3.015 1.03c-1.36 0-2.438-.43-3.237-1.29C7.4 15.85 7 14.618 7 13.012c0-2.09.606-3.817 1.817-5.184C9.897 6.61 11.237 6 12.84 6c1.186 0 2.145.33 2.878.99.738.66 1.165 1.546 1.282 2.66l-1.397.135c-.148-.84-.453-1.464-.916-1.876-.458-.42-1.05-.63-1.78-.63-1.368 0-2.475.63-3.32 1.89-.733 1.087-1.1 2.377-1.1 3.87 0 1.194.283 2.104.848 2.732.565.628 1.3.942 2.206.942.78 0 1.48-.26 2.1-.785.63-.52 1.08-1.26 1.37-2.216"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.png
new file mode 100644 (file)
index 0000000..5b36059
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-progressive.svg
new file mode 100644 (file)
index 0000000..29da4c6
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-c">
+        <path id="c" d="M15.008 13.718l1.48.214c-.467 1.34-1.15 2.354-2.045 3.04a4.835 4.835 0 0 1-3.015 1.03c-1.36 0-2.438-.43-3.237-1.29C7.4 15.85 7 14.618 7 13.012c0-2.09.606-3.817 1.817-5.184C9.897 6.61 11.237 6 12.84 6c1.186 0 2.145.33 2.878.99.738.66 1.165 1.546 1.282 2.66l-1.397.135c-.148-.84-.453-1.464-.916-1.876-.458-.42-1.05-.63-1.78-.63-1.368 0-2.475.63-3.32 1.89-.733 1.087-1.1 2.377-1.1 3.87 0 1.194.283 2.104.848 2.732.565.628 1.3.942 2.206.942.78 0 1.48-.26 2.1-.785.63-.52 1.08-1.26 1.37-2.216"/>
+    </g>
+</svg>
index 7774790..1c305e3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-d">
         <path id="d" d="M7 18L9.462 6h3.557c.85 0 1.5.063 1.95.188.642.17 1.192.472 1.65.91.454.43.8.97 1.03 1.62.23.65.344 1.378.344 2.186 0 .966-.146 1.847-.436 2.644-.284.79-.66 1.49-1.127 2.095-.46.6-.946 1.072-1.455 1.416-.504.33-1.1.582-1.794.75-.525.122-1.17.19-1.94.19H7m1.86-1.36h1.866c.842 0 1.59-.08 2.245-.24a3.26 3.26 0 0 0 1.05-.436 4.19 4.19 0 0 0 1.04-.975 6.652 6.652 0 0 0 .975-1.825c.247-.687.37-1.467.37-2.34 0-.97-.166-1.716-.5-2.235-.332-.522-.755-.87-1.27-1.04-.38-.124-.974-.186-1.78-.186H11L9.095 16.64"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.png
new file mode 100644 (file)
index 0000000..73e4545
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-progressive.svg
new file mode 100644 (file)
index 0000000..e995e48
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-d">
+        <path id="d" d="M7 18L9.462 6h3.557c.85 0 1.5.063 1.95.188.642.17 1.192.472 1.65.91.454.43.8.97 1.03 1.62.23.65.344 1.378.344 2.186 0 .966-.146 1.847-.436 2.644-.284.79-.66 1.49-1.127 2.095-.46.6-.946 1.072-1.455 1.416-.504.33-1.1.582-1.794.75-.525.122-1.17.19-1.94.19H7m1.86-1.36h1.866c.842 0 1.59-.08 2.245-.24a3.26 3.26 0 0 0 1.05-.436 4.19 4.19 0 0 0 1.04-.975 6.652 6.652 0 0 0 .975-1.825c.247-.687.37-1.467.37-2.34 0-.97-.166-1.716-.5-2.235-.332-.522-.755-.87-1.27-1.04-.38-.124-.974-.186-1.78-.186H11L9.095 16.64"/>
+    </g>
+</svg>
index da226ae..21d3dc7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-e">
         <path id="e" d="M7 18L9.474 6H18l-.282 1.367H10.77L10.02 11h6.09l-.28 1.367H9.74l-.88 4.273h7.44L16.018 18H7"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.png
new file mode 100644 (file)
index 0000000..0444ac2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-progressive.svg
new file mode 100644 (file)
index 0000000..351d968
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-e">
+        <path id="e" d="M7 18L9.474 6H18l-.282 1.367H10.77L10.02 11h6.09l-.28 1.367H9.74l-.88 4.273h7.44L16.018 18H7"/>
+    </g>
+</svg>
index d848197..5b57252 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-geor-kan">
         <path id="geor-kan" d="M15.057 14.663C14.617 16.888 13.223 18 10.88 18 8.96 18 8 17.213 8 15.64c0-.298.036-.624.108-.977.083-.43.245-.836.488-1.217l1.24.605-.206.62c-.055.26-.083.497-.083.71 0 .97.52 1.46 1.564 1.46 1.31 0 2.108-.724 2.39-2.17l.058-.33a3.17 3.17 0 0 0 .066-.615c0-.927-.546-1.39-1.64-1.39H10.87l.247-1.26h1.118c1.203-.004 1.91-.55 2.12-1.64.04-.18.057-.355.057-.52 0-1.144-.9-1.715-2.696-1.715L11.94 6C14.646 6 16 6.877 16 8.627c0 .248-.027.516-.082.803-.204 1.092-1.05 1.824-2.54 2.194l-.033.166c1.23.2 1.845.823 1.845 1.872 0 .21-.025.433-.074.67l-.058.332"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.png
new file mode 100644 (file)
index 0000000..ff933e2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-progressive.svg
new file mode 100644 (file)
index 0000000..a9ee28c
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-geor-kan">
+        <path id="geor-kan" d="M15.057 14.663C14.617 16.888 13.223 18 10.88 18 8.96 18 8 17.213 8 15.64c0-.298.036-.624.108-.977.083-.43.245-.836.488-1.217l1.24.605-.206.62c-.055.26-.083.497-.083.71 0 .97.52 1.46 1.564 1.46 1.31 0 2.108-.724 2.39-2.17l.058-.33a3.17 3.17 0 0 0 .066-.615c0-.927-.546-1.39-1.64-1.39H10.87l.247-1.26h1.118c1.203-.004 1.91-.55 2.12-1.64.04-.18.057-.355.057-.52 0-1.144-.9-1.715-2.696-1.715L11.94 6C14.646 6 16 6.877 16 8.627c0 .248-.027.516-.082.803-.204 1.092-1.05 1.824-2.54 2.194l-.033.166c1.23.2 1.845.823 1.845 1.872 0 .21-.025.433-.074.67l-.058.332"/>
+    </g>
+</svg>
index 3b471d2..ddb927e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-i">
         <path id="i" d="M12.5 18l.25-.995h-1.5l2.508-10.037h1.5L15.5 6h-5l-.242.968h1.5l-2.51 10.037h-1.5L7.5 18z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.png
new file mode 100644 (file)
index 0000000..5c78d59
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-progressive.svg
new file mode 100644 (file)
index 0000000..0ae89f9
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-i">
+        <path id="i" d="M12.5 18l.25-.995h-1.5l2.508-10.037h1.5L15.5 6h-5l-.242.968h1.5l-2.51 10.037h-1.5L7.5 18z"/>
+    </g>
+</svg>
index b719095..2bdddd5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-k">
         <path id="k" d="M12.018 10.652L17 6h-2l-5.31 5.234L11 6H9.5l-3 12H8l1.173-4.693 1.54-1.438C11 16 14 18 14 18h2s-4-2-3.982-7.348z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.png
new file mode 100644 (file)
index 0000000..a2e715f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-progressive.svg
new file mode 100644 (file)
index 0000000..778b92c
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-k">
+        <path id="k" d="M12.018 10.652L17 6h-2l-5.31 5.234L11 6H9.5l-3 12H8l1.173-4.693 1.54-1.438C11 16 14 18 14 18h2s-4-2-3.982-7.348z"/>
+    </g>
+</svg>
index 1cfeb7a..1d48f72 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="italic-s">
         <path id="s" d="M16.474 6.59l-.302 1.525a7.36 7.36 0 0 0-1.557-.628 5.432 5.432 0 0 0-1.487-.217c-.935 0-1.68.204-2.23.612-.554.408-.83.95-.83 1.627 0 .37.1.65.302.86.207.19.733.4 1.58.63l.937.23c1.06.274 1.795.622 2.208 1.046.413.42.62 1.007.62 1.766 0 1.167-.46 2.117-1.38 2.85-.913.734-2.12 1.1-3.617 1.1-.615 0-1.232-.06-1.852-.185-.62-.12-1.242-.3-1.867-.55l.31-1.61a7.613 7.613 0 0 0 1.72.805c.58.18 1.155.27 1.73.27.976 0 1.76-.216 2.347-.65.59-.434.883-1 .883-1.697 0-.465-.12-.816-.354-1.054-.233-.242-.737-.46-1.512-.657l-.937-.24c-1.07-.28-1.8-.6-2.19-.964-.39-.368-.584-.88-.584-1.535 0-1.152.442-2.094 1.325-2.828.89-.74 2.043-1.108 3.463-1.108.555 0 1.1.05 1.644.146.542.1 1.085.245 1.627.442"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.png
new file mode 100644 (file)
index 0000000..d517352
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-progressive.svg
new file mode 100644 (file)
index 0000000..901d0e7
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="italic-s">
+        <path id="s" d="M16.474 6.59l-.302 1.525a7.36 7.36 0 0 0-1.557-.628 5.432 5.432 0 0 0-1.487-.217c-.935 0-1.68.204-2.23.612-.554.408-.83.95-.83 1.627 0 .37.1.65.302.86.207.19.733.4 1.58.63l.937.23c1.06.274 1.795.622 2.208 1.046.413.42.62 1.007.62 1.766 0 1.167-.46 2.117-1.38 2.85-.913.734-2.12 1.1-3.617 1.1-.615 0-1.232-.06-1.852-.185-.62-.12-1.242-.3-1.867-.55l.31-1.61a7.613 7.613 0 0 0 1.72.805c.58.18 1.155.27 1.73.27.976 0 1.76-.216 2.347-.65.59-.434.883-1 .883-1.697 0-.465-.12-.816-.354-1.054-.233-.242-.737-.46-1.512-.657l-.937-.24c-1.07-.28-1.8-.6-2.19-.964-.39-.368-.584-.88-.584-1.535 0-1.152.442-2.094 1.325-2.828.89-.74 2.043-1.108 3.463-1.108.555 0 1.1.05 1.644.146.542.1 1.085.245 1.627.442"/>
+    </g>
+</svg>
index 7ce988d..e7dc90e 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-invert.png differ
index 45b0391..d9eb01e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 9V8h-6v1h6zm-2 2v-1h-4v1h4zM6 5h1v16H6V5zm2 0h10v13c0 1.7-1.3 3-3 3H8V5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.png
new file mode 100644 (file)
index 0000000..444d48b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..5375d92
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 9V8h-6v1h6zm-2 2v-1h-4v1h4zM6 5h1v16H6V5zm2 0h10v13c0 1.7-1.3 3-3 3H8V5z"/>
+</svg>
index 4c30950..deb4f76 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-invert.png differ
index 77cf757..bd7ba07 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M8 9V8h6v1H8zm2 2v-1h4v1h-4zm8-6h-1v16h1V5zm-2 0H6v13c0 1.7 1.3 3 3 3h7V5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.png
new file mode 100644 (file)
index 0000000..e5a4fa1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e93ed43
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M8 9V8h6v1H8zm2 2v-1h4v1h-4zm8-6h-1v16h1V5zm-2 0H6v13c0 1.7 1.3 3 3 3h7V5z"/>
+</svg>
index 988f38e..fa77f37 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14.5 4C11.5 4 9 6.5 9 9.5c0 1 .3 1.9.7 2.8L4 18v2h4v-2h2v-2h2l1.2-1.2c.4.1.9.2 1.3.2 3 0 5.5-2.5 5.5-5.5S17.5 4 14.5 4zM16 9c-.8 0-1.5-.7-1.5-1.5S15.2 6 16 6s1.5.7 1.5 1.5S16.8 9 16 9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.png
new file mode 100644 (file)
index 0000000..37b1788
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..7ba0a1a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14.5 4C11.5 4 9 6.5 9 9.5c0 1 .3 1.9.7 2.8L4 18v2h4v-2h2v-2h2l1.2-1.2c.4.1.9.2 1.3.2 3 0 5.5-2.5 5.5-5.5S17.5 4 14.5 4zM16 9c-.8 0-1.5-.7-1.5-1.5S15.2 6 16 6s1.5.7 1.5 1.5S16.8 9 16 9z"/>
+</svg>
index 7ca2f8f..2a03797 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9.5 4c3 0 5.5 2.5 5.5 5.5 0 1-.3 1.9-.7 2.8L20 18v2h-4v-2h-2v-2h-2l-1.2-1.2c-.4.1-.9.2-1.3.2-3 0-5.5-2.5-5.5-5.5S6.5 4 9.5 4zM8 9c.8 0 1.5-.7 1.5-1.5S8.8 6 8 6s-1.5.7-1.5 1.5S7.2 9 8 9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.png
new file mode 100644 (file)
index 0000000..69a032a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..308c9c0
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9.5 4c3 0 5.5 2.5 5.5 5.5 0 1-.3 1.9-.7 2.8L20 18v2h-4v-2h-2v-2h-2l-1.2-1.2c-.4.1-.9.2-1.3.2-3 0-5.5-2.5-5.5-5.5S6.5 4 9.5 4zM8 9c.8 0 1.5-.7 1.5-1.5S8.8 6 8 6s-1.5.7-1.5 1.5S7.2 9 8 9z"/>
+</svg>
index b9cbad0..b206787 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 7v9c0 1.7 1.3 3 3 3h15V7H3zm8 2h2v2h-2V9zm0 3h2v2h-2v-2zM8 9h2v2H8V9zm0 3h2v2H8v-2zm-1 5H6c-.6 0-1-.4-1-1v-1h2v2zm0-3H5v-2h2v2zm0-3H5V9h2v2zm9 6H8v-2h8v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2zm3 6h-2v-2h2v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.png
new file mode 100644 (file)
index 0000000..49a0c4a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b17e024
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 7v9c0 1.7 1.3 3 3 3h15V7H3zm8 2h2v2h-2V9zm0 3h2v2h-2v-2zM8 9h2v2H8V9zm0 3h2v2H8v-2zm-1 5H6c-.6 0-1-.4-1-1v-1h2v2zm0-3H5v-2h2v2zm0-3H5V9h2v2zm9 6H8v-2h8v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2zm3 6h-2v-2h2v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2z"/>
+</svg>
index d235a35..d1799f9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 7v9c0 1.7-1.3 3-3 3H3V7h18zm-8 2h-2v2h2V9zm0 3h-2v2h2v-2zm3-3h-2v2h2V9zm0 3h-2v2h2v-2zm1 5h1c.6 0 1-.4 1-1v-1h-2v2zm0-3h2v-2h-2v2zm0-3h2V9h-2v2zm-9 6h8v-2H8v2zm0-3h2v-2H8v2zm0-3h2V9H8v2zm-3 6h2v-2H5v2zm0-3h2v-2H5v2zm0-3h2V9H5v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.png
new file mode 100644 (file)
index 0000000..cf144b7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..48fb71c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 7v9c0 1.7-1.3 3-3 3H3V7h18zm-8 2h-2v2h2V9zm0 3h-2v2h2v-2zm3-3h-2v2h2V9zm0 3h-2v2h2v-2zm1 5h1c.6 0 1-.4 1-1v-1h-2v2zm0-3h2v-2h-2v2zm0-3h2V9h-2v2zm-9 6h8v-2H8v2zm0-3h2v-2H8v2zm0-3h2V9H8v2zm-3 6h2v-2H5v2zm0-3h2v-2H5v2zm0-3h2V9H5v2z"/>
+</svg>
index 429ee29..4a36a34 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="A">
         <path d="M18.738 15.673l1.137 3.15h1.575L17.775 7.448h-2.188l-3.85 11.375h1.575l1.05-3.15h4.375zM16.55 8.76l1.837 5.427h-3.675l1.838-5.425z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.png
new file mode 100644 (file)
index 0000000..cfc3a36
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..4a3893b
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="A">
+        <path d="M18.738 15.673l1.137 3.15h1.575L17.775 7.448h-2.188l-3.85 11.375h1.575l1.05-3.15h4.375zM16.55 8.76l1.837 5.427h-3.675l1.838-5.425z"/>
+    </g>
+    <g id="文">
+        <path d="M8.325 6.573h.787l-.875-1.75h-1.75l.438.875a1.56 1.56 0 0 0 1.4.875z"/>
+        <path d="m 9.202,12.874 c 0.7,0.525 1.486,0.963 2.45,1.225 l -0.438,1.31 A 9.17,9.17 0 0 1 8.151,13.835 c -1.49,1.137 -3.063,1.837 -4.813,2.363 L 2.9,14.885 C 4.386,14.36 5.874,13.835 7.1,12.872 5.962,11.648 5.174,10.335 4.65,8.758 l -1.663,0 0,-1.31 10.85,0 -0.438,1.312 -1.75,0 c -0.308,1.33 -1.255,2.957 -2.45,4.114 z m 1.05,-4.114 -4.114,0 c 0.35,1.226 1.138,2.363 2.013,3.238 0.926,-1 1.617,-1.957 2.1,-3.237 z"/>
+    </g>
+</svg>
index f3c32eb..152a252 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="A">
         <path d="M5.612 15.673l-1.137 3.15H2.9L6.575 7.448h2.188l3.85 11.375h-1.575l-1.05-3.15H5.613zM7.8 8.76l-1.837 5.427h3.675L7.8 8.762z" id="path5"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.png
new file mode 100644 (file)
index 0000000..51d8ecf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..3b11ea9
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="A">
+        <path d="M5.612 15.673l-1.137 3.15H2.9L6.575 7.448h2.188l3.85 11.375h-1.575l-1.05-3.15H5.613zM7.8 8.76l-1.837 5.427h3.675L7.8 8.762z" id="path5"/>
+    </g>
+    <g id="文">
+        <path d="M16.384 6.573h.787l-.873-1.75h-1.75l.438.875c.26.535.805.874 1.4.875z" id="path7"/>
+        <path d="M15.15 12.874c-.7.525-1.486.963-2.45 1.225l.438 1.31a9.17 9.17 0 0 0 3.063-1.575c1.49 1.137 3.064 1.837 4.814 2.363l.438-1.313c-1.486-.525-2.974-1.05-4.2-2.013 1.138-1.224 1.926-2.537 2.45-4.114h1.663v-1.31h-10.85l.438 1.312h1.75c.308 1.33 1.255 2.957 2.45 4.114zM14.1 8.76h4.114c-.35 1.226-1.138 2.363-2.013 3.238-.925-1-1.616-1.957-2.1-3.237z" id="path11-7"/>
+    </g>
+</svg>
index 68f2452..844b6f9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-invert.png differ
index 36a8165..c75fed3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="layout-ltr">
         <path id="text" d="M5 19V5h6v8h8v6H5z"/>
         <path id="float" d="M13 5v6h6V5h-6zm5 5h-4V6h4v4z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.png
new file mode 100644 (file)
index 0000000..8dc8a31
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..a2bd2eb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="layout-ltr">
+        <path id="text" d="M5 19V5h6v8h8v6H5z"/>
+        <path id="float" d="M13 5v6h6V5h-6zm5 5h-4V6h4v4z"/>
+    </g>
+</svg>
index 39ba9c4..9229671 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="layout-rtl">
         <path id="text" d="M5 19v-6h8V5h6v14H5z"/>
         <path id="float" d="M5 5v6h6V5H5zm1 1h4v4H6V6z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.png
new file mode 100644 (file)
index 0000000..2c190ce
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..eaad0a0
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="layout-rtl">
+        <path id="text" d="M5 19v-6h8V5h6v14H5z"/>
+        <path id="float" d="M5 5v6h6V5H5zm1 1h4v4H6V6z"/>
+    </g>
+</svg>
index 5bee6d5..d5a69c0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15.387 4.33c-2.1 0-3.6 1.9-5.1 3.3.2 0 .5-.1.8-.1.5 0 1 .1 1.5.3.8-.8 1.6-1.7 2.8-1.7.6 0 1.3.3 1.8.7 1 1 1 2.6 0 3.6l-2.6 2.6c-.4.4-1.2.7-1.8.7-1.4 0-2.1-.9-2.6-2l-1.3 1.3c.8 1.5 2 2.6 3.8 2.6 1.2 0 2.3-.5 3-1.3l2.6-2.6c.9-.9 1.5-2 1.5-3.3-.2-2.2-2.2-4.1-4.4-4.1zm-4.3 12.1l-.9.9c-.4.4-1.2.7-1.8.7-.6 0-1.3-.3-1.8-.7-1-1-1-2.7 0-3.6l2.6-2.6c.4-.4 1.2-.7 1.8-.7 1.4 0 2.1 1 2.6 2l1.3-1.3c-.8-1.5-2-2.6-3.8-2.6-1.2 0-2.3.5-3 1.3l-2.6 2.6c-1.7 1.7-1.7 4.4 0 6 1.6 1.6 4.4 1.7 5.9 0l1.9-1.9c-.3.1-.6.1-.9.1-.5 0-.9 0-1.3-.2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.png
new file mode 100644 (file)
index 0000000..fb88cdf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c62fcca
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15.387 4.33c-2.1 0-3.6 1.9-5.1 3.3.2 0 .5-.1.8-.1.5 0 1 .1 1.5.3.8-.8 1.6-1.7 2.8-1.7.6 0 1.3.3 1.8.7 1 1 1 2.6 0 3.6l-2.6 2.6c-.4.4-1.2.7-1.8.7-1.4 0-2.1-.9-2.6-2l-1.3 1.3c.8 1.5 2 2.6 3.8 2.6 1.2 0 2.3-.5 3-1.3l2.6-2.6c.9-.9 1.5-2 1.5-3.3-.2-2.2-2.2-4.1-4.4-4.1zm-4.3 12.1l-.9.9c-.4.4-1.2.7-1.8.7-.6 0-1.3-.3-1.8-.7-1-1-1-2.7 0-3.6l2.6-2.6c.4-.4 1.2-.7 1.8-.7 1.4 0 2.1 1 2.6 2l1.3-1.3c-.8-1.5-2-2.6-3.8-2.6-1.2 0-2.3.5-3 1.3l-2.6 2.6c-1.7 1.7-1.7 4.4 0 6 1.6 1.6 4.4 1.7 5.9 0l1.9-1.9c-.3.1-.6.1-.9.1-.5 0-.9 0-1.3-.2z"/>
+</svg>
index 8e34361..7bb02fa 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9.025 3.6c2.1 0 3.6 1.9 5.1 3.3-.2 0-.5-.1-.8-.1-.5 0-1 .1-1.5.3-.8-.8-1.6-1.7-2.8-1.7-.6 0-1.3.3-1.8.7-1 1-1 2.6 0 3.6l2.6 2.6c.4.4 1.2.7 1.8.7 1.4 0 2.1-.9 2.6-2l1.3 1.3c-.8 1.5-2 2.6-3.8 2.6-1.2 0-2.3-.5-3-1.3l-2.6-2.6c-.9-.9-1.5-2-1.5-3.3.2-2.2 2.2-4.1 4.4-4.1zm4.3 12.1l.9.9c.4.4 1.2.7 1.8.7.6 0 1.3-.3 1.8-.7 1-1 1-2.7 0-3.6l-2.6-2.6c-.4-.4-1.2-.7-1.8-.7-1.4 0-2.1 1-2.6 2l-1.3-1.3c.8-1.5 2-2.6 3.8-2.6 1.2 0 2.3.5 3 1.3l2.6 2.6c1.7 1.7 1.7 4.4 0 6-1.6 1.6-4.4 1.7-5.9 0l-1.9-1.9c.3.1.6.1.9.1.5 0 .9 0 1.3-.2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.png
new file mode 100644 (file)
index 0000000..c030b01
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..1d6be90
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9.025 3.6c2.1 0 3.6 1.9 5.1 3.3-.2 0-.5-.1-.8-.1-.5 0-1 .1-1.5.3-.8-.8-1.6-1.7-2.8-1.7-.6 0-1.3.3-1.8.7-1 1-1 2.6 0 3.6l2.6 2.6c.4.4 1.2.7 1.8.7 1.4 0 2.1-.9 2.6-2l1.3 1.3c-.8 1.5-2 2.6-3.8 2.6-1.2 0-2.3-.5-3-1.3l-2.6-2.6c-.9-.9-1.5-2-1.5-3.3.2-2.2 2.2-4.1 4.4-4.1zm4.3 12.1l.9.9c.4.4 1.2.7 1.8.7.6 0 1.3-.3 1.8-.7 1-1 1-2.7 0-3.6l-2.6-2.6c-.4-.4-1.2-.7-1.8-.7-1.4 0-2.1 1-2.6 2l-1.3-1.3c.8-1.5 2-2.6 3.8-2.6 1.2 0 2.3.5 3 1.3l2.6 2.6c1.7 1.7 1.7 4.4 0 6-1.6 1.6-4.4 1.7-5.9 0l-1.9-1.9c.3.1.6.1.9.1.5 0 .9 0 1.3-.2z"/>
+</svg>
index 05d4180..88cdd9d 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-invert.png differ
index 2eb5329..d63a791 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 7H9V5h12v2zM7 6c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 12c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.png
new file mode 100644 (file)
index 0000000..350c708
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..db97e4b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 7H9V5h12v2zM7 6c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 12c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2z"/>
+</svg>
index dcce2ae..49879fb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 7h12V5H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 13h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 19h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.png
new file mode 100644 (file)
index 0000000..9a9e5ca
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..05b4b85
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 7h12V5H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 13h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 19h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2z"/>
+</svg>
index 208c726..13810b2 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-invert.png differ
index 1ab3f23..0d37861 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 7H8V5h13v2zm0 6H8v-2h13v2zm0 6H8v-2h13v2zM4 4h2v4H5V5H4zm-1 6V9h3v3H4v1h2v1H3v-3h2v-1zm3 10H3v-1h2v-1H4v-1h1v-1H3v-1h3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.png
new file mode 100644 (file)
index 0000000..bf3c311
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d3328a1
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 7H8V5h13v2zm0 6H8v-2h13v2zm0 6H8v-2h13v2zM4 4h2v4H5V5H4zm-1 6V9h3v3H4v1h2v1H3v-3h2v-1zm3 10H3v-1h2v-1H4v-1h1v-1H3v-1h3z"/>
+</svg>
index c2d28dc..631c266 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr.png differ
index 58be38a..9b2e821 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-invert.png differ
index ab12c83..fff182c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 7h13V5H3zm0 6h13v-2H3zm0 6h13v-2H3zM18 4h2v4h-1V5h-1zm0 6V9h3v3h-2v1h2v1h-3v-3h2v-1zm3 10h-3v-1h2v-1h-1v-1h1v-1h-2v-1h3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.png
new file mode 100644 (file)
index 0000000..7e1df5d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..c4c9396
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 7h13V5H3zm0 6h13v-2H3zm0 6h13v-2H3zM18 4h2v4h-1V5h-1zm0 6V9h3v3h-2v1h2v1h-3v-3h2v-1zm3 10h-3v-1h2v-1h-1v-1h1v-1h-2v-1h3z"/>
+</svg>
index b0bd959..2c3b9d0 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl.png differ
index 9c3c948..42311de 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-destructive.png differ
index e0c482b..a9900c1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M15 8s0-3-2.5-3S10 8 10 8v1h5zm2 0v1h2v10H9c-1.7 0-3-1.3-3-3V9h2V8s0-5 4.5-5S17 8 17 8z"/>
 </svg>
index ee341a9..fa72442 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 8s0-3-2.5-3S10 8 10 8v1h5zm2 0v1h2v10H9c-1.7 0-3-1.3-3-3V9h2V8s0-5 4.5-5S17 8 17 8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.png
new file mode 100644 (file)
index 0000000..181c0cb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..e21e755
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 8s0-3-2.5-3S10 8 10 8v1h5zm2 0v1h2v10H9c-1.7 0-3-1.3-3-3V9h2V8s0-5 4.5-5S17 8 17 8z"/>
+</svg>
index 53ca51a..72d6a7b 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-destructive.png differ
index edc9312..2811b25 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M10 8s0-3 2.5-3S15 8 15 8v1h-5zM8 8v1H6v10h10c1.7 0 3-1.3 3-3V9h-2V8s0-5-4.5-5S8 8 8 8z"/>
 </svg>
index 2f8851c..cfbad71 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10 8s0-3 2.5-3S15 8 15 8v1h-5zM8 8v1H6v10h10c1.7 0 3-1.3 3-3V9h-2V8s0-5-4.5-5S8 8 8 8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.png
new file mode 100644 (file)
index 0000000..d17e790
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..afd2e07
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10 8s0-3 2.5-3S15 8 15 8v1h-5zM8 8v1H6v10h10c1.7 0 3-1.3 3-3V9h-2V8s0-5-4.5-5S8 8 8 8z"/>
+</svg>
index f81d7c4..0d5cc21 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 14v3l5-4.5L15 8v3H8c0 1.7 1.3 3 3 3h4zm-1-9H4v15h10v-2H6V7h8V5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.png
new file mode 100644 (file)
index 0000000..8ede173
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..9e8d1a6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 14v3l5-4.5L15 8v3H8c0 1.7 1.3 3 3 3h4zm-1-9H4v15h10v-2H6V7h8V5z"/>
+</svg>
index d3fae89..07ef00f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9 14v3l-5-4.5L9 8v3h7c0 1.7-1.3 3-3 3H9zm1-9h10v15H10v-2h8V7h-8V5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.png
new file mode 100644 (file)
index 0000000..642db7d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..5a8617a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9 14v3l-5-4.5L9 8v3h7c0 1.7-1.3 3-3 3H9zm1-9h10v15H10v-2h8V7h-8V5z"/>
+</svg>
index 48eafd4..da611ba 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 6c-3.9 0-7 3.1-7 7s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm-1.7-4.6c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5zm4 0c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.png
new file mode 100644 (file)
index 0000000..2e37da7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-progressive.svg
new file mode 100644 (file)
index 0000000..c24cba6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 6c-3.9 0-7 3.1-7 7s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm-1.7-4.6c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5zm4 0c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5z"/>
+</svg>
index 4794f33..9414378 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15.4 7.8c-2-.9-2.3-2.5-2.4-2.8.1.1 2 1 2 1l-3-5-3 5 2-1s0 .8.6 2.1c.8 1.5 2.2 2.2 2.2 2.2s1.6.7 2.2 1.3l-.7.7-.5-.5-.4 1.8 1.8-.4-.5-.5.7-.7c.9 1 1.5 2.3 1.6 3.8h-1V14l-1.5 1 1.5 1v-.8h1c-.1 1.5-.6 2.8-1.6 3.8l-.7-.7.5-.5-1.8-.4.4 1.8.5-.5.7.7c-1 .9-2.3 1.5-3.8 1.6v-1h.8l-1-1.5-1 1.5h.8v1c-1.5-.1-2.8-.6-3.8-1.6l.7-.7.5.5.4-1.8-1.8.4.5.5-.7.7c-.9-1-1.5-2.3-1.6-3.8h1v.8l1.5-1L7 14v.8H6c.1-1.5.6-2.8 1.6-3.8l.7.7-.5.5 1.8.4-.4-1.8-.5.5-.7-.7-1.5-1.4A7.99 7.99 0 0 0 4 15c0 4.4 3.6 8 8 8s8-3.6 8-8c0-3.2-1.9-5.9-4.6-7.2z"/>
     <circle cx="12" cy="15" r="3"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.png
new file mode 100644 (file)
index 0000000..6aa047c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-progressive.svg
new file mode 100644 (file)
index 0000000..5e89b3d
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15.4 7.8c-2-.9-2.3-2.5-2.4-2.8.1.1 2 1 2 1l-3-5-3 5 2-1s0 .8.6 2.1c.8 1.5 2.2 2.2 2.2 2.2s1.6.7 2.2 1.3l-.7.7-.5-.5-.4 1.8 1.8-.4-.5-.5.7-.7c.9 1 1.5 2.3 1.6 3.8h-1V14l-1.5 1 1.5 1v-.8h1c-.1 1.5-.6 2.8-1.6 3.8l-.7-.7.5-.5-1.8-.4.4 1.8.5-.5.7.7c-1 .9-2.3 1.5-3.8 1.6v-1h.8l-1-1.5-1 1.5h.8v1c-1.5-.1-2.8-.6-3.8-1.6l.7-.7.5.5.4-1.8-1.8.4.5.5-.7.7c-.9-1-1.5-2.3-1.6-3.8h1v.8l1.5-1L7 14v.8H6c.1-1.5.6-2.8 1.6-3.8l.7.7-.5.5 1.8.4-.4-1.8-.5.5-.7-.7-1.5-1.4A7.99 7.99 0 0 0 4 15c0 4.4 3.6 8 8 8s8-3.6 8-8c0-3.2-1.9-5.9-4.6-7.2z"/>
+    <circle cx="12" cy="15" r="3"/>
+</svg>
index 1fe3277..f7293c8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M22.3 6.3c0 .2 0 .3-.1.3-.7.1-1.2.5-1.6 1.1-.1.2-.2.4-.3.7l-4.6 10.1c-.1.2-.2.3-.2.3s-.1.1-.2.1c-.2 0-.4-.1-.5-.4L12.2 13l-2.8 5.5c-.1.3-.3.4-.5.4s-.4-.1-.5-.4L4.1 8.4c-.3-.8-.6-1.2-.8-1.4-.2-.2-.5-.3-1-.4-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h4.3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.6.1-1 .2-1.1.4-.1.2 0 .6.3 1.2l3.6 8.2h.1l2.2-4.4L10 8.4c-.3-.7-.6-1.2-.8-1.4s-.5-.3-.9-.4c-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h3.6c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.4.1-.6.2-.6.4s.1.6.4 1.2l1 1.9 1-1.9c.3-.6.5-.9.5-1.1 0-.2 0-.3-.1-.4-.1-.1-.3-.1-.5-.1l-.1-.3c0-.2 0-.3.1-.3h3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.5.1-.8.2-1.1.5-.3.3-.6.7-.8 1.3l-1.3 2.8 2.5 5.2h.1l3.7-8.1c.3-.5.3-.9.2-1.2-.1-.3-.5-.4-1.1-.5-.1-.1-.1-.2-.1-.3s0-.3.1-.3h3.7c-.2.1-.2.2-.2.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.png
new file mode 100644 (file)
index 0000000..bc141ed
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-progressive.svg
new file mode 100644 (file)
index 0000000..64d2e28
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M22.3 6.3c0 .2 0 .3-.1.3-.7.1-1.2.5-1.6 1.1-.1.2-.2.4-.3.7l-4.6 10.1c-.1.2-.2.3-.2.3s-.1.1-.2.1c-.2 0-.4-.1-.5-.4L12.2 13l-2.8 5.5c-.1.3-.3.4-.5.4s-.4-.1-.5-.4L4.1 8.4c-.3-.8-.6-1.2-.8-1.4-.2-.2-.5-.3-1-.4-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h4.3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.6.1-1 .2-1.1.4-.1.2 0 .6.3 1.2l3.6 8.2h.1l2.2-4.4L10 8.4c-.3-.7-.6-1.2-.8-1.4s-.5-.3-.9-.4c-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h3.6c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.4.1-.6.2-.6.4s.1.6.4 1.2l1 1.9 1-1.9c.3-.6.5-.9.5-1.1 0-.2 0-.3-.1-.4-.1-.1-.3-.1-.5-.1l-.1-.3c0-.2 0-.3.1-.3h3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.5.1-.8.2-1.1.5-.3.3-.6.7-.8 1.3l-1.3 2.8 2.5 5.2h.1l3.7-8.1c.3-.5.3-.9.2-1.2-.1-.3-.5-.4-1.1-.5-.1-.1-.1-.2-.1-.3s0-.3.1-.3h3.7c-.2.1-.2.2-.2.3z"/>
+</svg>
index 2d6d0a0..fb772cf 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 6L9 4 3 6v15l6-2 6 2 6-2V4l-6 2zM8.7 18.1L4 19.6V6.7L9 5v12.9l-.3.2zm11.3.2L15 20V7.1l.3-.1L20 5.4v12.9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.png
new file mode 100644 (file)
index 0000000..e856237
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c148e74
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 6L9 4 3 6v15l6-2 6 2 6-2V4l-6 2zM8.7 18.1L4 19.6V6.7L9 5v12.9l-.3.2zm11.3.2L15 20V7.1l.3-.1L20 5.4v12.9z"/>
+</svg>
index 00d3efc..bfbfe72 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9 6l6-2 6 2v15l-6-2-6 2-6-2V4l6 2zm6.3 12.1l4.7 1.5V6.7L15 5v12.9l.3.2zM4 18.3L9 20V7.1L8.7 7 4 5.4v12.9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.png
new file mode 100644 (file)
index 0000000..6dbb6c5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e928f61
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9 6l6-2 6 2v15l-6-2-6 2-6-2V4l6 2zm6.3 12.1l4.7 1.5V6.7L15 5v12.9l.3.2zM4 18.3L9 20V7.1L8.7 7 4 5.4v12.9z"/>
+</svg>
index dc9791e..663913a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19 12c0-3.9-3.1-7-7-7s-7 3.1-7 7c0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7zm-7 4c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.png
new file mode 100644 (file)
index 0000000..c1676e6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-progressive.svg
new file mode 100644 (file)
index 0000000..a9631cc
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19 12c0-3.9-3.1-7-7-7s-7 3.1-7 7c0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7zm-7 4c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+</svg>
index 68fe22a..43074af 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M24 4h-4V0h-2v4h-4v2h4v4h2V6h4z"/>
     <path d="M18 11h-1V7.1l-.1-.1H13V5.1c-.3-.1-.7-.1-1-.1-3.9 0-7 3.1-7 7 0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7 0-.3 0-.7-.1-1H18zm-6 5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.png
new file mode 100644 (file)
index 0000000..2fcf2e1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..7dc09d4
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M24 4h-4V0h-2v4h-4v2h4v4h2V6h4z"/>
+    <path d="M18 11h-1V7.1l-.1-.1H13V5.1c-.3-.1-.7-.1-1-.1-3.9 0-7 3.1-7 7 0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7 0-.3 0-.7-.1-1H18zm-6 5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+</svg>
index e3ba379..6a4af93 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M0 4h4V0h2v4h4v2H6v4H4V6H0z"/>
     <path d="M6 11h1V7.1l.1-.1H11V5.1c.3-.1.7-.1 1-.1 3.9 0 7 3.1 7 7 0 1.4-.4 2.6-1.1 3.7L12 23l-5.9-7.3C5.4 14.6 5 13.4 5 12c0-.3 0-.7.1-1H6zm6 5c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.png
new file mode 100644 (file)
index 0000000..56b7924
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..8108685
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M0 4h4V0h2v4h4v2H6v4H4V6H0z"/>
+    <path d="M6 11h1V7.1l.1-.1H11V5.1c.3-.1.7-.1 1-.1 3.9 0 7 3.1 7 7 0 1.4-.4 2.6-1.1 3.7L12 23l-5.9-7.3C5.4 14.6 5 13.4 5 12c0-.3 0-.7.1-1H6zm6 5c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4z"/>
+</svg>
index 9d87725..0fde2c9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="markup">
         <path id="left-bracket" d="M9.665 6.32l-4.259 4.274-1.406 1.406 1.406 1.406 4.259 4.274 1.406-1.438-4.259-4.243 4.259-4.243z"/>
         <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" xlink:href="#left-bracket"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.png
new file mode 100644 (file)
index 0000000..47ac730
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/markup-progressive.svg
new file mode 100644 (file)
index 0000000..891f51d
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="markup">
+        <path id="left-bracket" d="M9.665 6.32l-4.259 4.274-1.406 1.406 1.406 1.406 4.259 4.274 1.406-1.438-4.259-4.243 4.259-4.243z"/>
+        <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" xlink:href="#left-bracket"/>
+    </g>
+</svg>
index dbd4a98..1c1dc22 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="menu">
         <path id="lines" d="M6 15h12a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1zm-1-4v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1zm0-5v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.png
new file mode 100644 (file)
index 0000000..f395e3a
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-progressive.svg
new file mode 100644 (file)
index 0000000..a94207a
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="menu">
+        <path id="lines" d="M6 15h12a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1zm-1-4v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1zm0-5v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1z"/>
+    </g>
+</svg>
index df72450..b7a09c6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 9c0-1.7-1.3-3-3-3H3v3l9 4 9-4zM3 11v6c0 1.7 1.3 3 3 3h15v-9l-9 4-9-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.png
new file mode 100644 (file)
index 0000000..fb11ff2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..47e2797
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M21 9c0-1.7-1.3-3-3-3H3v3l9 4 9-4zM3 11v6c0 1.7 1.3 3 3 3h15v-9l-9 4-9-4z"/>
+</svg>
index 1bb1dae..63493b7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 9c0-1.7 1.3-3 3-3h15v3l-9 4-9-4zm18 2v6c0 1.7-1.3 3-3 3H3v-9l9 4 9-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.png
new file mode 100644 (file)
index 0000000..a6975d7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..c7b2c68
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 9c0-1.7 1.3-3 3-3h15v3l-9 4-9-4zm18 2v6c0 1.7-1.3 3-3 3H3v-9l9 4 9-4z"/>
+</svg>
index 63c7b4c..5d9b12a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 11l-4-3v2h-3V7h2l-3-4-3 4h2v3H8V8l-4 3 4 3v-2h3v3H9l3 4 3-4h-2v-3h3v2z"/>
 </svg>
index 6fdddd8..b5b9a8a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="move-ltr">
         <path id="arrow" d="M8.935 7.18l5.302 5.303-5.302 5.303L10.35 19.2l6.715-6.717-6.716-6.716z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.png
new file mode 100644 (file)
index 0000000..5f37c25
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..0a9917f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="move-ltr">
+        <path id="arrow" d="M8.935 7.18l5.302 5.303-5.302 5.303L10.35 19.2l6.715-6.717-6.716-6.716z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.png
new file mode 100644 (file)
index 0000000..c2d6a84
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-progressive.svg
new file mode 100644 (file)
index 0000000..00ae97b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 11l-4-3v2h-3V7h2l-3-4-3 4h2v3H8V8l-4 3 4 3v-2h3v3H9l3 4 3-4h-2v-3h3v2z"/>
+</svg>
index 2f1e91e..6ba135e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="move-rtl">
         <path id="arrow" d="M15.065 17.786l-5.302-5.303 5.302-5.302-1.415-1.41-6.714 6.72 6.714 6.71z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.png
new file mode 100644 (file)
index 0000000..3dbedd5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e98ca08
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="move-rtl">
+        <path id="arrow" d="M15.065 17.786l-5.302-5.303 5.302-5.302-1.415-1.41-6.714 6.72 6.714 6.71z"/>
+    </g>
+</svg>
index 25cf321..8b86916 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 5l2.5 2.5L11 11c-1.2 1.2-1.2 2.8 0 4l5.5-5.5L19 12V5h-7zm5 12H8c-.6 0-1-.4-1-1V7h3L8 5H5v11c0 1.7 1.3 3 3 3h11v-3l-2-2v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.png
new file mode 100644 (file)
index 0000000..26be088
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..54e3194
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 5l2.5 2.5L11 11c-1.2 1.2-1.2 2.8 0 4l5.5-5.5L19 12V5h-7zm5 12H8c-.6 0-1-.4-1-1V7h3L8 5H5v11c0 1.7 1.3 3 3 3h11v-3l-2-2v3z"/>
+</svg>
index cb0a035..a372d88 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 5L9.5 7.5 13 11c1.2 1.2 1.2 2.8 0 4L7.5 9.5 5 12V5h7zM7 17h9c.6 0 1-.4 1-1V7h-3l2-2h3v11c0 1.7-1.3 3-3 3H5v-3l2-2v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.png
new file mode 100644 (file)
index 0000000..10e2735
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..3bcf517
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 5L9.5 7.5 13 11c1.2 1.2 1.2 2.8 0 4L7.5 9.5 5 12V5h7zM7 17h9c.6 0 1-.4 1-1V7h-3l2-2h3v11c0 1.7-1.3 3-3 3H5v-3l2-2v3z"/>
+</svg>
index 220450a..5f531f3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17.8 5.7c-.5 0-.9.2-1.2.5s-.5.7-.5 1.2v4.3H11v-4l-6 5.5 6 5.5v-4h8v-9h-1.2z" id="line_return"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.png
new file mode 100644 (file)
index 0000000..67ec969
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..bf7a001
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M17.8 5.7c-.5 0-.9.2-1.2.5s-.5.7-.5 1.2v4.3H11v-4l-6 5.5 6 5.5v-4h8v-9h-1.2z" id="line_return"/>
+</svg>
index 214aea9..34d15fd 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6.2 5.7c.5 0 .9.2 1.2.5.3.3.5.7.5 1.2v4.3H13v-4l6 5.5-6 5.5v-4H5v-9h1.2z" id="line_return"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.png
new file mode 100644 (file)
index 0000000..8e748e5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..b209825
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6.2 5.7c.5 0 .9.2 1.2.5.3.3.5.7.5 1.2v4.3H13v-4l6 5.5-6 5.5v-4H5v-9h1.2z" id="line_return"/>
+</svg>
index 171f6e6..903b3a0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6 7v12c-.6 0-1-.4-1-1V9H4v9c0 1.1.9 2 2 2h15V7H6zm9 11H8v-1h7v1zm0-2H8v-1h7v1zm0-2H8v-1h7v1zm4 4h-3v-5h3v5zm0-7H8V9h11v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.png
new file mode 100644 (file)
index 0000000..182f316
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..f2c23c1
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6 7v12c-.6 0-1-.4-1-1V9H4v9c0 1.1.9 2 2 2h15V7H6zm9 11H8v-1h7v1zm0-2H8v-1h7v1zm0-2H8v-1h7v1zm4 4h-3v-5h3v5zm0-7H8V9h11v2z"/>
+</svg>
index c161b6e..86bdeb0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19 7v12c.6 0 1-.4 1-1V9h1v9c0 1.1-.9 2-2 2H4V7h15zm-9 11h7v-1h-7v1zm0-2h7v-1h-7v1zm0-2h7v-1h-7v1zm-4 4h3v-5H6v5zm0-7h11V9H6v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.png
new file mode 100644 (file)
index 0000000..8a6d99c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..edcab40
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19 7v12c.6 0 1-.4 1-1V9h1v9c0 1.1-.9 2-2 2H4V7h15zm-9 11h7v-1h-7v1zm0-2h7v-1h-7v1zm0-2h7v-1h-7v1zm-4 4h3v-5H6v5zm0-7h11V9H6v2z"/>
+</svg>
index aacacca..a17092e 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png differ
index 92882b0..f89b415 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z" id="noWikiText-rtl"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.png
new file mode 100644 (file)
index 0000000..2ac38b3
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..898c674
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z" id="noWikiText-rtl"/>
+</svg>
index f9fcbba..2623b84 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png differ
index 5e50951..1f7ed87 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png differ
index b07a6a1..ddac4c9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z" id="noWikiText-rtl"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.png
new file mode 100644 (file)
index 0000000..aba76c8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..a81c3a5
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z" id="noWikiText-rtl"/>
+</svg>
index a4dad7f..4d0ce86 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png differ
index 50d4ad6..c7d59cc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="svg3116"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="svg3116"><style>* { fill: #fff }</style>
     <path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm-1-5h2V8h-2zm0 3h2v-2h-2z" id="alert"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.png
new file mode 100644 (file)
index 0000000..52e8b87
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-progressive.svg
new file mode 100644 (file)
index 0000000..fefbc9f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="svg3116"><style>* { fill: #36c }</style>
+    <path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm-1-5h2V8h-2zm0 3h2v-2h-2z" id="alert"/>
+</svg>
index e8a56bc..6e878c3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #ffffff }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #fff }</style>
     <path d="M17.8 18.6H2.5l2.7-2.7V6h15.3v9.9c0 1.53-1.17 2.7-2.7 2.7zm-7.542-4.95c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 6fffe1a..dd7100e 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-progressive.png differ
index 89d2745..25ca20d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347bff }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #36c }</style>
     <path d="M17.8 18.6H2.5l2.7-2.7V6h15.3v9.9c0 1.53-1.17 2.7-2.7 2.7zm-7.542-4.95c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index a66123e..f483a16 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #ffffff }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #fff }</style>
     <path d="M5.2 18.6h15.3l-2.7-2.7V6H2.5v9.9c0 1.53 1.17 2.7 2.7 2.7zm7.542-4.95c0 .405.135.675.405.945.27.27.607.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.607-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.608-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.332 1.332 0 0 0-.945-.405c-.337 0-.675.135-.945.405-.27.27-.405.608-.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 4c0bd30..950fad5 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-progressive.png differ
index d0c3c64..659dde8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347bff }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #36c }</style>
     <path d="M5.2 18.6h15.3l-2.7-2.7V6H2.5v9.9c0 1.53 1.17 2.7 2.7 2.7zm7.542-4.95c0 .405.135.675.405.945.27.27.607.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.607-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.608-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.332 1.332 0 0 0-.945-.405c-.337 0-.675.135-.945.405-.27.27-.405.608-.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 8136cb9..14cf6d7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 12l5 4V8l-5 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.png
new file mode 100644 (file)
index 0000000..00e8a09
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c5f296f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 12l5 4V8l-5 4z"/>
+</svg>
index 4a08f5f..2d47900 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zm18-8l-5 4V8l5 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.png
new file mode 100644 (file)
index 0000000..b0223bf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..16eb16e
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zm18-8l-5 4V8l5 4z"/>
+</svg>
index d9d1390..56d505c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="outline-ltr">
         <path id="text" d="M5 13h14v6H5v-6z"/>
         <path id="float" d="M5 5v6h6V5H5zm5 5H6V6h4v4z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.png
new file mode 100644 (file)
index 0000000..df70c26
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..bed7254
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="outline-ltr">
+        <path id="text" d="M5 13h14v6H5v-6z"/>
+        <path id="float" d="M5 5v6h6V5H5zm5 5H6V6h4v4z"/>
+    </g>
+</svg>
index f1dd2df..49dd03e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="outline-rtl">
         <path id="text" d="M19 19H5v-6h14v6z"/>
         <path id="float" d="M13 5v6h6V5h-6zm1 1h4v4h-4V6z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.png
new file mode 100644 (file)
index 0000000..f5af3f3
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..8c126eb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="outline-rtl">
+        <path id="text" d="M19 19H5v-6h14v6z"/>
+        <path id="float" d="M13 5v6h6V5h-6zm1 1h4v4h-4V6z"/>
+    </g>
+</svg>
index ffc0cc0..2ced125 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-2 12V9l6 4-6 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7f2ef08
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..8703508
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-2 12V9l6 4-6 4z"/>
+</svg>
index 9c3220b..4e1287f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 5c4.4 0 8 3.6 8 8s-3.6 8-8 8-8-3.6-8-8 3.6-8 8-8zm2 12V9l-6 4 6 4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.png
new file mode 100644 (file)
index 0000000..a6bf5b1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..c8529dc
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 5c4.4 0 8 3.6 8 8s-3.6 8-8 8-8-3.6-8-8 3.6-8 8-8zm2 12V9l-6 4 6 4z"/>
+</svg>
index 2517166..ca07bcc 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-invert.png differ
index 4737769..cc215e7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M18 8h-1V4H7v4H3v6c0 1.7 1.3 3 3 3h1v3h10v-3h4v-6c0-1.7-1.3-3-3-3zM8 5h8v3H8V5zm8 14H8v-6h8v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.png
new file mode 100644 (file)
index 0000000..e37b3ea
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..0d68d7d
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M18 8h-1V4H7v4H3v6c0 1.7 1.3 3 3 3h1v3h10v-3h4v-6c0-1.7-1.3-3-3-3zM8 5h8v3H8V5zm8 14H8v-6h8v6z"/>
+</svg>
index 0084ac9..a7f6717 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr.png differ
index 37a9e3d..95ef54a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-invert.png differ
index 14d5bfe..8b66878 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6 8h1V4h10v4h4v6c0 1.7-1.3 3-3 3h-1v3H7v-3H3v-6c0-1.7 1.3-3 3-3zm10-3H8v3h8V5zM8 19h8v-6H8v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.png
new file mode 100644 (file)
index 0000000..0a0a198
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6f3ccbb
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6 8h1V4h10v4h4v6c0 1.7-1.3 3-3 3h-1v3H7v-3H3v-6c0-1.7 1.3-3 3-3zm10-3H8v3h8V5zM8 19h8v-6H8v6z"/>
+</svg>
index fb03c63..f1dba0f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M18 9.9c-.7 0-1.4.3-1.8.9V6h-4c.2-.4.4-.8.4-1.2 0-1.2-1-2.2-2.2-2.2-1.3-.1-2.3.9-2.3 2.2 0 .4.2.8.4 1.2H4.1v3.6l.6-.1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5c-.2 0-.4 0-.6-.1V18H9c-.5.4-.9 1-.9 1.8 0 1.2 1 2.2 2.3 2.2 1.2 0 2.2-1 2.2-2.2 0-.7-.3-1.4-.9-1.8h4.5v-4.5c.4.5 1 .9 1.8.9 1.2 0 2.2-1 2.2-2.2 0-1.3-1-2.3-2.2-2.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.png
new file mode 100644 (file)
index 0000000..75418d7
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c4af15c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M18 9.9c-.7 0-1.4.3-1.8.9V6h-4c.2-.4.4-.8.4-1.2 0-1.2-1-2.2-2.2-2.2-1.3-.1-2.3.9-2.3 2.2 0 .4.2.8.4 1.2H4.1v3.6l.6-.1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5c-.2 0-.4 0-.6-.1V18H9c-.5.4-.9 1-.9 1.8 0 1.2 1 2.2 2.3 2.2 1.2 0 2.2-1 2.2-2.2 0-.7-.3-1.4-.9-1.8h4.5v-4.5c.4.5 1 .9 1.8.9 1.2 0 2.2-1 2.2-2.2 0-1.3-1-2.3-2.2-2.3z"/>
+</svg>
index 9f7ce1e..7cce537 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6.3 9.9c.7 0 1.4.3 1.8.9V6h4c-.2-.4-.4-.8-.4-1.2 0-1.2 1-2.2 2.2-2.2 1.3-.1 2.3.9 2.3 2.2 0 .4-.2.8-.4 1.2h4.4v3.6l-.6-.1c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5c.2 0 .4 0 .6-.1V18h-4.9c.5.4.9 1 .9 1.8 0 1.2-1 2.2-2.3 2.2-1.2 0-2.2-1-2.2-2.2 0-.7.3-1.4.9-1.8H8.1v-4.5c-.4.5-1 .9-1.8.9-1.2 0-2.2-1-2.2-2.2 0-1.3 1-2.3 2.2-2.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.png
new file mode 100644 (file)
index 0000000..e8273ec
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..3ea0529
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6.3 9.9c.7 0 1.4.3 1.8.9V6h4c-.2-.4-.4-.8-.4-1.2 0-1.2 1-2.2 2.2-2.2 1.3-.1 2.3.9 2.3 2.2 0 .4-.2.8-.4 1.2h4.4v3.6l-.6-.1c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5c.2 0 .4 0 .6-.1V18h-4.9c.5.4.9 1 .9 1.8 0 1.2-1 2.2-2.3 2.2-1.2 0-2.2-1-2.2-2.2 0-.7.3-1.4.9-1.8H8.1v-4.5c-.4.5-1 .9-1.8.9-1.2 0-2.2-1-2.2-2.2 0-1.3 1-2.3 2.2-2.3z"/>
+</svg>
index 17de62b..ca34c66 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="quotes">
         <path id="quote" d="M6.9 8.4c-.446.55-1.974 2.6-1.9 5.7V17h4.7c.9 0 1.6-.7 1.6-1.6V11H8.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.png
new file mode 100644 (file)
index 0000000..ce64429
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..f795285
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="quotes">
+        <path id="quote" d="M6.9 8.4c-.446.55-1.974 2.6-1.9 5.7V17h4.7c.9 0 1.6-.7 1.6-1.6V11H8.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
+    </g>
+    <use transform="translate(8)" id="quote2" width="24" height="24" xlink:href="#quote"/>
+</svg>
index 0ac72cb..38e0cbc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="quotes">
         <path id="quote" d="M17.1 8.4c.446.55 1.9 2.6 1.9 5.7V17h-4.7c-.9 0-1.6-.7-1.6-1.6V11h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.png
new file mode 100644 (file)
index 0000000..dff8962
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..097b62a
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="quotes">
+        <path id="quote" d="M17.1 8.4c.446.55 1.9 2.6 1.9 5.7V17h-4.7c-.9 0-1.6-.7-1.6-1.6V11h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
+    </g>
+    <use transform="translate(-8)" id="quote2" width="24" height="24" xlink:href="#quote"/>
+</svg>
index 8953f4d..425815b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="quotes-add">
         <path id="quote" d="M5.9 10.4c-.446.55-1.974 2.6-1.9 5.7V19h4.7c.9 0 1.593-.7 1.6-1.6V13H7.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
         <path id="quote2" d="M15 9.344c-.476.32-.78.677-1.094 1.062A8.76 8.76 0 0 0 12 16.094V19h4.688a1.6 1.6 0 0 0 1.625-1.594V13H15V9.344z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.png
new file mode 100644 (file)
index 0000000..f36a0ca
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..be02943
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="quotes-add">
+        <path id="quote" d="M5.9 10.4c-.446.55-1.974 2.6-1.9 5.7V19h4.7c.9 0 1.593-.7 1.6-1.6V13H7.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
+        <path id="quote2" d="M15 9.344c-.476.32-.78.677-1.094 1.062A8.76 8.76 0 0 0 12 16.094V19h4.688a1.6 1.6 0 0 0 1.625-1.594V13H15V9.344z"/>
+        <path id="add" d="M18 6V2h-2v4h-4v2h4v4h2V8h4V6z"/>
+    </g>
+</svg>
index 1ada793..c7aea7a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="quotes-add">
         <path id="quote" d="M18.097 10.4c.446.55 1.974 2.6 1.9 5.7V19h-4.7c-.9 0-1.593-.7-1.6-1.6V13h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
         <path id="quote2" d="M8.997 9.344c.476.32.782.677 1.094 1.062A8.758 8.758 0 0 1 12 16.094V19H7.31c-.9 0-1.618-.694-1.625-1.594V13h3.312V9.344z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.png
new file mode 100644 (file)
index 0000000..f824259
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..2758fa6
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="quotes-add">
+        <path id="quote" d="M18.097 10.4c.446.55 1.974 2.6 1.9 5.7V19h-4.7c-.9 0-1.593-.7-1.6-1.6V13h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
+        <path id="quote2" d="M8.997 9.344c.476.32.782.677 1.094 1.062A8.758 8.758 0 0 1 12 16.094V19H7.31c-.9 0-1.618-.694-1.625-1.594V13h3.312V9.344z"/>
+        <path id="add" d="M5.997 6V2h2v4h4v2h-4v4h-2V8h-4V6z"/>
+    </g>
+</svg>
index 61b1550..76ead20 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="regular-expression">
         <path id="left-bracket" d="M3 12.045c0-.99.15-1.915.45-2.777A6.886 6.886 0 0 1 4.764 7H6.23a7.923 7.923 0 0 0-1.25 2.374 8.563 8.563 0 0 0 .007 5.314c.29.85.7 1.622 1.23 2.312h-1.45a6.53 6.53 0 0 1-1.314-2.223 8.126 8.126 0 0 1-.45-2.732"/>
         <path id="dot" d="M10 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.png
new file mode 100644 (file)
index 0000000..af85e03
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-progressive.svg
new file mode 100644 (file)
index 0000000..82598da
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="regular-expression">
+        <path id="left-bracket" d="M3 12.045c0-.99.15-1.915.45-2.777A6.886 6.886 0 0 1 4.764 7H6.23a7.923 7.923 0 0 0-1.25 2.374 8.563 8.563 0 0 0 .007 5.314c.29.85.7 1.622 1.23 2.312h-1.45a6.53 6.53 0 0 1-1.314-2.223 8.126 8.126 0 0 1-.45-2.732"/>
+        <path id="dot" d="M10 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
+        <path id="star" d="M14.25 7.013l-.24 2.156 2.187-.61.193 1.47-1.992.14 1.307 1.74-1.33.71-.914-1.833-.8 1.822-1.38-.698 1.296-1.74-1.98-.152.23-1.464 2.14.61-.24-2.158h1.534"/>
+        <path id="right-bracket" d="M21 12.045c0 .982-.152 1.896-.457 2.744A6.51 6.51 0 0 1 19.236 17h-1.453a8.017 8.017 0 0 0 1.225-2.31c.29-.855.434-1.74.434-2.66 0-.91-.14-1.797-.422-2.66a7.913 7.913 0 0 0-1.248-2.374h1.465a6.764 6.764 0 0 1 1.313 2.28c.3.86.45 1.782.45 2.764"/>
+    </g>
+</svg>
index 458abd0..64430f9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <circle cx="11.5" cy="8.5" r="2.5"/>
     <path d="M16.3 8.7L17 8l-.8-.8.4-.8-1.1-.5.1-.9-1.2-.2-.1-.9-1.2.2-.4-.8-1.1.5L11 3l-.8.8-.9-.4-.5 1.1-.9-.2-.2 1.2-.9.2.2 1.2-.9.4.5 1.1L6 9l.8.8-.4.8 1.1.5-.1.9 1.2.2.1.9 1.2-.2.4.8 1.1-.5.6.8.8-.8.8.4.5-1.1.9.1.2-1.2.9-.1-.2-1.2.8-.4-.4-1zM11.5 12C9.6 12 8 10.4 8 8.5S9.6 5 11.5 5 15 6.6 15 8.5 13.4 12 11.5 12zm.5 3l-.7-.7-1.1.6-.4-.7-.8.3V23l2.5-3 2.5 3v-8.5l-1-.5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.png
new file mode 100644 (file)
index 0000000..79effe1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-progressive.svg
new file mode 100644 (file)
index 0000000..cf0888d
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <circle cx="11.5" cy="8.5" r="2.5"/>
+    <path d="M16.3 8.7L17 8l-.8-.8.4-.8-1.1-.5.1-.9-1.2-.2-.1-.9-1.2.2-.4-.8-1.1.5L11 3l-.8.8-.9-.4-.5 1.1-.9-.2-.2 1.2-.9.2.2 1.2-.9.4.5 1.1L6 9l.8.8-.4.8 1.1.5-.1.9 1.2.2.1.9 1.2-.2.4.8 1.1-.5.6.8.8-.8.8.4.5-1.1.9.1.2-1.2.9-.1-.2-1.2.8-.4-.4-1zM11.5 12C9.6 12 8 10.4 8 8.5S9.6 5 11.5 5 15 6.6 15 8.5 13.4 12 11.5 12zm.5 3l-.7-.7-1.1.6-.4-.7-.8.3V23l2.5-3 2.5 3v-8.5l-1-.5z"/>
+</svg>
index 316ac6d..5b608cf 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M10.5 4a6.5 6.5 0 1 0 2.844 12.344L16 19c1.4 1.4 2.5 1.5 4 0l-4.438-4.438A6.426 6.426 0 0 0 17 10.5 6.5 6.5 0 0 0 10.5 4zm0 2a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.png
new file mode 100644 (file)
index 0000000..50a7305
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..cb64033
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="search">
+        <path id="magnifying-glass" d="M10.5 4a6.5 6.5 0 1 0 2.844 12.344L16 19c1.4 1.4 2.5 1.5 4 0l-4.438-4.438A6.426 6.426 0 0 0 17 10.5 6.5 6.5 0 0 0 10.5 4zm0 2a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9z"/>
+    </g>
+</svg>
index 1d36daf..9969490 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M13.5 4a6.5 6.5 0 1 1-2.844 12.344L8 19c-1.4 1.4-2.5 1.5-4 0l4.438-4.438A6.426 6.426 0 0 1 7 10.5 6.5 6.5 0 0 1 13.5 4zm0 2a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.png
new file mode 100644 (file)
index 0000000..4f49e18
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..d95aa42
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="search">
+        <path id="magnifying-glass" d="M13.5 4a6.5 6.5 0 1 1-2.844 12.344L8 19c-1.4 1.4-2.5 1.5-4 0l4.438-4.438A6.426 6.426 0 0 1 7 10.5 6.5 6.5 0 0 1 13.5 4zm0 2a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9z"/>
+    </g>
+</svg>
index 488e2e2..3fbdebd 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="secure">
         <path id="lock" d="M8 5h.02v-.997c0-.057.003-1.41-.833-2.255-.434-.438-.998-.66-1.68-.66s-1.244.222-1.677.66c-.837.846-.833 2.198-.832 2.25V5H3a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zM3.998 5V3.993c0-.01.005-1 .543-1.543.49-.485 1.45-.487 1.94-.002.543.546.545 1.536.545 1.55V5H3.998z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.png
new file mode 100644 (file)
index 0000000..f29d856
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-progressive.svg
new file mode 100644 (file)
index 0000000..3b755c1
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="secure">
+        <path id="lock" d="M8 5h.02v-.997c0-.057.003-1.41-.833-2.255-.434-.438-.998-.66-1.68-.66s-1.244.222-1.677.66c-.837.846-.833 2.198-.832 2.25V5H3a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zM3.998 5V3.993c0-.01.005-1 .543-1.543.49-.485 1.45-.487 1.94-.002.543.546.545 1.536.545 1.55V5H3.998z"/>
+    </g>
+</svg>
index 543aded..da266da 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="settings">
         <path id="gear" d="M3 4h3v2H3zm9 0h9v2h-9zM8 3h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm-5 8h9v2H3zm15 0h3v2h-3zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zM3 18h6v2H3zm12 0h6v2h-6zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.png
new file mode 100644 (file)
index 0000000..46011d2
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-progressive.svg
new file mode 100644 (file)
index 0000000..9d28697
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="settings">
+        <path id="gear" d="M3 4h3v2H3zm9 0h9v2h-9zM8 3h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm-5 8h9v2H3zm15 0h3v2h-3zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zM3 18h6v2H3zm12 0h6v2h-6zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/>
+    </g>
+</svg>
index 101e2af..bde5f71 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M0 20h24v1H0v-1zm6-8l-1-1-2 2-2-2-1 1 2 2-2 2 1 1 2-2 2 2 1-1-2-2zm15.6 3.7c-.9-.5-1.9-.5-2.7 0-1.5.9-3.1.4-3.1.4-.4-.2-.8-.4-1.1-.6 2.2-.6 4.4-1.8 6-3.9 1.1-1.2 2.5-3.9.4-6-.7-.7-1.6-1.1-2.7-1-1.4.1-2.8.9-3.9 2.1-.9 1.1-3.1 4.5-2.3 7.5 0 .1 0 .2.1.3-2.3.3-4.2.2-4.4.1v1.5c.7.1 2.7.2 5.1-.2.5.7 1.3 1.2 2.3 1.6.1 0 2.4.8 4.5-.6.5-.3.9-.1 1.1 0 .4.2.7.6.7 1H23c0-.8-.6-1.7-1.4-2.2zm-8-1.7c-.5-2.2 1.1-5.1 2-6.2.8-.9 1.8-1.5 2.8-1.6h.1c.6 0 1.1.2 1.5.6 1.6 1.6-.4 3.9-.5 4-1.5 2-3.7 3-5.8 3.5l-.1-.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.png
new file mode 100644 (file)
index 0000000..f7d6da8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d38c7ec
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M0 20h24v1H0v-1zm6-8l-1-1-2 2-2-2-1 1 2 2-2 2 1 1 2-2 2 2 1-1-2-2zm15.6 3.7c-.9-.5-1.9-.5-2.7 0-1.5.9-3.1.4-3.1.4-.4-.2-.8-.4-1.1-.6 2.2-.6 4.4-1.8 6-3.9 1.1-1.2 2.5-3.9.4-6-.7-.7-1.6-1.1-2.7-1-1.4.1-2.8.9-3.9 2.1-.9 1.1-3.1 4.5-2.3 7.5 0 .1 0 .2.1.3-2.3.3-4.2.2-4.4.1v1.5c.7.1 2.7.2 5.1-.2.5.7 1.3 1.2 2.3 1.6.1 0 2.4.8 4.5-.6.5-.3.9-.1 1.1 0 .4.2.7.6.7 1H23c0-.8-.6-1.7-1.4-2.2zm-8-1.7c-.5-2.2 1.1-5.1 2-6.2.8-.9 1.8-1.5 2.8-1.6h.1c.6 0 1.1.2 1.5.6 1.6 1.6-.4 3.9-.5 4-1.5 2-3.7 3-5.8 3.5l-.1-.3z"/>
+</svg>
index 67bd738..de006d7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M24 20H0v1h24v-1zm-6-8l1-1 2 2 2-2 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2zM2.4 15.7c.9-.5 1.9-.5 2.7 0 1.5.9 3.1.4 3.1.4.4-.2.8-.4 1.1-.6-2.2-.6-4.4-1.8-6-3.9-1.1-1.2-2.5-3.9-.4-6 .7-.7 1.6-1.1 2.7-1 1.4.1 2.8.9 3.9 2.1.9 1.1 3.1 4.5 2.3 7.5 0 .1 0 .2-.1.3 2.3.3 4.2.2 4.4.1v1.5c-.7.1-2.7.2-5.1-.2-.5.7-1.3 1.2-2.3 1.6-.1 0-2.4.8-4.5-.6-.5-.3-.9-.1-1.1 0-.4.2-.7.6-.7 1H1c0-.8.6-1.7 1.4-2.2zm8-1.7c.5-2.2-1.1-5.1-2-6.2-.8-.9-1.8-1.5-2.8-1.6h-.1c-.6 0-1.1.2-1.5.6-1.6 1.6.4 3.9.5 4 1.5 2 3.7 3 5.8 3.5l.1-.3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.png
new file mode 100644 (file)
index 0000000..b5e08ca
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..caa2839
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M24 20H0v1h24v-1zm-6-8l1-1 2 2 2-2 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2zM2.4 15.7c.9-.5 1.9-.5 2.7 0 1.5.9 3.1.4 3.1.4.4-.2.8-.4 1.1-.6-2.2-.6-4.4-1.8-6-3.9-1.1-1.2-2.5-3.9-.4-6 .7-.7 1.6-1.1 2.7-1 1.4.1 2.8.9 3.9 2.1.9 1.1 3.1 4.5 2.3 7.5 0 .1 0 .2-.1.3 2.3.3 4.2.2 4.4.1v1.5c-.7.1-2.7.2-5.1-.2-.5.7-1.3 1.2-2.3 1.6-.1 0-2.4.8-4.5-.6-.5-.3-.9-.1-1.1 0-.4.2-.7.6-.7 1H1c0-.8.6-1.7 1.4-2.2zm8-1.7c.5-2.2-1.1-5.1-2-6.2-.8-.9-1.8-1.5-2.8-1.6h-.1c-.6 0-1.1.2-1.5.6-1.6 1.6.4 3.9.5 4 1.5 2 3.7 3 5.8 3.5l.1-.3z"/>
+</svg>
index ebbc3c1..0eb2bfa 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
     <g id="down">
         <path id="arrow" d="M22 3l-3.5 6L15 3z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.png
new file mode 100644 (file)
index 0000000..ba4fcef
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b3c6452
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+    <g id="down">
+        <path id="arrow" d="M22 3l-3.5 6L15 3z"/>
+    </g>
+</svg>
index 02a8fe6..a87f7ba 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
     <g id="down">
         <path id="arrow" d="M9 3L5.5 9 2 3z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.png
new file mode 100644 (file)
index 0000000..03a4bcc
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..64d103c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+    <g id="down">
+        <path id="arrow" d="M9 3L5.5 9 2 3z"/>
+    </g>
+</svg>
index 43e2606..6dd9266 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="special-character">
         <path id="omega" d="M12 6.708c-.794 0-1.368.103-1.894.31-.525.207-.944.496-1.255.867-.31.366-.53.808-.66 1.327a7.232 7.232 0 0 0-.19 1.7c0 .512.06 1 .18 1.46.12.46.31.87.567 1.23.63.862 1.156 1.138 2.012 1.362L11 18H6v-3h.604l.53 1.353.395.053.6.044.75.035.455.01H10l-.09-.895c-.63-.094-.812-.268-1.337-.522-.525-.26-.98-.59-1.365-.99a4.428 4.428 0 0 1-.89-1.4 4.78 4.78 0 0 1-.32-1.778c0-.82.13-1.537.394-2.15a3.97 3.97 0 0 1 1.163-1.54c.507-.407 1.133-.71 1.878-.912.745-.206 1.6-.31 2.565-.31.96 0 1.81.103 2.556.31.75.2 1.38.504 1.887.912.51.407.9.92 1.16 1.54.27.614.404 1.33.404 2.15a4.79 4.79 0 0 1-.32 1.78 4.35 4.35 0 0 1-.9 1.397c-.38.4-.83.732-1.355.99-.526.255-.708.43-1.337.523l-.092.894h.66l.448-.01.75-.034.606-.044.4-.053.534-1.354H18v3h-5l.246-3.04c1.066-.11 1.337-.698 2.002-1.365.263-.36.452-.77.568-1.23.122-.46.183-.947.183-1.46 0-.62-.07-1.186-.198-1.7a3.175 3.175 0 0 0-.66-1.326c-.31-.37-.73-.66-1.255-.867-.525-.206-1.1-.31-1.894-.31"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.png
new file mode 100644 (file)
index 0000000..1ce4549
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-progressive.svg
new file mode 100644 (file)
index 0000000..4473704
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="special-character">
+        <path id="omega" d="M12 6.708c-.794 0-1.368.103-1.894.31-.525.207-.944.496-1.255.867-.31.366-.53.808-.66 1.327a7.232 7.232 0 0 0-.19 1.7c0 .512.06 1 .18 1.46.12.46.31.87.567 1.23.63.862 1.156 1.138 2.012 1.362L11 18H6v-3h.604l.53 1.353.395.053.6.044.75.035.455.01H10l-.09-.895c-.63-.094-.812-.268-1.337-.522-.525-.26-.98-.59-1.365-.99a4.428 4.428 0 0 1-.89-1.4 4.78 4.78 0 0 1-.32-1.778c0-.82.13-1.537.394-2.15a3.97 3.97 0 0 1 1.163-1.54c.507-.407 1.133-.71 1.878-.912.745-.206 1.6-.31 2.565-.31.96 0 1.81.103 2.556.31.75.2 1.38.504 1.887.912.51.407.9.92 1.16 1.54.27.614.404 1.33.404 2.15a4.79 4.79 0 0 1-.32 1.78 4.35 4.35 0 0 1-.9 1.397c-.38.4-.83.732-1.355.99-.526.255-.708.43-1.337.523l-.092.894h.66l.448-.01.75-.034.606-.044.4-.053.534-1.354H18v3h-5l.246-3.04c1.066-.11 1.337-.698 2.002-1.365.263-.36.452-.77.568-1.23.122-.46.183-.947.183-1.46 0-.62-.07-1.186-.198-1.7a3.175 3.175 0 0 0-.66-1.326c-.31-.37-.73-.66-1.255-.867-.525-.206-1.1-.31-1.894-.31"/>
+    </g>
+</svg>
index 500bbfb..a5f137d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19 20H2l3-3V6h17v11c0 1.7-1.3 3-3 3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.png
new file mode 100644 (file)
index 0000000..049b6ca
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..3cc8dc6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19 20H2l3-3V6h17v11c0 1.7-1.3 3-3 3z"/>
+</svg>
index 1a9f6c8..03d53d2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M5 20h17l-3-3V6H2v11c0 1.7 1.3 3 3 3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.png
new file mode 100644 (file)
index 0000000..0524649
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..2722814
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M5 20h17l-3-3V6H2v11c0 1.7 1.3 3 3 3z"/>
+</svg>
index 9de1eab..9575bdb 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-invert.png differ
index 701dbd4..e85c9a6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3V6H5zm8 3h1v3h3v1h-3v3h-1v-3h-3v-1h3V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.png
new file mode 100644 (file)
index 0000000..19a88b5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..6e28601
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3V6H5zm8 3h1v3h3v1h-3v3h-1v-3h-3v-1h3V9z"/>
+</svg>
index 8ae203e..810766a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M2 6v11c0 1.7 1.3 3 3 3h17l-3-3V6H2zm8 3h1v3h3v1h-3v3h-1v-3H7v-1h3V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.png
new file mode 100644 (file)
index 0000000..2140a27
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..266efc4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M2 6v11c0 1.7 1.3 3 3 3h17l-3-3V6H2zm8 3h1v3h3v1h-3v3h-1v-3H7v-1h3V9z"/>
+</svg>
index 9254844..feff2ba 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl.png differ
index 090099b..1cf7d78 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 9v9l2 2H8V9h12zM3 4h12v4H7v7H1l2-2V4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.png
new file mode 100644 (file)
index 0000000..8b7029b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..4a065cd
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 9v9l2 2H8V9h12zM3 4h12v4H7v7H1l2-2V4z"/>
+</svg>
index 4119746..4b9ca3a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-invert.png differ
index 1845684..ed874d1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 9v9l-2 2h14V9H3zm17-5H8v4h8v7h6l-2-2V4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.png
new file mode 100644 (file)
index 0000000..856fd7b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..eb262e7
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 9v9l-2 2h14V9H3zm17-5H8v4h8v7h6l-2-2V4z"/>
+</svg>
index 39e7978..1cdcf99 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/star-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/star-constructive.png differ
index 4a6dae9..defc618 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5L12 7.4M12 4L9.2 9.6l-6.2.9 4.5 4.4L6.4 21l5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9L12 4z"/>
 </svg>
index c745706..ba60a4b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5L12 7.4M12 4L9.2 9.6l-6.2.9 4.5 4.4L6.4 21l5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9L12 4z"/>
 </svg>
index 39e7978..1cdcf99 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/star-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/star-progressive.png differ
index 4a6dae9..defc618 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5L12 7.4M12 4L9.2 9.6l-6.2.9 4.5 4.4L6.4 21l5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9L12 4z"/>
 </svg>
index 2d060f3..eed2650 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
        <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 11.1H9v-6h6v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.png
new file mode 100644 (file)
index 0000000..d3c906c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-progressive.svg
new file mode 100644 (file)
index 0000000..1e702f9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+       <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 11.1H9v-6h6v6z"/>
+</svg>
index 6f3ab7c..11949f8 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="strikethrough-a">
         <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
         <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.png
new file mode 100644 (file)
index 0000000..a3189f4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-progressive.svg
new file mode 100644 (file)
index 0000000..f80a245
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="strikethrough-a">
+        <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
+        <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
+    </g>
+</svg>
index b3361b1..c570ba9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="strikethrough-s">
         <path id="strikethrough" d="M6 12h12v1H6v-1z"/>
         <path id="s" d="M12.094 6c-1.133 0-2.076.287-2.75.9-.67.613-1 1.49-1 2.52 0 .89.22 1.602.72 2.13.497.528 1.278.91 2.31 1.14l.813.182v-.03c.656.147 1.128.375 1.375.63.252.256.375.607.375 1.11 0 .573-.172.97-.53 1.26-.36.29-.895.45-1.626.45-.47 0-.962-.074-1.462-.24a7.288 7.288 0 0 1-1.562-.75l-.374-.238v2.158l.156.062c.58.237 1.144.417 1.69.54.548.12 1.07.18 1.56.18 1.287 0 2.298-.293 3-.9.71-.605 1.063-1.486 1.063-2.608 0-.943-.256-1.726-.78-2.312-.522-.592-1.306-1-2.345-1.23l-.812-.18c-.714-.148-1.202-.352-1.404-.54-.206-.202-.313-.484-.313-.934 0-.533.162-.9.5-1.17.342-.27.836-.42 1.53-.42.396 0 .82.052 1.25.18.434.128.91.334 1.407.6l.375.18V6.63s-1.19-.383-1.69-.48c-.5-.097-.983-.15-1.467-.15z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.png
new file mode 100644 (file)
index 0000000..0b215bd
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-progressive.svg
new file mode 100644 (file)
index 0000000..6446e8e
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="strikethrough-s">
+        <path id="strikethrough" d="M6 12h12v1H6v-1z"/>
+        <path id="s" d="M12.094 6c-1.133 0-2.076.287-2.75.9-.67.613-1 1.49-1 2.52 0 .89.22 1.602.72 2.13.497.528 1.278.91 2.31 1.14l.813.182v-.03c.656.147 1.128.375 1.375.63.252.256.375.607.375 1.11 0 .573-.172.97-.53 1.26-.36.29-.895.45-1.626.45-.47 0-.962-.074-1.462-.24a7.288 7.288 0 0 1-1.562-.75l-.374-.238v2.158l.156.062c.58.237 1.144.417 1.69.54.548.12 1.07.18 1.56.18 1.287 0 2.298-.293 3-.9.71-.605 1.063-1.486 1.063-2.608 0-.943-.256-1.726-.78-2.312-.522-.592-1.306-1-2.345-1.23l-.812-.18c-.714-.148-1.202-.352-1.404-.54-.206-.202-.313-.484-.313-.934 0-.533.162-.9.5-1.17.342-.27.836-.42 1.53-.42.396 0 .82.052 1.25.18.434.128.91.334 1.407.6l.375.18V6.63s-1.19-.383-1.69-.48c-.5-.097-.983-.15-1.467-.15z"/>
+    </g>
+</svg>
index bdb6528..bd80846 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="strikethrough-y">
         <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
         <path id="a" d="M7 6h1.724l3.288 4.935L15.276 6H17l-4.194 6.285V18h-1.612v-5.715L7 6"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.png
new file mode 100644 (file)
index 0000000..a96d906
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-progressive.svg
new file mode 100644 (file)
index 0000000..95d7218
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="strikethrough-y">
+        <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
+        <path id="a" d="M7 6h1.724l3.288 4.935L15.276 6H17l-4.194 6.285V18h-1.612v-5.715L7 6"/>
+    </g>
+</svg>
index 966d7d9..41fabd9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-invert.png differ
index 7eaeea5..b774dce 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 9h12v2H4V9zm0 3h8v2H4v-2zm0-7h16v3H4V5zm16 14H4v-3h16v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7f4954c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..c6c541c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 9h12v2H4V9zm0 3h8v2H4v-2zm0-7h16v3H4V5zm16 14H4v-3h16v3z"/>
+</svg>
index a89c992..fc723a6 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-invert.png differ
index f23d8ab..eb52a4d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 9H8v2h12V9zm0 3h-8v2h8v-2zm0-7H4v3h16V5zM4 19h16v-3H4v3z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.png
new file mode 100644 (file)
index 0000000..5e82b68
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..a59254c
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 9H8v2h12V9zm0 3h-8v2h8v-2zm0-7H4v3h16V5zM4 19h16v-3H4v3z"/>
+</svg>
index 1969195..4dd5fd8 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-invert.png differ
index 5600c60..7d8ccdc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 19H4v-2h16v2zM20 15H4v-2h16v2zM20 11H4V9h16v2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.png
new file mode 100644 (file)
index 0000000..6d0aa12
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-progressive.svg
new file mode 100644 (file)
index 0000000..862a2f7
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 19H4v-2h16v2zM20 15H4v-2h16v2zM20 11H4V9h16v2z"/>
+</svg>
index 93c22fb..e031527 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu.png differ
index fdbdabe..453cb73 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-invert.png differ
index 8f263c0..893d559 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20 11H4V9h16v2zM4 12h8v2H4v-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.png
new file mode 100644 (file)
index 0000000..cc2cd8c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..503dc27
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20 11H4V9h16v2zM4 12h8v2H4v-2z"/>
+</svg>
index 21af785..ce15bd8 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr.png differ
index 9679530..458015c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-invert.png differ
index f543b9d..a5f1417 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 11h16V9H4v2zm16 1h-8v2h8v-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.png
new file mode 100644 (file)
index 0000000..9180978
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..75b3cb6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 11h16V9H4v2zm16 1h-8v2h8v-2z"/>
+</svg>
index 52487c4..7424959 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17 13H4v-3h13v3zm-5 6H4v-3h8v3zM4 7V4h16v3H4z"/>
 </svg>
index 126e8bd..cee45e8 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-progressive.png differ
index 7c36776..1a59715 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M17 13H4v-3h13v3zm-5 6H4v-3h8v3zM4 7V4h16v3H4z"/>
 </svg>
index f656017..e1802d1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 13h13v-3H7v3zm5 6h8v-3h-8v3zm8-12V4H4v3h16z"/>
 </svg>
index 03b6555..424aa99 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-progressive.png differ
index 26a9fc5..15aa549 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M7 13h13v-3H7v3zm5 6h8v-3h-8v3zm8-12V4H4v3h16z"/>
 </svg>
index 4638e31..d168e2f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
     <path d="M18 13l-1 1v3l1 1h-1l-.527-.46L16 18h-1l1-1v-3l-1-1h1l.485.497L17 13z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.png
new file mode 100644 (file)
index 0000000..55df817
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..da428f7
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+    <path d="M18 13l-1 1v3l1 1h-1l-.527-.46L16 18h-1l1-1v-3l-1-1h1l.485.497L17 13z"/>
+</svg>
index 76a8659..25fcb14 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
     <path d="M8 13l1 1v3l-1 1h1l.527-.46L10 18h1l-1-1v-3l1-1h-1l-.485.497L9 13z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.png
new file mode 100644 (file)
index 0000000..bf343f1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e65abbb
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+    <path d="M8 13l1 1v3l-1 1h1l.527-.46L10 18h1l-1-1v-3l1-1h-1l-.485.497L9 13z"/>
+</svg>
index 76601ef..6abcb57 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M18.1 5.1c0 .3-.1.6-.3.9l-1.4 1.4-.9-.8 2.2-2.2c.3.1.4.4.4.7zm-.5 5.3h3.2c0 .3-.1.6-.4.9s-.5.4-.8.4h-2v-1.3zm-6.2-5V2.2c.3 0 .6.1.9.4s.4.5.4.8v2h-1.3zm6.4 11.7c-.3 0-.6-.1-.8-.3l-1.4-1.4.8-.8 2.2 2.2c-.2.2-.5.3-.8.3zM6.2 4.9c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2H3.2c0-.3.1-.6.4-.9s.5-.3.8-.3zM6.2 16l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8s.1-.6.3-.8z"/>
     <circle cx="12" cy="11" r="4"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.png
new file mode 100644 (file)
index 0000000..dbe14b5
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..29f965b
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M18.1 5.1c0 .3-.1.6-.3.9l-1.4 1.4-.9-.8 2.2-2.2c.3.1.4.4.4.7zm-.5 5.3h3.2c0 .3-.1.6-.4.9s-.5.4-.8.4h-2v-1.3zm-6.2-5V2.2c.3 0 .6.1.9.4s.4.5.4.8v2h-1.3zm6.4 11.7c-.3 0-.6-.1-.8-.3l-1.4-1.4.8-.8 2.2 2.2c-.2.2-.5.3-.8.3zM6.2 4.9c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2H3.2c0-.3.1-.6.4-.9s.5-.3.8-.3zM6.2 16l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8s.1-.6.3-.8z"/>
+    <circle cx="12" cy="11" r="4"/>
+</svg>
index 99bed35..92a045d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M5.9 5.1c0 .3.1.6.3.9l1.4 1.4.9-.8-2.2-2.2c-.3.1-.4.4-.4.7zm.5 5.3H3.2c0 .3.1.6.4.9.3.3.5.4.8.4h2v-1.3zm6.2-5V2.2c-.3 0-.6.1-.9.4-.3.3-.4.5-.4.8v2h1.3zM6.2 17.1c.3 0 .6-.1.8-.3l1.4-1.4-.8-.8-2.2 2.2c.2.2.5.3.8.3zM17.8 4.9c-.3 0-.6.1-.8.3l-1.4 1.4.8.9 2.2-2.3c-.2-.2-.5-.3-.8-.3zm-5.2 11.7h-1.2v3.2c.3 0 .6-.1.9-.4.3-.3.4-.5.4-.8l-.1-2zm7-6.2h-2v1.2h3.2c0-.3-.1-.6-.4-.9-.3-.3-.5-.3-.8-.3zM17.8 16l-1.4-1.4-.8.8 2.2 2.2c.2-.2.3-.5.3-.8 0-.3-.1-.6-.3-.8z"/>
     <circle cx="12" cy="11" r="4" transform="matrix(-1 0 0 1 24 0)"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.png
new file mode 100644 (file)
index 0000000..5ee10ee
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..1f78472
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M5.9 5.1c0 .3.1.6.3.9l1.4 1.4.9-.8-2.2-2.2c-.3.1-.4.4-.4.7zm.5 5.3H3.2c0 .3.1.6.4.9.3.3.5.4.8.4h2v-1.3zm6.2-5V2.2c-.3 0-.6.1-.9.4-.3.3-.4.5-.4.8v2h1.3zM6.2 17.1c.3 0 .6-.1.8-.3l1.4-1.4-.8-.8-2.2 2.2c.2.2.5.3.8.3zM17.8 4.9c-.3 0-.6.1-.8.3l-1.4 1.4.8.9 2.2-2.3c-.2-.2-.5-.3-.8-.3zm-5.2 11.7h-1.2v3.2c.3 0 .6-.1.9-.4.3-.3.4-.5.4-.8l-.1-2zm7-6.2h-2v1.2h3.2c0-.3-.1-.6-.4-.9-.3-.3-.5-.3-.8-.3zM17.8 16l-1.4-1.4-.8.8 2.2 2.2c.2-.2.3-.5.3-.8 0-.3-.1-.6-.3-.8z"/>
+    <circle cx="12" cy="11" r="4" transform="matrix(-1 0 0 1 24 0)"/>
+</svg>
index bd2e11d..03636db 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
     <path d="M18 7l-1 1v3l1 1h-1l-.527-.46L16 12h-1l1-1V8l-1-1h1l.485.497L17 7z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.png
new file mode 100644 (file)
index 0000000..37c45c0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..d926f44
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
+    <path d="M18 7l-1 1v3l1 1h-1l-.527-.46L16 12h-1l1-1V8l-1-1h1l.485.497L17 7z"/>
+</svg>
index 7e0f907..59418fe 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
     <path d="M8 7l1 1v3l-1 1h1l.527-.46L10 12h1l-1-1V8l1-1h-1l-.485.497L9 7z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.png
new file mode 100644 (file)
index 0000000..2c63a82
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..20db11c
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
+    <path d="M8 7l1 1v3l-1 1h1l.527-.46L10 12h1l-1-1V8l1-1h-1l-.485.497L9 7z"/>
+</svg>
index 72af30c..f62c61c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-caption">
         <path id="caption" d="M6 6h12v3H6z"/>
         <path id="table" d="M4 10v7h16v-7H4zm1 1h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2zM5 14h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.png
new file mode 100644 (file)
index 0000000..ef657ee
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-progressive.svg
new file mode 100644 (file)
index 0000000..9270ddf
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-caption">
+        <path id="caption" d="M6 6h12v3H6z"/>
+        <path id="table" d="M4 10v7h16v-7H4zm1 1h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2zM5 14h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2z"/>
+    </g>
+</svg>
index 5884af3..b97ac0a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-invert.png differ
index 37449b3..2944ec1 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-insert-column-ltr">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 5h2v14H5z" id="column"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.png
new file mode 100644 (file)
index 0000000..5565c15
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..9c5550b
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-insert-column-ltr">
+        <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
+        <path d="M5 5h2v14H5z" id="column"/>
+    </g>
+</svg>
index 64c2148..fe3eb18 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr.png differ
index b379072..c10abc5 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-invert.png differ
index 7dbd4f6..2f4627b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-insert-column-rtl">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M17 5h2v14h-2z" id="column"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.png
new file mode 100644 (file)
index 0000000..d974a07
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..224cd80
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-insert-column-rtl">
+        <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
+        <path d="M17 5h2v14h-2z" id="column"/>
+    </g>
+</svg>
index 650be0c..ddaafe9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl.png differ
index e073790..ea1a5d8 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-invert.png differ
index fdb668f..8ce9614 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-insert-row-after">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 17h14v2H5z" id="row"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.png
new file mode 100644 (file)
index 0000000..ba1e54c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-progressive.svg
new file mode 100644 (file)
index 0000000..6085525
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-insert-row-after">
+        <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
+        <path d="M5 17h14v2H5z" id="row"/>
+    </g>
+</svg>
index 67d488d..31a2306 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after.png differ
index 6a5f1a0..a54f05c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-invert.png differ
index 38998d4..98a8732 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-insert-row-before">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 5h14v2H5z" id="row"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.png
new file mode 100644 (file)
index 0000000..0a54444
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-progressive.svg
new file mode 100644 (file)
index 0000000..b23e4a0
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-insert-row-before">
+        <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
+        <path d="M5 5h14v2H5z" id="row"/>
+    </g>
+</svg>
index 3866463..171473d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-insert">
         <path id="table" d="M4 6v11h15V6zm1 3h6v3H5zm7 0h6v3h-6zm-7 4h6v3H5zm7 0h6v3h-6z"/>
     </g>
index bd571af..bc8f537 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="table-merge-cells">
         <g id="merge-cell-left">
             <path id="cell-border" d="M4 7v9h7v-3l-1 .834V15H5V8h5v1.167L11 10V7z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.png
new file mode 100644 (file)
index 0000000..a75b2a0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-progressive.svg
new file mode 100644 (file)
index 0000000..cb90635
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-merge-cells">
+        <g id="merge-cell-left">
+            <path id="cell-border" d="M4 7v9h7v-3l-1 .834V15H5V8h5v1.167L11 10V7z"/>
+            <path id="arrow" d="M8 9v2H6v1h2v2l3-2.5z"/>
+        </g>
+        <use id="merge-cell-right" xlink:href="#merge-cell-left" transform="matrix(-1 0 0 1 24 0)"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.png
new file mode 100644 (file)
index 0000000..b364a8f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/table-progressive.svg
new file mode 100644 (file)
index 0000000..5aa79ee
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="table-insert">
+        <path id="table" d="M4 6v11h15V6zm1 3h6v3H5zm7 0h6v3h-6zm-7 4h6v3H5zm7 0h6v3h-6z"/>
+    </g>
+</svg>
index 41e2735..6372f39 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.png differ
index 1526306..e55880e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 45d2767..55ab6c4 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.png differ
index 1058e83..7048a40 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 066801a..e90541b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 41e2735..6372f39 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.png differ
index 1526306..e55880e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 880616f..24dff07 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-warning.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-warning.png differ
index 2f1a02a..c427bba 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="template-add">
         <path id="add" d="M23 7h-4V3h-2v4h-4v2h4v4h2V9h4z"/>
         <path id="template" d="M18 14v4H6c-1.1 0-2-.9-2-2V8h8V7H3v9c0 1.7 1.3 3 3 3h13v-5z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7034110
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..1800115
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="template-add">
+        <path id="add" d="M23 7h-4V3h-2v4h-4v2h4v4h2V9h4z"/>
+        <path id="template" d="M18 14v4H6c-1.1 0-2-.9-2-2V8h8V7H3v9c0 1.7 1.3 3 3 3h13v-5z"/>
+    </g>
+</svg>
index 70ce39f..0142745 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="template-add">
         <path id="add" d="M1 7h4V3h2v4h4v2H7v4H5V9H1z"/>
         <path id="template" d="M6 14v4h12c1.1 0 2-.9 2-2V8h-8V7h9v9c0 1.7-1.3 3-3 3H5v-5z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.png
new file mode 100644 (file)
index 0000000..7514278
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..134d304
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="template-add">
+        <path id="add" d="M1 7h4V3h2v4h4v2H7v4H5V9H1z"/>
+        <path id="template" d="M6 14v4h12c1.1 0 2-.9 2-2V8h-8V7h9v9c0 1.7-1.3 3-3 3H5v-5z"/>
+    </g>
+</svg>
index fb1a466..00b1928 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 7H5V6h2l.47.5L8 6h2v1H8v10h2v1H8l-.5-.53L7 18H5v-1h2zm6.976 9v-2H11v-4h2.976V8.044L20 12.022z" id="text-dir-ltr"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.png
new file mode 100644 (file)
index 0000000..4e9b0fc
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-progressive.svg
new file mode 100644 (file)
index 0000000..f67802a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 7H5V6h2l.47.5L8 6h2v1H8v10h2v1H8l-.5-.53L7 18H5v-1h2zm6.976 9v-2H11v-4h2.976V8.044L20 12.022z" id="text-dir-ltr"/>
+</svg>
index 867e464..c1dc96e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M17 17h2v1h-2l-.47-.5-.53.5h-2v-1h2V7h-2V6h2l.5.53L17 6h2v1h-2zm-6.976-9v2H13v4h-2.976v1.956L4 11.978z" id="text-dir-rtl"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.png
new file mode 100644 (file)
index 0000000..955a65f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-progressive.svg
new file mode 100644 (file)
index 0000000..52fa585
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M17 17h2v1h-2l-.47-.5-.53.5h-2v-1h2V7h-2V6h2l.5.53L17 6h2v1h-2zm-6.976-9v2H13v4h-2.976v1.956L4 11.978z" id="text-dir-rtl"/>
+</svg>
index 9a713d9..d26f68f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="text-style">
         <path id="a" d="M15.296 18h2.79l-1.14-12h-2.79L6 18h2.79l2.038-3h4.183l.29 3zm-3.11-5L14.5 9.6l.323 3.4H12.19z"/>
         <path id="underline" d="M6 19h12v1H6v-1z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.png
new file mode 100644 (file)
index 0000000..5cbdfa4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-progressive.svg
new file mode 100644 (file)
index 0000000..fc53802
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="text-style">
+        <path id="a" d="M15.296 18h2.79l-1.14-12h-2.79L6 18h2.79l2.038-3h4.183l.29 3zm-3.11-5L14.5 9.6l.323 3.4H12.19z"/>
+        <path id="underline" d="M6 19h12v1H6v-1z"/>
+    </g>
+</svg>
index 722eebe..c1d2a66 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-destructive.png differ
index 7489b7a..3ebc63b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2H6zm1 1h10l-1 11H8z"/>
 </svg>
index 90c82f2..92c7966 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2H6zm1 1h10l-1 11H8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.png
new file mode 100644 (file)
index 0000000..478c702
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-progressive.svg
new file mode 100644 (file)
index 0000000..6a328db
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2H6zm1 1h10l-1 11H8z"/>
+</svg>
index 70b6f83..0763ff8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M20.5 20.5L5 5 4 6l3 3 1 11h8l.2-1.8 3.3 3.3zM17 9h-6l5.5 5.5zm1-1c0-1.1-.9-2-2-2h-2l-1-1h-2l-1 1H8l2 2h8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.png
new file mode 100644 (file)
index 0000000..2f09eb6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..6d1fdd9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M20.5 20.5L5 5 4 6l3 3 1 11h8l.2-1.8 3.3 3.3zM17 9h-6l5.5 5.5zm1-1c0-1.1-.9-2-2-2h-2l-1-1h-2l-1 1H8l2 2h8z"/>
+</svg>
index d5edf14..12e57b2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M4 20.5L19.5 5l1 1-3 3-1 11h-8l-.2-1.8L5 21.5zM7.5 9h6L8 14.5zm-1-1c0-1.1.9-2 2-2h2l1-1h2l1 1h2l-2 2h-8z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.png
new file mode 100644 (file)
index 0000000..e828b36
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..6395968
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M4 20.5L19.5 5l1 1-3 3-1 11h-8l-.2-1.8L5 21.5zM7.5 9h6L8 14.5zm-1-1c0-1.1.9-2 2-2h2l1-1h2l1 1h2l-2 2h-8z"/>
+</svg>
index 9cd3854..fe8600a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.png
new file mode 100644 (file)
index 0000000..f655449
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-progressive.svg
new file mode 100644 (file)
index 0000000..5544fd3
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
+</svg>
index 3b9a736..8fb039c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-destructive.png differ
index 385686c..7ee7522 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M12 9V7s0-5-4.5-5S3 7 3 7h2s0-3 2.5-3S10 7 10 7v2H7v7c0 1.7 1.3 3 3 3h10V9z"/>
 </svg>
index 3ac59e0..bfc7a3f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 9V7s0-5-4.5-5S3 7 3 7h2s0-3 2.5-3S10 7 10 7v2H7v7c0 1.7 1.3 3 3 3h10V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.png
new file mode 100644 (file)
index 0000000..6f6a5d4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..86a7bb9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 9V7s0-5-4.5-5S3 7 3 7h2s0-3 2.5-3S10 7 10 7v2H7v7c0 1.7 1.3 3 3 3h10V9z"/>
+</svg>
index 7db9fea..7c2786d 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-destructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-destructive.png differ
index 0f79916..a5f2721 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #c33 }</style>
     <path d="M11 9V7s0-5 4.5-5S20 7 20 7h-2s0-3-2.5-3S13 7 13 7v2h3v7c0 1.7-1.3 3-3 3H3V9z"/>
 </svg>
index d182c6d..78cd92b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M11 9V7s0-5 4.5-5S20 7 20 7h-2s0-3-2.5-3S13 7 13 7v2h3v7c0 1.7-1.3 3-3 3H3V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.png
new file mode 100644 (file)
index 0000000..a7e7d32
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..272c543
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M11 9V7s0-5 4.5-5S20 7 20 7h-2s0-3-2.5-3S13 7 13 7v2h3v7c0 1.7-1.3 3-3 3H3V9z"/>
+</svg>
index 7601951..580d694 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-constructive.png differ
index 131743d..f9c609a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
 </svg>
index 73d8054..8105090 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
 </svg>
index 7601951..580d694 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-progressive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-progressive.png differ
index 131743d..f9c609a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
     <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
 </svg>
index 79fcbf3..1fb4ca9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="underline-a">
         <path id="a" d="M14.424 16H16.5L13.037 6H10.96L7.5 16h2.077l.627-2h3.604l.616 2zm-3.92-3.623L12 7.997l1.51 4.38h-3z"/>
         <path id="underline" d="M7 17h10v1H7v-1z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.png
new file mode 100644 (file)
index 0000000..f84ad6c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-progressive.svg
new file mode 100644 (file)
index 0000000..ec858c9
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="underline-a">
+        <path id="a" d="M14.424 16H16.5L13.037 6H10.96L7.5 16h2.077l.627-2h3.604l.616 2zm-3.92-3.623L12 7.997l1.51 4.38h-3z"/>
+        <path id="underline" d="M7 17h10v1H7v-1z"/>
+    </g>
+</svg>
index 9e7353e..0c73875 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="underline-u">
         <path id="u" d="M8 6h2v5.96c-.104 1.706.695 2 2 2.04 1.777.062 2.002-.88 2-2.04V6h2v6.123c0 1.28-.338 2.245-1.016 2.898-.672.658-1.666.98-2.98.98-1.32 0-2.32-.32-2.996-.98C8.336 14.37 8 13.41 8 12.13V6"/>
         <path id="underline" d="M7 17h10v1H7v-1z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.png
new file mode 100644 (file)
index 0000000..a8bcf2d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-progressive.svg
new file mode 100644 (file)
index 0000000..691846b
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="underline-u">
+        <path id="u" d="M8 6h2v5.96c-.104 1.706.695 2 2 2.04 1.777.062 2.002-.88 2-2.04V6h2v6.123c0 1.28-.338 2.245-1.016 2.898-.672.658-1.666.98-2.98.98-1.32 0-2.32-.32-2.996-.98C8.336 14.37 8 13.41 8 12.13V6"/>
+        <path id="underline" d="M7 17h10v1H7v-1z"/>
+    </g>
+</svg>
index 52223ab..7d91326 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M12 8l8 10H4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.png
new file mode 100644 (file)
index 0000000..5cc7e79
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-progressive.svg
new file mode 100644 (file)
index 0000000..1757188
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M12 8l8 10H4z"/>
+</svg>
index f29501f..818178e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M10 13c0 1.7 1.3 3 3 3V9h3l-4.5-5L7 9h3v4zm7 0v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7ec3b64
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..edeb448
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M10 13c0 1.7 1.3 3 3 3V9h3l-4.5-5L7 9h3v4zm7 0v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
+</svg>
index 68b8b1f..9abf396 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M13 13c0 1.7-1.3 3-3 3V9H7l4.5-5L16 9h-3v4zm-7 0v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.png
new file mode 100644 (file)
index 0000000..42fb647
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..29e8f16
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M13 13c0 1.7-1.3 3-3 3V9H7l4.5-5L16 9h-3v4zm-7 0v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
+</svg>
index af68b98..93b7c5e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 5H4v12c0 1.7 1.3 3 3 3h12V8c0-1.7-1.3-3-3-3zm-2 4c.7 0 1.2.6 1.2 1.2s-.6 1.2-1.2 1.2-1.2-.6-1.2-1.2S13.3 9 14 9zM9 9c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2S8.3 9 9 9zm7 5.4c0 .2-.1.3-.3.5-.7.6-1.6 1-2.6 1.3s-2.1.2-3.1 0-2-.9-2.7-1.5c-.1-.1-.2-.3-.2-.4s.1-.3.2-.4c.1-.1.3-.2.4-.2.2 0 .3.1.4.2.5.5 1.2.9 2.1 1.1s1.7.2 2.6 0 1.6-.5 2.1-1c.1-.1.3-.2.4-.2s.3.1.5.2.2.2.2.4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.png
new file mode 100644 (file)
index 0000000..decc2ca
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b1147fc
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 5H4v12c0 1.7 1.3 3 3 3h12V8c0-1.7-1.3-3-3-3zm-2 4c.7 0 1.2.6 1.2 1.2s-.6 1.2-1.2 1.2-1.2-.6-1.2-1.2S13.3 9 14 9zM9 9c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2S8.3 9 9 9zm7 5.4c0 .2-.1.3-.3.5-.7.6-1.6 1-2.6 1.3s-2.1.2-3.1 0-2-.9-2.7-1.5c-.1-.1-.2-.3-.2-.4s.1-.3.2-.4c.1-.1.3-.2.4-.2.2 0 .3.1.4.2.5.5 1.2.9 2.1 1.1s1.7.2 2.6 0 1.6-.5 2.1-1c.1-.1.3-.2.4-.2s.3.1.5.2.2.2.2.4z"/>
+</svg>
index 80c3aa8..ad40b9e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 5h12v12c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3zm2 4c-.7 0-1.2.6-1.2 1.2s.6 1.2 1.2 1.2 1.2-.6 1.2-1.2S9.7 9 9 9zm5 0c-.7 0-1.2.6-1.2 1.2s.5 1.3 1.2 1.3 1.2-.6 1.2-1.2S14.7 9 14 9zm-7 5.4c0 .2.1.3.3.5.7.6 1.6 1 2.6 1.3 1 .3 2.1.2 3.1 0s2-.9 2.7-1.5c.1-.1.2-.3.2-.4 0-.1-.1-.3-.2-.4-.1-.1-.3-.2-.4-.2-.2 0-.3.1-.4.2-.5.5-1.2.9-2.1 1.1-.9.2-1.7.2-2.6 0-.9-.2-1.6-.5-2.1-1-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.5.2s-.2.2-.2.4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.png
new file mode 100644 (file)
index 0000000..fde69b8
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userActive-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..958e480
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 5h12v12c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3zm2 4c-.7 0-1.2.6-1.2 1.2s.6 1.2 1.2 1.2 1.2-.6 1.2-1.2S9.7 9 9 9zm5 0c-.7 0-1.2.6-1.2 1.2s.5 1.3 1.2 1.3 1.2-.6 1.2-1.2S14.7 9 14 9zm-7 5.4c0 .2.1.3.3.5.7.6 1.6 1 2.6 1.3 1 .3 2.1.2 3.1 0s2-.9 2.7-1.5c.1-.1.2-.3.2-.4 0-.1-.1-.3-.2-.4-.1-.1-.3-.2-.4-.2-.2 0-.3.1-.4.2-.5.5-1.2.9-2.1 1.1-.9.2-1.7.2-2.6 0-.9-.2-1.6-.5-2.1-1-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.5.2s-.2.2-.2.4z"/>
+</svg>
index 60a35c7..a2e039b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M18.75 17.4c-1.08-.36-3.6-1.35-3.6-1.35-.81-.27-.81-.99-.9-1.8v-.09c1.26-1.08 2.25-2.88 2.25-4.86 0-4.23-1.8-5.85-4.5-5.85-1.89 0-4.5 1.08-4.5 5.85 0 1.89.99 3.69 2.25 4.86v.09c0 .81-.09 1.53-.9 1.8 0 0-2.61.99-3.6 1.35-1.17.36-2.25.9-2.25 2.25v.9h18v-.9c0-1.08-.72-1.8-2.25-2.25z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.png
new file mode 100644 (file)
index 0000000..74b699b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userAvatar-progressive.svg
new file mode 100644 (file)
index 0000000..257f593
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M18.75 17.4c-1.08-.36-3.6-1.35-3.6-1.35-.81-.27-.81-.99-.9-1.8v-.09c1.26-1.08 2.25-2.88 2.25-4.86 0-4.23-1.8-5.85-4.5-5.85-1.89 0-4.5 1.08-4.5 5.85 0 1.89.99 3.69 2.25 4.86v.09c0 .81-.09 1.53-.9 1.8 0 0-2.61.99-3.6 1.35-1.17.36-2.25.9-2.25 2.25v.9h18v-.9c0-1.08-.72-1.8-2.25-2.25z"/>
+</svg>
index 9bfdcd9..c4049a5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M16 5H4v12c0 1.7 1.3 3 3 3h12V8c0-1.7-1.3-3-3-3zm-9.3 5.4C6.2 10 6 9.6 6 9c.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6zm8.4 4.3c0 .2-.1.3-.3.4-1 .6-2.2.9-3.5.9-1.2 0-2.3-.3-3.3-1-.2-.1-.2-.2-.3-.4s0-.3.1-.5.2-.2.4-.3.3 0 .5.1c.8.5 1.7.8 2.8.8s2-.2 2.8-.7c.1-.1.3-.1.5-.1s.3.1.4.3l-.1.5zm1.2-4.3c-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6S12 9.6 12 9c.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.png
new file mode 100644 (file)
index 0000000..4d7f0eb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..60165e7
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M16 5H4v12c0 1.7 1.3 3 3 3h12V8c0-1.7-1.3-3-3-3zm-9.3 5.4C6.2 10 6 9.6 6 9c.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6zm8.4 4.3c0 .2-.1.3-.3.4-1 .6-2.2.9-3.5.9-1.2 0-2.3-.3-3.3-1-.2-.1-.2-.2-.3-.4s0-.3.1-.5.2-.2.4-.3.3 0 .5.1c.8.5 1.7.8 2.8.8s2-.2 2.8-.7c.1-.1.3-.1.5-.1s.3.1.4.3l-.1.5zm1.2-4.3c-.5.4-1.1.6-1.8.6s-1.3-.2-1.8-.6S12 9.6 12 9c.6.6 1.5.9 2.5.9s1.9-.3 2.5-.9c0 .6-.2 1-.7 1.4z"/>
+</svg>
index 309c972..2759926 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M7 5h12v12c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3zm9.3 5.4c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9S12.6 9.6 12 9c0 .6.2 1 .7 1.4.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6zm-8.4 4.3c0 .2.1.3.3.4 1 .6 2.2.9 3.5.9 1.2 0 2.3-.3 3.3-1 .2-.1.2-.2.3-.4.1-.2 0-.3-.1-.5s-.2-.2-.4-.3c-.2-.1-.3 0-.5.1-.8.5-1.7.8-2.8.8-1.1 0-2-.2-2.8-.7-.1-.1-.3-.1-.5-.1s-.3.1-.4.3l.1.5zm-1.2-4.3c.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9S6.6 9.6 6 9c0 .6.2 1 .7 1.4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.png
new file mode 100644 (file)
index 0000000..10ebac4
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userInactive-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..9ec8f4f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M7 5h12v12c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3zm9.3 5.4c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9S12.6 9.6 12 9c0 .6.2 1 .7 1.4.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6zm-8.4 4.3c0 .2.1.3.3.4 1 .6 2.2.9 3.5.9 1.2 0 2.3-.3 3.3-1 .2-.1.2-.2.3-.4.1-.2 0-.3-.1-.5s-.2-.2-.4-.3c-.2-.1-.3 0-.5.1-.8.5-1.7.8-2.8.8-1.1 0-2-.2-2.8-.7-.1-.1-.3-.1-.5-.1s-.3.1-.4.3l.1.5zm-1.2-4.3c.5.4 1.1.6 1.8.6s1.3-.2 1.8-.6c.5-.4.7-.8.7-1.4-.6.6-1.5.9-2.5.9S6.6 9.6 6 9c0 .6.2 1 .7 1.4z"/>
+</svg>
index 9d1146e..c781931 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3V6H5zm11.2 2.5c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2.6-1.3 1.2-1.3zm-5.4 0c.7 0 1.2.6 1.2 1.2s-.6 1.3-1.2 1.3-1.2-.6-1.2-1.2.5-1.3 1.2-1.3zm2.7 8.5c-5.1 0-6-5-6-5s2 1 6 1l6-1s-1 5-6 5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.png
new file mode 100644 (file)
index 0000000..2ef91f0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..539f6f4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3V6H5zm11.2 2.5c.7 0 1.2.6 1.2 1.2s-.5 1.3-1.2 1.3-1.2-.6-1.2-1.2.6-1.3 1.2-1.3zm-5.4 0c.7 0 1.2.6 1.2 1.2s-.6 1.3-1.2 1.3-1.2-.6-1.2-1.2.5-1.3 1.2-1.3zm2.7 8.5c-5.1 0-6-5-6-5s2 1 6 1l6-1s-1 5-6 5z"/>
+</svg>
index 4735526..dd8588a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M19 6v11l3 3H5c-1.7 0-3-1.3-3-3V6h17zM7.8 8.5c-.7 0-1.2.6-1.2 1.2S7.1 11 7.8 11 9 10.4 9 9.8s-.6-1.3-1.2-1.3zm5.4 0c-.7 0-1.2.6-1.2 1.2s.6 1.3 1.2 1.3 1.2-.6 1.2-1.2-.5-1.3-1.2-1.3zM10.5 17c5.1 0 6-5 6-5s-2 1-6 1l-6-1s1 5 6 5z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.png
new file mode 100644 (file)
index 0000000..91c703d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/userTalk-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..32cad91
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M19 6v11l3 3H5c-1.7 0-3-1.3-3-3V6h17zM7.8 8.5c-.7 0-1.2.6-1.2 1.2S7.1 11 7.8 11 9 10.4 9 9.8s-.6-1.3-1.2-1.3zm5.4 0c-.7 0-1.2.6-1.2 1.2s.6 1.3 1.2 1.3 1.2-.6 1.2-1.2-.5-1.3-1.2-1.3zM10.5 17c5.1 0 6-5 6-5s-2 1-6 1l-6-1s1 5 6 5z"/>
+</svg>
index 25923e2..f33f917 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="viewCompact">
         <circle cx="6" cy="6" r="2"/>
         <circle cx="12" cy="6" r="2"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.png
new file mode 100644 (file)
index 0000000..545814b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-progressive.svg
new file mode 100644 (file)
index 0000000..7f66bc7
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="viewCompact">
+        <circle cx="6" cy="6" r="2"/>
+        <circle cx="12" cy="6" r="2"/>
+        <circle cx="18" cy="6" r="2"/>
+        <circle cx="6" cy="12" r="2"/>
+        <circle cx="12" cy="12" r="2"/>
+        <circle cx="18" cy="12" r="2"/>
+        <circle cx="6" cy="18" r="2"/>
+        <circle cx="12" cy="18" r="2"/>
+        <circle cx="18" cy="18" r="2"/>
+    </g>
+</svg>
index 3cfba94..3ea093a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-invert.png differ
index 4471f59..cea83f9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="viewDetails">
         <circle cx="5.5" cy="8.5" r="2.5"/>
         <path d="M10 6h12v1H10zm0 2h9v1h-9zm0 2h4v1h-4z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.png
new file mode 100644 (file)
index 0000000..9cef283
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..1e1d76b
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="viewDetails">
+        <circle cx="5.5" cy="8.5" r="2.5"/>
+        <path d="M10 6h12v1H10zm0 2h9v1h-9zm0 2h4v1h-4z"/>
+        <circle cx="5.5" cy="16.5" r="2.5"/>
+        <path d="M10 14h12v1H10zm0 2h9v1h-9zm0 2h4v1h-4z"/>
+    </g>
+</svg>
index 9bac0da..ea861d8 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-invert.png differ
index dad4b91..34ee977 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="viewDetails">
         <circle cx="18.5" cy="8.5" r="2.5"/>
         <path d="M14 6H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.png
new file mode 100644 (file)
index 0000000..0317820
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..d253a88
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="viewDetails">
+        <circle cx="18.5" cy="8.5" r="2.5"/>
+        <path d="M14 6H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
+        <circle cx="18.5" cy="16.5" r="2.5"/>
+        <path d="M14 14H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
+    </g>
+</svg>
index 2905903..338294c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl.png differ
index d52e65c..5600602 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M13 14h5v1h-5v-1zm0 3h5v-1h-5v1zm0 1h5v1h-5v-1zm-1-5v3l-5 3 1-6-4-3 6-1 2-5s1.9 5 2 5l6 1-4 3h-4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.png
new file mode 100644 (file)
index 0000000..1767614
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..212193f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M13 14h5v1h-5v-1zm0 3h5v-1h-5v1zm0 1h5v1h-5v-1zm-1-5v3l-5 3 1-6-4-3 6-1 2-5s1.9 5 2 5l6 1-4 3h-4z"/>
+</svg>
index f193915..08986e5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M11 14H6v1h5v-1zm0 3H6v-1h5v1zm0 1H6v1h5v-1zm1-5v3l5 3-1-6 4-3-6-1-2-5s-1.9 5-2 5l-6 1 4 3h4z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.png
new file mode 100644 (file)
index 0000000..53c5983
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..e529a67
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M11 14H6v1h5v-1zm0 3H6v-1h5v1zm0 1H6v1h5v-1zm1-5v3l5 3-1-6 4-3-6-1-2-5s-1.9 5-2 5l-6 1 4 3h4z"/>
+</svg>
index bed3a11..30ef53e 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png differ
index 131c83d..12e246c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="wikiText">
         <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
         <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.png
new file mode 100644 (file)
index 0000000..e1f516c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-progressive.svg
new file mode 100644 (file)
index 0000000..4afd5e0
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="wikiText">
+        <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
+        <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="closing-bracket-outer" d="M21 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="opening-bracket-outer" d="M3 19h3v-2H5V7h1V5H3z"/>
+    </g>
+</svg>
index df03c66..d373c0d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M15 9l.7-1.8c.9.4 1.8.7 2.4.9l-.6 1.7v.2L15 9zm-4.3-1.9l.8-1.8c1.2.5 2.6 1.1 3 1.4l-.8 1.8-3-1.4zm-5.9-1c-.8 0-1.4.2-2 .6L1.7 5c.9-.6 1.9-.9 3.1-.9v2zm-4.3.7l1.8.8c-.3.7-.3 1.3-.1 1.8l-1.9.7C0 8.9 0 7.8.5 6.8zm4.2 5.4l-1.3 1.5c-1-1-1.7-1.6-2-2l1.5-1.3c.7.8 1.3 1.4 1.8 1.8zm7.3 4.3c0 1.9-1.6 3.5-3.5 3.5S5 18.4 5 16.5 6.6 13 8.5 13s3.5 1.6 3.5 3.5zM24 8l-1-1-1.5 1.5L20 7l-1 1 1.5 1.5L19 11l1 1 1.5-1.5L23 12l1-1-1.5-1.5z"/>
     <circle cx="8" cy="5" r="2"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.png
new file mode 100644 (file)
index 0000000..2b26e15
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..7a49393
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M15 9l.7-1.8c.9.4 1.8.7 2.4.9l-.6 1.7v.2L15 9zm-4.3-1.9l.8-1.8c1.2.5 2.6 1.1 3 1.4l-.8 1.8-3-1.4zm-5.9-1c-.8 0-1.4.2-2 .6L1.7 5c.9-.6 1.9-.9 3.1-.9v2zm-4.3.7l1.8.8c-.3.7-.3 1.3-.1 1.8l-1.9.7C0 8.9 0 7.8.5 6.8zm4.2 5.4l-1.3 1.5c-1-1-1.7-1.6-2-2l1.5-1.3c.7.8 1.3 1.4 1.8 1.8zm7.3 4.3c0 1.9-1.6 3.5-3.5 3.5S5 18.4 5 16.5 6.6 13 8.5 13s3.5 1.6 3.5 3.5zM24 8l-1-1-1.5 1.5L20 7l-1 1 1.5 1.5L19 11l1 1 1.5-1.5L23 12l1-1-1.5-1.5z"/>
+    <circle cx="8" cy="5" r="2"/>
+</svg>
index 9153a1a..4021b30 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <path d="M9.095 9l-.7-1.8c-.9.4-1.8.7-2.4.9l.6 1.7v.2l2.5-1zm4.3-1.9l-.8-1.8c-1.2.5-2.6 1.1-3 1.4l.8 1.8 3-1.4zm5.9-1c.8 0 1.4.2 2 .6l1.1-1.7c-.9-.6-1.9-.9-3.1-.9v2zm4.3.7l-1.8.8c.3.7.3 1.3.1 1.8l1.9.7c.3-1.2.3-2.3-.2-3.3zm-4.2 5.4l1.3 1.5c1-1 1.7-1.6 2-2l-1.5-1.3c-.7.8-1.3 1.4-1.8 1.8zm-7.3 4.3c0 1.9 1.6 3.5 3.5 3.5s3.5-1.6 3.5-3.5-1.6-3.5-3.5-3.5-3.5 1.6-3.5 3.5zM.095 8l1-1 1.5 1.5 1.5-1.5 1 1-1.5 1.5 1.5 1.5-1 1-1.5-1.5-1.5 1.5-1-1 1.5-1.5z"/>
     <circle cx="8" cy="5" r="2" transform="matrix(-1 0 0 1 24.095 0)"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.png
new file mode 100644 (file)
index 0000000..fc0b2d9
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..a937688
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <path d="M9.095 9l-.7-1.8c-.9.4-1.8.7-2.4.9l.6 1.7v.2l2.5-1zm4.3-1.9l-.8-1.8c-1.2.5-2.6 1.1-3 1.4l.8 1.8 3-1.4zm5.9-1c.8 0 1.4.2 2 .6l1.1-1.7c-.9-.6-1.9-.9-3.1-.9v2zm4.3.7l-1.8.8c.3.7.3 1.3.1 1.8l1.9.7c.3-1.2.3-2.3-.2-3.3zm-4.2 5.4l1.3 1.5c1-1 1.7-1.6 2-2l-1.5-1.3c-.7.8-1.3 1.4-1.8 1.8zm-7.3 4.3c0 1.9 1.6 3.5 3.5 3.5s3.5-1.6 3.5-3.5-1.6-3.5-3.5-3.5-3.5 1.6-3.5 3.5zM.095 8l1-1 1.5 1.5 1.5-1.5 1 1-1.5 1.5 1.5 1.5-1 1-1.5-1.5-1.5 1.5-1-1 1.5-1.5z"/>
+    <circle cx="8" cy="5" r="2" transform="matrix(-1 0 0 1 24.095 0)"/>
+</svg>
index 699d6b9..eaaca54 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #fff }</style>
     <g id="window">
         <path id="title" d="M7 10h10v1H7z"/>
         <path id="frame" d="M16 19H8c-2.206 0-4-1.794-4-4V9c0-2.206 1.794-4 4-4h8c2.206 0 4 1.794 4 4v6c0 2.206-1.794 4-4 4zM8 7c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2h8c1.103 0 2-.897 2-2V9c0-1.103-.897-2-2-2H8z"/>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.png
new file mode 100644 (file)
index 0000000..2a18bea
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/window-progressive.svg
new file mode 100644 (file)
index 0000000..cb2de06
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #36c }</style>
+    <g id="window">
+        <path id="title" d="M7 10h10v1H7z"/>
+        <path id="frame" d="M16 19H8c-2.206 0-4-1.794-4-4V9c0-2.206 1.794-4 4-4h8c2.206 0 4 1.794 4 4v6c0 2.206-1.794 4-4 4zM8 7c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2h8c1.103 0 2-.897 2-2V9c0-1.103-.897-2-2-2H8z"/>
+    </g>
+</svg>
index 0b692e3..bb38ce5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <path d="M6 12A6 6 0 1 1 6 0a6 6 0 0 1 0 12zM5 7h2V2H5zm0 3h2V8H5z" id="alert"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.png
new file mode 100644 (file)
index 0000000..fa85ac3
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-progressive.svg
new file mode 100644 (file)
index 0000000..8bff5c9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <path d="M6 12A6 6 0 1 1 6 0a6 6 0 0 1 0 12zM5 7h2V2H5zm0 3h2V8H5z" id="alert"/>
+</svg>
index 3f7f447..a02090b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="down">
         <path id="arrow" d="M1 4h10L6 9 1 4"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.png
new file mode 100644 (file)
index 0000000..3539c88
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-progressive.svg
new file mode 100644 (file)
index 0000000..422d51a
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="down">
+        <path id="arrow" d="M1 4h10L6 9 1 4"/>
+    </g>
+</svg>
index 01ec154..d0c754b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="ltr">
         <path id="arrow" d="M4 1v10l5-5-5-5"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.png
new file mode 100644 (file)
index 0000000..7d50f19
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..b866ef9
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="ltr">
+        <path id="arrow" d="M4 1v10l5-5-5-5"/>
+    </g>
+</svg>
index a12a0e7..157f000 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="rtl">
         <path id="arrow" d="M8 11V1L3 6l5 5"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.png
new file mode 100644 (file)
index 0000000..27bfa29
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..0623742
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="rtl">
+        <path id="arrow" d="M8 11V1L3 6l5 5"/>
+    </g>
+</svg>
index a561ff7..14bc04c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="up">
         <path id="arrow" d="M1 8h10L6 3 1 8"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.png
new file mode 100644 (file)
index 0000000..521e438
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-progressive.svg
new file mode 100644 (file)
index 0000000..ef83982
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="up">
+        <path id="arrow" d="M1 8h10L6 3 1 8"/>
+    </g>
+</svg>
index 36b5dd7..e3fd49c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="clear">
         <path id="circle-with-cross" d="M6 0C2.7 0 0 2.7 0 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM3.5 2.5L6 5l2.5-2.5 1 1L7 6l2.5 2.5-1 1L6 7 3.5 9.5l-1-1L5 6 2.5 3.5z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.png
new file mode 100644 (file)
index 0000000..325dbeb
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-progressive.svg
new file mode 100644 (file)
index 0000000..ff05cfa
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="clear">
+        <path id="circle-with-cross" d="M6 0C2.7 0 0 2.7 0 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM3.5 2.5L6 5l2.5-2.5 1 1L7 6l2.5 2.5-1 1L6 7 3.5 9.5l-1-1L5 6 2.5 3.5z"/>
+    </g>
+</svg>
index b3a3745..632eb89 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <path d="M5 1h2v10H5zm4.83 1.634l1 1.732-8.66 5-1-1.732zM1.17 4.366l1-1.732 8.66 5-1 1.732z" id="required"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.png
new file mode 100644 (file)
index 0000000..01e84e6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-progressive.svg
new file mode 100644 (file)
index 0000000..26e4913
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <path d="M5 1h2v10H5zm4.83 1.634l1 1.732-8.66 5-1-1.732zM1.17 4.366l1-1.732 8.66 5-1 1.732z" id="required"/>
+</svg>
index b16e101..036697e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M10.37 9.474L7.994 7.1l-.17-.1a3.45 3.45 0 0 0 .644-2.01A3.478 3.478 0 1 0 4.99 8.47c.75 0 1.442-.24 2.01-.648l.098.17 2.375 2.373c.19.188.543.142.79-.105s.293-.6.104-.79zm-5.38-2.27a2.21 2.21 0 1 1 2.21-2.21A2.21 2.21 0 0 1 4.99 7.21z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.png
new file mode 100644 (file)
index 0000000..84c7dbc
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-progressive.svg
new file mode 100644 (file)
index 0000000..51ff91d
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="search">
+        <path id="magnifying-glass" d="M10.37 9.474L7.994 7.1l-.17-.1a3.45 3.45 0 0 0 .644-2.01A3.478 3.478 0 1 0 4.99 8.47c.75 0 1.442-.24 2.01-.648l.098.17 2.375 2.373c.19.188.543.142.79-.105s.293-.6.104-.79zm-5.38-2.27a2.21 2.21 0 1 1 2.21-2.21A2.21 2.21 0 0 1 4.99 7.21z"/>
+    </g>
+</svg>
index ee8ad61..3b2e1b6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #fff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M1.63 9.474L4.006 7.1l.17-.1a3.45 3.45 0 0 1-.644-2.01A3.478 3.478 0 1 1 7.01 8.47 3.43 3.43 0 0 1 5 7.822l-.098.17-2.375 2.373c-.19.188-.543.142-.79-.105s-.293-.6-.104-.79zm5.378-2.27A2.21 2.21 0 1 0 4.8 4.994 2.21 2.21 0 0 0 7.01 7.21z"/>
     </g>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.png
new file mode 100644 (file)
index 0000000..0207a8b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-progressive.svg
new file mode 100644 (file)
index 0000000..2cd760a
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #36c }</style>
+    <g id="search">
+        <path id="magnifying-glass" d="M1.63 9.474L4.006 7.1l.17-.1a3.45 3.45 0 0 1-.644-2.01A3.478 3.478 0 1 1 7.01 8.47 3.43 3.43 0 0 1 5 7.822l-.098.17-2.375 2.373c-.19.188-.543.142-.79-.105s-.293-.6-.104-.79zm5.378-2.27A2.21 2.21 0 1 0 4.8 4.994 2.21 2.21 0 0 0 7.01 7.21z"/>
+    </g>
+</svg>
index 876a055..349227a 100644 (file)
@@ -4,8 +4,21 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#ffffff",
+                       "color": "#fff",
                        "global": true
+               },
+               "progressive": {
+                       "color": "#36c",
+                       "global": true
+               },
+               "constructive": {
+                       "color": "#36c"
+               },
+               "destructive": {
+                       "color": "#c33"
+               },
+               "warning": {
+                       "color": "#ff5d00"
                }
        },
        "images": {
diff --git a/resources/lib/phpjs-sha1/LICENSE.txt b/resources/lib/phpjs-sha1/LICENSE.txt
deleted file mode 100644 (file)
index 04caf53..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io) 
-and Contributors (http://phpjs.org/authors)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.css
deleted file mode 100644 (file)
index 327c9c8..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*!
- * Diff rendering
- */
-table.diff {
-       border: none;
-       border-spacing: 4px;
-       margin: 0;
-       width: 100%;
-       /* Ensure that colums are of equal width */
-       table-layout: fixed;
-}
-
-table.diff td {
-       padding: 0.33em 0.5em;
-}
-
-table.diff td.diff-marker {
-       /* Compensate padding for increased font-size */
-       padding: 0.25em;
-}
-
-table.diff col.diff-marker {
-       width: 2%;
-}
-
-table.diff col.diff-content {
-       width: 48%;
-}
-
-table.diff td div {
-       /* Force-wrap very long lines such as URLs or page-widening char strings */
-       word-wrap: break-word;
-}
-
-td.diff-otitle,
-td.diff-ntitle {
-       text-align: center;
-}
-
-td.diff-lineno {
-       font-weight: bold;
-}
-
-td.diff-marker {
-       text-align: right;
-       font-weight: bold;
-       font-size: 1.25em;
-       line-height: 1.2;
-}
-
-td.diff-addedline,
-td.diff-deletedline,
-td.diff-context {
-       font-size: 88%;
-       line-height: 1.6;
-       vertical-align: top;
-       white-space: -moz-pre-wrap;
-       white-space: pre-wrap;
-       border-style: solid;
-       border-width: 1px 1px 1px 4px;
-       border-radius: 0.33em;
-}
-
-td.diff-addedline {
-       border-color: #a3d3ff;
-}
-
-td.diff-deletedline {
-       border-color: #ffe49c;
-}
-
-td.diff-context {
-       background: #f9f9f9;
-       border-color: #e6e6e6;
-       color: #333;
-}
-
-.diffchange {
-       font-weight: bold;
-       text-decoration: none;
-}
-
-td.diff-addedline .diffchange,
-td.diff-deletedline .diffchange {
-       border-radius: 0.33em;
-       padding: 0.25em 0;
-}
-
-td.diff-addedline .diffchange {
-       background: #d8ecff;
-}
-
-td.diff-deletedline .diffchange {
-       background: #feeec8;
-}
-
-/* Correct user & content directionality when viewing a diff */
-.diff-currentversion-title,
-.diff {
-       direction: ltr;
-       unicode-bidi: embed;
-}
-
-/* @noflip */ .diff-contentalign-right td {
-       direction: rtl;
-       unicode-bidi: embed;
-}
-
-/* @noflip */ .diff-contentalign-left td {
-       direction: ltr;
-       unicode-bidi: embed;
-}
-
-.diff-multi,
-.diff-otitle,
-.diff-ntitle,
-.diff-lineno {
-       direction: ltr !important;
-       unicode-bidi: embed;
-}
diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css
deleted file mode 100644 (file)
index 76b5c9b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*!
- * Diff rendering
- */
-td.diff-context,
-td.diff-addedline .diffchange,
-td.diff-deletedline .diffchange {
-       background-color: transparent;
-}
-
-td.diff-addedline .diffchange {
-       text-decoration: underline;
-}
-
-td.diff-deletedline .diffchange {
-       text-decoration: line-through;
-}
index de442e9..84ab8c4 100644 (file)
@@ -162,6 +162,7 @@ input#wpSummary {
 
 .mw-input-with-label {
        white-space: nowrap;
+       display: inline-block;
 }
 
 /**
index 507109a..b6f6568 100644 (file)
@@ -51,6 +51,7 @@
 @colorButtonTextActive: @colorGray7;
 @colorDisabledText: @colorGray12;
 @colorErrorText: #c00;
+@colorWarningText: #705000;
 
 // UI colors
 @colorFieldBorder: @colorGray12;
index 3d90307..5c3715d 100644 (file)
 
                capsuleWidget: {
                        getApiValue: function () {
-                               return this.getItemsData().join( '|' );
+                               var items = this.getItemsData();
+                               if ( items.join( '' ).indexOf( '|' ) === -1 ) {
+                                       return items.join( '|' );
+                               } else {
+                                       return '\x1f' + items.join( '\x1f' );
+                               }
                        },
                        setApiValue: function ( v ) {
-                               this.setItemsFromData( v === undefined || v === '' ? [] : String( v ).split( '|' ) );
+                               if ( v === undefined || v === '' || v === '\x1f' ) {
+                                       this.setItemsFromData( [] );
+                               } else {
+                                       v = String( v );
+                                       if ( v.indexOf( '\x1f' ) !== 0 ) {
+                                               this.setItemsFromData( v.split( '|' ) );
+                                       } else {
+                                               this.setItemsFromData( v.substr( 1 ).split( '\x1f' ) );
+                                       }
+                               }
                        },
                        apiCheckValid: function () {
                                var ok = this.getApiValue() !== undefined || suppressErrors;
index 9af81b8..bce512c 100644 (file)
@@ -1,11 +1,13 @@
 /*!
  * JavaScript for Special:MovePage
  */
-jQuery( function () {
+jQuery( function ( $ ) {
        // Infuse for pretty dropdown
        OO.ui.infuse( 'wpNewTitle' );
        // Limit to 255 bytes, not characters
        OO.ui.infuse( 'wpReason' ).$input.byteLimit();
        // Infuse for nicer "help" popup
-       OO.ui.infuse( 'wpMovetalk-field' );
+       if ( $( '#wpMovetalk-field' ).length ) {
+               OO.ui.infuse( 'wpMovetalk-field' );
+       }
 } );
index cc96a5c..aedec5b 100644 (file)
        //
        // Styleguide 5.2.
        .error,
+       .warning,
        .errorbox,
        .warningbox,
        .successbox {
                color: @colorErrorText;
                border: 1px solid #fac5c5;
                background-color: #fae3e3;
-               text-shadow: 0 1px #fae3e3;
+       }
+
+       // Colours taken from those for .warningbox in shared.css
+       .warning {
+               color: @colorWarningText;
+               border: 1px solid #fde29b;
+               background-color: #fdf1d1;
        }
 
        // This specifies styling for individual field validation error messages.
index 946823d..4d86cfd 100644 (file)
                        prop: [ 'info' ],
                        titles: titles
                } ).done( function ( response ) {
+                       var
+                               normalized = {},
+                               pages = {};
+                       $.each( response.query.normalized || [], function ( index, data ) {
+                               normalized[ data.fromencoded ? decodeURIComponent( data.from ) : data.from ] = data.to;
+                       } );
                        $.each( response.query.pages, function ( index, page ) {
-                               var title = new ForeignTitle( page.title ).getPrefixedText();
-                               cache.existenceCache[ title ] = !page.missing;
-                               if ( !queue[ title ] ) {
-                                       // Debugging for T139130
-                                       throw new Error( 'No queue for "' + title + '", requested "' + titles.join( '|' ) + '"' );
+                               pages[ page.title ] = !page.missing;
+                       } );
+                       $.each( titles, function ( index, title ) {
+                               var normalizedTitle = title;
+                               while ( normalized[ normalizedTitle ] ) {
+                                       normalizedTitle = normalized[ normalizedTitle ];
                                }
+                               cache.existenceCache[ title ] = pages[ normalizedTitle ];
                                queue[ title ].resolve( cache.existenceCache[ title ] );
                        } );
                } );
index ffb7736..267eebb 100644 (file)
         * Category selector widget. Displays an OO.ui.CapsuleMultiselectWidget
         * and autocompletes with available categories.
         *
-        *     var selector = new mw.widgets.CategorySelector( {
-        *       searchTypes: [
-        *         mw.widgets.CategorySelector.SearchType.OpenSearch,
-        *         mw.widgets.CategorySelector.SearchType.InternalSearch
-        *       ]
-        *     } );
+        *     mw.loader.using( 'mediawiki.widgets.CategorySelector', function () {
+        *       var selector = new mw.widgets.CategorySelector( {
+        *         searchTypes: [
+        *           mw.widgets.CategorySelector.SearchType.OpenSearch,
+        *           mw.widgets.CategorySelector.SearchType.InternalSearch
+        *         ]
+        *       } );
         *
-        *     $( '#content' ).append( selector.$element );
+        *       $( 'body' ).append( selector.$element );
         *
-        *     selector.setSearchTypes( [ mw.widgets.CategorySelector.SearchType.SubCategories ] );
+        *       selector.setSearchTypes( [ mw.widgets.CategorySelector.SearchType.SubCategories ] );
+        *     } );
         *
         * @class mw.widgets.CategorySelector
         * @uses mw.Api
@@ -63,6 +65,7 @@
 
                // Initialize
                this.api = config.api || new mw.Api();
+               this.searchCache = {};
        }
 
        /* Setup */
         * @return {jQuery.Promise} Resolves with an array of categories
         */
        CSP.searchCategories = function ( input, searchType ) {
-               var deferred = $.Deferred();
+               var deferred = $.Deferred(),
+                       cacheKey = input + searchType.toString();
+
+               // Check cache
+               if ( this.searchCache[ cacheKey ] !== undefined ) {
+                       return this.searchCache[ cacheKey ];
+               }
 
                switch ( searchType ) {
                        case CategorySelector.SearchType.OpenSearch:
                                        var categories = [];
 
                                        $.each( res.query.pages, function ( index, page ) {
-                                               if ( !page.missing ) {
-                                                       if ( $.isArray( page.categories ) ) {
-                                                               categories.push.apply( categories, page.categories.map( function ( category ) {
-                                                                       return category.title;
-                                                               } ) );
-                                                       }
+                                               if ( !page.missing && $.isArray( page.categories ) ) {
+                                                       categories.push.apply( categories, page.categories.map( function ( category ) {
+                                                               return category.title;
+                                                       } ) );
                                                }
                                        } );
 
                                throw new Error( 'Unknown searchType' );
                }
 
+               // Cache the result
+               this.searchCache[ cacheKey ] = deferred.promise();
+
                return deferred.promise();
        };
 
index 899daa5..f51403f 100644 (file)
@@ -35,6 +35,9 @@
         * @constructor
         * @param {string|mw.Uri} url URL pointing to another wiki's `api.php` endpoint.
         * @param {Object} [options] See mw.Api.
+        * @param {Object} [options.anonymous=false] Perform all requests anonymously. Use this option if
+        *     the target wiki may otherwise not accept cross-origin requests, or if you don't need to
+        *     perform write actions or read restricted information and want to avoid the overhead.
         *
         * @author Bartosz Dziewoński
         * @author Jon Robson
                }
 
                this.apiUrl = String( url );
+               this.anonymous = options && options.anonymous;
 
                options = $.extend( /*deep=*/ true,
                        {
                                ajax: {
                                        url: this.apiUrl,
                                        xhrFields: {
-                                               withCredentials: true
+                                               withCredentials: this.anonymous ? false : true
                                        }
                                },
                                parameters: {
         * @return {string}
         */
        CoreForeignApi.prototype.getOrigin = function () {
-               var origin = location.protocol + '//' + location.hostname;
+               var origin;
+               if ( this.anonymous ) {
+                       return '*';
+               }
+               origin = location.protocol + '//' + location.hostname;
                if ( location.port ) {
                        origin += ':' + location.port;
                }
index a8ee4c7..b7579ff 100644 (file)
@@ -9,6 +9,9 @@
         *     `options` to mw.Api constructor.
         * @property {Object} defaultOptions.parameters Default query parameters for API requests.
         * @property {Object} defaultOptions.ajax Default options for jQuery#ajax.
+        * @property {boolean} defaultOptions.useUS Whether to use U+001F when joining multi-valued
+        *     parameters (since 1.28). Default is true if ajax.url is not set, false otherwise for
+        *     compatibility.
         * @private
         */
        var defaultOptions = {
@@ -95,6 +98,8 @@
                        options.ajax.url = String( options.ajax.url );
                }
 
+               options = $.extend( { useUS: !options.ajax || !options.ajax.url }, options );
+
                options.parameters = $.extend( {}, defaultOptions.parameters, options.parameters );
                options.ajax = $.extend( {}, defaultOptions.ajax, options.ajax );
 
                 *
                 * @private
                 * @param {Object} parameters (modified in-place)
+                * @param {boolean} useUS Whether to use U+001F when joining multi-valued parameters.
                 */
-               preprocessParameters: function ( parameters ) {
+               preprocessParameters: function ( parameters, useUS ) {
                        var key;
                        // Handle common MediaWiki API idioms for passing parameters
                        for ( key in parameters ) {
                                // Multiple values are pipe-separated
                                if ( $.isArray( parameters[ key ] ) ) {
-                                       parameters[ key ] = parameters[ key ].join( '|' );
+                                       if ( !useUS || parameters[ key ].join( '' ).indexOf( '|' ) === -1 ) {
+                                               parameters[ key ] = parameters[ key ].join( '|' );
+                                       } else {
+                                               parameters[ key ] = '\x1f' + parameters[ key ].join( '\x1f' );
+                                       }
                                }
                                // Boolean values are only false when not given at all
                                if ( parameters[ key ] === false || parameters[ key ] === undefined ) {
                                delete parameters.token;
                        }
 
-                       this.preprocessParameters( parameters );
+                       this.preprocessParameters( parameters, this.defaults.useUS );
 
                        // If multipart/form-data has been requested and emulation is possible, emulate it
                        if (
index 9ba562e..a1a4999 100644 (file)
                 * Get a set of messages.
                 *
                 * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
                 * @return {jQuery.Promise}
                 */
-               getMessages: function ( messages ) {
-                       return this.get( {
+               getMessages: function ( messages, options ) {
+                       options = options || {};
+                       return this.get( $.extend( {
                                action: 'query',
                                meta: 'allmessages',
                                ammessages: messages,
                                amlang: mw.config.get( 'wgUserLanguage' ),
                                formatversion: 2
-                       } ).then( function ( data ) {
+                       }, options ) ).then( function ( data ) {
                                var result = {};
 
                                $.each( data.query.allmessages, function ( i, obj ) {
                },
 
                /**
-                * Loads a set of mesages and add them to mw.messages.
+                * Loads a set of messages and add them to mw.messages.
                 *
                 * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
                 * @return {jQuery.Promise}
                 */
-               loadMessages: function ( messages ) {
-                       return this.getMessages( messages ).then( $.proxy( mw.messages, 'set' ) );
+               loadMessages: function ( messages, options ) {
+                       return this.getMessages( messages, options ).then( $.proxy( mw.messages, 'set' ) );
                },
 
                /**
-                * Loads a set of mesages and add them to mw.messages. Only messages that are not already known
+                * Loads a set of messages and add them to mw.messages. Only messages that are not already known
                 * are loaded. If all messages are known, the returned promise is resolved immediately.
                 *
                 * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
                 * @return {jQuery.Promise}
                 */
-               loadMessagesIfMissing: function ( messages ) {
+               loadMessagesIfMissing: function ( messages, options ) {
                        var missing = messages.filter( function ( msg ) {
                                return !mw.message( msg ).exists();
                        } );
@@ -62,7 +66,7 @@
                                return $.Deferred().resolve();
                        }
 
-                       return this.getMessages( missing ).then( $.proxy( mw.messages, 'set' ) );
+                       return this.getMessages( missing, options ).then( $.proxy( mw.messages, 'set' ) );
                }
        } );
 
index 0af2a75..069fbbf 100644 (file)
                                value = options[ name ] === null ? null : String( options[ name ] );
 
                                // Can we bundle this option, or does it need a separate request?
-                               bundleable =
-                                       ( value === null || value.indexOf( '|' ) === -1 ) &&
-                                       ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
+                               if ( this.defaults.useUS ) {
+                                       bundleable = name.indexOf( '=' ) === -1;
+                               } else {
+                                       bundleable =
+                                               ( value === null || value.indexOf( '|' ) === -1 ) &&
+                                               ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
+                               }
 
                                if ( bundleable ) {
                                        if ( value !== null ) {
index eb2b3fc..cdc4dbf 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @class mw.Api.plugin.rollback
- * @since 1.27
+ * @since 1.28
  */
 ( function ( mw, $ ) {
 
index 4c57faa..e468768 100644 (file)
                '|&#x[0-9A-Fa-f]+;'
        ),
 
-       // From MediaWikiTitleCodec.php#L225 @26fcab1f18c568a41
-       // "Clean up whitespace" in function MediaWikiTitleCodec::splitTitleString()
-       rWhitespace = /[ _\u0009\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\s]+/g,
+       // From MediaWikiTitleCodec::splitTitleString() in PHP
+       // Note that this is not equivalent to /\s/, e.g. underscore is included, tab is not included.
+       rWhitespace = /[ _\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g,
+
+       // From MediaWikiTitleCodec::splitTitleString() in PHP
+       rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]/g,
 
        /**
         * Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
                        replace: '',
                        generalRule: true
                },
-               // Space, underscore, tab, NBSP and other unusual spaces
-               {
-                       pattern: rWhitespace,
-                       replace: ' ',
-                       generalRule: true
-               },
-               // unicode bidi override characters: Implicit, Embeds, Overrides
-               {
-                       pattern: /[\u200E\u200F\u202A-\u202E]/g,
-                       replace: '',
-                       generalRule: true
-               },
                // control characters
                {
                        pattern: /[\x00-\x1f\x7f]/g,
                namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
 
                title = title
+                       // Strip Unicode bidi override characters
+                       .replace( rUnicodeBidi, '' )
                        // Normalise whitespace to underscores and remove duplicates
-                       .replace( /[ _\s]+/g, '_' )
+                       .replace( rWhitespace, '_' )
                        // Trim underscores
                        .replace( rUnderscoreTrim, '' );
 
 
                namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
 
-               // Normalise whitespace and remove duplicates
-               title = $.trim( title.replace( rWhitespace, ' ' ) );
+               // Normalise additional whitespace
+               title = $.trim( title.replace( /\s/g, ' ' ) );
 
                // Process initial colon
                if ( title !== '' && title[ 0 ] === ':' ) {
index 31e4492..7c4855f 100644 (file)
                        if ( error.message ) {
                                return this.upload.getApi()
                                        .then( function ( api ) {
-                                               return api.loadMessagesIfMissing( [ error.message.key ] ).then( function () {
-                                                       if ( !mw.message( error.message.key ).exists() ) {
-                                                               return $.Deferred().reject();
-                                                       }
-                                                       return new OO.ui.Error(
-                                                               $( '<p>' ).msg( error.message.key, error.message.params || [] ),
-                                                               { recoverable: false }
-                                                       );
-                                               } );
+                                               // 'amenableparser' will expand templates and parser functions server-side.
+                                               // We still do the rest of wikitext parsing here (through jqueryMsg).
+                                               return api.loadMessagesIfMissing( [ error.message.key ], { amenableparser: true } )
+                                                       .then( function () {
+                                                               if ( !mw.message( error.message.key ).exists() ) {
+                                                                       return $.Deferred().reject();
+                                                               }
+                                                               return new OO.ui.Error(
+                                                                       $( '<p>' ).msg( error.message.key, error.message.params || [] ),
+                                                                       { recoverable: false }
+                                                               );
+                                                       } );
                                        } )
                                        .then( null, function () {
                                                // We failed when loading the error message, or it doesn't actually exist, fall back
diff --git a/resources/src/mediawiki/mediawiki.diff.styles.css b/resources/src/mediawiki/mediawiki.diff.styles.css
new file mode 100644 (file)
index 0000000..327c9c8
--- /dev/null
@@ -0,0 +1,120 @@
+/*!
+ * Diff rendering
+ */
+table.diff {
+       border: none;
+       border-spacing: 4px;
+       margin: 0;
+       width: 100%;
+       /* Ensure that colums are of equal width */
+       table-layout: fixed;
+}
+
+table.diff td {
+       padding: 0.33em 0.5em;
+}
+
+table.diff td.diff-marker {
+       /* Compensate padding for increased font-size */
+       padding: 0.25em;
+}
+
+table.diff col.diff-marker {
+       width: 2%;
+}
+
+table.diff col.diff-content {
+       width: 48%;
+}
+
+table.diff td div {
+       /* Force-wrap very long lines such as URLs or page-widening char strings */
+       word-wrap: break-word;
+}
+
+td.diff-otitle,
+td.diff-ntitle {
+       text-align: center;
+}
+
+td.diff-lineno {
+       font-weight: bold;
+}
+
+td.diff-marker {
+       text-align: right;
+       font-weight: bold;
+       font-size: 1.25em;
+       line-height: 1.2;
+}
+
+td.diff-addedline,
+td.diff-deletedline,
+td.diff-context {
+       font-size: 88%;
+       line-height: 1.6;
+       vertical-align: top;
+       white-space: -moz-pre-wrap;
+       white-space: pre-wrap;
+       border-style: solid;
+       border-width: 1px 1px 1px 4px;
+       border-radius: 0.33em;
+}
+
+td.diff-addedline {
+       border-color: #a3d3ff;
+}
+
+td.diff-deletedline {
+       border-color: #ffe49c;
+}
+
+td.diff-context {
+       background: #f9f9f9;
+       border-color: #e6e6e6;
+       color: #333;
+}
+
+.diffchange {
+       font-weight: bold;
+       text-decoration: none;
+}
+
+td.diff-addedline .diffchange,
+td.diff-deletedline .diffchange {
+       border-radius: 0.33em;
+       padding: 0.25em 0;
+}
+
+td.diff-addedline .diffchange {
+       background: #d8ecff;
+}
+
+td.diff-deletedline .diffchange {
+       background: #feeec8;
+}
+
+/* Correct user & content directionality when viewing a diff */
+.diff-currentversion-title,
+.diff {
+       direction: ltr;
+       unicode-bidi: embed;
+}
+
+/* @noflip */ .diff-contentalign-right td {
+       direction: rtl;
+       unicode-bidi: embed;
+}
+
+/* @noflip */ .diff-contentalign-left td {
+       direction: ltr;
+       unicode-bidi: embed;
+}
+
+.diff-multi,
+.diff-otitle,
+.diff-ntitle,
+.diff-lineno {
+       direction: ltr !important;
+       unicode-bidi: embed;
+}
diff --git a/resources/src/mediawiki/mediawiki.diff.styles.print.css b/resources/src/mediawiki/mediawiki.diff.styles.print.css
new file mode 100644 (file)
index 0000000..76b5c9b
--- /dev/null
@@ -0,0 +1,16 @@
+/*!
+ * Diff rendering
+ */
+td.diff-context,
+td.diff-addedline .diffchange,
+td.diff-deletedline .diffchange {
+       background-color: transparent;
+}
+
+td.diff-addedline .diffchange {
+       text-decoration: underline;
+}
+
+td.diff-deletedline .diffchange {
+       text-decoration: line-through;
+}
index 491564a..04807f4 100644 (file)
                                cssBuffer = '',
                                cssBufferTimer = null,
                                cssCallbacks = $.Callbacks(),
-                               isIE9 = document.documentMode === 9;
+                               isIE9 = document.documentMode === 9,
+                               rAF = window.requestAnimationFrame || setTimeout;
 
                        function getMarker() {
                                if ( !marker ) {
                                        if ( !cssBuffer || cssText.slice( 0, '@import'.length ) !== '@import' ) {
                                                // Linebreak for somewhat distinguishable sections
                                                cssBuffer += '\n' + cssText;
-                                               // TODO: Using requestAnimationFrame would perform better by not injecting
-                                               // styles while the browser is busy painting.
                                                if ( !cssBufferTimer ) {
-                                                       cssBufferTimer = setTimeout( function () {
+                                                       cssBufferTimer = rAF( function () {
+                                                               // Wrap in anonymous function that takes no arguments
                                                                // Support: Firefox < 13
                                                                // Firefox 12 has non-standard behaviour of passing a number
                                                                // as first argument to a setTimeout callback.
                                                j -= 1;
                                                try {
                                                        if ( hasErrors ) {
-                                                               if ( $.isFunction( job.error ) ) {
+                                                               if ( typeof job.error === 'function' ) {
                                                                        job.error( new Error( 'Module ' + module + ' has failed dependencies' ), [ module ] );
                                                                }
                                                        } else {
-                                                               if ( $.isFunction( job.ready ) ) {
+                                                               if ( typeof job.ready === 'function' ) {
                                                                        job.ready();
                                                                }
                                                        }
                                }
 
                                // Resolves dynamic loader function and replaces it with its own results
-                               if ( $.isFunction( registry[ module ].dependencies ) ) {
+                               if ( typeof registry[ module ].dependencies === 'function' ) {
                                        registry[ module ].dependencies = registry[ module ].dependencies();
                                        // Ensures the module's dependencies are always in an array
                                        if ( typeof registry[ module ].dependencies !== 'object' ) {
                                        // Force jQuery behaviour to be for crossDomain. Otherwise jQuery would use
                                        // XHR for a same domain request instead of <script>, which changes the request
                                        // headers (potentially missing a cache hit), and reduces caching in general
-                                       // since browsers cache XHR much less (if at all). And XHR means we retreive
+                                       // since browsers cache XHR much less (if at all). And XHR means we retrieve
                                        // text, so we'd need to $.globalEval, which then messes up line numbers.
                                        crossDomain: true,
                                        cache: true
                                                legacyWait.always( function () {
                                                        if ( $.isArray( script ) ) {
                                                                nestedAddScript( script, markModuleReady, 0 );
-                                                       } else if ( $.isFunction( script ) ) {
+                                                       } else if ( typeof script === 'function' ) {
                                                                // Pass jQuery twice so that the signature of the closure which wraps
                                                                // the script can bind both '$' and 'jQuery'.
                                                                script( $, $, mw.loader.require, registry[ module ].module );
                         * @param {Array} modules Modules array
                         */
                        function resolveIndexedDependencies( modules ) {
-                               $.each( modules, function ( idx, module ) {
-                                       if ( module[ 2 ] ) {
-                                               module[ 2 ] = $.map( module[ 2 ], function ( dep ) {
-                                                       return typeof dep === 'number' ? modules[ dep ][ 0 ] : dep;
-                                               } );
+                               var i, j, deps;
+                               function resolveIndex( dep ) {
+                                       return typeof dep === 'number' ? modules[ dep ][ 0 ] : dep;
+                               }
+                               for ( i = 0; i < modules.length; i++ ) {
+                                       deps = modules[ i ][ 2 ];
+                                       if ( deps ) {
+                                               for ( j = 0; j < deps.length; j++ ) {
+                                                       deps[ j ] = resolveIndex( deps[ j ] );
+                                               }
                                        }
-                               } );
+                               }
                        }
 
                        /**
                                }
                        }
 
+                       /**
+                        * Evaluate a batch of load.php responses retrieved from mw.loader.store.
+                        *
+                        * @private
+                        * @param {string[]} implementations Array containing pieces of JavaScript code in the
+                        *  form of calls to mw.loader#implement().
+                        * @param {Function} cb Callback in case of failure
+                        * @param {Error} cb.err
+                        */
+                       function batchEval( implementations, cb ) {
+                               if ( !implementations.length ) {
+                                       return;
+                               }
+                               mw.requestIdleCallback( function iterate( deadline ) {
+                                       while ( implementations[ 0 ] && deadline.timeRemaining() > 5 ) {
+                                               try {
+                                                       $.globalEval( implementations.shift() );
+                                               } catch ( err ) {
+                                                       cb( err );
+                                                       return;
+                                               }
+                                       }
+                                       if ( implementations[ 0 ] ) {
+                                               mw.requestIdleCallback( iterate );
+                                       }
+                               } );
+                       }
+
                        /* Public Members */
                        return {
                                /**
                                 * @protected
                                 */
                                work: function () {
-                                       var q, batch, concatSource, origBatch;
+                                       var q, batch, implementations, sourceModules;
 
                                        batch = [];
 
                                                }
                                        }
 
+                                       // Now that the queue has been processed into a batch, clear the queue.
+                                       // This MUST happen before we initiate any eval or network request. Otherwise,
+                                       // it is possible for a cached script to instantly trigger the same work queue
+                                       // again; all before we've cleared it causing each request to include modules
+                                       // which are already loaded.
+                                       queue = [];
+
+                                       if ( !batch.length ) {
+                                               return;
+                                       }
+
                                        mw.loader.store.init();
                                        if ( mw.loader.store.enabled ) {
-                                               concatSource = [];
-                                               origBatch = batch;
+                                               implementations = [];
+                                               sourceModules = [];
                                                batch = $.grep( batch, function ( module ) {
-                                                       var source = mw.loader.store.get( module );
-                                                       if ( source ) {
-                                                               concatSource.push( source );
+                                                       var implementation = mw.loader.store.get( module );
+                                                       if ( implementation ) {
+                                                               implementations.push( implementation );
+                                                               sourceModules.push( module );
                                                                return false;
                                                        }
                                                        return true;
                                                } );
-                                               try {
-                                                       $.globalEval( concatSource.join( ';' ) );
-                                               } catch ( err ) {
+                                               batchEval( implementations, function ( err ) {
                                                        // Not good, the cached mw.loader.implement calls failed! This should
                                                        // never happen, barring ResourceLoader bugs, browser bugs and PEBKACs.
                                                        // Depending on how corrupt the string is, it is likely that some
                                                        // modules' implement() succeeded while the ones after the error will
                                                        // never run and leave their modules in the 'loading' state forever.
-
                                                        // Since this is an error not caused by an individual module but by
                                                        // something that infected the implement call itself, don't take any
                                                        // risks and clear everything in this cache.
                                                        mw.loader.store.clear();
-                                                       // Re-add the ones still pending back to the batch and let the server
-                                                       // repopulate these modules to the cache.
-                                                       // This means that at most one module will be useless (the one that had
-                                                       // the error) instead of all of them.
                                                        mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
-                                                       origBatch = $.grep( origBatch, function ( module ) {
+
+                                                       // Re-add the failed ones that are still pending back to the batch
+                                                       var failed = $.grep( sourceModules, function ( module ) {
                                                                return registry[ module ].state === 'loading';
                                                        } );
-                                                       batch = batch.concat( origBatch );
-                                               }
+                                                       batchRequest( failed );
+                                               } );
                                        }
 
-                                       // Now that the queue has been processed into a batch, clear up the queue.
-                                       // This MUST happen before we initiate any network request. Else it's possible
-                                       // that a script will be locally cached, instantly load, and work the queue
-                                       // again; all before we've cleared it causing each request to include modules
-                                       // which are already loaded.
-                                       queue = [];
-
                                        batchRequest( batch );
                                },
 
                                 * @param {string} [skip=null] Script body of the skip function
                                 */
                                register: function ( module, version, dependencies, group, source, skip ) {
-                                       var i;
+                                       var i, deps;
                                        // Allow multiple registration
                                        if ( typeof module === 'object' ) {
                                                resolveIndexedDependencies( module );
                                        if ( hasOwn.call( registry, module ) ) {
                                                throw new Error( 'module already registered: ' + module );
                                        }
+                                       if ( typeof dependencies === 'string' ) {
+                                               // A single module name
+                                               deps = [ dependencies ];
+                                       } else if ( typeof dependencies === 'object' || typeof dependencies === 'function' ) {
+                                               // Array of module names or a function that returns an array
+                                               deps = dependencies;
+                                       }
                                        // List the module as registered
                                        registry[ module ] = {
                                                // Exposed to execute() for mw.loader.implement() closures.
                                                        exports: {}
                                                },
                                                version: version !== undefined ? String( version ) : '',
-                                               dependencies: [],
+                                               dependencies: deps || [],
                                                group: typeof group === 'string' ? group : null,
                                                source: typeof source === 'string' ? source : 'local',
                                                state: 'registered',
                                                skip: typeof skip === 'string' ? skip : null
                                        };
-                                       if ( typeof dependencies === 'string' ) {
-                                               // Allow dependencies to be given as a single module name
-                                               registry[ module ].dependencies = [ dependencies ];
-                                       } else if ( typeof dependencies === 'object' || $.isFunction( dependencies ) ) {
-                                               // Allow dependencies to be given as an array of module names
-                                               // or a function which returns an array
-                                               registry[ module ].dependencies = dependencies;
-                                       }
                                },
 
                                /**
                                 * response contain calls to this function.
                                 *
                                 * @param {string} module Name of module
-                                * @param {Function|Array} [script] Function with module code or Array of URLs to
-                                *  be used as the src attribute of a new `<script>` tag.
+                                * @param {Function|Array|string} [script] Function with module code, list of URLs
+                                *  to load via `<script src>`, or string of module code for `$.globalEval()`.
                                 * @param {Object} [style] Should follow one of the following patterns:
                                 *
                                 *     { "css": [css, ..] }
                                        if ( !hasOwn.call( registry, module ) ) {
                                                mw.loader.register( module );
                                        }
-                                       if ( $.inArray( state, [ 'ready', 'error', 'missing' ] ) !== -1
-                                               && registry[ module ].state !== state ) {
+                                       registry[ module ].state = state;
+                                       if ( $.inArray( state, [ 'ready', 'error', 'missing' ] ) !== -1 ) {
                                                // Make sure pending modules depending on this one get executed if their
                                                // dependencies are now fulfilled!
-                                               registry[ module ].state = state;
                                                handlePending( module );
-                                       } else {
-                                               registry[ module ].state = state;
                                        }
                                },
 
                                                        // Unversioned, private, or site-/user-specific
                                                        ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user' ] ) !== -1 ) ||
                                                        // Partial descriptor
+                                                       // (e.g. skipped module, or style module with state=ready)
                                                        $.inArray( undefined, [ descriptor.script, descriptor.style,
                                                                        descriptor.messages, descriptor.templates ] ) !== -1
                                                ) {
                var loading = $.grep( mw.loader.getModuleNames(), function ( module ) {
                        return mw.loader.getState( module ) === 'loading';
                } );
-               // In order to use jQuery.when (which stops early if one of the promises got rejected)
-               // cast any loading failures into successes. We only need a callback, not the module.
-               loading = $.map( loading, function ( module ) {
-                       return mw.loader.using( module ).then( null, function () {
-                               return $.Deferred().resolve();
+               // We only need a callback, not any actual module. First try a single using()
+               // for all loading modules. If one fails, fall back to tracking each module
+               // separately via $.when(), this is expensive.
+               loading = mw.loader.using( loading ).then( null, function () {
+                       var all = $.map( loading, function ( module ) {
+                               return mw.loader.using( module ).then( null, function () {
+                                       return $.Deferred().resolve();
+                               } );
                        } );
+                       return $.when.apply( $, all );
                } );
-               $.when.apply( $, loading ).then( function () {
+               loading.then( function () {
                        mwPerformance.mark( 'mwLoadEnd' );
                        mw.hook( 'resourceloader.loadEnd' ).fire();
                } );
diff --git a/resources/src/mediawiki/mediawiki.notification.hideForPrint.css b/resources/src/mediawiki/mediawiki.notification.hideForPrint.css
deleted file mode 100644 (file)
index 4f9162e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-.mw-notification-area {
-       display: none;
-}
diff --git a/resources/src/mediawiki/mediawiki.notification.print.css b/resources/src/mediawiki/mediawiki.notification.print.css
new file mode 100644 (file)
index 0000000..4f9162e
--- /dev/null
@@ -0,0 +1,3 @@
+.mw-notification-area {
+       display: none;
+}
index 78627fc..7bf73b6 100644 (file)
@@ -10,7 +10,8 @@
                $tocList = $toc.find( 'ul' ).eq( 0 );
 
                // Hide/show the table of contents element
-               function toggleToc() {
+               function toggleToc( e ) {
+                       e.preventDefault();
                        if ( $tocList.is( ':hidden' ) ) {
                                $tocList.slideDown( 'fast' );
                                $tocToggleLink.text( mw.msg( 'hidetoc' ) );
                        hideToc = mw.cookie.get( 'hidetoc' ) === '1';
 
                        $tocToggleLink = $( '<a href="#" id="togglelink"></a>' )
-                               .text( hideToc ? mw.msg( 'showtoc' ) : mw.msg( 'hidetoc' ) )
-                               .click( function ( e ) {
-                                       e.preventDefault();
-                                       toggleToc();
-                               } );
+                               .text( mw.msg( hideToc ? 'showtoc' : 'hidetoc' ) )
+                               .click( toggleToc );
 
                        $tocTitle.append(
                                $tocToggleLink
index 0fdd9aa..52a1efb 100644 (file)
@@ -76,8 +76,8 @@
                                hexRnds[ i ] = byteToHex[ rnds[ i ] ];
                        }
 
-                       // Concatenation of two random integers with entrophy n and m
-                       // returns a string with entrophy n+m if those strings are independent
+                       // Concatenation of two random integers with entropy n and m
+                       // returns a string with entropy n+m if those strings are independent
                        return hexRnds.join( '' );
                },
 
                /**
                 * Get date user registered, if available
                 *
-                * @return {Date|boolean|null} Date user registered, or false for anonymous users, or
-                *  null when data is not available
+                * @return {boolean|null|Date} False for anonymous users, null if data is
+                *  unavailable, or Date for when the user registered.
                 */
                getRegistration: function () {
-                       var registration = mw.config.get( 'wgUserRegistration' );
                        if ( mw.user.isAnon() ) {
                                return false;
                        }
-                       if ( registration === null ) {
-                               // Information may not be available if they signed up before
-                               // MW began storing this.
-                               return null;
-                       }
-                       return new Date( registration );
+                       var registration = mw.config.get( 'wgUserRegistration' );
+                       // Registration may be unavailable if the user signed up before MediaWiki
+                       // began tracking this.
+                       return !registration ? null : new Date( registration );
                },
 
                /**
index 2ce54e4..294b5de 100644 (file)
         */
        mw.log.deprecate( util, 'wikiGetlink', util.getUrl, 'Use mw.util.getUrl instead.' );
 
-       /**
-        * Access key prefix. Might be wrong for browsers implementing the accessKeyLabel property.
-        * @property {string} tooltipAccessKeyPrefix
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyPrefix', $.fn.updateTooltipAccessKeys.getAccessKeyPrefix(), 'Use jquery.accessKeyLabel instead.' );
-
-       /**
-        * Regex to match accesskey tooltips.
-        *
-        * Should match:
-        *
-        * - "[ctrl-option-x]"
-        * - "[alt-shift-x]"
-        * - "[ctrl-alt-x]"
-        * - "[ctrl-x]"
-        *
-        * The accesskey is matched in group $6.
-        *
-        * Will probably not work for browsers implementing the accessKeyLabel property.
-        *
-        * @property {RegExp} tooltipAccessKeyRegexp
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyRegexp', /\[(ctrl-)?(option-)?(alt-)?(shift-)?(esc-)?(.)\]$/, 'Use jquery.accessKeyLabel instead.' );
-
        /**
         * Add the appropriate prefix to the accesskey shown in the tooltip.
         *
diff --git a/resources/src/mediawiki/page/gallery-print.css b/resources/src/mediawiki/page/gallery-print.css
deleted file mode 100644 (file)
index 0c14865..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-li.gallerybox {
-       vertical-align: top;
-       display: inline-block;
-}
-
-ul.gallery, li.gallerybox {
-       zoom: 1;
-       *display: inline;
-}
-
-ul.gallery {
-       margin: 2px;
-       padding: 2px;
-       display: block;
-}
-
-li.gallerycaption {
-       font-weight: bold;
-       text-align: center;
-       display: block;
-       word-wrap: break-word;
-}
-
-li.gallerybox div.thumb {
-       text-align: center;
-       border: 1px solid #ccc;
-       margin: 2px;
-}
-
-div.gallerytext {
-       overflow: hidden;
-       font-size: 94%;
-       padding: 2px 4px;
-       word-wrap: break-word;
-}
index dabf475..474d541 100644 (file)
@@ -1,6 +1,6 @@
 /* Galleries */
 /* These display attributes look nonsensical, but are needed to support IE and FF2 */
-/* Don't forget to update gallery-print.css */
+/* Don't forget to update gallery.print.css */
 li.gallerybox {
        vertical-align: top;
        display: -moz-inline-box;
diff --git a/resources/src/mediawiki/page/gallery.print.css b/resources/src/mediawiki/page/gallery.print.css
new file mode 100644 (file)
index 0000000..0c14865
--- /dev/null
@@ -0,0 +1,35 @@
+li.gallerybox {
+       vertical-align: top;
+       display: inline-block;
+}
+
+ul.gallery, li.gallerybox {
+       zoom: 1;
+       *display: inline;
+}
+
+ul.gallery {
+       margin: 2px;
+       padding: 2px;
+       display: block;
+}
+
+li.gallerycaption {
+       font-weight: bold;
+       text-align: center;
+       display: block;
+       word-wrap: break-word;
+}
+
+li.gallerybox div.thumb {
+       text-align: center;
+       border: 1px solid #ccc;
+       margin: 2px;
+}
+
+div.gallerytext {
+       overflow: hidden;
+       font-size: 94%;
+       padding: 2px 4px;
+       word-wrap: break-word;
+}
index d973d07..83d14b3 100644 (file)
@@ -2,7 +2,7 @@
  * Enhance rollback links by using asynchronous API requests,
  * rather than navigating to an action page.
  *
- * @since 1.27
+ * @since 1.28
  * @author Timo Tijhof
  */
 ( function ( mw, $ ) {
@@ -15,7 +15,7 @@
                                page = mw.util.getParamValue( 'title', url ),
                                user = mw.util.getParamValue( 'from', url );
 
-                       if ( !page || !user ) {
+                       if ( !page || user === null ) {
                                // Let native browsing handle the link
                                return true;
                        }
diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php
deleted file mode 100644 (file)
index 5488280..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-/**
- * AutoLoader for the testing suite.
- *
- * 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 Testing
- */
-
-global $wgAutoloadClasses;
-$testDir = __DIR__;
-
-$wgAutoloadClasses += [
-
-       # tests
-       'DbTestPreviewer' => "$testDir/testHelpers.inc",
-       'DbTestRecorder' => "$testDir/testHelpers.inc",
-       'DelayedParserTest' => "$testDir/testHelpers.inc",
-       'ParserTestResult' => "$testDir/parser/ParserTestResult.php",
-       'TestFileIterator' => "$testDir/testHelpers.inc",
-       'TestFileDataProvider' => "$testDir/testHelpers.inc",
-       'TestRecorder' => "$testDir/testHelpers.inc",
-       'ITestRecorder' => "$testDir/testHelpers.inc",
-       'DjVuSupport' => "$testDir/testHelpers.inc",
-       'TidySupport' => "$testDir/testHelpers.inc",
-
-       # tests/phpunit
-       'MediaWikiTestCase' => "$testDir/phpunit/MediaWikiTestCase.php",
-       'MediaWikiPHPUnitTestListener' => "$testDir/phpunit/MediaWikiPHPUnitTestListener.php",
-       'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
-       'ResourceLoaderTestCase' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'EmptyResourceLoader' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'TestUser' => "$testDir/phpunit/includes/TestUser.php",
-       'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
-       'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
-
-       # tests/phpunit/includes
-       'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
-       'TestingAccessWrapper' => "$testDir/phpunit/includes/TestingAccessWrapper.php",
-       'TestLogger' => "$testDir/phpunit/includes/TestLogger.php",
-
-       # tests/phpunit/includes/api
-       'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
-       'ApiQueryTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryTestBase.php",
-       'ApiQueryContinueTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryContinueTestBase.php",
-       'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
-       'ApiTestCaseUpload' => "$testDir/phpunit/includes/api/ApiTestCaseUpload.php",
-       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
-       'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
-       'MockApiQueryBase' => "$testDir/phpunit/includes/api/MockApiQueryBase.php",
-       'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
-       'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
-
-       # tests/phpunit/includes/auth
-       'MediaWiki\\Auth\\AuthenticationRequestTestCase' =>
-               "$testDir/phpunit/includes/auth/AuthenticationRequestTestCase.php",
-
-       # tests/phpunit/includes/changes
-       'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
-
-       # tests/phpunit/includes/content
-       'DummyContentHandlerForTesting' =>
-               "$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
-       'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
-       'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
-       'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
-       'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
-       'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
-       'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
-       'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
-
-       # tests/phpunit/includes/db
-       'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
-
-       # tests/phpunit/includes/diff
-       'FakeDiffOp' => "$testDir/phpunit/includes/diff/FakeDiffOp.php",
-
-       # tests/phpunit/includes/logging
-       'LogFormatterTestCase' => "$testDir/phpunit/includes/logging/LogFormatterTestCase.php",
-
-       # tests/phpunit/includes/page
-       'WikiPageTest' => "$testDir/phpunit/includes/page/WikiPageTest.php",
-
-       # tests/phpunit/includes/password
-       'PasswordTestCase' => "$testDir/phpunit/includes/password/PasswordTestCase.php",
-
-       # tests/phpunit/includes/resourceloader
-       'ResourceLoaderImageModuleTest' =>
-               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
-       'ResourceLoaderImageModuleTestable' =>
-               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
-
-       # tests/phpunit/includes/session
-       'MediaWiki\\Session\\TestBagOStuff' => "$testDir/phpunit/includes/session/TestBagOStuff.php",
-       'MediaWiki\\Session\\TestUtils' => "$testDir/phpunit/includes/session/TestUtils.php",
-
-       # tests/phpunit/includes/specials
-       'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
-       'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
-
-       # tests/phpunit/languages
-       'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
-
-       # tests/phpunit/includes/libs
-       'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
-
-       # tests/phpunit/maintenance
-       'DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
-
-       # tests/phpunit/media
-       'FakeDimensionFile' => "$testDir/phpunit/includes/media/FakeDimensionFile.php",
-       'MediaWikiMediaTestCase' => "$testDir/phpunit/includes/media/MediaWikiMediaTestCase.php",
-
-       # tests/phpunit/mocks
-       'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
-       'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
-       'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
-       'MockImageHandler' => "$testDir/phpunit/mocks/media/MockImageHandler.php",
-       'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockSvgHandler.php",
-       'MockDjVuHandler' => "$testDir/phpunit/mocks/media/MockDjVuHandler.php",
-       'MockOggHandler' => "$testDir/phpunit/mocks/media/MockOggHandler.php",
-       'MockMediaHandlerFactory' => "$testDir/phpunit/mocks/media/MockMediaHandlerFactory.php",
-       'MockWebRequest' => "$testDir/phpunit/mocks/MockWebRequest.php",
-       'MediaWiki\\Session\\DummySessionBackend'
-               => "$testDir/phpunit/mocks/session/DummySessionBackend.php",
-       'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php",
-
-       # tests/parser
-       'NewParserTest' => "$testDir/phpunit/includes/parser/NewParserTest.php",
-       'MediaWikiParserTest' => "$testDir/phpunit/includes/parser/MediaWikiParserTest.php",
-       'ParserTest' => "$testDir/parser/parserTest.inc",
-       'ParserTestResultNormalizer' => "$testDir/parser/parserTest.inc",
-       'ParserTestParserHook' => "$testDir/parser/parserTestsParserHook.php",
-
-       # tests/phpunit/includes/site
-       'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
-       'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
-
-       # tests/phpunit/includes/specialpage
-       'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
-];
index 8740e5d..3f451f1 100644 (file)
@@ -21,6 +21,7 @@ mw-vagrant-host: &default
 mw-vagrant-guest:
   user_factory: true
   mediawiki_url: http://127.0.0.1/wiki/
+  headless: 'true'
 
 beta:
   mediawiki_url: https://en.wikipedia.beta.wmflabs.org/wiki/
diff --git a/tests/common/TestSetup.php b/tests/common/TestSetup.php
new file mode 100644 (file)
index 0000000..6c3ad07
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Common code for test environment initialisation and teardown
+ */
+class TestSetup {
+       /**
+        * This should be called before Setup.php, e.g. from the finalSetup() method
+        * of a Maintenance subclass
+        */
+       public static function applyInitialConfig() {
+               global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgMainWANCache;
+               global $wgMainStash;
+               global $wgLanguageConverterCacheType, $wgUseDatabaseMessages;
+               global $wgLocaltimezone, $wgLocalisationCacheConf;
+               global $wgDevelopmentWarnings;
+               global $wgSessionProviders, $wgSessionPbkdf2Iterations;
+               global $wgJobTypeConf;
+               global $wgAuthManagerConfig, $wgAuth;
+
+               // wfWarn should cause tests to fail
+               $wgDevelopmentWarnings = true;
+
+               // Make sure all caches and stashes are either disabled or use
+               // in-process cache only to prevent tests from using any preconfigured
+               // cache meant for the local wiki from outside the test run.
+               // See also MediaWikiTestCase::run() which mocks CACHE_DB and APC.
+
+               // Disabled in DefaultSettings, override local settings
+               $wgMainWANCache =
+               $wgMainCacheType = CACHE_NONE;
+               // Uses CACHE_ANYTHING in DefaultSettings, use hash instead of db
+               $wgMessageCacheType =
+               $wgParserCacheType =
+               $wgSessionCacheType =
+               $wgLanguageConverterCacheType = 'hash';
+               // Uses db-replicated in DefaultSettings
+               $wgMainStash = 'hash';
+               // Use memory job queue
+               $wgJobTypeConf = [
+                       'default' => [ 'class' => 'JobQueueMemory', 'order' => 'fifo' ],
+               ];
+
+               $wgUseDatabaseMessages = false; # Set for future resets
+
+               // Assume UTC for testing purposes
+               $wgLocaltimezone = 'UTC';
+
+               $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull';
+
+               // Generic MediaWiki\Session\SessionManager configuration for tests
+               // We use CookieSessionProvider because things might be expecting
+               // cookies to show up in a FauxRequest somewhere.
+               $wgSessionProviders = [
+                       [
+                               'class' => MediaWiki\Session\CookieSessionProvider::class,
+                               'args' => [ [
+                                       'priority' => 30,
+                                       'callUserSetCookiesHook' => true,
+                               ] ],
+                       ],
+               ];
+
+               // Single-iteration PBKDF2 session secret derivation, for speed.
+               $wgSessionPbkdf2Iterations = 1;
+
+               // Generic AuthManager configuration for testing
+               $wgAuthManagerConfig = [
+                       'preauth' => [],
+                       'primaryauth' => [
+                               [
+                                       'class' => MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class,
+                                       'args' => [ [
+                                               'authoritative' => false,
+                                       ] ],
+                               ],
+                               [
+                                       'class' => MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class,
+                                       'args' => [ [
+                                               'authoritative' => true,
+                                       ] ],
+                               ],
+                       ],
+                       'secondaryauth' => [],
+               ];
+               $wgAuth = new MediaWiki\Auth\AuthManagerAuthPlugin();
+
+               // Bug 44192 Do not attempt to send a real e-mail
+               Hooks::clear( 'AlternateUserMailer' );
+               Hooks::register(
+                       'AlternateUserMailer',
+                       function () {
+                               return false;
+                       }
+               );
+               // xdebug's default of 100 is too low for MediaWiki
+               ini_set( 'xdebug.max_nesting_level', 1000 );
+
+               // Bug T116683 serialize_precision of 100
+               // may break testing against floating point values
+               // treated with PHP's serialize()
+               ini_set( 'serialize_precision', 17 );
+
+               // TODO: we should call MediaWikiTestCase::prepareServices( new GlobalVarConfig() ) here.
+               // But PHPUnit may not be loaded yet, so we have to wait until just
+               // before PHPUnit_TextUI_Command::main() is executed.
+       }
+
+}
diff --git a/tests/common/TestsAutoLoader.php b/tests/common/TestsAutoLoader.php
new file mode 100644 (file)
index 0000000..2a985fe
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * AutoLoader for the testing suite.
+ *
+ * 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 Testing
+ */
+
+global $wgAutoloadClasses;
+$testDir = __DIR__ . "/..";
+
+$wgAutoloadClasses += [
+
+       # tests/common
+       'TestSetup' => "$testDir/common/TestSetup.php",
+
+       # tests/parser
+       'DbTestPreviewer' => "$testDir/parser/DbTestPreviewer.php",
+       'DbTestRecorder' => "$testDir/parser/DbTestRecorder.php",
+       'DjVuSupport' => "$testDir/parser/DjVuSupport.php",
+       'TestRecorder' => "$testDir/parser/TestRecorder.php",
+       'MultiTestRecorder' => "$testDir/parser/MultiTestRecorder.php",
+       'ParserTestRunner' => "$testDir/parser/ParserTestRunner.php",
+       'ParserTestParserHook' => "$testDir/parser/ParserTestParserHook.php",
+       'ParserTestPrinter' => "$testDir/parser/ParserTestPrinter.php",
+       'ParserTestResult' => "$testDir/parser/ParserTestResult.php",
+       'ParserTestResultNormalizer' => "$testDir/parser/ParserTestResultNormalizer.php",
+       'PhpunitTestRecorder' => "$testDir/parser/PhpunitTestRecorder.php",
+       'TestFileReader' => "$testDir/parser/TestFileReader.php",
+       'TestRecorder' => "$testDir/parser/TestRecorder.php",
+       'TidySupport' => "$testDir/parser/TidySupport.php",
+
+       # tests/phpunit
+       'MediaWikiTestCase' => "$testDir/phpunit/MediaWikiTestCase.php",
+       'MediaWikiPHPUnitTestListener' => "$testDir/phpunit/MediaWikiPHPUnitTestListener.php",
+       'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
+       'ResourceLoaderTestCase' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'EmptyResourceLoader' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'TestUser' => "$testDir/phpunit/includes/TestUser.php",
+       'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
+       'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
+
+       # tests/phpunit/includes
+       'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
+       'TestingAccessWrapper' => "$testDir/phpunit/includes/TestingAccessWrapper.php",
+       'TestLogger' => "$testDir/phpunit/includes/TestLogger.php",
+
+       # tests/phpunit/includes/api
+       'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
+       'ApiQueryTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryTestBase.php",
+       'ApiQueryContinueTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryContinueTestBase.php",
+       'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+       'ApiTestCaseUpload' => "$testDir/phpunit/includes/api/ApiTestCaseUpload.php",
+       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
+       'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
+       'MockApiQueryBase' => "$testDir/phpunit/includes/api/MockApiQueryBase.php",
+       'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
+       'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
+
+       # tests/phpunit/includes/auth
+       'MediaWiki\\Auth\\AuthenticationRequestTestCase' =>
+               "$testDir/phpunit/includes/auth/AuthenticationRequestTestCase.php",
+
+       # tests/phpunit/includes/changes
+       'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
+
+       # tests/phpunit/includes/content
+       'DummyContentHandlerForTesting' =>
+               "$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
+       'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
+       'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
+       'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
+       'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+       'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
+       'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
+       'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
+
+       # tests/phpunit/includes/db
+       'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
+
+       # tests/phpunit/includes/diff
+       'FakeDiffOp' => "$testDir/phpunit/includes/diff/FakeDiffOp.php",
+
+       # tests/phpunit/includes/logging
+       'LogFormatterTestCase' => "$testDir/phpunit/includes/logging/LogFormatterTestCase.php",
+
+       # tests/phpunit/includes/page
+       'WikiPageTest' => "$testDir/phpunit/includes/page/WikiPageTest.php",
+
+       # tests/phpunit/includes/parser
+       'ParserIntegrationTest' => "$testDir/phpunit/includes/parser/ParserIntegrationTest.php",
+
+       # tests/phpunit/includes/password
+       'PasswordTestCase' => "$testDir/phpunit/includes/password/PasswordTestCase.php",
+
+       # tests/phpunit/includes/resourceloader
+       'ResourceLoaderImageModuleTest' =>
+               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
+       'ResourceLoaderImageModuleTestable' =>
+               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
+
+       # tests/phpunit/includes/session
+       'MediaWiki\\Session\\TestBagOStuff' => "$testDir/phpunit/includes/session/TestBagOStuff.php",
+       'MediaWiki\\Session\\TestUtils' => "$testDir/phpunit/includes/session/TestUtils.php",
+
+       # tests/phpunit/includes/site
+       'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
+       'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
+
+       # tests/phpunit/includes/specialpage
+       'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
+
+       # tests/phpunit/includes/specials
+       'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
+       'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
+
+       # tests/phpunit/languages
+       'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
+
+       # tests/phpunit/includes/libs
+       'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
+
+       # tests/phpunit/maintenance
+       'DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
+
+       # tests/phpunit/media
+       'FakeDimensionFile' => "$testDir/phpunit/includes/media/FakeDimensionFile.php",
+       'MediaWikiMediaTestCase' => "$testDir/phpunit/includes/media/MediaWikiMediaTestCase.php",
+
+       # tests/phpunit/mocks
+       'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
+       'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
+       'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+       'MockImageHandler' => "$testDir/phpunit/mocks/media/MockImageHandler.php",
+       'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockSvgHandler.php",
+       'MockDjVuHandler' => "$testDir/phpunit/mocks/media/MockDjVuHandler.php",
+       'MockOggHandler' => "$testDir/phpunit/mocks/media/MockOggHandler.php",
+       'MockMediaHandlerFactory' => "$testDir/phpunit/mocks/media/MockMediaHandlerFactory.php",
+       'MockWebRequest' => "$testDir/phpunit/mocks/MockWebRequest.php",
+       'MediaWiki\\Session\\DummySessionBackend'
+               => "$testDir/phpunit/mocks/session/DummySessionBackend.php",
+       'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php",
+
+       # tests/suites
+       'ParserTestFileSuite' => "$testDir/phpunit/suites/ParserTestFileSuite.php",
+       'ParserTestTopLevelSuite' => "$testDir/phpunit/suites/ParserTestTopLevelSuite.php",
+];
diff --git a/tests/parser/DbTestPreviewer.php b/tests/parser/DbTestPreviewer.php
new file mode 100644 (file)
index 0000000..7809ab3
--- /dev/null
@@ -0,0 +1,204 @@
+<?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 Testing
+ */
+
+class DbTestPreviewer extends TestRecorder {
+       protected $filter; // /< Test name filter callback
+       protected $lb; // /< Database load balancer
+       protected $db; // /< Database connection to the main DB
+       protected $curRun; // /< run ID number for the current run
+       protected $prevRun; // /< run ID number for the previous run, if any
+       protected $results; // /< Result array
+
+       /**
+        * This should be called before the table prefix is changed
+        */
+       function __construct( $db, $filter = false ) {
+               $this->db = $db;
+               $this->filter = $filter;
+       }
+
+       /**
+        * Set up result recording; insert a record for the run with the date
+        * and all that fun stuff
+        */
+       function start() {
+               if ( !$this->db->tableExists( 'testrun', __METHOD__ )
+                       || !$this->db->tableExists( 'testitem', __METHOD__ )
+               ) {
+                       print "WARNING> `testrun` table not found in database.\n";
+                       $this->prevRun = false;
+               } else {
+                       // We'll make comparisons against the previous run later...
+                       $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' );
+               }
+
+               $this->results = [];
+       }
+
+       function record( $test, ParserTestResult $result ) {
+               $this->results[$test['desc']] = $result->isSuccess() ? 1 : 0;
+       }
+
+       function report() {
+               if ( $this->prevRun ) {
+                       // f = fail, p = pass, n = nonexistent
+                       // codes show before then after
+                       $table = [
+                               'fp' => 'previously failing test(s) now PASSING! :)',
+                               'pn' => 'previously PASSING test(s) removed o_O',
+                               'np' => 'new PASSING test(s) :)',
+
+                               'pf' => 'previously passing test(s) now FAILING! :(',
+                               'fn' => 'previously FAILING test(s) removed O_o',
+                               'nf' => 'new FAILING test(s) :(',
+                               'ff' => 'still FAILING test(s) :(',
+                       ];
+
+                       $prevResults = [];
+
+                       $res = $this->db->select( 'testitem', [ 'ti_name', 'ti_success' ],
+                               [ 'ti_run' => $this->prevRun ], __METHOD__ );
+                       $filter = $this->filter;
+
+                       foreach ( $res as $row ) {
+                               if ( !$filter || $filter( $row->ti_name ) ) {
+                                       $prevResults[$row->ti_name] = $row->ti_success;
+                               }
+                       }
+
+                       $combined = array_keys( $this->results + $prevResults );
+
+                       # Determine breakdown by change type
+                       $breakdown = [];
+                       foreach ( $combined as $test ) {
+                               if ( !isset( $prevResults[$test] ) ) {
+                                       $before = 'n';
+                               } elseif ( $prevResults[$test] == 1 ) {
+                                       $before = 'p';
+                               } else /* if ( $prevResults[$test] == 0 )*/ {
+                                       $before = 'f';
+                               }
+
+                               if ( !isset( $this->results[$test] ) ) {
+                                       $after = 'n';
+                               } elseif ( $this->results[$test] == 1 ) {
+                                       $after = 'p';
+                               } else /*if ( $this->results[$test] == 0 ) */ {
+                                       $after = 'f';
+                               }
+
+                               $code = $before . $after;
+
+                               if ( isset( $table[$code] ) ) {
+                                       $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after );
+                               }
+                       }
+
+                       # Write out results
+                       foreach ( $table as $code => $label ) {
+                               if ( !empty( $breakdown[$code] ) ) {
+                                       $count = count( $breakdown[$code] );
+                                       printf( "\n%4d %s\n", $count, $label );
+
+                                       foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) {
+                                               print "      * $differing_test_name  [$statusInfo]\n";
+                                       }
+                               }
+                       }
+               } else {
+                       print "No previous test runs to compare against.\n";
+               }
+
+               print "\n";
+       }
+
+       /**
+        * Returns a string giving information about when a test last had a status change.
+        * Could help to track down when regressions were introduced, as distinct from tests
+        * which have never passed (which are more change requests than regressions).
+        * @param string $testname
+        * @param string $after
+        * @return string
+        */
+       private function getTestStatusInfo( $testname, $after ) {
+               // If we're looking at a test that has just been removed, then say when it first appeared.
+               if ( $after == 'n' ) {
+                       $changedRun = $this->db->selectField( 'testitem',
+                               'MIN(ti_run)',
+                               [ 'ti_name' => $testname ],
+                               __METHOD__ );
+                       $appear = $this->db->selectRow( 'testrun',
+                               [ 'tr_date', 'tr_mw_version' ],
+                               [ 'tr_id' => $changedRun ],
+                               __METHOD__ );
+
+                       return "First recorded appearance: "
+                               . date( "d-M-Y H:i:s", strtotime( $appear->tr_date ) )
+                               . ", " . $appear->tr_mw_version;
+               }
+
+               // Otherwise, this test has previous recorded results.
+               // See when this test last had a different result to what we're seeing now.
+               $conds = [
+                       'ti_name' => $testname,
+                       'ti_success' => ( $after == 'f' ? "1" : "0" ) ];
+
+               if ( $this->curRun ) {
+                       $conds[] = "ti_run != " . $this->db->addQuotes( $this->curRun );
+               }
+
+               $changedRun = $this->db->selectField( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ );
+
+               // If no record of ever having had a different result.
+               if ( is_null( $changedRun ) ) {
+                       if ( $after == "f" ) {
+                               return "Has never passed";
+                       } else {
+                               return "Has never failed";
+                       }
+               }
+
+               // Otherwise, we're looking at a test whose status has changed.
+               // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.)
+               // In this situation, give as much info as we can as to when it changed status.
+               $pre = $this->db->selectRow( 'testrun',
+                       [ 'tr_date', 'tr_mw_version' ],
+                       [ 'tr_id' => $changedRun ],
+                       __METHOD__ );
+               $post = $this->db->selectRow( 'testrun',
+                       [ 'tr_date', 'tr_mw_version' ],
+                       [ "tr_id > " . $this->db->addQuotes( $changedRun ) ],
+                       __METHOD__,
+                       [ "LIMIT" => 1, "ORDER BY" => 'tr_id' ]
+               );
+
+               if ( $post ) {
+                       $postDate = date( "d-M-Y H:i:s", strtotime( $post->tr_date ) ) . ", {$post->tr_mw_version}";
+               } else {
+                       $postDate = 'now';
+               }
+
+               return ( $after == "f" ? "Introduced" : "Fixed" ) . " between "
+                       . date( "d-M-Y H:i:s", strtotime( $pre->tr_date ) ) . ", " . $pre->tr_mw_version
+                       . " and $postDate";
+       }
+}
+
diff --git a/tests/parser/DbTestRecorder.php b/tests/parser/DbTestRecorder.php
new file mode 100644 (file)
index 0000000..0e94301
--- /dev/null
@@ -0,0 +1,84 @@
+<?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 Testing
+ */
+
+class DbTestRecorder extends TestRecorder {
+       public $version;
+       private $db;
+
+       public function __construct( IDatabase $db ) {
+               $this->db = $db;
+       }
+
+       /**
+        * Set up result recording; insert a record for the run with the date
+        * and all that fun stuff
+        */
+       function start() {
+               $this->db->begin( __METHOD__ );
+
+               if ( !$this->db->tableExists( 'testrun' )
+                       || !$this->db->tableExists( 'testitem' )
+               ) {
+                       print "WARNING> `testrun` table not found in database. Trying to create table.\n";
+                       $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) );
+                       echo "OK, resuming.\n";
+               }
+
+               $this->db->insert( 'testrun',
+                       [
+                               'tr_date' => $this->db->timestamp(),
+                               'tr_mw_version' => $this->version,
+                               'tr_php_version' => PHP_VERSION,
+                               'tr_db_version' => $this->db->getServerVersion(),
+                               'tr_uname' => php_uname()
+                       ],
+                       __METHOD__ );
+               if ( $this->db->getType() === 'postgres' ) {
+                       $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' );
+               } else {
+                       $this->curRun = $this->db->insertId();
+               }
+       }
+
+       /**
+        * Record an individual test item's success or failure to the db
+        *
+        * @param array $test
+        * @param ParserTestResult $result
+        */
+       function record( $test, ParserTestResult $result ) {
+               $this->db->insert( 'testitem',
+                       [
+                               'ti_run' => $this->curRun,
+                               'ti_name' => $test['desc'],
+                               'ti_success' => $result->isSuccess() ? 1 : 0,
+                       ],
+                       __METHOD__ );
+       }
+
+       /**
+        * Commit transaction and clean up for result recording
+        */
+       function end() {
+               $this->db->commit( __METHOD__ );
+       }
+}
+
diff --git a/tests/parser/DjVuSupport.php b/tests/parser/DjVuSupport.php
new file mode 100644 (file)
index 0000000..4739be4
--- /dev/null
@@ -0,0 +1,58 @@
+<?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 Testing
+ */
+
+/**
+ * Initialize and detect the DjVu files support
+ */
+class DjVuSupport {
+
+       /**
+        * Initialises DjVu tools global with default values
+        */
+       public function __construct() {
+               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgFileExtensions, $wgDjvuTxt;
+
+               $wgDjvuRenderer = $wgDjvuRenderer ? $wgDjvuRenderer : '/usr/bin/ddjvu';
+               $wgDjvuDump = $wgDjvuDump ? $wgDjvuDump : '/usr/bin/djvudump';
+               $wgDjvuToXML = $wgDjvuToXML ? $wgDjvuToXML : '/usr/bin/djvutoxml';
+               $wgDjvuTxt = $wgDjvuTxt ? $wgDjvuTxt : '/usr/bin/djvutxt';
+
+               if ( !in_array( 'djvu', $wgFileExtensions ) ) {
+                       $wgFileExtensions[] = 'djvu';
+               }
+       }
+
+       /**
+        * Returns true if the DjVu tools are usable
+        *
+        * @return bool
+        */
+       public function isEnabled() {
+               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgDjvuTxt;
+
+               return is_executable( $wgDjvuRenderer )
+                       && is_executable( $wgDjvuDump )
+                       && is_executable( $wgDjvuToXML )
+                       && is_executable( $wgDjvuTxt );
+       }
+}
+
diff --git a/tests/parser/MultiTestRecorder.php b/tests/parser/MultiTestRecorder.php
new file mode 100644 (file)
index 0000000..5fbfecf
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * This is a TestRecorder representing a collection of other TestRecorders.
+ * It proxies calls to all constituent objects.
+ */
+class MultiTestRecorder extends TestRecorder {
+       private $recorders = [];
+
+       public function addRecorder( TestRecorder $recorder ) {
+               $this->recorders[] = $recorder;
+       }
+
+       private function proxy( $funcName, $args ) {
+               foreach ( $this->recorders as $recorder ) {
+                       call_user_func_array( [ $recorder, $funcName ], $args );
+               }
+       }
+
+       public function start() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function startTest( $test ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function startSuite( $path ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function endSuite( $path ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function record( $test, ParserTestResult $result ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function warning( $message ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function skipped( $test, $subtest ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function report() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function end() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+}
diff --git a/tests/parser/ParserTestParserHook.php b/tests/parser/ParserTestParserHook.php
new file mode 100644 (file)
index 0000000..5bf50ea
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * A basic extension that's used by the parser tests to test whether input and
+ * arguments are passed to extensions properly.
+ *
+ * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Testing
+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+class ParserTestParserHook {
+
+       static function setup( &$parser ) {
+               $parser->setHook( 'tag', [ __CLASS__, 'dumpHook' ] );
+               $parser->setHook( 'tåg', [ __CLASS__, 'dumpHook' ] );
+               $parser->setHook( 'statictag', [ __CLASS__, 'staticTagHook' ] );
+               return true;
+       }
+
+       static function dumpHook( $in, $argv ) {
+               return "<pre>\n" .
+                       var_export( $in, true ) . "\n" .
+                       var_export( $argv, true ) . "\n" .
+                       "</pre>";
+       }
+
+       static function staticTagHook( $in, $argv, $parser ) {
+               if ( !count( $argv ) ) {
+                       $parser->static_tag_buf = $in;
+                       return '';
+               } elseif ( count( $argv ) === 1 && isset( $argv['action'] )
+                       && $argv['action'] === 'flush' && $in === null
+               ) {
+                       // Clear the buffer, we probably don't need to
+                       if ( isset( $parser->static_tag_buf ) ) {
+                               $tmp = $parser->static_tag_buf;
+                       } else {
+                               $tmp = '';
+                       }
+                       $parser->static_tag_buf = null;
+                       return $tmp;
+               } else { // wtf?
+                       return
+                               "\nCall this extension as <statictag>string</statictag> or as" .
+                               " <statictag action=flush/>, not in any other way.\n" .
+                               "text: " . var_export( $in, true ) . "\n" .
+                               "argv: " . var_export( $argv, true ) . "\n";
+               }
+       }
+}
diff --git a/tests/parser/ParserTestPrinter.php b/tests/parser/ParserTestPrinter.php
new file mode 100644 (file)
index 0000000..cad3a53
--- /dev/null
@@ -0,0 +1,326 @@
+<?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 Testing
+ */
+
+/**
+ * This is a TestRecorder responsible for printing information about progress,
+ * success and failure to the console. It is specific to the parserTests.php
+ * frontend.
+ */
+class ParserTestPrinter extends TestRecorder {
+       private $total;
+       private $success;
+       private $skipped;
+       private $term;
+       private $showDiffs;
+       private $showProgress;
+       private $showFailure;
+       private $showOutput;
+       private $useDwdiff;
+       private $markWhitespace;
+       private $xmlError;
+
+       function __construct( $term, $options ) {
+               $this->term = $term;
+               $options += [
+                       'showDiffs' => true,
+                       'showProgress' => true,
+                       'showFailure' => true,
+                       'showOutput' => false,
+                       'useDwdiff' => false,
+                       'markWhitespace' => false,
+               ];
+               $this->showDiffs = $options['showDiffs'];
+               $this->showProgress = $options['showProgress'];
+               $this->showFailure = $options['showFailure'];
+               $this->showOutput = $options['showOutput'];
+               $this->useDwdiff = $options['useDwdiff'];
+               $this->markWhitespace = $options['markWhitespace'];
+       }
+
+       public function start() {
+               $this->total = 0;
+               $this->success = 0;
+               $this->skipped = 0;
+       }
+
+       public function startTest( $test ) {
+               if ( $this->showProgress ) {
+                       $this->showTesting( $test['desc'] );
+               }
+       }
+
+       private function showTesting( $desc ) {
+               print "Running test $desc... ";
+       }
+
+       /**
+        * Show "Reading tests from ..."
+        *
+        * @param string $path
+        */
+       public function startSuite( $path ) {
+               print $this->term->color( 1 ) .
+                       "Running parser tests from \"$path\"..." .
+                       $this->term->reset() .
+                       "\n";
+       }
+
+       public function endSuite( $path ) {
+               print "\n";
+       }
+
+       public function record( $test, ParserTestResult $result ) {
+               $this->total++;
+               $this->success += ( $result->isSuccess() ? 1 : 0 );
+
+               if ( $result->isSuccess() ) {
+                       $this->showSuccess( $result );
+               } else {
+                       $this->showFailure( $result );
+               }
+       }
+
+       /**
+        * Print a happy success message.
+        *
+        * @param ParserTestResult $testResult
+        * @return bool
+        */
+       private function showSuccess( ParserTestResult $testResult ) {
+               if ( $this->showProgress ) {
+                       print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
+               }
+       }
+
+       /**
+        * Print a failure message and provide some explanatory output
+        * about what went wrong if so configured.
+        *
+        * @param ParserTestResult $testResult
+        * @return bool
+        */
+       private function showFailure( ParserTestResult $testResult ) {
+               if ( $this->showFailure ) {
+                       if ( !$this->showProgress ) {
+                               # In quiet mode we didn't show the 'Testing' message before the
+                               # test, in case it succeeded. Show it now:
+                               $this->showTesting( $testResult->getDescription() );
+                       }
+
+                       print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
+
+                       if ( $this->showOutput ) {
+                               print "--- Expected ---\n{$testResult->expected}\n";
+                               print "--- Actual ---\n{$testResult->actual}\n";
+                       }
+
+                       if ( $this->showDiffs ) {
+                               print $this->quickDiff( $testResult->expected, $testResult->actual );
+                               if ( !$this->wellFormed( $testResult->actual ) ) {
+                                       print "XML error: $this->xmlError\n";
+                               }
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Run given strings through a diff and return the (colorized) output.
+        * Requires writable /tmp directory and a 'diff' command in the PATH.
+        *
+        * @param string $input
+        * @param string $output
+        * @param string $inFileTail Tailing for the input file name
+        * @param string $outFileTail Tailing for the output file name
+        * @return string
+        */
+       private function quickDiff( $input, $output,
+               $inFileTail = 'expected', $outFileTail = 'actual'
+       ) {
+               if ( $this->markWhitespace ) {
+                       $pairs = [
+                               "\n" => '¶',
+                               ' ' => '·',
+                               "\t" => '→'
+                       ];
+                       $input = strtr( $input, $pairs );
+                       $output = strtr( $output, $pairs );
+               }
+
+               # Windows, or at least the fc utility, is retarded
+               $slash = wfIsWindows() ? '\\' : '/';
+               $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
+
+               $infile = "$prefix-$inFileTail";
+               $this->dumpToFile( $input, $infile );
+
+               $outfile = "$prefix-$outFileTail";
+               $this->dumpToFile( $output, $outfile );
+
+               $shellInfile = wfEscapeShellArg( $infile );
+               $shellOutfile = wfEscapeShellArg( $outfile );
+
+               global $wgDiff3;
+               // we assume that people with diff3 also have usual diff
+               if ( $this->useDwdiff ) {
+                       $shellCommand = 'dwdiff -Pc';
+               } else {
+                       $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
+               }
+
+               $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
+
+               unlink( $infile );
+               unlink( $outfile );
+
+               if ( $this->useDwdiff ) {
+                       return $diff;
+               } else {
+                       return $this->colorDiff( $diff );
+               }
+       }
+
+       /**
+        * Write the given string to a file, adding a final newline.
+        *
+        * @param string $data
+        * @param string $filename
+        */
+       private function dumpToFile( $data, $filename ) {
+               $file = fopen( $filename, "wt" );
+               fwrite( $file, $data . "\n" );
+               fclose( $file );
+       }
+
+       /**
+        * Colorize unified diff output if set for ANSI color output.
+        * Subtractions are colored blue, additions red.
+        *
+        * @param string $text
+        * @return string
+        */
+       private function colorDiff( $text ) {
+               return preg_replace(
+                       [ '/^(-.*)$/m', '/^(\+.*)$/m' ],
+                       [ $this->term->color( 34 ) . '$1' . $this->term->reset(),
+                               $this->term->color( 31 ) . '$1' . $this->term->reset() ],
+                       $text );
+       }
+
+       private function wellFormed( $text ) {
+               $html =
+                       Sanitizer::hackDocType() .
+                               '<html>' .
+                               $text .
+                               '</html>';
+
+               $parser = xml_parser_create( "UTF-8" );
+
+               # case folding violates XML standard, turn it off
+               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+               if ( !xml_parse( $parser, $html, true ) ) {
+                       $err = xml_error_string( xml_get_error_code( $parser ) );
+                       $position = xml_get_current_byte_index( $parser );
+                       $fragment = $this->extractFragment( $html, $position );
+                       $this->xmlError = "$err at byte $position:\n$fragment";
+                       xml_parser_free( $parser );
+
+                       return false;
+               }
+
+               xml_parser_free( $parser );
+
+               return true;
+       }
+
+       private function extractFragment( $text, $position ) {
+               $start = max( 0, $position - 10 );
+               $before = $position - $start;
+               $fragment = '...' .
+                       $this->term->color( 34 ) .
+                       substr( $text, $start, $before ) .
+                       $this->term->color( 0 ) .
+                       $this->term->color( 31 ) .
+                       $this->term->color( 1 ) .
+                       substr( $text, $position, 1 ) .
+                       $this->term->color( 0 ) .
+                       $this->term->color( 34 ) .
+                       substr( $text, $position + 1, 9 ) .
+                       $this->term->color( 0 ) .
+                       '...';
+               $display = str_replace( "\n", ' ', $fragment );
+               $caret = '   ' .
+                       str_repeat( ' ', $before ) .
+                       $this->term->color( 31 ) .
+                       '^' .
+                       $this->term->color( 0 );
+
+               return "$display\n$caret";
+       }
+
+       /**
+        * Show a warning to the user
+        */
+       public function warning( $message ) {
+               echo "$message\n";
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $subtest ) {
+               if ( $this->showProgress ) {
+                       print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n";
+               }
+               $this->skipped++;
+       }
+
+       public function report() {
+               if ( $this->total > 0 ) {
+                       $this->reportPercentage( $this->success, $this->total );
+               } else {
+                       print $this->term->color( 31 ) . "No tests found." . $this->term->reset() . "\n";
+               }
+       }
+
+       private function reportPercentage( $success, $total ) {
+               $ratio = wfPercent( 100 * $success / $total );
+               print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)";
+               if ( $this->skipped ) {
+                       print ", skipped {$this->skipped}";
+               }
+               print "... ";
+
+               if ( $success == $total ) {
+                       print $this->term->color( 32 ) . "ALL TESTS PASSED!";
+               } else {
+                       $failed = $total - $success;
+                       print $this->term->color( 31 ) . "$failed tests failed!";
+               }
+
+               print $this->term->reset() . "\n";
+
+               return ( $success == $total );
+       }
+}
+
index a7b3672..6396a01 100644 (file)
  * @since 1.22
  */
 class ParserTestResult {
-       /**
-        * Description of the parser test.
-        *
-        * This is usually the text used to describe a parser test in the .txt
-        * files.  It is initialized on a construction and you most probably
-        * never want to change it.
-        */
-       public $description;
+       /** The test info array */
+       public $test;
        /** Text that was expected */
        public $expected;
        /** Actual text rendered */
        public $actual;
 
        /**
-        * @param string $description A short text describing the parser test
-        *   usually the text in the parser test .txt file.  The description
-        *   is later available using the property $description.
+        * @param array $test The test info array from TestIterator
+        * @param string $expected The normalized expected output
+        * @param string $actual The actual output
         */
-       public function __construct( $description ) {
-               $this->description = $description;
+       public function __construct( $test, $expected, $actual ) {
+               $this->test = $test;
+               $this->expected = $expected;
+               $this->actual = $actual;
        }
 
        /**
@@ -41,4 +37,8 @@ class ParserTestResult {
        public function isSuccess() {
                return $this->expected === $this->actual;
        }
+
+       public function getDescription() {
+               return $this->test['desc'];
+       }
 }
diff --git a/tests/parser/ParserTestResultNormalizer.php b/tests/parser/ParserTestResultNormalizer.php
new file mode 100644 (file)
index 0000000..a15d09e
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @file
+ * @ingroup Testing
+ */
+
+class ParserTestResultNormalizer {
+       protected $doc, $xpath, $invalid;
+
+       public static function normalize( $text, $funcs ) {
+               $norm = new self( $text );
+               if ( $norm->invalid ) {
+                       return $text;
+               }
+               foreach ( $funcs as $func ) {
+                       $norm->$func();
+               }
+               return $norm->serialize();
+       }
+
+       protected function __construct( $text ) {
+               $this->doc = new DOMDocument( '1.0', 'utf-8' );
+
+               // Note: parsing a supposedly XHTML document with an XML parser is not
+               // guaranteed to give accurate results. For example, it may introduce
+               // differences in the number of line breaks in <pre> tags.
+
+               MediaWiki\suppressWarnings();
+               if ( !$this->doc->loadXML( '<html><body>' . $text . '</body></html>' ) ) {
+                       $this->invalid = true;
+               }
+               MediaWiki\restoreWarnings();
+               $this->xpath = new DOMXPath( $this->doc );
+               $this->body = $this->xpath->query( '//body' )->item( 0 );
+       }
+
+       protected function removeTbody() {
+               foreach ( $this->xpath->query( '//tbody' ) as $tbody ) {
+                       while ( $tbody->firstChild ) {
+                               $child = $tbody->firstChild;
+                               $tbody->removeChild( $child );
+                               $tbody->parentNode->insertBefore( $child, $tbody );
+                       }
+                       $tbody->parentNode->removeChild( $tbody );
+               }
+       }
+
+       /**
+        * The point of this function is to produce a normalized DOM in which
+        * Tidy's output matches the output of html5depurate. Tidy both trims
+        * and pretty-prints, so this requires fairly aggressive treatment.
+        *
+        * In particular, note that Tidy converts <pre>x</pre> to <pre>\nx\n</pre>,
+        * which theoretically affects display since the second line break is not
+        * ignored by compliant HTML parsers.
+        *
+        * This function also removes empty elements, as does Tidy.
+        */
+       protected function trimWhitespace() {
+               foreach ( $this->xpath->query( '//text()' ) as $child ) {
+                       if ( strtolower( $child->parentNode->nodeName ) === 'pre' ) {
+                               // Just trim one line break from the start and end
+                               if ( substr_compare( $child->data, "\n", 0 ) === 0 ) {
+                                       $child->data = substr( $child->data, 1 );
+                               }
+                               if ( substr_compare( $child->data, "\n", -1 ) === 0 ) {
+                                       $child->data = substr( $child->data, 0, -1 );
+                               }
+                       } else {
+                               // Trim all whitespace
+                               $child->data = trim( $child->data );
+                       }
+                       if ( $child->data === '' ) {
+                               $child->parentNode->removeChild( $child );
+                       }
+               }
+       }
+
+       /**
+        * Serialize the XML DOM for comparison purposes. This does not generate HTML.
+        */
+       protected function serialize() {
+               return strtr( $this->doc->saveXML( $this->body ),
+                       [ '<body>' => '', '</body>' => '' ] );
+       }
+}
+
diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php
new file mode 100644 (file)
index 0000000..4ef778d
--- /dev/null
@@ -0,0 +1,1598 @@
+<?php
+/**
+ * Generic backend for the MediaWiki parser test suite, used by both the
+ * standalone parserTests.php and the PHPUnit "parsertests" suite.
+ *
+ * Copyright © 2004, 2010 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
+ *
+ * @todo Make this more independent of the configuration (and if possible the database)
+ * @file
+ * @ingroup Testing
+ */
+use MediaWiki\MediaWikiServices;
+
+/**
+ * @ingroup Testing
+ */
+class ParserTestRunner {
+       /**
+        * @var bool $useTemporaryTables Use temporary tables for the temporary database
+        */
+       private $useTemporaryTables = true;
+
+       /**
+        * @var array $setupDone The status of each setup function
+        */
+       private $setupDone = [
+               'staticSetup' => false,
+               'perTestSetup' => false,
+               'setupDatabase' => false,
+               'setDatabase' => false,
+               'setupUploads' => false,
+       ];
+
+       /**
+        * Our connection to the database
+        * @var DatabaseBase
+        */
+       private $db;
+
+       /**
+        * Database clone helper
+        * @var CloneDatabase
+        */
+       private $dbClone;
+
+       /**
+        * @var DjVuSupport
+        */
+       private $djVuSupport;
+
+       /**
+        * @var TidySupport
+        */
+       private $tidySupport;
+
+       /**
+        * @var TidyDriverBase
+        */
+       private $tidyDriver = null;
+
+       /**
+        * @var TestRecorder
+        */
+       private $recorder;
+
+       /**
+        * The upload directory, or null to not set up an upload directory
+        *
+        * @var string|null
+        */
+       private $uploadDir = null;
+
+       /**
+        * The name of the file backend to use, or null to use MockFileBackend.
+        * @var string|null
+        */
+       private $fileBackendName;
+
+       /**
+        * A complete regex for filtering tests.
+        * @var string
+        */
+       private $regex;
+
+       /**
+        * A list of normalization functions to apply to the expected and actual
+        * output.
+        * @var array
+        */
+       private $normalizationFunctions = [];
+
+       /**
+        * @param TestRecorder $recorder
+        * @param array $options
+        */
+       public function __construct( TestRecorder $recorder, $options = [] ) {
+               $this->recorder = $recorder;
+
+               if ( isset( $options['norm'] ) ) {
+                       foreach ( $options['norm'] as $func ) {
+                               if ( in_array( $func, [ 'removeTbody', 'trimWhitespace' ] ) ) {
+                                       $this->normalizationFunctions[] = $func;
+                               } else {
+                                       $this->recorder->warning(
+                                               "Warning: unknown normalization option \"$func\"\n" );
+                               }
+                       }
+               }
+
+               if ( isset( $options['regex'] ) && $options['regex'] !== false ) {
+                       $this->regex = $options['regex'];
+               } else {
+                       # Matches anything
+                       $this->regex = '//';
+               }
+
+               $this->keepUploads = !empty( $options['keep-uploads'] );
+
+               $this->fileBackendName = isset( $options['file-backend'] ) ?
+                       $options['file-backend'] : false;
+
+               $this->runDisabled = !empty( $options['run-disabled'] );
+               $this->runParsoid = !empty( $options['run-parsoid'] );
+
+               $this->djVuSupport = new DjVuSupport();
+               $this->tidySupport = new TidySupport( !empty( $options['use-tidy-config'] ) );
+               if ( !$this->tidySupport->isEnabled() ) {
+                       $this->recorder->warning(
+                               "Warning: tidy is not installed, skipping some tests\n" );
+               }
+
+               if ( isset( $options['upload-dir'] ) ) {
+                       $this->uploadDir = $options['upload-dir'];
+               }
+       }
+
+       public function getRecorder() {
+               return $this->recorder;
+       }
+
+       /**
+        * Do any setup which can be done once for all tests, independent of test
+        * options, except for database setup.
+        *
+        * Public setup functions in this class return a ScopedCallback object. When
+        * this object is destroyed by going out of scope, teardown of the
+        * corresponding test setup is performed.
+        *
+        * Teardown objects may be chained by passing a ScopedCallback from a
+        * previous setup stage as the $nextTeardown parameter. This enforces the
+        * convention that teardown actions are taken in reverse order to the
+        * corresponding setup actions. When $nextTeardown is specified, a
+        * ScopedCallback will be returned which first tears down the current
+        * setup stage, and then tears down the previous setup stage which was
+        * specified by $nextTeardown.
+        *
+        * @param ScopedCallback|null $nextTeardown
+        * @return ScopedCallback
+        */
+       public function staticSetup( $nextTeardown = null ) {
+               // A note on coding style:
+
+               // The general idea here is to keep setup code together with
+               // corresponding teardown code, in a fine-grained manner. We have two
+               // arrays: $setup and $teardown. The code snippets in the $setup array
+               // are executed at the end of the method, before it returns, and the
+               // code snippets in the $teardown array are executed in reverse order
+               // when the ScopedCallback object is consumed.
+
+               // Because it is a common operation to save, set and restore global
+               // variables, we have an additional convention: when the array key of
+               // $setup is a string, the string is taken to be the name of the global
+               // variable, and the element value is taken to be the desired new value.
+
+               // It's acceptable to just do the setup immediately, instead of adding
+               // a closure to $setup, except when the setup action depends on global
+               // variable initialisation being done first. In this case, you have to
+               // append a closure to $setup after the global variable is appended.
+
+               // When you add to setup functions in this class, please keep associated
+               // setup and teardown actions together in the source code, and please
+               // add comments explaining why the setup action is necessary.
+
+               $setup = [];
+               $teardown = [];
+
+               $teardown[] = $this->markSetupDone( 'staticSetup' );
+
+               // Some settings which influence HTML output
+               $setup['wgSitename'] = 'MediaWiki';
+               $setup['wgServer'] = 'http://example.org';
+               $setup['wgServerName'] = 'example.org';
+               $setup['wgScriptPath'] = '';
+               $setup['wgScript'] = '/index.php';
+               $setup['wgResourceBasePath'] = '';
+               $setup['wgStylePath'] = '/skins';
+               $setup['wgExtensionAssetsPath'] = '/extensions';
+               $setup['wgArticlePath'] = '/wiki/$1';
+               $setup['wgActionPaths'] = [];
+               $setup['wgVariantArticlePath'] = false;
+               $setup['wgUploadNavigationUrl'] = false;
+               $setup['wgCapitalLinks'] = true;
+               $setup['wgNoFollowLinks'] = true;
+               $setup['wgNoFollowDomainExceptions'] = [ 'no-nofollow.org' ];
+               $setup['wgExternalLinkTarget'] = false;
+               $setup['wgExperimentalHtmlIds'] = false;
+               $setup['wgLocaltimezone'] = 'UTC';
+               $setup['wgHtml5'] = true;
+               $setup['wgDisableLangConversion'] = false;
+               $setup['wgDisableTitleConversion'] = false;
+
+               // "extra language links"
+               // see https://gerrit.wikimedia.org/r/111390
+               $setup['wgExtraInterlanguageLinkPrefixes'] = [ 'mul' ];
+
+               // All FileRepo changes should be done here by injecting services,
+               // there should be no need to change global variables.
+               RepoGroup::setSingleton( $this->createRepoGroup() );
+               $teardown[] = function () {
+                       RepoGroup::destroySingleton();
+               };
+
+               // Set up null lock managers
+               $setup['wgLockManagers'] = [ [
+                       'name' => 'fsLockManager',
+                       'class' => 'NullLockManager',
+               ], [
+                       'name' => 'nullLockManager',
+                       'class' => 'NullLockManager',
+               ] ];
+               $reset = function() {
+                       LockManagerGroup::destroySingletons();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // This allows article insertion into the prefixed DB
+               $setup['wgDefaultExternalStore'] = false;
+
+               // This might slightly reduce memory usage
+               $setup['wgAdaptiveMessageCache'] = true;
+
+               // This is essential and overrides disabling of database messages in TestSetup
+               $setup['wgUseDatabaseMessages'] = true;
+               $reset = function () {
+                       MessageCache::destroyInstance();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // It's not necessary to actually convert any files
+               $setup['wgSVGConverter'] = 'null';
+               $setup['wgSVGConverters'] = [ 'null' => 'echo "1">$output' ];
+
+               // Fake constant timestamp
+               Hooks::register( 'ParserGetVariableValueTs', 'ParserTestRunner::getFakeTimestamp' );
+               $teardown[] = function () {
+                       Hooks::clear( 'ParserGetVariableValueTs' );
+               };
+
+               $this->appendNamespaceSetup( $setup, $teardown );
+
+               // Set up interwikis and append teardown function
+               $teardown[] = $this->setupInterwikis();
+
+               // This affects title normalization in links. It invalidates
+               // MediaWikiTitleCodec objects.
+               $setup['wgLocalInterwikis'] = [ 'local', 'mi' ];
+               $reset = function () {
+                       $this->resetTitleServices();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // Set up a mock MediaHandlerFactory
+               MediaWikiServices::getInstance()->disableService( 'MediaHandlerFactory' );
+               MediaWikiServices::getInstance()->redefineService(
+                       'MediaHandlerFactory',
+                       function() {
+                               return new MockMediaHandlerFactory();
+                       }
+               );
+               $teardown[] = function () {
+                       MediaWikiServices::getInstance()->resetServiceForTesting( 'MediaHandlerFactory' );
+               };
+
+               // SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
+               // It seems to have been fixed since (r55079?), but regressed at some point before r85701.
+               // This works around it for now...
+               global $wgObjectCaches;
+               $setup['wgObjectCaches'] = [ CACHE_DB => $wgObjectCaches['hash'] ] + $wgObjectCaches;
+               if ( isset( ObjectCache::$instances[CACHE_DB] ) ) {
+                       $savedCache = ObjectCache::$instances[CACHE_DB];
+                       ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+                       $teardown[] = function () use ( $savedCache ) {
+                               ObjectCache::$instances[CACHE_DB] = $savedCache;
+                       };
+               }
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               // Schedule teardown snippets in reverse order
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       private function appendNamespaceSetup( &$setup, &$teardown ) {
+               // Add a namespace shadowing a interwiki link, to test
+               // proper precedence when resolving links. (bug 51680)
+               $setup['wgExtraNamespaces'] = [
+                       100 => 'MemoryAlpha',
+                       101 => 'MemoryAlpha_talk'
+               ];
+               // Changing wgExtraNamespaces invalidates caches in MWNamespace and
+               // any live Language object, both on setup and teardown
+               $reset = function () {
+                       MWNamespace::getCanonicalNamespaces( true );
+                       $GLOBALS['wgContLang']->resetNamespaces();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+       }
+
+       /**
+        * Create a RepoGroup object appropriate for the current configuration
+        * @return RepoGroup
+        */
+       protected function createRepoGroup() {
+               if ( $this->uploadDir ) {
+                       if ( $this->fileBackendName ) {
+                               throw new MWException( 'You cannot specify both use-filebackend and upload-dir' );
+                       }
+                       $backend = new FSFileBackend( [
+                               'name' => 'local-backend',
+                               'wikiId' => wfWikiID(),
+                               'basePath' => $this->uploadDir
+                       ] );
+               } elseif ( $this->fileBackendName ) {
+                       global $wgFileBackends;
+                       $name = $this->fileBackendName;
+                       $useConfig = false;
+                       foreach ( $wgFileBackends as $conf ) {
+                               if ( $conf['name'] === $name ) {
+                                       $useConfig = $conf;
+                               }
+                       }
+                       if ( $useConfig === false ) {
+                               throw new MWException( "Unable to find file backend \"$name\"" );
+                       }
+                       $useConfig['name'] = 'local-backend'; // swap name
+                       unset( $useConfig['lockManager'] );
+                       unset( $useConfig['fileJournal'] );
+                       $class = $useConfig['class'];
+                       $backend = new $class( $useConfig );
+               } else {
+                       # Replace with a mock. We do not care about generating real
+                       # files on the filesystem, just need to expose the file
+                       # informations.
+                       $backend = new MockFileBackend( [
+                               'name' => 'local-backend',
+                               'wikiId' => wfWikiID()
+                       ] );
+               }
+
+               return new RepoGroup(
+                       [
+                               'class' => 'LocalRepo',
+                               'name' => 'local',
+                               'url' => 'http://example.com/images',
+                               'hashLevels' => 2,
+                               'transformVia404' => false,
+                               'backend' => $backend
+                       ],
+                       []
+               );
+       }
+
+       /**
+        * Execute an array in which elements with integer keys are taken to be
+        * callable objects, and other elements are taken to be global variable
+        * set operations, with the key giving the variable name and the value
+        * giving the new global variable value. A closure is returned which, when
+        * executed, sets the global variables back to the values they had before
+        * this function was called.
+        *
+        * @see staticSetup
+        *
+        * @param array $setup
+        * @return closure
+        */
+       protected function executeSetupSnippets( $setup ) {
+               $saved = [];
+               foreach ( $setup as $name => $value ) {
+                       if ( is_int( $name ) ) {
+                               $value();
+                       } else {
+                               $saved[$name] = isset( $GLOBALS[$name] ) ? $GLOBALS[$name] : null;
+                               $GLOBALS[$name] = $value;
+                       }
+               }
+               return function () use ( $saved ) {
+                       $this->executeSetupSnippets( $saved );
+               };
+       }
+
+       /**
+        * Take a setup array in the same format as the one given to
+        * executeSetupSnippets(), and return a ScopedCallback which, when consumed,
+        * executes the snippets in the setup array in reverse order. This is used
+        * to create "teardown objects" for the public API.
+        *
+        * @see staticSetup
+        *
+        * @param array $teardown The snippet array
+        * @param ScopedCallback|null A ScopedCallback to consume
+        * @return ScopedCallback
+        */
+       protected function createTeardownObject( $teardown, $nextTeardown ) {
+               return new ScopedCallback( function() use ( $teardown, $nextTeardown ) {
+                       // Schedule teardown snippets in reverse order
+                       $teardown = array_reverse( $teardown );
+
+                       $this->executeSetupSnippets( $teardown );
+                       if ( $nextTeardown ) {
+                               ScopedCallback::consume( $nextTeardown );
+                       }
+               } );
+       }
+
+       /**
+        * Set a setupDone flag to indicate that setup has been done, and return
+        * the teardown closure. If the flag was already set, throw an exception.
+        *
+        * @param string $funcName The setup function name
+        * @return closure
+        */
+       protected function markSetupDone( $funcName ) {
+               if ( $this->setupDone[$funcName] ) {
+                       throw new MWException( "$funcName is already done" );
+               }
+               $this->setupDone[$funcName] = true;
+               return function () use ( $funcName ) {
+                       wfDebug( "markSetupDone unmarked $funcName" );
+                       $this->setupDone[$funcName] = false;
+               };
+       }
+
+       /**
+        * Ensure a given setup stage has been done, throw an exception if it has
+        * not.
+        */
+       protected function checkSetupDone( $funcName, $funcName2 = null ) {
+               if ( !$this->setupDone[$funcName]
+                       && ( $funcName === null || !$this->setupDone[$funcName2] )
+               ) {
+                       throw new MWException( "$funcName must be called before calling " .
+                               wfGetCaller() );
+               }
+       }
+
+       /**
+        * Determine whether a particular setup function has been run
+        *
+        * @param string $funcName
+        * @return boolean
+        */
+       public function isSetupDone( $funcName ) {
+               return isset( $this->setupDone[$funcName] ) ? $this->setupDone[$funcName] : false;
+       }
+
+       /**
+        * Insert hardcoded interwiki in the lookup table.
+        *
+        * This function insert a set of well known interwikis that are used in
+        * the parser tests. They can be considered has fixtures are injected in
+        * the interwiki cache by using the 'InterwikiLoadPrefix' hook.
+        * Since we are not interested in looking up interwikis in the database,
+        * the hook completely replace the existing mechanism (hook returns false).
+        *
+        * @return closure for teardown
+        */
+       private function setupInterwikis() {
+               # Hack: insert a few Wikipedia in-project interwiki prefixes,
+               # for testing inter-language links
+               Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
+                       static $testInterwikis = [
+                               'local' => [
+                                       'iw_url' => 'http://doesnt.matter.org/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'wikipedia' => [
+                                       'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'meatball' => [
+                                       'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'memoryalpha' => [
+                                       'iw_url' => 'http://www.memory-alpha.org/en/index.php/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'zh' => [
+                                       'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'es' => [
+                                       'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'fr' => [
+                                       'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'ru' => [
+                                       'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'mi' => [
+                                       'iw_url' => 'http://mi.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'mul' => [
+                                       'iw_url' => 'http://wikisource.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                       ];
+                       if ( array_key_exists( $prefix, $testInterwikis ) ) {
+                               $iwData = $testInterwikis[$prefix];
+                       }
+
+                       // We only want to rely on the above fixtures
+                       return false;
+               } );// hooks::register
+
+               return function () {
+                       // Tear down
+                       Hooks::clear( 'InterwikiLoadPrefix' );
+               };
+       }
+
+       /**
+        * Reset the Title-related services that need resetting
+        * for each test
+        */
+       private function resetTitleServices() {
+               $services = MediaWikiServices::getInstance();
+               $services->resetServiceForTesting( 'TitleFormatter' );
+               $services->resetServiceForTesting( 'TitleParser' );
+               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
+               $services->resetServiceForTesting( 'LinkRenderer' );
+               $services->resetServiceForTesting( 'LinkRendererFactory' );
+       }
+
+       /**
+        * Remove last character if it is a newline
+        * @group utility
+        * @param string $s
+        * @return string
+        */
+       public static function chomp( $s ) {
+               if ( substr( $s, -1 ) === "\n" ) {
+                       return substr( $s, 0, -1 );
+               } else {
+                       return $s;
+               }
+       }
+
+       /**
+        * Run a series of tests listed in the given text files.
+        * Each test consists of a brief description, wikitext input,
+        * and the expected HTML output.
+        *
+        * Prints status updates on stdout and counts up the total
+        * number and percentage of passed tests.
+        *
+        * Handles all setup and teardown.
+        *
+        * @param array $filenames Array of strings
+        * @return bool True if passed all tests, false if any tests failed.
+        */
+       public function runTestsFromFiles( $filenames ) {
+               $ok = false;
+
+               $teardownGuard = $this->staticSetup();
+               $teardownGuard = $this->setupDatabase( $teardownGuard );
+               $teardownGuard = $this->setupUploads( $teardownGuard );
+
+               $this->recorder->start();
+               try {
+                       $ok = true;
+
+                       foreach ( $filenames as $filename ) {
+                               $testFileInfo = TestFileReader::read( $filename, [
+                                       'runDisabled' => $this->runDisabled,
+                                       'runParsoid' => $this->runParsoid,
+                                       'regex' => $this->regex ] );
+
+                               // Don't start the suite if there are no enabled tests in the file
+                               if ( !$testFileInfo['tests'] ) {
+                                       continue;
+                               }
+
+                               $this->recorder->startSuite( $filename );
+                               $ok = $this->runTests( $testFileInfo ) && $ok;
+                               $this->recorder->endSuite( $filename );
+                       }
+
+                       $this->recorder->report();
+               } catch ( DBError $e ) {
+                       $this->recorder->warning( $e->getMessage() );
+               }
+               $this->recorder->end();
+
+               ScopedCallback::consume( $teardownGuard );
+
+               return $ok;
+       }
+
+       /**
+        * Determine whether the current parser has the hooks registered in it
+        * that are required by a file read by TestFileReader.
+        */
+       public function meetsRequirements( $requirements ) {
+               foreach ( $requirements as $requirement ) {
+                       switch ( $requirement['type'] ) {
+                       case 'hook':
+                               $ok = $this->requireHook( $requirement['name'] );
+                               break;
+                       case 'functionHook':
+                               $ok = $this->requireFunctionHook( $requirement['name'] );
+                               break;
+                       case 'transparentHook':
+                               $ok = $this->requireTransparentHook( $requirement['name'] );
+                               break;
+                       }
+                       if ( !$ok ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Run the tests from a single file. staticSetup() and setupDatabase()
+        * must have been called already.
+        *
+        * @param array $testFileInfo Parsed file info returned by TestFileReader
+        * @return bool True if passed all tests, false if any tests failed.
+        */
+       public function runTests( $testFileInfo ) {
+               $ok = true;
+
+               $this->checkSetupDone( 'staticSetup' );
+
+               // Don't add articles from the file if there are no enabled tests from the file
+               if ( !$testFileInfo['tests'] ) {
+                       return true;
+               }
+
+               // If any requirements are not met, mark all tests from the file as skipped
+               if ( !$this->meetsRequirements( $testFileInfo['requirements'] ) ) {
+                       foreach ( $testFileInfo['tests'] as $test ) {
+                               $this->recorder->startTest( $test );
+                               $this->recorder->skipped( $test, 'required extension not enabled' );
+                       }
+                       return true;
+               }
+
+               // Add articles
+               $this->addArticles( $testFileInfo['articles'] );
+
+               // Run tests
+               foreach ( $testFileInfo['tests'] as $test ) {
+                       $this->recorder->startTest( $test );
+                       $result =
+                               $this->runTest( $test );
+                       if ( $result !== false ) {
+                               $ok = $ok && $result->isSuccess();
+                               $this->recorder->record( $test, $result );
+                       }
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Get a Parser object
+        *
+        * @param string $preprocessor
+        * @return Parser
+        */
+       function getParser( $preprocessor = null ) {
+               global $wgParserConf;
+
+               $class = $wgParserConf['class'];
+               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
+               ParserTestParserHook::setup( $parser );
+
+               return $parser;
+       }
+
+       /**
+        * Run a given wikitext input through a freshly-constructed wiki parser,
+        * and compare the output against the expected results.
+        * Prints status and explanatory messages to stdout.
+        *
+        * staticSetup() and setupWikiData() must be called before this function
+        * is entered.
+        *
+        * @param array $test The test parameters:
+        *  - test: The test name
+        *  - desc: The subtest description
+        *  - input: Wikitext to try rendering
+        *  - options: Array of test options
+        *  - config: Overrides for global variables, one per line
+        *
+        * @return ParserTestResult or false if skipped
+        */
+       public function runTest( $test ) {
+               wfDebug( __METHOD__.": running {$test['desc']}" );
+               $opts = $this->parseOptions( $test['options'] );
+               $teardownGuard = $this->perTestSetup( $test );
+
+               $context = RequestContext::getMain();
+               $user = $context->getUser();
+               $options = ParserOptions::newFromContext( $context );
+
+               if ( isset( $opts['djvu'] ) ) {
+                       if ( !$this->djVuSupport->isEnabled() ) {
+                               $this->recorder->skipped( $test,
+                                       'djvu binaries do not exist or are not executable' );
+                               return false;
+                       }
+               }
+
+               if ( isset( $opts['tidy'] ) ) {
+                       if ( !$this->tidySupport->isEnabled() ) {
+                               $this->recorder->skipped( $test, 'tidy extension is not installed' );
+                               return false;
+                       } else {
+                               $options->setTidy( true );
+                       }
+               }
+
+               if ( isset( $opts['title'] ) ) {
+                       $titleText = $opts['title'];
+               } else {
+                       $titleText = 'Parser test';
+               }
+
+               $local = isset( $opts['local'] );
+               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
+               $parser = $this->getParser( $preprocessor );
+               $title = Title::newFromText( $titleText );
+
+               if ( isset( $opts['pst'] ) ) {
+                       $out = $parser->preSaveTransform( $test['input'], $title, $user, $options );
+               } elseif ( isset( $opts['msg'] ) ) {
+                       $out = $parser->transformMsg( $test['input'], $options, $title );
+               } elseif ( isset( $opts['section'] ) ) {
+                       $section = $opts['section'];
+                       $out = $parser->getSection( $test['input'], $section );
+               } elseif ( isset( $opts['replace'] ) ) {
+                       $section = $opts['replace'][0];
+                       $replace = $opts['replace'][1];
+                       $out = $parser->replaceSection( $test['input'], $section, $replace );
+               } elseif ( isset( $opts['comment'] ) ) {
+                       $out = Linker::formatComment( $test['input'], $title, $local );
+               } elseif ( isset( $opts['preload'] ) ) {
+                       $out = $parser->getPreloadText( $test['input'], $title, $options );
+               } else {
+                       $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 );
+                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
+                       $out = $output->getText();
+                       if ( isset( $opts['tidy'] ) ) {
+                               $out = preg_replace( '/\s+$/', '', $out );
+                       }
+
+                       if ( isset( $opts['showtitle'] ) ) {
+                               if ( $output->getTitleText() ) {
+                                       $title = $output->getTitleText();
+                               }
+
+                               $out = "$title\n$out";
+                       }
+
+                       if ( isset( $opts['showindicators'] ) ) {
+                               $indicators = '';
+                               foreach ( $output->getIndicators() as $id => $content ) {
+                                       $indicators .= "$id=$content\n";
+                               }
+                               $out = $indicators . $out;
+                       }
+
+                       if ( isset( $opts['ill'] ) ) {
+                               $out = implode( ' ', $output->getLanguageLinks() );
+                       } elseif ( isset( $opts['cat'] ) ) {
+                               $out = '';
+                               foreach ( $output->getCategories() as $name => $sortkey ) {
+                                       if ( $out !== '' ) {
+                                               $out .= "\n";
+                                       }
+                                       $out .= "cat=$name sort=$sortkey";
+                               }
+                       }
+               }
+
+               ScopedCallback::consume( $teardownGuard );
+
+               $expected = $test['result'];
+               if ( count( $this->normalizationFunctions ) ) {
+                       $expected = ParserTestResultNormalizer::normalize(
+                               $test['expected'], $this->normalizationFunctions );
+                       $out = ParserTestResultNormalizer::normalize( $out, $this->normalizationFunctions );
+               }
+
+               $testResult = new ParserTestResult( $test, $expected, $out );
+               return $testResult;
+       }
+
+       /**
+        * Use a regex to find out the value of an option
+        * @param string $key Name of option val to retrieve
+        * @param array $opts Options array to look in
+        * @param mixed $default Default value returned if not found
+        * @return mixed
+        */
+       private static function getOptionValue( $key, $opts, $default ) {
+               $key = strtolower( $key );
+
+               if ( isset( $opts[$key] ) ) {
+                       return $opts[$key];
+               } else {
+                       return $default;
+               }
+       }
+
+       /**
+        * Given the options string, return an associative array of options.
+        * @todo Move this to TestFileReader
+        *
+        * @param string $instring
+        * @return array
+        */
+       private function parseOptions( $instring ) {
+               $opts = [];
+               // foo
+               // foo=bar
+               // foo="bar baz"
+               // foo=[[bar baz]]
+               // foo=bar,"baz quux"
+               // foo={...json...}
+               $defs = '(?(DEFINE)
+                       (?<qstr>                                        # Quoted string
+                               "
+                               (?:[^\\\\"] | \\\\.)*
+                               "
+                       )
+                       (?<json>
+                               \{              # Open bracket
+                               (?:
+                                       [^"{}] |                                # Not a quoted string or object, or
+                                       (?&qstr) |                              # A quoted string, or
+                                       (?&json)                                # A json object (recursively)
+                               )*
+                               \}              # Close bracket
+                       )
+                       (?<value>
+                               (?:
+                                       (?&qstr)                        # Quoted val
+                               |
+                                       \[\[
+                                               [^]]*                   # Link target
+                                       \]\]
+                               |
+                                       [\w-]+                          # Plain word
+                               |
+                                       (?&json)                        # JSON object
+                               )
+                       )
+               )';
+               $regex = '/' . $defs . '\b
+                       (?<k>[\w-]+)                            # Key
+                       \b
+                       (?:\s*
+                               =                                               # First sub-value
+                               \s*
+                               (?<v>
+                                       (?&value)
+                                       (?:\s*
+                                               ,                               # Sub-vals 1..N
+                                               \s*
+                                               (?&value)
+                                       )*
+                               )
+                       )?
+                       /x';
+               $valueregex = '/' . $defs . '(?&value)/x';
+
+               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
+                       foreach ( $matches as $bits ) {
+                               $key = strtolower( $bits['k'] );
+                               if ( !isset( $bits['v'] ) ) {
+                                       $opts[$key] = true;
+                               } else {
+                                       preg_match_all( $valueregex, $bits['v'], $vmatches );
+                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $vmatches[0] );
+                                       if ( count( $opts[$key] ) == 1 ) {
+                                               $opts[$key] = $opts[$key][0];
+                                       }
+                               }
+                       }
+               }
+               return $opts;
+       }
+
+       private function cleanupOption( $opt ) {
+               if ( substr( $opt, 0, 1 ) == '"' ) {
+                       return stripcslashes( substr( $opt, 1, -1 ) );
+               }
+
+               if ( substr( $opt, 0, 2 ) == '[[' ) {
+                       return substr( $opt, 2, -2 );
+               }
+
+               if ( substr( $opt, 0, 1 ) == '{' ) {
+                       return FormatJson::decode( $opt, true );
+               }
+               return $opt;
+       }
+
+       /**
+        * Do any required setup which is dependent on test options.
+        *
+        * @see staticSetup() for more information about setup/teardown
+        *
+        * @param array $test Test info supplied by TestFileReader
+        * @param callable|null $nextTeardown
+        * @return ScopedCallback
+        */
+       public function perTestSetup( $test, $nextTeardown = null ) {
+               $teardown = [];
+
+               $this->checkSetupDone( 'setupDatabase', 'setDatabase' );
+               $teardown[] = $this->markSetupDone( 'perTestSetup' );
+
+               $opts = $this->parseOptions( $test['options'] );
+               $config = $test['config'];
+
+               // Find out values for some special options.
+               $langCode =
+                       self::getOptionValue( 'language', $opts, 'en' );
+               $variant =
+                       self::getOptionValue( 'variant', $opts, false );
+               $maxtoclevel =
+                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
+               $linkHolderBatchSize =
+                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
+
+               $setup = [
+                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
+                       'wgLanguageCode' => $langCode,
+                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
+                       'wgNamespacesWithSubpages' => [ 0 => isset( $opts['subpage'] ) ],
+                       'wgMaxTocLevel' => $maxtoclevel,
+                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
+                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
+                       'wgDefaultLanguageVariant' => $variant,
+                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
+                       // Set as a JSON object like:
+                       // wgEnableMagicLinks={"ISBN":false, "PMID":false, "RFC":false}
+                       'wgEnableMagicLinks' => self::getOptionValue( 'wgEnableMagicLinks', $opts, [] )
+                               + [ 'ISBN' => true, 'PMID' => true, 'RFC' => true ],
+               ];
+
+               if ( $config ) {
+                       $configLines = explode( "\n", $config );
+
+                       foreach ( $configLines as $line ) {
+                               list( $var, $value )  = explode( '=', $line, 2 );
+                               $setup[$var] = eval( "return $value;" );
+                       }
+               }
+
+               /** @since 1.20 */
+               Hooks::run( 'ParserTestGlobals', [ &$setup ] );
+
+               // Create tidy driver
+               if ( isset( $opts['tidy'] ) ) {
+                       // Cache a driver instance
+                       if ( $this->tidyDriver === null ) {
+                               $this->tidyDriver = MWTidy::factory( $this->tidySupport->getConfig() );
+                       }
+                       $tidy = $this->tidyDriver;
+               } else {
+                       $tidy = false;
+               }
+               MWTidy::setInstance( $tidy );
+               $teardown[] = function () {
+                       MWTidy::destroySingleton();
+               };
+
+               // Set content language. This invalidates the magic word cache and title services
+               wfDebug( "Setting up language $langCode" );
+               $lang = Language::factory( $langCode );
+               $setup['wgContLang'] = $lang;
+               $reset = function () {
+                       MagicWord::clearCache();
+                       $this->resetTitleServices();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // Make a user object with the same language
+               $user = new User;
+               $user->setOption( 'language', $langCode );
+               $setup['wgLang'] = $lang;
+
+               // We (re)set $wgThumbLimits to a single-element array above.
+               $user->setOption( 'thumbsize', 0 );
+
+               $setup['wgUser'] = $user;
+
+               // And put both user and language into the context
+               $context = RequestContext::getMain();
+               $context->setUser( $user );
+               $context->setLanguage( $lang );
+               $teardown[] = function () use ( $context ) {
+                       // Reset context to the restored globals
+                       $context->setUser( $GLOBALS['wgUser'] );
+                       $context->setLanguage( $GLOBALS['wgContLang'] );
+               };
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * List of temporary tables to create, without prefix.
+        * Some of these probably aren't necessary.
+        * @return array
+        */
+       private function listTables() {
+               $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
+                       'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
+                       'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
+                       'site_stats', 'ipblocks', 'image', 'oldimage',
+                       'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
+                       'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
+                       'archive', 'user_groups', 'page_props', 'category'
+               ];
+
+               if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite', 'oracle' ] ) ) {
+                       array_push( $tables, 'searchindex' );
+               }
+
+               // Allow extensions to add to the list of tables to duplicate;
+               // may be necessary if they hook into page save or other code
+               // which will require them while running tests.
+               Hooks::run( 'ParserTestTables', [ &$tables ] );
+
+               return $tables;
+       }
+
+       public function setDatabase( IDatabase $db ) {
+               $this->db = $db;
+               $this->setupDone['setDatabase'] = true;
+       }
+
+       /**
+        * Set up temporary DB tables.
+        *
+        * For best performance, call this once only for all tests. However, it can
+        * be called at the start of each test if more isolation is desired.
+        *
+        * @todo: This is basically an unrefactored copy of
+        * MediaWikiTestCase::setupAllTestDBs. They should be factored out somehow.
+        *
+        * Do not call this function from a MediaWikiTestCase subclass, since
+        * MediaWikiTestCase does its own DB setup. Instead use setDatabase().
+        *
+        * @see staticSetup() for more information about setup/teardown
+        *
+        * @param ScopedCallback|null $nextTeardown The next teardown object
+        * @return ScopedCallback The teardown object
+        */
+       public function setupDatabase( $nextTeardown = null ) {
+               global $wgDBprefix;
+
+               $this->db = wfGetDB( DB_MASTER );
+               $dbType = $this->db->getType();
+
+               if ( $dbType == 'oracle' ) {
+                       $suspiciousPrefixes = [ 'pt_', MediaWikiTestCase::ORA_DB_PREFIX ];
+               } else {
+                       $suspiciousPrefixes = [ 'parsertest_', MediaWikiTestCase::DB_PREFIX ];
+               }
+               if ( in_array( $wgDBprefix, $suspiciousPrefixes ) ) {
+                       throw new MWException( "\$wgDBprefix=$wgDBprefix suggests DB setup is already done" );
+               }
+
+               $teardown = [];
+
+               $teardown[] = $this->markSetupDone( 'setupDatabase' );
+
+               # CREATE TEMPORARY TABLE breaks if there is more than one server
+               if ( wfGetLB()->getServerCount() != 1 ) {
+                       $this->useTemporaryTables = false;
+               }
+
+               $temporary = $this->useTemporaryTables || $dbType == 'postgres';
+               $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
+
+               $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
+               $this->dbClone->useTemporaryTables( $temporary );
+               $this->dbClone->cloneTableStructure();
+
+               if ( $dbType == 'oracle' ) {
+                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+                       # Insert 0 user to prevent FK violations
+
+                       # Anonymous user
+                       $this->db->insert( 'user', [
+                               'user_id' => 0,
+                               'user_name' => 'Anonymous' ] );
+               }
+
+               $teardown[] = function () {
+                       $this->teardownDatabase();
+               };
+
+               // Wipe some DB query result caches on setup and teardown
+               $reset = function () {
+                       LinkCache::singleton()->clear();
+
+                       // Clear the message cache
+                       MessageCache::singleton()->clear();
+               };
+               $reset();
+               $teardown[] = $reset;
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * Add data about uploads to the new test DB, and set up the upload
+        * directory. This should be called after either setDatabase() or
+        * setupDatabase().
+        *
+        * @param ScopedCallback|null $nextTeardown The next teardown object
+        * @return ScopedCallback The teardown object
+        */
+       public function setupUploads( $nextTeardown = null ) {
+               $teardown = [];
+
+               $this->checkSetupDone( 'setupDatabase', 'setDatabase' );
+               $teardown[] = $this->markSetupDone( 'setupUploads' );
+
+               // Create the files in the upload directory (or pretend to create them
+               // in a MockFileBackend). Append teardown callback.
+               $teardown[] = $this->setupUploadBackend();
+
+               // Create a user
+               $user = User::createNew( 'WikiSysop' );
+
+               // Register the uploads in the database
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+               # note that the size/width/height/bits/etc of the file
+               # are actually set by inspecting the file itself; the arguments
+               # to recordUpload2 have no effect.  That said, we try to make things
+               # match up so it is less confusing to readers of the code & tests.
+               $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', [
+                       'size' => 7881,
+                       'width' => 1941,
+                       'height' => 220,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
+               # again, note that size/width/height below are ignored; see above.
+               $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', [
+                       'size' => 22589,
+                       'width' => 135,
+                       'height' => 135,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/png',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20130225203040' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
+                               'size'        => 12345,
+                               'width'       => 240,
+                               'height'      => 180,
+                               'bits'        => 0,
+                               'media_type'  => MEDIATYPE_DRAWING,
+                               'mime'        => 'image/svg+xml',
+                               'metadata'    => serialize( [] ),
+                               'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
+                               'fileExists'  => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               # This image will be blacklisted in [[MediaWiki:Bad image list]]
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
+               $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', [
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 24,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
+               $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_VIDEO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+               $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
+                       'size' => 12345,
+                       'width' => 0,
+                       'height' => 0,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_AUDIO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               # A DjVu file
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
+               $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
+                       'size' => 3249,
+                       'width' => 2480,
+                       'height' => 3508,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/vnd.djvu',
+                       'metadata' => '<?xml version="1.0" ?>
+<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
+<DjVuXML>
+<HEAD></HEAD>
+<BODY><OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+</BODY>
+</DjVuXML>',
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123600' ), $user );
+
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * Helper for database teardown, called from the teardown closure. Destroy
+        * the database clone and fix up some things that CloneDatabase doesn't fix.
+        *
+        * @todo Move most things here to CloneDatabase
+        */
+       private function teardownDatabase() {
+               $this->checkSetupDone( 'setupDatabase' );
+
+               $this->dbClone->destroy();
+               $this->databaseSetupDone = false;
+
+               if ( $this->useTemporaryTables ) {
+                       if ( $this->db->getType() == 'sqlite' ) {
+                               # Under SQLite the searchindex table is virtual and need
+                               # to be explicitly destroyed. See bug 29912
+                               # See also MediaWikiTestCase::destroyDB()
+                               wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
+                               $this->db->query( "DROP TABLE `parsertest_searchindex`" );
+                       }
+                       # Don't need to do anything
+                       return;
+               }
+
+               $tables = $this->listTables();
+
+               foreach ( $tables as $table ) {
+                       if ( $this->db->getType() == 'oracle' ) {
+                               $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
+                       } else {
+                               $this->db->query( "DROP TABLE `parsertest_$table`" );
+                       }
+               }
+
+               if ( $this->db->getType() == 'oracle' ) {
+                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+               }
+       }
+
+       /**
+        * Upload test files to the backend created by createRepoGroup().
+        *
+        * @return callable The teardown callback
+        */
+       private function setupUploadBackend() {
+               global $IP;
+
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $base = $repo->getZonePath( 'public' );
+               $backend = $repo->getBackend();
+               $backend->prepare( [ 'dir' => "$base/3/3a" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
+                       'dst' => "$base/3/3a/Foobar.jpg"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/e/ea" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/wiki.png",
+                       'dst' => "$base/e/ea/Thumb.png"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/0/09" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
+                       'dst' => "$base/0/09/Bad.jpg"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/5/5f" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/LoremIpsum.djvu",
+                       'dst' => "$base/5/5f/LoremIpsum.djvu"
+               ] );
+
+               // No helpful SVG file to copy, so make one ourselves
+               $data = '<?xml version="1.0" encoding="utf-8"?>' .
+                       '<svg xmlns="http://www.w3.org/2000/svg"' .
+                       ' version="1.1" width="240" height="180"/>';
+
+               $backend->prepare( [ 'dir' => "$base/f/ff" ] );
+               $backend->quickCreate( [
+                       'content' => $data, 'dst' => "$base/f/ff/Foobar.svg"
+               ] );
+
+               return function () use ( $backend ) {
+                       if ( $backend instanceof MockFileBackend ) {
+                               // In memory backend, so dont bother cleaning them up.
+                               return;
+                       }
+                       $this->teardownUploadBackend();
+               };
+       }
+
+       /**
+        * Remove the dummy uploads directory
+        */
+       private function teardownUploadBackend() {
+               if ( $this->keepUploads ) {
+                       return;
+               }
+
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $public = $repo->getZonePath( 'public' );
+
+               $this->deleteFiles(
+                       [
+                               "$public/3/3a/Foobar.jpg",
+                               "$public/e/ea/Thumb.png",
+                               "$public/0/09/Bad.jpg",
+                               "$public/5/5f/LoremIpsum.djvu",
+                               "$public/f/ff/Foobar.svg",
+                               "$public/0/00/Video.ogv",
+                               "$public/4/41/Audio.oga",
+                       ]
+               );
+       }
+
+       /**
+        * Delete the specified files and their parent directories
+        * @param array $files File backend URIs mwstore://...
+        */
+       private function deleteFiles( $files ) {
+               // Delete the files
+               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
+               foreach ( $files as $file ) {
+                       $backend->delete( [ 'src' => $file ], [ 'force' => 1 ] );
+               }
+
+               // Delete the parent directories
+               foreach ( $files as $file ) {
+                       $tmp = FileBackend::parentStoragePath( $file );
+                       while ( $tmp ) {
+                               if ( !$backend->clean( [ 'dir' => $tmp ] )->isOK() ) {
+                                       break;
+                               }
+                               $tmp = FileBackend::parentStoragePath( $tmp );
+                       }
+               }
+       }
+
+       /**
+        * Add articles to the test DB.
+        *
+        * @param $articles Article info array from TestFileReader
+        */
+       public function addArticles( $articles ) {
+               global $wgContLang;
+               $setup = [];
+               $teardown = [];
+
+               // Be sure ParserTestRunner::addArticle has correct language set,
+               // so that system messages get into the right language cache
+               if ( $wgContLang->getCode() !== 'en' ) {
+                       $setup['wgLanguageCode'] = 'en';
+                       $setup['wgContLang'] = Language::factory( 'en' );
+               }
+
+               // Add special namespaces, in case that hasn't been done by staticSetup() yet
+               $this->appendNamespaceSetup( $setup, $teardown );
+
+               // wgCapitalLinks obviously needs initialisation
+               $setup['wgCapitalLinks'] = true;
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               foreach ( $articles as $info ) {
+                       $this->addArticle( $info['name'], $info['text'], $info['file'], $info['line'] );
+               }
+
+               // Wipe WANObjectCache process cache, which is invalidated by article insertion
+               // due to T144706
+               ObjectCache::getMainWANInstance()->clearProcessCache();
+
+               $this->executeSetupSnippets( $teardown );
+       }
+
+       /**
+        * Insert a temporary test article
+        * @param string $name The title, including any prefix
+        * @param string $text The article text
+        * @param string $file The input file name
+        * @param int|string $line The input line number, for reporting errors
+        * @throws Exception
+        * @throws MWException
+        */
+       private function addArticle( $name, $text, $file, $line ) {
+               $text = self::chomp( $text );
+               $name = self::chomp( $name );
+
+               $title = Title::newFromText( $name );
+               wfDebug( __METHOD__ . ": adding $name" );
+
+               if ( is_null( $title ) ) {
+                       throw new MWException( "invalid title '$name' at $file:$line\n" );
+               }
+
+               $page = WikiPage::factory( $title );
+               $page->loadPageData( 'fromdbmaster' );
+
+               if ( $page->exists() ) {
+                       throw new MWException( "duplicate article '$name' at $file:$line\n" );
+               }
+
+               $status = $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
+               if ( !$status->isOK() ) {
+                       throw new MWException( $status->getWikiText( false, false, 'en' ) );
+               }
+
+               // The RepoGroup cache is invalidated by the creation of file redirects
+               if ( $title->getNamespace() === NS_IMAGE ) {
+                       RepoGroup::singleton()->clearCache( $title );
+               }
+       }
+
+       /**
+        * Check if a hook is installed
+        *
+        * @param string $name
+        * @return bool True if tag hook is present
+        */
+       public function requireHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+               if ( isset( $wgParser->mTagHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' hook " .
+                               "extension, skipping." );
+                       return false;
+               }
+       }
+
+       /**
+        * Check if a function hook is installed
+        *
+        * @param string $name
+        * @return bool True if function hook is present
+        */
+       public function requireFunctionHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+               if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' function " .
+                               "hook extension, skipping." );
+                       return false;
+               }
+       }
+
+       /**
+        * Check if a transparent tag hook is installed
+        *
+        * @param string $name
+        * @return bool True if function hook is present
+        */
+       public function requireTransparentHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+               if ( isset( $wgParser->mTransparentTagHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' transparent " .
+                               "hook extension, skipping.\n" );
+                       return false;
+               }
+       }
+
+       /**
+        * The ParserGetVariableValueTs hook, used to make sure time-related parser
+        * functions give a persistent value.
+        */
+       static function getFakeTimestamp( &$parser, &$ts ) {
+               $ts = 123; // parsed as '1970-01-01T00:02:03Z'
+               return true;
+       }
+}
diff --git a/tests/parser/PhpunitTestRecorder.php b/tests/parser/PhpunitTestRecorder.php
new file mode 100644 (file)
index 0000000..238d018
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+class PhpunitTestRecorder extends TestRecorder {
+       private $testCase;
+
+       public function setTestCase( PHPUnit_Framework_TestCase $testCase ) {
+               $this->testCase = $testCase;
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $reason ) {
+               $this->testCase->markTestSkipped( "SKIPPED: $reason" );
+       }
+}
index 8b41337..f1a82ee 100644 (file)
@@ -1,8 +1,12 @@
-Parser tests are run using our PHPUnit test suite in tests/phpunit:
+Parser tests can be run either via PHPUnit or by using the standalone
+parserTests.php in this directory. The standalone version provides more
+options.
+
+To run parser tests via PHPUnit:
 
  $ cd tests/phpunit
- ./phpunit.php --group Parser
+ ./phpunit.php --testsuite parsertests
 
-You can optionally filter by title using --regex. I.e. :
+You can optionally filter by title using --filter, e.g.
 
- ./phpunit.php --group Parser --regex="Bug 6200"
+ ./phpunit.php --testsuite parsertests --filter="Bug 6200"
diff --git a/tests/parser/TestFileReader.php b/tests/parser/TestFileReader.php
new file mode 100644 (file)
index 0000000..a1a8d19
--- /dev/null
@@ -0,0 +1,289 @@
+<?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 Testing
+ */
+
+class TestFileReader {
+       private $file;
+       private $fh;
+       private $section = null;
+       /** String|null: current test section being analyzed */
+       private $sectionData = [];
+       private $lineNum = 0;
+       private $runDisabled;
+       private $runParsoid;
+       private $regex;
+
+       private $articles = [];
+       private $requirements = [];
+       private $tests = [];
+
+       public static function read( $file, array $options = [] ) {
+               $reader = new self( $file, $options );
+               $reader->execute();
+
+               $requirements = [];
+               foreach ( $reader->requirements as $type => $reqsOfType ) {
+                       foreach ( $reqsOfType as $name => $unused ) {
+                               $requirements[] = [
+                                       'type' => $type,
+                                       'name' => $name
+                               ];
+                       }
+               }
+
+               return [
+                       'requirements' => $requirements,
+                       'tests' => $reader->tests,
+                       'articles' => $reader->articles
+               ];
+       }
+
+       private function __construct( $file, $options ) {
+               $this->file = $file;
+               $this->fh = fopen( $this->file, "rt" );
+
+               if ( !$this->fh ) {
+                       throw new MWException( "Couldn't open file '$file'\n" );
+               }
+
+               $options = $options + [
+                       'runDisabled' => false,
+                       'runParsoid' => false,
+                       'regex' => '//',
+               ];
+               $this->runDisabled = $options['runDisabled'];
+               $this->runParsoid = $options['runParsoid'];
+               $this->regex = $options['regex'];
+       }
+
+       private function addCurrentTest() {
+               // "input" and "result" are old section names allowed
+               // for backwards-compatibility.
+               $input = $this->checkSection( [ 'wikitext', 'input' ], false );
+               $result = $this->checkSection( [ 'html/php', 'html/*', 'html', 'result' ], false );
+               // Some tests have "with tidy" and "without tidy" variants
+               $tidy = $this->checkSection( [ 'html/php+tidy', 'html+tidy' ], false );
+
+               if ( !isset( $this->sectionData['options'] ) ) {
+                       $this->sectionData['options'] = '';
+               }
+
+               if ( !isset( $this->sectionData['config'] ) ) {
+                       $this->sectionData['config'] = '';
+               }
+
+               $isDisabled = preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) &&
+                       !$this->runDisabled;
+               $isParsoidOnly = preg_match( '/\\bparsoid\\b/i', $this->sectionData['options'] ) &&
+                       $result == 'html' &&
+                       !$this->runParsoid;
+               $isFiltered = !preg_match( $this->regex, $this->sectionData['test'] );
+               if ( $input == false || $result == false || $isDisabled || $isParsoidOnly || $isFiltered ) {
+                       // Disabled test
+                       return;
+               }
+
+               $test = [
+                       'test' => ParserTestRunner::chomp( $this->sectionData['test'] ),
+                       'input' => ParserTestRunner::chomp( $this->sectionData[$input] ),
+                       'result' => ParserTestRunner::chomp( $this->sectionData[$result] ),
+                       'options' => ParserTestRunner::chomp( $this->sectionData['options'] ),
+                       'config' => ParserTestRunner::chomp( $this->sectionData['config'] ),
+               ];
+               $test['desc'] = $test['test'];
+               $this->tests[] = $test;
+
+               if ( $tidy !== false ) {
+                       $test['options'] .= " tidy";
+                       $test['desc'] .= ' (with tidy)';
+                       $test['result'] = ParserTestRunner::chomp( $this->sectionData[$tidy] );
+                       $this->tests[] = $test;
+               }
+       }
+
+       private function execute() {
+               while ( false !== ( $line = fgets( $this->fh ) ) ) {
+                       $this->lineNum++;
+                       $matches = [];
+
+                       if ( preg_match( '/^!!\s*(\S+)/', $line, $matches ) ) {
+                               $this->section = strtolower( $matches[1] );
+
+                               if ( $this->section == 'endarticle' ) {
+                                       $this->checkSection( 'text' );
+                                       $this->checkSection( 'article' );
+
+                                       $this->addArticle(
+                                               ParserTestRunner::chomp( $this->sectionData['article'] ),
+                                               $this->sectionData['text'], $this->lineNum );
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endhooks' ) {
+                                       $this->checkSection( 'hooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'hook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endfunctionhooks' ) {
+                                       $this->checkSection( 'functionhooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'functionHook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endtransparenthooks' ) {
+                                       $this->checkSection( 'transparenthooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['transparenthooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'transparentHook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'end' ) {
+                                       $this->checkSection( 'test' );
+                                       $this->addCurrentTest();
+                                       $this->clearSection();
+                                       continue;
+                               }
+
+                               if ( isset( $this->sectionData[$this->section] ) ) {
+                                       throw new MWException( "duplicate section '$this->section' "
+                                               . "at line {$this->lineNum} of $this->file\n" );
+                               }
+
+                               $this->sectionData[$this->section] = '';
+
+                               continue;
+                       }
+
+                       if ( $this->section ) {
+                               $this->sectionData[$this->section] .= $line;
+                       }
+               }
+       }
+
+       /**
+        * Clear section name and its data
+        */
+       private function clearSection() {
+               $this->sectionData = [];
+               $this->section = null;
+
+       }
+
+       /**
+        * Verify the current section data has some value for the given token
+        * name(s) (first parameter).
+        * Throw an exception if it is not set, referencing current section
+        * and adding the current file name and line number
+        *
+        * @param string|array $tokens Expected token(s) that should have been
+        * mentioned before closing this section
+        * @param bool $fatal True iff an exception should be thrown if
+        * the section is not found.
+        * @return bool|string
+        * @throws MWException
+        */
+       private function checkSection( $tokens, $fatal = true ) {
+               if ( is_null( $this->section ) ) {
+                       throw new MWException( __METHOD__ . " can not verify a null section!\n" );
+               }
+               if ( !is_array( $tokens ) ) {
+                       $tokens = [ $tokens ];
+               }
+               if ( count( $tokens ) == 0 ) {
+                       throw new MWException( __METHOD__ . " can not verify zero sections!\n" );
+               }
+
+               $data = $this->sectionData;
+               $tokens = array_filter( $tokens, function ( $token ) use ( $data ) {
+                       return isset( $data[$token] );
+               } );
+
+               if ( count( $tokens ) == 0 ) {
+                       if ( !$fatal ) {
+                               return false;
+                       }
+                       throw new MWException( sprintf(
+                               "'%s' without '%s' at line %s of %s\n",
+                               $this->section,
+                               implode( ',', $tokens ),
+                               $this->lineNum,
+                               $this->file
+                       ) );
+               }
+               if ( count( $tokens ) > 1 ) {
+                       throw new MWException( sprintf(
+                               "'%s' with unexpected tokens '%s' at line %s of %s\n",
+                               $this->section,
+                               implode( ',', $tokens ),
+                               $this->lineNum,
+                               $this->file
+                       ) );
+               }
+
+               return array_values( $tokens )[0];
+       }
+
+       private function addArticle( $name, $text, $line ) {
+               $this->articles[] = [
+                       'name' => $name,
+                       'text' => $text,
+                       'line' => $line,
+                       'file' => $this->file
+               ];
+       }
+
+       private function addRequirement( $type, $name ) {
+               $this->requirements[$type][$name] = true;
+       }
+}
+
diff --git a/tests/parser/TestRecorder.php b/tests/parser/TestRecorder.php
new file mode 100644 (file)
index 0000000..70215b6
--- /dev/null
@@ -0,0 +1,94 @@
+<?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 Testing
+ */
+
+/**
+ * Interface to record parser test results.
+ *
+ * The TestRecorder is an class hierarchy to record the result of
+ * MediaWiki parser tests. One should call start() before running the
+ * full parser tests and end() once all the tests have been finished.
+ * After each test, you should use record() to keep track of your tests
+ * results. Finally, report() is used to generate a summary of your
+ * test run, one could dump it to the console for human consumption or
+ * register the result in a database for tracking purposes.
+ *
+ * @since 1.22
+ */
+abstract class TestRecorder {
+
+       /**
+        * Called at beginning of the parser test run
+        */
+       public function start() {
+       }
+
+       /**
+        * Called before starting a test
+        */
+       public function startTest( $test ) {
+       }
+
+       /**
+        * Called before starting an input file
+        */
+       public function startSuite( $path ) {
+       }
+
+       /**
+        * Called after ending an input file
+        */
+       public function endSuite( $path ) {
+       }
+
+       /**
+        * Called after each test
+        * @param array $test
+        * @param ParserTestResult $result
+        */
+       public function record( $test, ParserTestResult $result ) {
+       }
+
+       /**
+        * Show a warning to the user
+        */
+       public function warning( $message ) {
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $subtest ) {
+       }
+
+       /**
+        * Called before finishing the test run
+        */
+       public function report() {
+       }
+
+       /**
+        * Called at the end of the parser test run
+        */
+       public function end() {
+       }
+
+}
+
diff --git a/tests/parser/TidySupport.php b/tests/parser/TidySupport.php
new file mode 100644 (file)
index 0000000..6b5fb48
--- /dev/null
@@ -0,0 +1,95 @@
+<?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 Testing
+ */
+
+/**
+ * Initialize and detect the tidy support
+ */
+class TidySupport {
+       private $enabled;
+       private $config;
+
+       /**
+        * Determine if there is a usable tidy.
+        */
+       public function __construct( $useConfiguration = false ) {
+               global $IP, $wgUseTidy, $wgTidyBin, $wgTidyInternal, $wgTidyConfig,
+                       $wgTidyConf, $wgTidyOpts;
+
+               $this->enabled = true;
+               if ( $useConfiguration ) {
+                       if ( $wgTidyConfig !== null ) {
+                               $this->config = $wgTidyConfig;
+                       } elseif ( $wgUseTidy ) {
+                               $this->config = [
+                                       'tidyConfigFile' => $wgTidyConf,
+                                       'debugComment' => false,
+                                       'tidyBin' => $wgTidyBin,
+                                       'tidyCommandLine' => $wgTidyOpts
+                               ];
+                               if ( $wgTidyInternal ) {
+                                       $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
+                               } else {
+                                       $this->config['driver'] = 'RaggettExternal';
+                               }
+                       } else {
+                               $this->enabled = false;
+                       }
+               } else {
+                       $this->config = [
+                               'tidyConfigFile' => "$IP/includes/tidy/tidy.conf",
+                               'tidyCommandLine' => '',
+                       ];
+                       if ( extension_loaded( 'tidy' ) && class_exists( 'tidy' ) ) {
+                               $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
+                       } else {
+                               if ( is_executable( $wgTidyBin ) ) {
+                                       $this->config['driver'] = 'RaggettExternal';
+                                       $this->config['tidyBin'] = $wgTidyBin;
+                               } else {
+                                       $path = Installer::locateExecutableInDefaultPaths( $wgTidyBin );
+                                       if ( $path !== false ) {
+                                               $this->config['driver'] = 'RaggettExternal';
+                                               $this->config['tidyBin'] = $wgTidyBin;
+                                       } else {
+                                               $this->enabled = false;
+                                       }
+                               }
+                       }
+               }
+               if ( !$this->enabled ) {
+                       $this->config = [ 'driver' => 'disabled' ];
+               }
+       }
+
+       /**
+        * Returns true if tidy is usable
+        *
+        * @return bool
+        */
+       public function isEnabled() {
+               return $this->enabled;
+       }
+
+       public function getConfig() {
+               return $this->config;
+       }
+}
diff --git a/tests/parser/fuzzTest.php b/tests/parser/fuzzTest.php
new file mode 100644 (file)
index 0000000..7437053
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+require __DIR__ . '/../../maintenance/Maintenance.php';
+
+// Make RequestContext::resetMain() happy
+define( 'MW_PARSER_TEST', 1 );
+
+class ParserFuzzTest extends Maintenance {
+       private $parserTest;
+       private $maxFuzzTestLength = 300;
+       private $memoryLimit = 100;
+       private $seed;
+
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Run a fuzz test on the parser, until it segfaults ' .
+                       'or throws an exception' );
+               $this->addOption( 'file', 'Use the specified file as a dictionary, ' .
+                       ' or leave blank to use parserTests.txt', false, true, true );
+
+               $this->addOption( 'seed', 'Start the fuzz test from the specified seed', false, true );
+       }
+
+       function finalSetup() {
+               self::requireTestsAutoloader();
+               TestSetup::applyInitialConfig();
+       }
+
+       function execute() {
+               $files = $this->getOption( 'file', [ __DIR__ . '/parserTests.txt' ] );
+               $this->seed = intval( $this->getOption( 'seed', 1 ) ) - 1;
+               $this->parserTest = new ParserTestRunner(
+                       new MultiTestRecorder,
+                       [] );
+               $this->fuzzTest( $files );
+       }
+
+       /**
+        * Run a fuzz test series
+        * Draw input from a set of test files
+        * @param array $filenames
+        */
+       function fuzzTest( $filenames ) {
+               $dict = $this->getFuzzInput( $filenames );
+               $dictSize = strlen( $dict );
+               $logMaxLength = log( $this->maxFuzzTestLength );
+
+               $teardown = $this->parserTest->staticSetup();
+               $teardown = $this->parserTest->setupDatabase( $teardown );
+               $teardown = $this->parserTest->setupUploads( $teardown );
+
+               $fakeTest = [
+                       'test' => '',
+                       'desc' => '',
+                       'input' => '',
+                       'result' => '',
+                       'options' => '',
+                       'config' => ''
+               ];
+
+               ini_set( 'memory_limit', $this->memoryLimit * 1048576 * 2 );
+
+               $numTotal = 0;
+               $numSuccess = 0;
+               $user = new User;
+               $opts = ParserOptions::newFromUser( $user );
+               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
+
+               while ( true ) {
+                       // Generate test input
+                       mt_srand( ++$this->seed );
+                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
+                       $input = '';
+
+                       while ( strlen( $input ) < $totalLength ) {
+                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
+                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
+                               $offset = mt_rand( 0, $dictSize - $hairLength );
+                               $input .= substr( $dict, $offset, $hairLength );
+                       }
+
+                       $perTestTeardown = $this->parserTest->perTestSetup( $fakeTest );
+                       $parser = $this->parserTest->getParser();
+
+                       // Run the test
+                       try {
+                               $parser->parse( $input, $title, $opts );
+                               $fail = false;
+                       } catch ( Exception $exception ) {
+                               $fail = true;
+                       }
+
+                       if ( $fail ) {
+                               echo "Test failed with seed {$this->seed}\n";
+                               echo "Input:\n";
+                               printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
+                               echo "$exception\n";
+                       } else {
+                               $numSuccess++;
+                       }
+
+                       $numTotal++;
+                       ScopedCallback::consume( $perTestTeardown );
+
+                       if ( $numTotal % 100 == 0 ) {
+                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
+                               echo "{$this->seed}: $numSuccess/$numTotal (mem: $usage%)\n";
+                               if ( $usage >= 100 ) {
+                                       echo "Out of memory:\n";
+                                       $memStats = $this->getMemoryBreakdown();
+
+                                       foreach ( $memStats as $name => $usage ) {
+                                               echo "$name: $usage\n";
+                                       }
+                                       if ( function_exists( 'hphpd_break' ) ) {
+                                               hphpd_break();
+                                       }
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get a memory usage breakdown
+        * @return array
+        */
+       function getMemoryBreakdown() {
+               $memStats = [];
+
+               foreach ( $GLOBALS as $name => $value ) {
+                       $memStats['$' . $name] = $this->guessVarSize( $value );
+               }
+
+               $classes = get_declared_classes();
+
+               foreach ( $classes as $class ) {
+                       $rc = new ReflectionClass( $class );
+                       $props = $rc->getStaticProperties();
+                       $memStats[$class] = $this->guessVarSize( $props );
+                       $methods = $rc->getMethods();
+
+                       foreach ( $methods as $method ) {
+                               $memStats[$class] += $this->guessVarSize( $method->getStaticVariables() );
+                       }
+               }
+
+               $functions = get_defined_functions();
+
+               foreach ( $functions['user'] as $function ) {
+                       $rf = new ReflectionFunction( $function );
+                       $memStats["$function()"] = $this->guessVarSize( $rf->getStaticVariables() );
+               }
+
+               asort( $memStats );
+
+               return $memStats;
+       }
+
+       /**
+        * Estimate the size of the input variable
+        */
+       function guessVarSize( $var ) {
+               $length = 0;
+               try {
+                       MediaWiki\suppressWarnings();
+                       $length = strlen( serialize( $var ) );
+                       MediaWiki\restoreWarnings();
+               } catch ( Exception $e ) {
+               }
+               return $length;
+       }
+
+       /**
+        * Get an input dictionary from a set of parser test files
+        * @param array $filenames
+        * @return string
+        */
+       function getFuzzInput( $filenames ) {
+               $dict = '';
+
+               foreach ( $filenames as $filename ) {
+                       $contents = file_get_contents( $filename );
+                       preg_match_all(
+                               '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s',
+                               $contents,
+                               $matches
+                       );
+
+                       foreach ( $matches[1] as $match ) {
+                               $dict .= $match . "\n";
+                       }
+               }
+
+               return $dict;
+       }
+}
+
+$maintClass = 'ParserFuzzTest';
+require RUN_MAINTENANCE_IF_MAIN;
diff --git a/tests/parser/parserTest.inc b/tests/parser/parserTest.inc
deleted file mode 100644 (file)
index e965e2d..0000000
+++ /dev/null
@@ -1,1815 +0,0 @@
-<?php
-/**
- * Helper code for the MediaWiki parser test suite. Some code is duplicated
- * in PHPUnit's NewParserTests.php, so you'll probably want to update both
- * at the same time.
- *
- * Copyright © 2004, 2010 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
- *
- * @todo Make this more independent of the configuration (and if possible the database)
- * @todo document
- * @file
- * @ingroup Testing
- */
-use MediaWiki\MediaWikiServices;
-
-/**
- * @ingroup Testing
- */
-class ParserTest {
-       /**
-        * @var bool $color whereas output should be colorized
-        */
-       private $color;
-
-       /**
-        * @var bool $showOutput Show test output
-        */
-       private $showOutput;
-
-       /**
-        * @var bool $useTemporaryTables Use temporary tables for the temporary database
-        */
-       private $useTemporaryTables = true;
-
-       /**
-        * @var bool $databaseSetupDone True if the database has been set up
-        */
-       private $databaseSetupDone = false;
-
-       /**
-        * Our connection to the database
-        * @var DatabaseBase
-        */
-       private $db;
-
-       /**
-        * Database clone helper
-        * @var CloneDatabase
-        */
-       private $dbClone;
-
-       /**
-        * @var DjVuSupport
-        */
-       private $djVuSupport;
-
-       /**
-        * @var TidySupport
-        */
-       private $tidySupport;
-
-       /**
-        * @var ITestRecorder
-        */
-       private $recorder;
-
-       private $maxFuzzTestLength = 300;
-       private $fuzzSeed = 0;
-       private $memoryLimit = 50;
-       private $uploadDir = null;
-
-       public $regex = "";
-       private $savedGlobals = [];
-       private $useDwdiff = false;
-       private $markWhitespace = false;
-       private $normalizationFunctions = [];
-
-       /**
-        * Sets terminal colorization and diff/quick modes depending on OS and
-        * command-line options (--color and --quick).
-        * @param array $options
-        */
-       public function __construct( $options = [] ) {
-               # Only colorize output if stdout is a terminal.
-               $this->color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
-
-               if ( isset( $options['color'] ) ) {
-                       switch ( $options['color'] ) {
-                               case 'no':
-                                       $this->color = false;
-                                       break;
-                               case 'yes':
-                               default:
-                                       $this->color = true;
-                                       break;
-                       }
-               }
-
-               $this->term = $this->color
-                       ? new AnsiTermColorer()
-                       : new DummyTermColorer();
-
-               $this->showDiffs = !isset( $options['quick'] );
-               $this->showProgress = !isset( $options['quiet'] );
-               $this->showFailure = !(
-                       isset( $options['quiet'] )
-                               && ( isset( $options['record'] )
-                               || isset( $options['compare'] ) ) ); // redundant output
-
-               $this->showOutput = isset( $options['show-output'] );
-               $this->useDwdiff = isset( $options['dwdiff'] );
-               $this->markWhitespace = isset( $options['mark-ws'] );
-
-               if ( isset( $options['norm'] ) ) {
-                       foreach ( explode( ',', $options['norm'] ) as $func ) {
-                               if ( in_array( $func, [ 'removeTbody', 'trimWhitespace' ] ) ) {
-                                       $this->normalizationFunctions[] = $func;
-                               } else {
-                                       echo "Warning: unknown normalization option \"$func\"\n";
-                               }
-                       }
-               }
-
-               if ( isset( $options['filter'] ) ) {
-                       $options['regex'] = $options['filter'];
-               }
-
-               if ( isset( $options['regex'] ) ) {
-                       if ( isset( $options['record'] ) ) {
-                               echo "Warning: --record cannot be used with --regex, disabling --record\n";
-                               unset( $options['record'] );
-                       }
-                       $this->regex = $options['regex'];
-               } else {
-                       # Matches anything
-                       $this->regex = '';
-               }
-
-               $this->setupRecorder( $options );
-               $this->keepUploads = isset( $options['keep-uploads'] );
-
-               if ( $this->keepUploads ) {
-                       $this->uploadDir = wfTempDir() . '/mwParser-images';
-               } else {
-                       $this->uploadDir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
-               }
-
-               if ( isset( $options['seed'] ) ) {
-                       $this->fuzzSeed = intval( $options['seed'] ) - 1;
-               }
-
-               $this->runDisabled = isset( $options['run-disabled'] );
-               $this->runParsoid = isset( $options['run-parsoid'] );
-
-               $this->djVuSupport = new DjVuSupport();
-               $this->tidySupport = new TidySupport( isset( $options['use-tidy-config'] ) );
-               if ( !$this->tidySupport->isEnabled() ) {
-                       echo "Warning: tidy is not installed, skipping some tests\n";
-               }
-
-               $this->hooks = [];
-               $this->functionHooks = [];
-               $this->transparentHooks = [];
-               $this->setUp();
-       }
-
-       function setUp() {
-               global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc,
-                       $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory,
-                       $wgExtraNamespaces, $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
-                       $wgExtraInterlanguageLinkPrefixes, $wgLocalInterwikis,
-                       $parserMemc, $wgThumbnailScriptPath, $wgScriptPath, $wgResourceBasePath,
-                       $wgArticlePath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
-                       $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
-
-               $wgScriptPath = '';
-               $wgScript = '/index.php';
-               $wgStylePath = '/skins';
-               $wgResourceBasePath = '';
-               $wgExtensionAssetsPath = '/extensions';
-               $wgArticlePath = '/wiki/$1';
-               $wgThumbnailScriptPath = false;
-               $wgLockManagers = [ [
-                       'name' => 'fsLockManager',
-                       'class' => 'FSLockManager',
-                       'lockDirectory' => $this->uploadDir . '/lockdir',
-               ], [
-                       'name' => 'nullLockManager',
-                       'class' => 'NullLockManager',
-               ] ];
-               $wgLocalFileRepo = [
-                       'class' => 'LocalRepo',
-                       'name' => 'local',
-                       'url' => 'http://example.com/images',
-                       'hashLevels' => 2,
-                       'transformVia404' => false,
-                       'backend' => new FSFileBackend( [
-                               'name' => 'local-backend',
-                               'wikiId' => wfWikiID(),
-                               'containerPaths' => [
-                                       'local-public' => $this->uploadDir . '/public',
-                                       'local-thumb' => $this->uploadDir . '/thumb',
-                                       'local-temp' => $this->uploadDir . '/temp',
-                                       'local-deleted' => $this->uploadDir . '/deleted',
-                               ]
-                       ] )
-               ];
-               $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
-               $wgNamespaceAliases['Image'] = NS_FILE;
-               $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
-               # add a namespace shadowing a interwiki link, to test
-               # proper precedence when resolving links. (bug 51680)
-               $wgExtraNamespaces[100] = 'MemoryAlpha';
-               $wgExtraNamespaces[101] = 'MemoryAlpha talk';
-
-               // XXX: tests won't run without this (for CACHE_DB)
-               if ( $wgMainCacheType === CACHE_DB ) {
-                       $wgMainCacheType = CACHE_NONE;
-               }
-               if ( $wgMessageCacheType === CACHE_DB ) {
-                       $wgMessageCacheType = CACHE_NONE;
-               }
-               if ( $wgParserCacheType === CACHE_DB ) {
-                       $wgParserCacheType = CACHE_NONE;
-               }
-
-               DeferredUpdates::clearPendingUpdates();
-               $wgMemc = wfGetMainCache(); // checks $wgMainCacheType
-               $messageMemc = wfGetMessageCacheStorage();
-               $parserMemc = wfGetParserCacheStorage();
-
-               RequestContext::resetMain();
-               $context = new RequestContext;
-               $wgUser = new User;
-               $wgLang = $context->getLanguage();
-               $wgOut = $context->getOutput();
-               $wgRequest = $context->getRequest();
-               $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], [ $wgParserConf ] );
-
-               if ( $wgStyleDirectory === false ) {
-                       $wgStyleDirectory = "$IP/skins";
-               }
-
-               self::setupInterwikis();
-               $wgLocalInterwikis = [ 'local', 'mi' ];
-               // "extra language links"
-               // see https://gerrit.wikimedia.org/r/111390
-               array_push( $wgExtraInterlanguageLinkPrefixes, 'mul' );
-
-               // Reset namespace cache
-               MWNamespace::getCanonicalNamespaces( true );
-               Language::factory( 'en' )->resetNamespaces();
-       }
-
-       /**
-        * Insert hardcoded interwiki in the lookup table.
-        *
-        * This function insert a set of well known interwikis that are used in
-        * the parser tests. They can be considered has fixtures are injected in
-        * the interwiki cache by using the 'InterwikiLoadPrefix' hook.
-        * Since we are not interested in looking up interwikis in the database,
-        * the hook completely replace the existing mechanism (hook returns false).
-        */
-       public static function setupInterwikis() {
-               # Hack: insert a few Wikipedia in-project interwiki prefixes,
-               # for testing inter-language links
-               Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
-                       static $testInterwikis = [
-                               'local' => [
-                                       'iw_url' => 'http://doesnt.matter.org/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'wikipedia' => [
-                                       'iw_url' => 'http://en.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'meatball' => [
-                                       'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'memoryalpha' => [
-                                       'iw_url' => 'http://www.memory-alpha.org/en/index.php/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'zh' => [
-                                       'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'es' => [
-                                       'iw_url' => 'http://es.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'fr' => [
-                                       'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'ru' => [
-                                       'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'mi' => [
-                                       'iw_url' => 'http://mi.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'mul' => [
-                                       'iw_url' => 'http://wikisource.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                       ];
-                       if ( array_key_exists( $prefix, $testInterwikis ) ) {
-                               $iwData = $testInterwikis[$prefix];
-                       }
-
-                       // We only want to rely on the above fixtures
-                       return false;
-               } );// hooks::register
-       }
-
-       /**
-        * Remove the hardcoded interwiki lookup table.
-        */
-       public static function tearDownInterwikis() {
-               Hooks::clear( 'InterwikiLoadPrefix' );
-       }
-
-       /**
-        * Reset the Title-related services that need resetting
-        * for each test
-        */
-       public static function resetTitleServices() {
-               $services = MediaWikiServices::getInstance();
-               $services->resetServiceForTesting( 'TitleFormatter' );
-               $services->resetServiceForTesting( 'TitleParser' );
-               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
-               $services->resetServiceForTesting( 'LinkRenderer' );
-               $services->resetServiceForTesting( 'LinkRendererFactory' );
-       }
-
-       public function setupRecorder( $options ) {
-               if ( isset( $options['record'] ) ) {
-                       $this->recorder = new DbTestRecorder( $this );
-                       $this->recorder->version = isset( $options['setversion'] ) ?
-                               $options['setversion'] : SpecialVersion::getVersion();
-               } elseif ( isset( $options['compare'] ) ) {
-                       $this->recorder = new DbTestPreviewer( $this );
-               } else {
-                       $this->recorder = new TestRecorder( $this );
-               }
-       }
-
-       /**
-        * Remove last character if it is a newline
-        * @group utility
-        * @param string $s
-        * @return string
-        */
-       public static function chomp( $s ) {
-               if ( substr( $s, -1 ) === "\n" ) {
-                       return substr( $s, 0, -1 );
-               } else {
-                       return $s;
-               }
-       }
-
-       /**
-        * Run a fuzz test series
-        * Draw input from a set of test files
-        * @param array $filenames
-        */
-       function fuzzTest( $filenames ) {
-               $GLOBALS['wgContLang'] = Language::factory( 'en' );
-               $dict = $this->getFuzzInput( $filenames );
-               $dictSize = strlen( $dict );
-               $logMaxLength = log( $this->maxFuzzTestLength );
-               $this->setupDatabase();
-               ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
-
-               $numTotal = 0;
-               $numSuccess = 0;
-               $user = new User;
-               $opts = ParserOptions::newFromUser( $user );
-               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
-
-               while ( true ) {
-                       // Generate test input
-                       mt_srand( ++$this->fuzzSeed );
-                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
-                       $input = '';
-
-                       while ( strlen( $input ) < $totalLength ) {
-                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
-                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
-                               $offset = mt_rand( 0, $dictSize - $hairLength );
-                               $input .= substr( $dict, $offset, $hairLength );
-                       }
-
-                       $this->setupGlobals();
-                       $parser = $this->getParser();
-
-                       // Run the test
-                       try {
-                               $parser->parse( $input, $title, $opts );
-                               $fail = false;
-                       } catch ( Exception $exception ) {
-                               $fail = true;
-                       }
-
-                       if ( $fail ) {
-                               echo "Test failed with seed {$this->fuzzSeed}\n";
-                               echo "Input:\n";
-                               printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
-                               echo "$exception\n";
-                       } else {
-                               $numSuccess++;
-                       }
-
-                       $numTotal++;
-                       $this->teardownGlobals();
-                       $parser->__destruct();
-
-                       if ( $numTotal % 100 == 0 ) {
-                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
-                               echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
-                               if ( $usage > 90 ) {
-                                       echo "Out of memory:\n";
-                                       $memStats = $this->getMemoryBreakdown();
-
-                                       foreach ( $memStats as $name => $usage ) {
-                                               echo "$name: $usage\n";
-                                       }
-                                       $this->abort();
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get an input dictionary from a set of parser test files
-        * @param array $filenames
-        * @return string
-        */
-       function getFuzzInput( $filenames ) {
-               $dict = '';
-
-               foreach ( $filenames as $filename ) {
-                       $contents = file_get_contents( $filename );
-                       preg_match_all(
-                               '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s',
-                               $contents,
-                               $matches
-                       );
-
-                       foreach ( $matches[1] as $match ) {
-                               $dict .= $match . "\n";
-                       }
-               }
-
-               return $dict;
-       }
-
-       /**
-        * Get a memory usage breakdown
-        * @return array
-        */
-       function getMemoryBreakdown() {
-               $memStats = [];
-
-               foreach ( $GLOBALS as $name => $value ) {
-                       $memStats['$' . $name] = strlen( serialize( $value ) );
-               }
-
-               $classes = get_declared_classes();
-
-               foreach ( $classes as $class ) {
-                       $rc = new ReflectionClass( $class );
-                       $props = $rc->getStaticProperties();
-                       $memStats[$class] = strlen( serialize( $props ) );
-                       $methods = $rc->getMethods();
-
-                       foreach ( $methods as $method ) {
-                               $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
-                       }
-               }
-
-               $functions = get_defined_functions();
-
-               foreach ( $functions['user'] as $function ) {
-                       $rf = new ReflectionFunction( $function );
-                       $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
-               }
-
-               asort( $memStats );
-
-               return $memStats;
-       }
-
-       function abort() {
-               $this->abort();
-       }
-
-       /**
-        * Run a series of tests listed in the given text files.
-        * Each test consists of a brief description, wikitext input,
-        * and the expected HTML output.
-        *
-        * Prints status updates on stdout and counts up the total
-        * number and percentage of passed tests.
-        *
-        * @param array $filenames Array of strings
-        * @return bool True if passed all tests, false if any tests failed.
-        */
-       public function runTestsFromFiles( $filenames ) {
-               $ok = false;
-
-               // be sure, ParserTest::addArticle has correct language set,
-               // so that system messages gets into the right language cache
-               $GLOBALS['wgLanguageCode'] = 'en';
-               $GLOBALS['wgContLang'] = Language::factory( 'en' );
-
-               $this->recorder->start();
-               try {
-                       $this->setupDatabase();
-                       $ok = true;
-
-                       foreach ( $filenames as $filename ) {
-                               echo "Running parser tests from: $filename\n";
-                               $tests = new TestFileIterator( $filename, $this );
-                               $ok = $this->runTests( $tests ) && $ok;
-                       }
-
-                       $this->teardownDatabase();
-                       $this->recorder->report();
-               } catch ( DBError $e ) {
-                       echo $e->getMessage();
-               }
-               $this->recorder->end();
-
-               return $ok;
-       }
-
-       function runTests( $tests ) {
-               $ok = true;
-
-               foreach ( $tests as $t ) {
-                       $result =
-                               $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] );
-                       $ok = $ok && $result;
-                       $this->recorder->record( $t['test'], $t['subtest'], $result );
-               }
-
-               if ( $this->showProgress ) {
-                       print "\n";
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get a Parser object
-        *
-        * @param string $preprocessor
-        * @return Parser
-        */
-       function getParser( $preprocessor = null ) {
-               global $wgParserConf;
-
-               $class = $wgParserConf['class'];
-               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
-
-               foreach ( $this->hooks as $tag => $callback ) {
-                       $parser->setHook( $tag, $callback );
-               }
-
-               foreach ( $this->functionHooks as $tag => $bits ) {
-                       list( $callback, $flags ) = $bits;
-                       $parser->setFunctionHook( $tag, $callback, $flags );
-               }
-
-               foreach ( $this->transparentHooks as $tag => $callback ) {
-                       $parser->setTransparentTagHook( $tag, $callback );
-               }
-
-               Hooks::run( 'ParserTestParser', [ &$parser ] );
-
-               return $parser;
-       }
-
-       /**
-        * Run a given wikitext input through a freshly-constructed wiki parser,
-        * and compare the output against the expected results.
-        * Prints status and explanatory messages to stdout.
-        *
-        * @param string $desc Test's description
-        * @param string $input Wikitext to try rendering
-        * @param string $result Result to output
-        * @param array $opts Test's options
-        * @param string $config Overrides for global variables, one per line
-        * @return bool
-        */
-       public function runTest( $desc, $input, $result, $opts, $config ) {
-               if ( $this->showProgress ) {
-                       $this->showTesting( $desc );
-               }
-
-               $opts = $this->parseOptions( $opts );
-               $context = $this->setupGlobals( $opts, $config );
-
-               $user = $context->getUser();
-               $options = ParserOptions::newFromContext( $context );
-
-               if ( isset( $opts['djvu'] ) ) {
-                       if ( !$this->djVuSupport->isEnabled() ) {
-                               return $this->showSkipped();
-                       }
-               }
-
-               if ( isset( $opts['tidy'] ) ) {
-                       if ( !$this->tidySupport->isEnabled() ) {
-                               return $this->showSkipped();
-                       } else {
-                               $options->setTidy( true );
-                       }
-               }
-
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
-               }
-
-               ObjectCache::getMainWANInstance()->clearProcessCache();
-               $local = isset( $opts['local'] );
-               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
-               $parser = $this->getParser( $preprocessor );
-               $title = Title::newFromText( $titleText );
-
-               if ( isset( $opts['pst'] ) ) {
-                       $out = $parser->preSaveTransform( $input, $title, $user, $options );
-               } elseif ( isset( $opts['msg'] ) ) {
-                       $out = $parser->transformMsg( $input, $options, $title );
-               } elseif ( isset( $opts['section'] ) ) {
-                       $section = $opts['section'];
-                       $out = $parser->getSection( $input, $section );
-               } elseif ( isset( $opts['replace'] ) ) {
-                       $section = $opts['replace'][0];
-                       $replace = $opts['replace'][1];
-                       $out = $parser->replaceSection( $input, $section, $replace );
-               } elseif ( isset( $opts['comment'] ) ) {
-                       $out = Linker::formatComment( $input, $title, $local );
-               } elseif ( isset( $opts['preload'] ) ) {
-                       $out = $parser->getPreloadText( $input, $title, $options );
-               } else {
-                       $output = $parser->parse( $input, $title, $options, true, true, 1337 );
-                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
-                       $out = $output->getText();
-                       if ( isset( $opts['tidy'] ) ) {
-                               $out = preg_replace( '/\s+$/', '', $out );
-                       }
-
-                       if ( isset( $opts['showtitle'] ) ) {
-                               if ( $output->getTitleText() ) {
-                                       $title = $output->getTitleText();
-                               }
-
-                               $out = "$title\n$out";
-                       }
-
-                       if ( isset( $opts['showindicators'] ) ) {
-                               $indicators = '';
-                               foreach ( $output->getIndicators() as $id => $content ) {
-                                       $indicators .= "$id=$content\n";
-                               }
-                               $out = $indicators . $out;
-                       }
-
-                       if ( isset( $opts['ill'] ) ) {
-                               $out = implode( ' ', $output->getLanguageLinks() );
-                       } elseif ( isset( $opts['cat'] ) ) {
-                               $outputPage = $context->getOutput();
-                               $outputPage->addCategoryLinks( $output->getCategories() );
-                               $cats = $outputPage->getCategoryLinks();
-
-                               if ( isset( $cats['normal'] ) ) {
-                                       $out = implode( ' ', $cats['normal'] );
-                               } else {
-                                       $out = '';
-                               }
-                       }
-               }
-
-               $this->teardownGlobals();
-
-               if ( count( $this->normalizationFunctions ) ) {
-                       $result = ParserTestResultNormalizer::normalize( $result, $this->normalizationFunctions );
-                       $out = ParserTestResultNormalizer::normalize( $out, $this->normalizationFunctions );
-               }
-
-               $testResult = new ParserTestResult( $desc );
-               $testResult->expected = $result;
-               $testResult->actual = $out;
-
-               return $this->showTestResult( $testResult );
-       }
-
-       /**
-        * Refactored in 1.22 to use ParserTestResult
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       function showTestResult( ParserTestResult $testResult ) {
-               if ( $testResult->isSuccess() ) {
-                       $this->showSuccess( $testResult );
-                       return true;
-               } else {
-                       $this->showFailure( $testResult );
-                       return false;
-               }
-       }
-
-       /**
-        * Use a regex to find out the value of an option
-        * @param string $key Name of option val to retrieve
-        * @param array $opts Options array to look in
-        * @param mixed $default Default value returned if not found
-        * @return mixed
-        */
-       private static function getOptionValue( $key, $opts, $default ) {
-               $key = strtolower( $key );
-
-               if ( isset( $opts[$key] ) ) {
-                       return $opts[$key];
-               } else {
-                       return $default;
-               }
-       }
-
-       private function parseOptions( $instring ) {
-               $opts = [];
-               // foo
-               // foo=bar
-               // foo="bar baz"
-               // foo=[[bar baz]]
-               // foo=bar,"baz quux"
-               // foo={...json...}
-               $defs = '(?(DEFINE)
-                       (?<qstr>                                        # Quoted string
-                               "
-                               (?:[^\\\\"] | \\\\.)*
-                               "
-                       )
-                       (?<json>
-                               \{              # Open bracket
-                               (?:
-                                       [^"{}] |                                # Not a quoted string or object, or
-                                       (?&qstr) |                              # A quoted string, or
-                                       (?&json)                                # A json object (recursively)
-                               )*
-                               \}              # Close bracket
-                       )
-                       (?<value>
-                               (?:
-                                       (?&qstr)                        # Quoted val
-                               |
-                                       \[\[
-                                               [^]]*                   # Link target
-                                       \]\]
-                               |
-                                       [\w-]+                          # Plain word
-                               |
-                                       (?&json)                        # JSON object
-                               )
-                       )
-               )';
-               $regex = '/' . $defs . '\b
-                       (?<k>[\w-]+)                            # Key
-                       \b
-                       (?:\s*
-                               =                                               # First sub-value
-                               \s*
-                               (?<v>
-                                       (?&value)
-                                       (?:\s*
-                                               ,                               # Sub-vals 1..N
-                                               \s*
-                                               (?&value)
-                                       )*
-                               )
-                       )?
-                       /x';
-               $valueregex = '/' . $defs . '(?&value)/x';
-
-               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
-                       foreach ( $matches as $bits ) {
-                               $key = strtolower( $bits['k'] );
-                               if ( !isset( $bits['v'] ) ) {
-                                       $opts[$key] = true;
-                               } else {
-                                       preg_match_all( $valueregex, $bits['v'], $vmatches );
-                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $vmatches[0] );
-                                       if ( count( $opts[$key] ) == 1 ) {
-                                               $opts[$key] = $opts[$key][0];
-                                       }
-                               }
-                       }
-               }
-               return $opts;
-       }
-
-       private function cleanupOption( $opt ) {
-               if ( substr( $opt, 0, 1 ) == '"' ) {
-                       return stripcslashes( substr( $opt, 1, -1 ) );
-               }
-
-               if ( substr( $opt, 0, 2 ) == '[[' ) {
-                       return substr( $opt, 2, -2 );
-               }
-
-               if ( substr( $opt, 0, 1 ) == '{' ) {
-                       return FormatJson::decode( $opt, true );
-               }
-               return $opt;
-       }
-
-       /**
-        * Set up the global variables for a consistent environment for each test.
-        * Ideally this should replace the global configuration entirely.
-        * @param string $opts
-        * @param string $config
-        * @return RequestContext
-        */
-       private function setupGlobals( $opts = '', $config = '' ) {
-               # Find out values for some special options.
-               $lang =
-                       self::getOptionValue( 'language', $opts, 'en' );
-               $variant =
-                       self::getOptionValue( 'variant', $opts, false );
-               $maxtoclevel =
-                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
-               $linkHolderBatchSize =
-                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
-
-               $settings = [
-                       'wgServer' => 'http://example.org',
-                       'wgServerName' => 'example.org',
-                       'wgScript' => '/index.php',
-                       'wgScriptPath' => '',
-                       'wgArticlePath' => '/wiki/$1',
-                       'wgActionPaths' => [],
-                       'wgLockManagers' => [ [
-                               'name' => 'fsLockManager',
-                               'class' => 'FSLockManager',
-                               'lockDirectory' => $this->uploadDir . '/lockdir',
-                       ], [
-                               'name' => 'nullLockManager',
-                               'class' => 'NullLockManager',
-                       ] ],
-                       'wgLocalFileRepo' => [
-                               'class' => 'LocalRepo',
-                               'name' => 'local',
-                               'url' => 'http://example.com/images',
-                               'hashLevels' => 2,
-                               'transformVia404' => false,
-                               'backend' => new FSFileBackend( [
-                                       'name' => 'local-backend',
-                                       'wikiId' => wfWikiID(),
-                                       'containerPaths' => [
-                                               'local-public' => $this->uploadDir,
-                                               'local-thumb' => $this->uploadDir . '/thumb',
-                                               'local-temp' => $this->uploadDir . '/temp',
-                                               'local-deleted' => $this->uploadDir . '/delete',
-                                       ]
-                               ] )
-                       ],
-                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
-                       'wgUploadNavigationUrl' => false,
-                       'wgStylePath' => '/skins',
-                       'wgSitename' => 'MediaWiki',
-                       'wgLanguageCode' => $lang,
-                       'wgDBprefix' => $this->db->getType() != 'oracle' ? 'parsertest_' : 'pt_',
-                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
-                       'wgLang' => null,
-                       'wgContLang' => null,
-                       'wgNamespacesWithSubpages' => [ 0 => isset( $opts['subpage'] ) ],
-                       'wgMaxTocLevel' => $maxtoclevel,
-                       'wgCapitalLinks' => true,
-                       'wgNoFollowLinks' => true,
-                       'wgNoFollowDomainExceptions' => [ 'no-nofollow.org' ],
-                       'wgThumbnailScriptPath' => false,
-                       'wgUseImageResize' => true,
-                       'wgSVGConverter' => 'null',
-                       'wgSVGConverters' => [ 'null' => 'echo "1">$output' ],
-                       'wgLocaltimezone' => 'UTC',
-                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
-                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
-                       'wgDefaultLanguageVariant' => $variant,
-                       'wgVariantArticlePath' => false,
-                       'wgGroupPermissions' => [ '*' => [
-                               'createaccount' => true,
-                               'read' => true,
-                               'edit' => true,
-                               'createpage' => true,
-                               'createtalk' => true,
-                       ] ],
-                       'wgNamespaceProtection' => [ NS_MEDIAWIKI => 'editinterface' ],
-                       'wgDefaultExternalStore' => [],
-                       'wgForeignFileRepos' => [],
-                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
-                       'wgExperimentalHtmlIds' => false,
-                       'wgExternalLinkTarget' => false,
-                       'wgHtml5' => true,
-                       'wgAdaptiveMessageCache' => true,
-                       'wgDisableLangConversion' => false,
-                       'wgDisableTitleConversion' => false,
-                       // Tidy options.
-                       'wgUseTidy' => false,
-                       'wgTidyConfig' => isset( $opts['tidy'] ) ? $this->tidySupport->getConfig() : null
-               ];
-
-               if ( $config ) {
-                       $configLines = explode( "\n", $config );
-
-                       foreach ( $configLines as $line ) {
-                               list( $var, $value ) = explode( '=', $line, 2 );
-
-                               $settings[$var] = eval( "return $value;" );
-                       }
-               }
-
-               $this->savedGlobals = [];
-
-               /** @since 1.20 */
-               Hooks::run( 'ParserTestGlobals', [ &$settings ] );
-
-               foreach ( $settings as $var => $val ) {
-                       if ( array_key_exists( $var, $GLOBALS ) ) {
-                               $this->savedGlobals[$var] = $GLOBALS[$var];
-                       }
-
-                       $GLOBALS[$var] = $val;
-               }
-
-               // Must be set before $context as user language defaults to $wgContLang
-               $GLOBALS['wgContLang'] = Language::factory( $lang );
-               $GLOBALS['wgMemc'] = new EmptyBagOStuff;
-
-               RequestContext::resetMain();
-               $context = RequestContext::getMain();
-               $GLOBALS['wgLang'] = $context->getLanguage();
-               $GLOBALS['wgOut'] = $context->getOutput();
-               $GLOBALS['wgUser'] = $context->getUser();
-
-               // We (re)set $wgThumbLimits to a single-element array above.
-               $context->getUser()->setOption( 'thumbsize', 0 );
-
-               global $wgHooks;
-
-               $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
-               $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
-
-               MagicWord::clearCache();
-               MWTidy::destroySingleton();
-               RepoGroup::destroySingleton();
-
-               self::resetTitleServices();
-
-               return $context;
-       }
-
-       /**
-        * List of temporary tables to create, without prefix.
-        * Some of these probably aren't necessary.
-        * @return array
-        */
-       private function listTables() {
-               $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
-                       'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
-                       'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
-                       'site_stats', 'ipblocks', 'image', 'oldimage',
-                       'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
-                       'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
-                       'archive', 'user_groups', 'page_props', 'category'
-               ];
-
-               if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite', 'oracle' ] ) ) {
-                       array_push( $tables, 'searchindex' );
-               }
-
-               // Allow extensions to add to the list of tables to duplicate;
-               // may be necessary if they hook into page save or other code
-               // which will require them while running tests.
-               Hooks::run( 'ParserTestTables', [ &$tables ] );
-
-               return $tables;
-       }
-
-       /**
-        * Set up a temporary set of wiki tables to work with for the tests.
-        * Currently this will only be done once per run, and any changes to
-        * the db will be visible to later tests in the run.
-        */
-       public function setupDatabase() {
-               global $wgDBprefix;
-
-               if ( $this->databaseSetupDone ) {
-                       return;
-               }
-
-               $this->db = wfGetDB( DB_MASTER );
-               $dbType = $this->db->getType();
-
-               if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
-                       throw new MWException( 'setupDatabase should be called before setupGlobals' );
-               }
-
-               $this->databaseSetupDone = true;
-
-               # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
-               # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
-               # This works around it for now...
-               ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
-
-               # CREATE TEMPORARY TABLE breaks if there is more than one server
-               if ( wfGetLB()->getServerCount() != 1 ) {
-                       $this->useTemporaryTables = false;
-               }
-
-               $temporary = $this->useTemporaryTables || $dbType == 'postgres';
-               $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
-
-               $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
-               $this->dbClone->useTemporaryTables( $temporary );
-               $this->dbClone->cloneTableStructure();
-
-               if ( $dbType == 'oracle' ) {
-                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
-                       # Insert 0 user to prevent FK violations
-
-                       # Anonymous user
-                       $this->db->insert( 'user', [
-                               'user_id' => 0,
-                               'user_name' => 'Anonymous' ] );
-               }
-
-               # Update certain things in site_stats
-               $this->db->insert( 'site_stats',
-                       [ 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ] );
-
-               # Reinitialise the LocalisationCache to match the database state
-               Language::getLocalisationCache()->unloadAll();
-
-               # Clear the message cache
-               MessageCache::singleton()->clear();
-
-               // Remember to update newParserTests.php after changing the below
-               // (and it uses a slightly different syntax just for teh lulz)
-               $this->setupUploadDir();
-               $user = User::createNew( 'WikiSysop' );
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
-               # note that the size/width/height/bits/etc of the file
-               # are actually set by inspecting the file itself; the arguments
-               # to recordUpload2 have no effect.  That said, we try to make things
-               # match up so it is less confusing to readers of the code & tests.
-               $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', [
-                       'size' => 7881,
-                       'width' => 1941,
-                       'height' => 220,
-                       'bits' => 8,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/jpeg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
-               # again, note that size/width/height below are ignored; see above.
-               $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', [
-                       'size' => 22589,
-                       'width' => 135,
-                       'height' => 135,
-                       'bits' => 8,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/png',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20130225203040' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
-               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
-                               'size'        => 12345,
-                               'width'       => 240,
-                               'height'      => 180,
-                               'bits'        => 0,
-                               'media_type'  => MEDIATYPE_DRAWING,
-                               'mime'        => 'image/svg+xml',
-                               'metadata'    => serialize( [] ),
-                               'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
-                               'fileExists'  => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               # This image will be blacklisted in [[MediaWiki:Bad image list]]
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
-               $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', [
-                       'size' => 12345,
-                       'width' => 320,
-                       'height' => 240,
-                       'bits' => 24,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/jpeg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
-               $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
-                       'size' => 12345,
-                       'width' => 320,
-                       'height' => 240,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_VIDEO,
-                       'mime' => 'application/ogg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
-               $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
-                       'size' => 12345,
-                       'width' => 0,
-                       'height' => 0,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_AUDIO,
-                       'mime' => 'application/ogg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               # A DjVu file
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
-               $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
-                       'size' => 3249,
-                       'width' => 2480,
-                       'height' => 3508,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/vnd.djvu',
-                       'metadata' => '<?xml version="1.0" ?>
-<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
-<DjVuXML>
-<HEAD></HEAD>
-<BODY><OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-</BODY>
-</DjVuXML>',
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123600' ), $user );
-       }
-
-       public function teardownDatabase() {
-               if ( !$this->databaseSetupDone ) {
-                       $this->teardownGlobals();
-                       return;
-               }
-               $this->teardownUploadDir( $this->uploadDir );
-
-               $this->dbClone->destroy();
-               $this->databaseSetupDone = false;
-
-               if ( $this->useTemporaryTables ) {
-                       if ( $this->db->getType() == 'sqlite' ) {
-                               # Under SQLite the searchindex table is virtual and need
-                               # to be explicitly destroyed. See bug 29912
-                               # See also MediaWikiTestCase::destroyDB()
-                               wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
-                               $this->db->query( "DROP TABLE `parsertest_searchindex`" );
-                       }
-                       # Don't need to do anything
-                       $this->teardownGlobals();
-                       return;
-               }
-
-               $tables = $this->listTables();
-
-               foreach ( $tables as $table ) {
-                       if ( $this->db->getType() == 'oracle' ) {
-                               $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
-                       } else {
-                               $this->db->query( "DROP TABLE `parsertest_$table`" );
-                       }
-               }
-
-               if ( $this->db->getType() == 'oracle' ) {
-                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
-               }
-
-               $this->teardownGlobals();
-       }
-
-       /**
-        * Create a dummy uploads directory which will contain a couple
-        * of files in order to pass existence tests.
-        *
-        * @return string The directory
-        */
-       private function setupUploadDir() {
-               global $IP;
-
-               $dir = $this->uploadDir;
-               if ( $this->keepUploads && is_dir( $dir ) ) {
-                       return;
-               }
-
-               // wfDebug( "Creating upload directory $dir\n" );
-               if ( file_exists( $dir ) ) {
-                       wfDebug( "Already exists!\n" );
-                       return;
-               }
-
-               wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
-               wfMkdirParents( $dir . '/e/ea', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/wiki.png", "$dir/e/ea/Thumb.png" );
-               wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/headbg.jpg", "$dir/0/09/Bad.jpg" );
-               wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
-               file_put_contents( "$dir/f/ff/Foobar.svg",
-                       '<?xml version="1.0" encoding="utf-8"?>' .
-                       '<svg xmlns="http://www.w3.org/2000/svg"' .
-                       ' version="1.1" width="240" height="180"/>' );
-               wfMkdirParents( $dir . '/5/5f', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/LoremIpsum.djvu", "$dir/5/5f/LoremIpsum.djvu" );
-               wfMkdirParents( $dir . '/0/00', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/320x240.ogv", "$dir/0/00/Video.ogv" );
-               wfMkdirParents( $dir . '/4/41', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/media/say-test.ogg", "$dir/4/41/Audio.oga" );
-
-               return;
-       }
-
-       /**
-        * Restore default values and perform any necessary clean-up
-        * after each test runs.
-        */
-       private function teardownGlobals() {
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-               LockManagerGroup::destroySingletons();
-               LinkCache::singleton()->clear();
-               MWTidy::destroySingleton();
-
-               foreach ( $this->savedGlobals as $var => $val ) {
-                       $GLOBALS[$var] = $val;
-               }
-       }
-
-       /**
-        * Remove the dummy uploads directory
-        * @param string $dir
-        */
-       private function teardownUploadDir( $dir ) {
-               if ( $this->keepUploads ) {
-                       return;
-               }
-
-               // delete the files first, then the dirs.
-               self::deleteFiles(
-                       [
-                               "$dir/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/*.jpg",
-                               "$dir/e/ea/Thumb.png",
-                               "$dir/0/09/Bad.jpg",
-                               "$dir/5/5f/LoremIpsum.djvu",
-                               "$dir/thumb/5/5f/LoremIpsum.djvu/*-LoremIpsum.djvu.jpg",
-                               "$dir/f/ff/Foobar.svg",
-                               "$dir/thumb/f/ff/Foobar.svg/*-Foobar.svg.png",
-                               "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
-                               "$dir/0/00/Video.ogv",
-                               "$dir/thumb/0/00/Video.ogv/120px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/180px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/240px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/270px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px-seek=2-Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px-seek=3.3666666666667-Video.ogv.jpg",
-                               "$dir/4/41/Audio.oga",
-                       ]
-               );
-
-               self::deleteDirs(
-                       [
-                               "$dir/3/3a",
-                               "$dir/3",
-                               "$dir/thumb/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a",
-                               "$dir/thumb/3",
-                               "$dir/e/ea",
-                               "$dir/e",
-                               "$dir/f/ff/",
-                               "$dir/f/",
-                               "$dir/thumb/f/ff/Foobar.svg",
-                               "$dir/thumb/f/ff/",
-                               "$dir/thumb/f/",
-                               "$dir/0/00/",
-                               "$dir/0/09/",
-                               "$dir/0/",
-                               "$dir/5/5f",
-                               "$dir/5",
-                               "$dir/thumb/0/00/Video.ogv",
-                               "$dir/thumb/0/00",
-                               "$dir/thumb/0",
-                               "$dir/thumb/5/5f/LoremIpsum.djvu",
-                               "$dir/thumb/5/5f",
-                               "$dir/thumb/5",
-                               "$dir/thumb",
-                               "$dir/4/41",
-                               "$dir/4",
-                               "$dir/math/f/a/5",
-                               "$dir/math/f/a",
-                               "$dir/math/f",
-                               "$dir/math",
-                               "$dir/lockdir",
-                               "$dir",
-                       ]
-               );
-       }
-
-       /**
-        * Delete the specified files, if they exist.
-        * @param array $files Full paths to files to delete.
-        */
-       private static function deleteFiles( $files ) {
-               foreach ( $files as $pattern ) {
-                       foreach ( glob( $pattern ) as $file ) {
-                               if ( file_exists( $file ) ) {
-                                       unlink( $file );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Delete the specified directories, if they exist. Must be empty.
-        * @param array $dirs Full paths to directories to delete.
-        */
-       private static function deleteDirs( $dirs ) {
-               foreach ( $dirs as $dir ) {
-                       if ( is_dir( $dir ) ) {
-                               rmdir( $dir );
-                       }
-               }
-       }
-
-       /**
-        * "Running test $desc..."
-        * @param string $desc
-        */
-       protected function showTesting( $desc ) {
-               print "Running test $desc... ";
-       }
-
-       /**
-        * Print a happy success message.
-        *
-        * Refactored in 1.22 to use ParserTestResult
-        *
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       protected function showSuccess( ParserTestResult $testResult ) {
-               if ( $this->showProgress ) {
-                       print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
-               }
-
-               return true;
-       }
-
-       /**
-        * Print a failure message and provide some explanatory output
-        * about what went wrong if so configured.
-        *
-        * Refactored in 1.22 to use ParserTestResult
-        *
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       protected function showFailure( ParserTestResult $testResult ) {
-               if ( $this->showFailure ) {
-                       if ( !$this->showProgress ) {
-                               # In quiet mode we didn't show the 'Testing' message before the
-                               # test, in case it succeeded. Show it now:
-                               $this->showTesting( $testResult->description );
-                       }
-
-                       print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
-
-                       if ( $this->showOutput ) {
-                               print "--- Expected ---\n{$testResult->expected}\n";
-                               print "--- Actual ---\n{$testResult->actual}\n";
-                       }
-
-                       if ( $this->showDiffs ) {
-                               print $this->quickDiff( $testResult->expected, $testResult->actual );
-                               if ( !$this->wellFormed( $testResult->actual ) ) {
-                                       print "XML error: $this->mXmlError\n";
-                               }
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Print a skipped message.
-        *
-        * @return bool
-        */
-       protected function showSkipped() {
-               if ( $this->showProgress ) {
-                       print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n";
-               }
-
-               return true;
-       }
-
-       /**
-        * Run given strings through a diff and return the (colorized) output.
-        * Requires writable /tmp directory and a 'diff' command in the PATH.
-        *
-        * @param string $input
-        * @param string $output
-        * @param string $inFileTail Tailing for the input file name
-        * @param string $outFileTail Tailing for the output file name
-        * @return string
-        */
-       protected function quickDiff( $input, $output,
-               $inFileTail = 'expected', $outFileTail = 'actual'
-       ) {
-               if ( $this->markWhitespace ) {
-                       $pairs = [
-                               "\n" => '¶',
-                               ' ' => '·',
-                               "\t" => '→'
-                       ];
-                       $input = strtr( $input, $pairs );
-                       $output = strtr( $output, $pairs );
-               }
-
-               # Windows, or at least the fc utility, is retarded
-               $slash = wfIsWindows() ? '\\' : '/';
-               $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
-
-               $infile = "$prefix-$inFileTail";
-               $this->dumpToFile( $input, $infile );
-
-               $outfile = "$prefix-$outFileTail";
-               $this->dumpToFile( $output, $outfile );
-
-               $shellInfile = wfEscapeShellArg( $infile );
-               $shellOutfile = wfEscapeShellArg( $outfile );
-
-               global $wgDiff3;
-               // we assume that people with diff3 also have usual diff
-               if ( $this->useDwdiff ) {
-                       $shellCommand = 'dwdiff -Pc';
-               } else {
-                       $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
-               }
-
-               $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
-
-               unlink( $infile );
-               unlink( $outfile );
-
-               if ( $this->useDwdiff ) {
-                       return $diff;
-               } else {
-                       return $this->colorDiff( $diff );
-               }
-       }
-
-       /**
-        * Write the given string to a file, adding a final newline.
-        *
-        * @param string $data
-        * @param string $filename
-        */
-       private function dumpToFile( $data, $filename ) {
-               $file = fopen( $filename, "wt" );
-               fwrite( $file, $data . "\n" );
-               fclose( $file );
-       }
-
-       /**
-        * Colorize unified diff output if set for ANSI color output.
-        * Subtractions are colored blue, additions red.
-        *
-        * @param string $text
-        * @return string
-        */
-       protected function colorDiff( $text ) {
-               return preg_replace(
-                       [ '/^(-.*)$/m', '/^(\+.*)$/m' ],
-                       [ $this->term->color( 34 ) . '$1' . $this->term->reset(),
-                               $this->term->color( 31 ) . '$1' . $this->term->reset() ],
-                       $text );
-       }
-
-       /**
-        * Show "Reading tests from ..."
-        *
-        * @param string $path
-        */
-       public function showRunFile( $path ) {
-               print $this->term->color( 1 ) .
-                       "Reading tests from \"$path\"..." .
-                       $this->term->reset() .
-                       "\n";
-       }
-
-       /**
-        * Insert a temporary test article
-        * @param string $name The title, including any prefix
-        * @param string $text The article text
-        * @param int|string $line The input line number, for reporting errors
-        * @param bool|string $ignoreDuplicate Whether to silently ignore duplicate pages
-        * @throws Exception
-        * @throws MWException
-        */
-       public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
-               global $wgCapitalLinks;
-
-               $oldCapitalLinks = $wgCapitalLinks;
-               $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
-
-               $text = self::chomp( $text );
-               $name = self::chomp( $name );
-
-               $title = Title::newFromText( $name );
-
-               if ( is_null( $title ) ) {
-                       throw new MWException( "invalid title '$name' at line $line\n" );
-               }
-
-               $page = WikiPage::factory( $title );
-               $page->loadPageData( 'fromdbmaster' );
-
-               if ( $page->exists() ) {
-                       if ( $ignoreDuplicate == 'ignoreduplicate' ) {
-                               return;
-                       } else {
-                               throw new MWException( "duplicate article '$name' at line $line\n" );
-                       }
-               }
-
-               $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
-
-               $wgCapitalLinks = $oldCapitalLinks;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if tag hook is present
-        */
-       public function requireHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mTagHooks[$name] ) ) {
-                       $this->hooks[$name] = $wgParser->mTagHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if function hook is present
-        */
-       public function requireFunctionHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
-                       $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' function hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if function hook is present
-        */
-       public function requireTransparentHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mTransparentTagHooks[$name] ) ) {
-                       $this->transparentHooks[$name] = $wgParser->mTransparentTagHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' transparent hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       private function wellFormed( $text ) {
-               $html =
-                       Sanitizer::hackDocType() .
-                               '<html>' .
-                               $text .
-                               '</html>';
-
-               $parser = xml_parser_create( "UTF-8" );
-
-               # case folding violates XML standard, turn it off
-               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
-               if ( !xml_parse( $parser, $html, true ) ) {
-                       $err = xml_error_string( xml_get_error_code( $parser ) );
-                       $position = xml_get_current_byte_index( $parser );
-                       $fragment = $this->extractFragment( $html, $position );
-                       $this->mXmlError = "$err at byte $position:\n$fragment";
-                       xml_parser_free( $parser );
-
-                       return false;
-               }
-
-               xml_parser_free( $parser );
-
-               return true;
-       }
-
-       private function extractFragment( $text, $position ) {
-               $start = max( 0, $position - 10 );
-               $before = $position - $start;
-               $fragment = '...' .
-                       $this->term->color( 34 ) .
-                       substr( $text, $start, $before ) .
-                       $this->term->color( 0 ) .
-                       $this->term->color( 31 ) .
-                       $this->term->color( 1 ) .
-                       substr( $text, $position, 1 ) .
-                       $this->term->color( 0 ) .
-                       $this->term->color( 34 ) .
-                       substr( $text, $position + 1, 9 ) .
-                       $this->term->color( 0 ) .
-                       '...';
-               $display = str_replace( "\n", ' ', $fragment );
-               $caret = '   ' .
-                       str_repeat( ' ', $before ) .
-                       $this->term->color( 31 ) .
-                       '^' .
-                       $this->term->color( 0 );
-
-               return "$display\n$caret";
-       }
-
-       static function getFakeTimestamp( &$parser, &$ts ) {
-               $ts = 123; // parsed as '1970-01-01T00:02:03Z'
-               return true;
-       }
-}
-
-class ParserTestResultNormalizer {
-       protected $doc, $xpath, $invalid;
-
-       public static function normalize( $text, $funcs ) {
-               $norm = new self( $text );
-               if ( $norm->invalid ) {
-                       return $text;
-               }
-               foreach ( $funcs as $func ) {
-                       $norm->$func();
-               }
-               return $norm->serialize();
-       }
-
-       protected function __construct( $text ) {
-               $this->doc = new DOMDocument( '1.0', 'utf-8' );
-
-               // Note: parsing a supposedly XHTML document with an XML parser is not
-               // guaranteed to give accurate results. For example, it may introduce
-               // differences in the number of line breaks in <pre> tags.
-
-               MediaWiki\suppressWarnings();
-               if ( !$this->doc->loadXML( '<html><body>' . $text . '</body></html>' ) ) {
-                       $this->invalid = true;
-               }
-               MediaWiki\restoreWarnings();
-               $this->xpath = new DOMXPath( $this->doc );
-               $this->body = $this->xpath->query( '//body' )->item( 0 );
-       }
-
-       protected function removeTbody() {
-               foreach ( $this->xpath->query( '//tbody' ) as $tbody ) {
-                       while ( $tbody->firstChild ) {
-                               $child = $tbody->firstChild;
-                               $tbody->removeChild( $child );
-                               $tbody->parentNode->insertBefore( $child, $tbody );
-                       }
-                       $tbody->parentNode->removeChild( $tbody );
-               }
-       }
-
-       /**
-        * The point of this function is to produce a normalized DOM in which
-        * Tidy's output matches the output of html5depurate. Tidy both trims
-        * and pretty-prints, so this requires fairly aggressive treatment.
-        *
-        * In particular, note that Tidy converts <pre>x</pre> to <pre>\nx\n</pre>,
-        * which theoretically affects display since the second line break is not
-        * ignored by compliant HTML parsers.
-        *
-        * This function also removes empty elements, as does Tidy.
-        */
-       protected function trimWhitespace() {
-               foreach ( $this->xpath->query( '//text()' ) as $child ) {
-                       if ( strtolower( $child->parentNode->nodeName ) === 'pre' ) {
-                               // Just trim one line break from the start and end
-                               if ( substr_compare( $child->data, "\n", 0 ) === 0 ) {
-                                       $child->data = substr( $child->data, 1 );
-                               }
-                               if ( substr_compare( $child->data, "\n", -1 ) === 0 ) {
-                                       $child->data = substr( $child->data, 0, -1 );
-                               }
-                       } else {
-                               // Trim all whitespace
-                               $child->data = trim( $child->data );
-                       }
-                       if ( $child->data === '' ) {
-                               $child->parentNode->removeChild( $child );
-                       }
-               }
-       }
-
-       /**
-        * Serialize the XML DOM for comparison purposes. This does not generate HTML.
-        */
-       protected function serialize() {
-               return strtr( $this->doc->saveXML( $this->body ),
-                       [ '<body>' => '', '</body>' => '' ] );
-       }
-}
diff --git a/tests/parser/parserTests.php b/tests/parser/parserTests.php
new file mode 100644 (file)
index 0000000..38923f0
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+/**
+ * MediaWiki parser test suite
+ *
+ * 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
+ * @ingroup Testing
+ */
+
+// Some methods which are discouraged for normal code throw exceptions unless
+// we declare this is just a test.
+define( 'MW_PARSER_TEST', true );
+
+require __DIR__ . '/../../maintenance/Maintenance.php';
+
+class ParserTestsMaintenance extends Maintenance {
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Run parser tests' );
+
+               $this->addOption( 'quick', 'Suppress diff output of failed tests' );
+               $this->addOption( 'quiet', 'Suppress notification of passed tests (shows only failed tests)' );
+               $this->addOption( 'show-output', 'Show expected and actual output' );
+               $this->addOption( 'color', '[=yes|no] Override terminal detection and force ' .
+                       'color output on or off. Use wgCommandLineDarkBg = true; if your term is dark',
+                       false, true );
+               $this->addOption( 'regex', 'Only run tests whose descriptions which match given regex',
+                       false, true );
+               $this->addOption( 'filter', 'Alias for --regex', false, true );
+               $this->addOption( 'file', 'Run test cases from a custom file instead of parserTests.txt',
+                       false, true, false, true );
+               $this->addOption( 'record', 'Record tests in database' );
+               $this->addOption( 'compare', 'Compare with recorded results, without updating the database.' );
+               $this->addOption( 'setversion', 'When using --record, set the version string to use (useful' .
+                       'with "git rev-parse HEAD" to get the exact revision)',
+                       false, true );
+               $this->addOption( 'keep-uploads', 'Re-use the same upload directory for each ' .
+                       'test, don\'t delete it' );
+               $this->addOption( 'file-backend', 'Use the file backend with the given name,' .
+                       'and upload files to it, instead of creating a mock file backend.', false, true );
+               $this->addOption( 'upload-dir', 'Specify the upload directory to use. Useful in ' .
+                       'conjunction with --keep-uploads. Causes a real (non-mock) file backend to ' .
+                       'be used.', false, true );
+               $this->addOption( 'run-disabled', 'run disabled tests' );
+               $this->addOption( 'run-parsoid', 'run parsoid tests (normally disabled)' );
+               $this->addOption( 'dwdiff', 'Use dwdiff to display diff output' );
+               $this->addOption( 'mark-ws', 'Mark whitespace in diffs by replacing it with symbols' );
+               $this->addOption( 'norm', 'Apply a comma-separated list of normalization functions to ' .
+                       'both the expected and actual output in order to resolve ' .
+                       'irrelevant differences. The accepted normalization functions ' .
+                       'are: removeTbody to remove <tbody> tags; and trimWhitespace ' .
+                       'to trim whitespace from the start and end of text nodes.',
+                       false, true );
+               $this->addOption( 'use-tidy-config', 'Use the wiki\'s Tidy configuration instead of known-good' .
+                       'defaults.' );
+       }
+
+       public function finalSetup() {
+               parent::finalSetup();
+               self::requireTestsAutoloader();
+               TestSetup::applyInitialConfig();
+       }
+
+       public function execute() {
+               global $wgParserTestFiles, $wgDBtype;
+
+               // Cases of weird db corruption were encountered when running tests on earlyish
+               // versions of SQLite
+               if ( $wgDBtype == 'sqlite' ) {
+                       $db = wfGetDB( DB_MASTER );
+                       $version = $db->getServerVersion();
+                       if ( version_compare( $version, '3.6' ) < 0 ) {
+                               die( "Parser tests require SQLite version 3.6 or later, you have $version\n" );
+                       }
+               }
+
+               // Print out software version to assist with locating regressions
+               $version = SpecialVersion::getVersion( 'nodb' );
+               echo "This is MediaWiki version {$version}.\n\n";
+
+               // Only colorize output if stdout is a terminal.
+               $color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
+
+               if ( $this->hasOption( 'color' ) ) {
+                       switch ( $this->getOption( 'color' ) ) {
+                               case 'no':
+                                       $color = false;
+                                       break;
+                               case 'yes':
+                               default:
+                                       $color = true;
+                                       break;
+                       }
+               }
+
+               $record = $this->hasOption( 'record' );
+               $compare = $this->hasOption( 'compare' );
+
+               $regex = $this->getOption( 'filter', $this->getOption( 'regex', false ) );
+               if ( $regex !== false ) {
+                       $regex = "/$regex/i";
+
+                       if ( $record ) {
+                               echo "Warning: --record cannot be used with --regex, disabling --record\n";
+                               $record = false;
+                       }
+               }
+
+               $term = $color
+                       ? new AnsiTermColorer()
+                       : new DummyTermColorer();
+
+               $recorder = new MultiTestRecorder;
+
+               $recorder->addRecorder( new ParserTestPrinter(
+                       $term,
+                       [
+                               'showDiffs' => !$this->hasOption( 'quick' ),
+                               'showProgress' => !$this->hasOption( 'quiet' ),
+                               'showFailure' => !$this->hasOption( 'quiet' )
+                                               || ( !$record && !$compare ), // redundant output
+                               'showOutput' => $this->hasOption( 'show-output' ),
+                               'useDwdiff' => $this->hasOption( 'dwdiff' ),
+                               'markWhitespace' => $this->hasOption( 'mark-ws' ),
+                       ]
+               ) );
+
+               $recorderLB = false;
+               if ( $record || $compare ) {
+                       $recorderLB = wfGetLBFactory()->newMainLB();
+                       // This connection will have the wiki's table prefix, not parsertest_
+                       $recorderDB = $recorderLB->getConnection( DB_MASTER );
+
+                       // Add recorder before previewer because recorder will create the
+                       // DB table if it doesn't exist
+                       if ( $record ) {
+                               $recorder->addRecorder( new DbTestRecorder( $recorderDB ) );
+                       }
+                       $recorder->addRecorder( new DbTestPreviewer(
+                               $recorderDB,
+                               function ( $name ) use ( $regex ) {
+                                       // Filter reports of old tests by the filter regex
+                                       if ( $regex === false ) {
+                                               return true;
+                                       } else {
+                                               return (bool)preg_match( $regex, $name );
+                                       }
+                               } ) );
+               }
+
+               // Default parser tests and any set from extensions or local config
+               $files = $this->getOption( 'file', $wgParserTestFiles );
+
+               $norm = $this->hasOption( 'norm' ) ? explode( ',', $this->getOption( 'norm' ) ) : [];
+
+               $tester = new ParserTestRunner( $recorder, [
+                       'norm' => $norm,
+                       'regex' => $regex,
+                       'keep-uploads' => $this->hasOption( 'keep-uploads' ),
+                       'run-disabled' => $this->hasOption( 'run-disabled' ),
+                       'run-parsoid' => $this->hasOption( 'run-parsoid' ),
+                       'use-tidy-config' => $this->hasOption( 'use-tidy-config' ),
+                       'file-backend' => $this->getOption( 'file-backend' ),
+                       'upload-dir' => $this->getOption( 'upload-dir' ),
+               ] );
+
+               $ok = $tester->runTestsFromFiles( $files );
+               if ( $recorderLB ) {
+                       $recorderLB->closeAll();
+               }
+               if ( !$ok ) {
+                       exit( 1 );
+               }
+       }
+}
+
+$maintClass = 'ParserTestsMaintenance';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 3e9fef8..2c8b163 100644 (file)
@@ -35,7 +35,8 @@
 #
 # You can also set the following parser properties via test options:
 #  wgEnableUploads, wgAllowExternalImages, wgMaxTocLevel,
-#  wgLinkHolderBatchSize, wgRawHtml, wgInterwikiMagic
+#  wgLinkHolderBatchSize, wgRawHtml, wgInterwikiMagic,
+#  wgEnableMagicLinks
 #
 # For testing purposes, temporary articles can created:
 # !!article / NAMESPACE:TITLE / !!text / ARTICLE TEXT / !!endarticle
@@ -3055,6 +3056,28 @@ a
 | c</pre>
 !!end
 
+!! test
+2g. Indented table markup mixed with indented pre content (proposed in bug 6200)
+!! wikitext
+ <table>
+ <tr>
+ <td>
+ Text that should be rendered preformatted
+ </td>
+ </tr>
+ </table>
+!! html
+ <table>
+ <tr>
+ <td>
+<pre>Text that should be rendered preformatted
+</pre>
+ </td>
+ </tr>
+ </table>
+
+!! end
+
 !!test
 3a. Indent-Pre and block tags (single-line html)
 !! wikitext
@@ -6392,26 +6415,55 @@ parsoid=wt2html,html2html
 <span typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"ho\">ha&lt;/div>"}},"i":0}}]}'>ho">ha</span>
 !! end
 
+## We don't support roundtripping of these attributes in Parsoid.
+## Selective serialization takes care of preventing dirty diffs.
+## But, on edits, we dirty-diff the invalid attribute text.
 !! test
-Indented table markup mixed with indented pre content (proposed in bug 6200)
+Invalid text in table attributes should be discarded
+!! options
+parsoid=wt2html
 !! wikitext
- <table>
- <tr>
- <td>
- Text that should be rendered preformatted
- </td>
- </tr>
- </table>
-!! html
- <table>
- <tr>
- <td>
-<pre>Text that should be rendered preformatted
-</pre>
- </td>
- </tr>
- </table>
+{| <span>boo</span> style='border:1px solid black'
+|  <span>boo</span> style='color:blue'  | 1
+|<span>boo</span> style='color:blue'| 2
+|}
+!! html/php
+<table style="border:1px solid black">
+<tr>
+<td style="color:blue"> 1
+</td>
+<td style="color:blue"> 2
+</td></tr></table>
 
+!! html/parsoid
+<table style="border:1px solid black">
+<tr>
+<td style="color:blue"> 1</td>
+<td style="color:blue"> 2</td>
+</tr>
+</table>
+!! end
+
+!! test
+Invalid text in table attributes should be preserved by selective serializer
+!! options
+parsoid={
+  "modes": ["selser"],
+  "changes": [
+    ["td:first-child", "text", "abc"],
+    ["td + td", "text", "xyz"]
+  ]
+}
+!! wikitext
+{| <span>boo</span> style='border:1px solid black'
+|  <span>boo</span> style='color:blue'  | 1
+|<span>boo</span> style='color:blue'| 2
+|}
+!! wikitext/edited
+{| <span>boo</span> style='border:1px solid black'
+|  <span>boo</span> style='color:blue'  |abc
+|<span>boo</span> style='color:blue'|xyz
+|}
 !! end
 
 !! test
@@ -8000,6 +8052,20 @@ title=[[User:test]]
 <p><a rel="mw:WikiLink" href="./User:Test/123" title="User:Test/123" data-parsoid='{"stx":"simple","a":{"href":"./User:Test/123"},"sa":{"href":"/123"}}'>/123</a></p>
 !! end
 
+!! test
+Ensure that transclusion titles are not url-decoded
+!! options
+subpage title=[[Test]]
+parsoid=wt2html
+!! wikitext
+{{Bar%C3%A9}} {{/Bar%C3%A9}}
+!! html/php
+<p>{{Bar%C3%A9}} {{/Bar%C3%A9}}
+</p>
+!! html/parsoid
+<p>{{Bar%C3%A9}} {{/Bar%C3%A9}}</p>
+!! end
+
 !! test
 Purely hash wikilink
 !! options
@@ -10476,6 +10542,21 @@ X[//tools.ietf.org/html/rfc1234 foo]
 <p>X<a rel="mw:ExtLink" href="//tools.ietf.org/html/rfc1234">foo</a></p>
 !! end
 
+!! test
+Magic links: All disabled (T47942)
+!! options
+wgEnableMagicLinks={"ISBN":false, "PMID":false, "RFC":false}
+!! wikitext
+ISBN 0-306-40615-2
+PMID 1234
+RFC 4321
+!! html/php
+<p>ISBN 0-306-40615-2
+PMID 1234
+RFC 4321
+</p>
+!! end
+
 ###
 ### Templates
 ####
@@ -14646,7 +14727,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=
 !! end
 
 !! test
@@ -14665,7 +14746,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide|Foo]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=Foo
 !! end
 
 !! test
@@ -14675,7 +14756,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=MediaWiki User's Guide
 !! end
 
 !! test
@@ -19388,9 +19469,11 @@ subpage title=[[Subpage test/L1/L2/L3]]
 parsoid=wt2html
 !! wikitext
 {{../../../../More than parent}}
-!! html
+!! html/php
 <p>{{../../../../More than parent}}
 </p>
+!! html/parsoid
+<p>{{../../../../More than parent}}</p>
 !! end
 
 !! test
@@ -19785,7 +19868,7 @@ language=sr cat
 !! wikitext
 [[Category:МедиаWики Усер'с Гуиде]]
 !! html
-<a href="/wiki/%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%98%D0%B0:MediaWiki_User%27s_Guide" title="Категорија:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=МедиаWики_Усер'с_Гуиде sort=
 !! end
 
 
@@ -19814,7 +19897,7 @@ parsoid=wt2html
 !! wikitext
 [[A]][[Category:分类]]
 !! html/php
-<a href="/wiki/Category:%E5%88%86%E7%B1%BB" title="Category:分类">分类</a>
+cat=分类 sort=
 !! html/parsoid
 <p><a rel="mw:WikiLink" href="A" title="A">A</a></p>
 <link rel="mw:PageProp/Category" href="Category:分类"/>
@@ -27241,7 +27324,9 @@ Thumbnail output
 unclosed internal link XSS (T137264)
 !! wikitext
 [[#%3Cscript%3Ealert(1)%3C/script%3E|
-!! html
+!! html/php
 <p>[[#&lt;script&gt;alert(1)&lt;/script&gt;|
 </p>
+!! html/parsoid
+<p>[[#%3Cscript%3Ealert(1)%3C/script%3E|</p>
 !! end
diff --git a/tests/parser/parserTestsParserHook.php b/tests/parser/parserTestsParserHook.php
deleted file mode 100644 (file)
index 5bf50ea..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * A basic extension that's used by the parser tests to test whether input and
- * arguments are passed to extensions properly.
- *
- * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Testing
- * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-class ParserTestParserHook {
-
-       static function setup( &$parser ) {
-               $parser->setHook( 'tag', [ __CLASS__, 'dumpHook' ] );
-               $parser->setHook( 'tåg', [ __CLASS__, 'dumpHook' ] );
-               $parser->setHook( 'statictag', [ __CLASS__, 'staticTagHook' ] );
-               return true;
-       }
-
-       static function dumpHook( $in, $argv ) {
-               return "<pre>\n" .
-                       var_export( $in, true ) . "\n" .
-                       var_export( $argv, true ) . "\n" .
-                       "</pre>";
-       }
-
-       static function staticTagHook( $in, $argv, $parser ) {
-               if ( !count( $argv ) ) {
-                       $parser->static_tag_buf = $in;
-                       return '';
-               } elseif ( count( $argv ) === 1 && isset( $argv['action'] )
-                       && $argv['action'] === 'flush' && $in === null
-               ) {
-                       // Clear the buffer, we probably don't need to
-                       if ( isset( $parser->static_tag_buf ) ) {
-                               $tmp = $parser->static_tag_buf;
-                       } else {
-                               $tmp = '';
-                       }
-                       $parser->static_tag_buf = null;
-                       return $tmp;
-               } else { // wtf?
-                       return
-                               "\nCall this extension as <statictag>string</statictag> or as" .
-                               " <statictag action=flush/>, not in any other way.\n" .
-                               "text: " . var_export( $in, true ) . "\n" .
-                               "argv: " . var_export( $argv, true ) . "\n";
-               }
-       }
-}
diff --git a/tests/parserTests.php b/tests/parserTests.php
deleted file mode 100644 (file)
index f961dd4..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-/**
- * MediaWiki parser test suite
- *
- * 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
- * @ingroup Testing
- */
-
-define( 'MW_PARSER_TEST', true );
-
-$options = [ 'quick', 'color', 'quiet', 'help', 'show-output',
-       'record', 'run-disabled', 'run-parsoid', 'dwdiff', 'mark-ws' ];
-$optionsWithArgs = [ 'regex', 'filter', 'seed', 'setversion', 'file', 'norm' ];
-
-require_once __DIR__ . '/../maintenance/commandLine.inc';
-require_once __DIR__ . '/TestsAutoLoader.php';
-
-if ( isset( $options['help'] ) ) {
-       echo <<<ENDS
-MediaWiki $wgVersion parser test suite
-Usage: php parserTests.php [options...]
-
-Options:
-  --quick          Suppress diff output of failed tests
-  --quiet          Suppress notification of passed tests (shows only failed tests)
-  --show-output    Show expected and actual output
-  --color[=yes|no] Override terminal detection and force color output on or off
-                   use wgCommandLineDarkBg = true; if your term is dark
-  --regex          Only run tests whose descriptions which match given regex
-  --filter         Alias for --regex
-  --file=<testfile> Run test cases from a custom file instead of parserTests.txt
-  --record         Record tests in database
-  --compare        Compare with recorded results, without updating the database.
-  --setversion     When using --record, set the version string to use (useful
-                   with git-svn so that you can get the exact revision)
-  --keep-uploads   Re-use the same upload directory for each test, don't delete it
-  --fuzz           Do a fuzz test instead of a normal test
-  --seed <n>       Start the fuzz test from the specified seed
-  --run-disabled   run disabled tests
-  --run-parsoid    run parsoid tests (normally disabled)
-  --dwdiff         Use dwdiff to display diff output
-  --mark-ws        Mark whitespace in diffs by replacing it with symbols
-  --norm=<funcs>   Apply a comma-separated list of normalization functions to
-                   both the expected and actual output in order to resolve
-                   irrelevant differences. The accepted normalization functions
-                   are: removeTbody to remove <tbody> tags; and trimWhitespace
-                   to trim whitespace from the start and end of text nodes.
-  --use-tidy-config Use the wiki's Tidy configuration instead of known-good
-                   defaults.
-  --help           Show this help message
-
-ENDS;
-       exit( 0 );
-}
-
-# Cases of weird db corruption were encountered when running tests on earlyish
-# versions of SQLite
-if ( $wgDBtype == 'sqlite' ) {
-       $db = wfGetDB( DB_MASTER );
-       $version = $db->getServerVersion();
-       if ( version_compare( $version, '3.6' ) < 0 ) {
-               die( "Parser tests require SQLite version 3.6 or later, you have $version\n" );
-       }
-}
-
-$tester = new ParserTest( $options );
-
-if ( isset( $options['file'] ) ) {
-       $files = [ $options['file'] ];
-} else {
-       // Default parser tests and any set from extensions or local config
-       $files = $wgParserTestFiles;
-}
-
-# Print out software version to assist with locating regressions
-$version = SpecialVersion::getVersion( 'nodb' );
-echo "This is MediaWiki version {$version}.\n\n";
-
-if ( isset( $options['fuzz'] ) ) {
-       $tester->fuzzTest( $files );
-} else {
-       $ok = $tester->runTestsFromFiles( $files );
-       exit( $ok ? 0 : 1 );
-}
index e1537bf..d34e183 100644 (file)
@@ -43,26 +43,17 @@ coverage:
 
 parser:
        ${PU} --group Parser
-parserfuzz:
-       @echo "******************************************************************"
-       @echo "* This WILL kill your computer by eating all memory AND all swap *"
-       @echo "*                                                                *"
-       @echo "* If you are on a production machine. ABORT NOW!!                *"
-       @echo "*  Press control+C to stop                                       *"
-       @echo "*                                                                *"
-       @echo "******************************************************************"
-       ${PU} --group Parser,ParserFuzz
 noparser:
-       ${PU} --exclude-group Parser,Broken,ParserFuzz,Stub
+       ${PU} --exclude-group Parser,Broken,Stub
 
 safe:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Stub
+       ${PU} --exclude-group Broken,Destructive,Stub
 
 databaseless:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Database,Stub
+       ${PU} --exclude-group Broken,Destructive,Database,Stub
 
 database:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Stub --group Database
+       ${PU} --exclude-group Broken,Destructive,Stub --group Database
 
 list-groups:
        ${PU} --list-groups
@@ -76,7 +67,7 @@ help:
        #   tap                 Run the tests individually through Test::Harness's prove(1)
        #   help                You're looking at it!
        #   coverage            Run the tests and generates an HTML code coverage report
-       #                       You will need the Xdebug PHP extension for the later.
+       #                       You will need the Xdebug PHP extension for the latter.
        #   [no]parser          Skip or only run Parser tests
        #
        #   list-groups         List available Tests groups.
index 27f1454..cfeb44f 100644 (file)
@@ -122,9 +122,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        public static function setUpBeforeClass() {
                parent::setUpBeforeClass();
 
-               // NOTE: Usually, PHPUnitMaintClass::finalSetup already called this,
-               // but let's make doubly sure.
-               self::prepareServices( new GlobalVarConfig() );
+               // Get the service locator, and reset services if it's not done already
+               self::$serviceLocator = self::prepareServices( new GlobalVarConfig() );
        }
 
        /**
@@ -180,28 +179,26 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         *
         * @param Config $bootstrapConfig The bootstrap config to use with the new
         *        MediaWikiServices. Only used for the first call to this method.
+        * @return MediaWikiServices
         */
        public static function prepareServices( Config $bootstrapConfig ) {
-               static $servicesPrepared = false;
+               static $services = null;
 
-               if ( $servicesPrepared ) {
-                       return;
-               } else {
-                       $servicesPrepared = true;
+               if ( !$services ) {
+                       $services = self::resetGlobalServices( $bootstrapConfig );
                }
-
-               self::resetGlobalServices( $bootstrapConfig );
+               return $services;
        }
 
        /**
         * Reset global services, and install testing environment.
         * This is the testing equivalent of MediaWikiServices::resetGlobalInstance().
         * This should only be used to set up the testing environment, not when
-        * running unit tests. Use overrideMwServices() for that.
+        * running unit tests. Use MediaWikiTestCase::overrideMwServices() for that.
         *
         * @see MediaWikiServices::resetGlobalInstance()
         * @see prepareServices()
-        * @see overrideMwServices()
+        * @see MediaWikiTestCase::overrideMwServices()
         *
         * @param Config|null $bootstrapConfig The bootstrap config to use with the new
         *        MediaWikiServices.
@@ -214,11 +211,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                MediaWikiServices::resetGlobalInstance( $testConfig );
 
-               self::$serviceLocator = MediaWikiServices::getInstance();
+               $serviceLocator = MediaWikiServices::getInstance();
                self::installTestServices(
                        $oldConfigFactory,
-                       self::$serviceLocator
+                       $serviceLocator
                );
+               return $serviceLocator;
        }
 
        /**
@@ -256,6 +254,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                $defaultOverrides->set( 'ObjectCaches', $objectCaches );
                $defaultOverrides->set( 'MainCacheType', CACHE_NONE );
+               $defaultOverrides->set( 'JobTypeConf', [ 'default' => [ 'class' => 'JobQueueMemory' ] ] );
 
                // Use a fast hash algorithm to hash passwords.
                $defaultOverrides->set( 'PasswordDefault', 'A' );
@@ -477,11 +476,20 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        while ( $this->db->trxLevel() > 0 ) {
                                $this->db->rollback( __METHOD__, 'flush' );
                        }
+                       // Check for unsafe queries
+                       if ( $this->db->getType() === 'mysql' ) {
+                               $this->db->query( "SET sql_mode = 'STRICT_ALL_TABLES'" );
+                       }
                }
 
                DeferredUpdates::clearPendingUpdates();
                ObjectCache::getMainWANInstance()->clearProcessCache();
 
+               // XXX: reset maintenance triggers
+               // Hook into period lag checks which often happen in long-running scripts
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               Maintenance::setLBFactoryTriggers( $lbFactory );
+
                ob_start( 'MediaWikiTestCase::wfResetOutputBuffersBarrier' );
        }
 
@@ -490,7 +498,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        }
 
        protected function tearDown() {
-               global $wgRequest;
+               global $wgRequest, $wgSQLMode;
 
                $status = ob_get_status();
                if ( isset( $status['name'] ) &&
@@ -514,6 +522,9 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        while ( $this->db->trxLevel() > 0 ) {
                                $this->db->rollback( __METHOD__, 'flush' );
                        }
+                       if ( $this->db->getType() === 'mysql' ) {
+                               $this->db->query( "SET sql_mode = " . $this->db->addQuotes( $wgSQLMode ) );
+                       }
                }
 
                // Restore mw globals
@@ -909,13 +920,22 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         *
         * Should be called from addDBData().
         *
-        * @since 1.25
-        * @param string $pageName Page name
+        * @since 1.25 ($namespace in 1.28)
+        * @param string|title $pageName Page name or title
         * @param string $text Page's content
+        * @param int $namespace Namespace id (name cannot already contain namespace)
         * @return array Title object and page id
         */
-       protected function insertPage( $pageName, $text = 'Sample page for unit test.' ) {
-               $title = Title::newFromText( $pageName, 0 );
+       protected function insertPage(
+               $pageName,
+               $text = 'Sample page for unit test.',
+               $namespace = null
+       ) {
+               if ( is_string( $pageName ) ) {
+                       $title = Title::newFromText( $pageName, $namespace );
+               } else {
+                       $title = $pageName;
+               }
 
                $user = static::getTestSysop()->getUser();
                $comment = __METHOD__ . ': Sample page for unit test.';
@@ -1109,15 +1129,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         * @throws MWException If the database table prefix is already $prefix
         */
        public static function setupTestDB( DatabaseBase $db, $prefix ) {
+               if ( self::$dbSetup ) {
+                       return;
+               }
+
                if ( $db->tablePrefix() === $prefix ) {
                        throw new MWException(
                                'Cannot run unit tests, the database prefix is already "' . $prefix . '"' );
                }
 
-               if ( self::$dbSetup ) {
-                       return;
-               }
-
                // TODO: the below should be re-written as soon as LBFactory, LoadBalancer,
                // and DatabaseBase no longer use global state.
 
index 18a49f6..f0eb12e 100644 (file)
@@ -22,9 +22,7 @@ abstract class ResourceLoaderTestCase extends MediaWikiTestCase {
                        ->setConstructorArgs( [ $resourceLoader, $request ] )
                        ->setMethods( [ 'getDirection' ] )
                        ->getMock();
-               $ctx->expects( $this->any() )->method( 'getDirection' )->will(
-                       $this->returnValue( $dir )
-               );
+               $ctx->method( 'getDirection' )->willReturn( $dir );
                return $ctx;
        }
 
index c259a51..5a01dc0 100644 (file)
@@ -348,6 +348,8 @@ class EditPageTest extends MediaWikiLangTestCase {
 
                wfGetDB( DB_MASTER )->commit( __METHOD__ );
 
+               $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
+
                if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
                        $latest = $page->getLatest();
                        $page->doDeleteArticleReal( $pageTitle );
index 4622a38..e3713ab 100644 (file)
@@ -1,8 +1,162 @@
 <?php
 
-class FauxRequestTest extends MediaWikiTestCase {
+class FauxRequestTest extends PHPUnit_Framework_TestCase {
+       /**
+        * @covers FauxRequest::__construct
+        */
+       public function testConstructInvalidData() {
+               $this->setExpectedException( MWException::class, 'bogus data' );
+               $req = new FauxRequest( 'x' );
+       }
+
+       /**
+        * @covers FauxRequest::__construct
+        */
+       public function testConstructInvalidSession() {
+               $this->setExpectedException( MWException::class, 'bogus session' );
+               $req = new FauxRequest( [], false, 'x' );
+       }
+
+       /**
+        * @covers FauxRequest::getText
+        */
+       public function testGetText() {
+               $req = new FauxRequest( [ 'x' => 'Value' ] );
+               $this->assertEquals( 'Value', $req->getText( 'x' ) );
+               $this->assertEquals( '', $req->getText( 'z' ) );
+       }
+
+       // Integration test for parent method.
+       public function testGetVal() {
+               $req = new FauxRequest( [ 'crlf' => "A\r\nb" ] );
+               $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
+       }
+
+       // Integration test for parent method.
+       public function testGetRawVal() {
+               $req = new FauxRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'crlf' => "A\r\nb"
+               ] );
+               $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
+               $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers FauxRequest::getValues
+        */
+       public function testGetValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+               $req = new FauxRequest( $values );
+               $this->assertEquals( $values, $req->getValues() );
+       }
+
+       /**
+        * @covers FauxRequest::getQueryValues
+        */
+       public function testGetQueryValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+
+               $req = new FauxRequest( $values );
+               $this->assertEquals( $values, $req->getQueryValues() );
+               $req = new FauxRequest( $values, /*wasPosted*/ true );
+               $this->assertEquals( [], $req->getQueryValues() );
+       }
+
+       /**
+        * @covers FauxRequest::getMethod
+        */
+       public function testGetMethod() {
+               $req = new FauxRequest( [] );
+               $this->assertEquals( 'GET', $req->getMethod() );
+               $req = new FauxRequest( [], /*wasPosted*/ true );
+               $this->assertEquals( 'POST', $req->getMethod() );
+       }
+
+       /**
+        * @covers FauxRequest::wasPosted
+        */
+       public function testWasPosted() {
+               $req = new FauxRequest( [] );
+               $this->assertFalse( $req->wasPosted() );
+               $req = new FauxRequest( [], /*wasPosted*/ true );
+               $this->assertTrue( $req->wasPosted() );
+       }
+
+       /**
+        * @covers FauxRequest::getCookie
+        * @covers FauxRequest::setCookie
+        * @covers FauxRequest::setCookies
+        */
+       public function testCookies() {
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getCookie( 'z', '' ) );
+
+               $req->setCookie( 'x', 'Value', '' );
+               $this->assertEquals( 'Value', $req->getCookie( 'x', '' ) );
+
+               $req->setCookies( [ 'x' => 'One', 'y' => 'Two' ], '' );
+               $this->assertEquals( 'One', $req->getCookie( 'x', '' ) );
+               $this->assertEquals( 'Two', $req->getCookie( 'y', '' ) );
+       }
+
+       /**
+        * @covers FauxRequest::getCookie
+        * @covers FauxRequest::setCookie
+        * @covers FauxRequest::setCookies
+        */
+       public function testCookiesDefaultPrefix() {
+               global $wgCookiePrefix;
+               $oldPrefix = $wgCookiePrefix;
+               $wgCookiePrefix = '_';
+
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getCookie( 'z' ) );
+
+               $req->setCookie( 'x', 'Value' );
+               $this->assertEquals( 'Value', $req->getCookie( 'x' ) );
+
+               $wgCookiePrefix = $oldPrefix;
+       }
+
+       /**
+        * @covers FauxRequest::getRequestURL
+        */
+       public function testGetRequestURL() {
+               $req = new FauxRequest();
+               $this->setExpectedException( MWException::class );
+               $req->getRequestURL();
+       }
+
+       /**
+        * @covers FauxRequest::setRequestURL
+        * @covers FauxRequest::getRequestURL
+        */
+       public function testSetRequestURL() {
+               $req = new FauxRequest();
+               $req->setRequestURL( 'https://example.org' );
+               $this->assertEquals( 'https://example.org', $req->getRequestURL() );
+       }
+
+       /**
+        * @covers FauxRequest::__construct
+        * @covers FauxRequest::getProtocol
+        */
+       public function testProtocol() {
+               $req = new FauxRequest();
+               $this->assertEquals( 'http', $req->getProtocol() );
+               $req = new FauxRequest( [], false, null, 'http' );
+               $this->assertEquals( 'http', $req->getProtocol() );
+               $req = new FauxRequest( [], false, null, 'https' );
+               $this->assertEquals( 'https', $req->getProtocol() );
+       }
+
        /**
         * @covers FauxRequest::setHeader
+        * @covers FauxRequest::setHeaders
         * @covers FauxRequest::getHeader
         */
        public function testGetSetHeader() {
@@ -22,7 +176,7 @@ class FauxRequestTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers FauxRequest::getAllHeaders
+        * @covers FauxRequest::initHeaders
         */
        public function testGetAllHeaders() {
                $_SERVER['HTTP_TEST'] = 'Example';
@@ -33,19 +187,36 @@ class FauxRequestTest extends MediaWikiTestCase {
                        [],
                        $request->getAllHeaders()
                );
+
+               $this->assertEquals(
+                       false,
+                       $request->getHeader( 'test' )
+               );
        }
 
        /**
-        * @covers FauxRequest::getHeader
+        * @covers FauxRequest::__construct
+        * @covers FauxRequest::getSessionArray
         */
-       public function testGetHeader() {
-               $_SERVER['HTTP_TEST'] = 'Example';
+       public function testSessionData() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
 
-               $request = new FauxRequest();
+               $req = new FauxRequest( [], false, /*session*/ $values );
+               $this->assertEquals( $values, $req->getSessionArray() );
 
-               $this->assertEquals(
-                       false,
-                       $request->getHeader( 'test' )
-               );
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getSessionArray() );
+       }
+
+       /**
+        * @covers FauxRequest::getRawQueryString
+        * @covers FauxRequest::getRawPostString
+        * @covers FauxRequest::getRawInput
+        */
+       public function testDummies() {
+               $req = new FauxRequest();
+               $this->assertEquals( '', $req->getRawQueryString() );
+               $this->assertEquals( '', $req->getRawPostString() );
+               $this->assertEquals( '', $req->getRawInput() );
        }
 }
index e44db83..85c95e4 100644 (file)
@@ -271,7 +271,7 @@ class HtmlTest extends MediaWikiTestCase {
        /**
         * How do we handle duplicate keys in HTML attributes expansion?
         * We could pass a "class" the values: 'GREEN' and array( 'GREEN' => false )
-        * The later will take precedence.
+        * The latter will take precedence.
         *
         * Feature added by r96188
         * @covers Html::expandAttributes
index 63753f9..3edf99f 100644 (file)
@@ -37,34 +37,34 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/JohnDoe" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/JohnDoe">JohnDoe</a>',
+                                       . 'title="Special:Contributions/JohnDoe"><bdi>JohnDoe</bdi></a>',
                                0, 'JohnDoe', false,
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">::1</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>::1</bdi></a>',
                                0, '::1', false,
                                'Anonymous with pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0:0:0:0:0:0:0:1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1">::1</a>',
+                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1"><bdi>::1</bdi></a>',
                                0, '0:0:0:0:0:0:0:1', false,
                                'Anonymous with almost pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001">::1</a>',
+                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001"><bdi>::1</bdi></a>',
                                0, '0000:0000:0000:0000:0000:0000:0000:0001', false,
                                'Anonymous with full IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>AlternativeUsername</bdi></a>',
                                0, '::1', 'AlternativeUsername',
                                'Anonymous with pretty IPv6 and an alternative username'
                        ],
@@ -73,14 +73,14 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">127.0.0.1</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>127.0.0.1</bdi></a>',
                                0, '127.0.0.1', false,
                                'Anonymous with IPv4'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>AlternativeUsername</bdi></a>',
                                0, '127.0.0.1', 'AlternativeUsername',
                                'Anonymous with IPv4 and an alternative username'
                        ],
index 70e4aea..4bca478 100644 (file)
@@ -4,7 +4,6 @@
  * Tests timestamp parsing and output.
  */
 class MWTimestampTest extends MediaWikiLangTestCase {
-
        protected function setUp() {
                parent::setUp();
 
@@ -12,128 +11,6 @@ class MWTimestampTest extends MediaWikiLangTestCase {
                $this->setMwGlobals( 'wgHooks', [] );
        }
 
-       /**
-        * @covers MWTimestamp::__construct
-        */
-       public function testConstructWithNoTimestamp() {
-               $timestamp = new MWTimestamp();
-               $this->assertInternalType( 'string', $timestamp->getTimestamp() );
-               $this->assertNotEmpty( $timestamp->getTimestamp() );
-               $this->assertNotEquals( false, strtotime( $timestamp->getTimestamp( TS_MW ) ) );
-       }
-
-       /**
-        * @covers MWTimestamp::__toString
-        */
-       public function testToString() {
-               $timestamp = new MWTimestamp( '1406833268' ); // Equivalent to 20140731190108
-               $this->assertEquals( '1406833268', $timestamp->__toString() );
-       }
-
-       public static function provideValidTimestampDifferences() {
-               return [
-                       [ '1406833268', '1406833269', '00 00 00 01' ],
-                       [ '1406833268', '1406833329', '00 00 01 01' ],
-                       [ '1406833268', '1406836929', '00 01 01 01' ],
-                       [ '1406833268', '1406923329', '01 01 01 01' ],
-               ];
-       }
-
-       /**
-        * @dataProvider provideValidTimestampDifferences
-        * @covers MWTimestamp::diff
-        */
-       public function testDiff( $timestamp1, $timestamp2, $expected ) {
-               $timestamp1 = new MWTimestamp( $timestamp1 );
-               $timestamp2 = new MWTimestamp( $timestamp2 );
-               $diff = $timestamp1->diff( $timestamp2 );
-               $this->assertEquals( $expected, $diff->format( '%D %H %I %S' ) );
-       }
-
-       /**
-        * Test parsing of valid timestamps and outputing to MW format.
-        * @dataProvider provideValidTimestamps
-        * @covers MWTimestamp::getTimestamp
-        */
-       public function testValidParse( $format, $original, $expected ) {
-               $timestamp = new MWTimestamp( $original );
-               $this->assertEquals( $expected, $timestamp->getTimestamp( TS_MW ) );
-       }
-
-       /**
-        * Test outputting valid timestamps to different formats.
-        * @dataProvider provideValidTimestamps
-        * @covers MWTimestamp::getTimestamp
-        */
-       public function testValidOutput( $format, $expected, $original ) {
-               $timestamp = new MWTimestamp( $original );
-               $this->assertEquals( $expected, (string)$timestamp->getTimestamp( $format ) );
-       }
-
-       /**
-        * Test an invalid timestamp.
-        * @expectedException TimestampException
-        * @covers MWTimestamp
-        */
-       public function testInvalidParse() {
-               new MWTimestamp( "This is not a timestamp." );
-       }
-
-       /**
-        * Test an out of range timestamp
-        * @dataProvider provideOutOfRangeTimestamps
-        * @expectedException TimestampException
-        * @covers MWTimestamp
-        */
-       public function testOutOfRangeTimestamps( $format, $input ) {
-               $timestamp = new MWTimestamp( $input );
-               $timestamp->getTimestamp( $format );
-       }
-
-       /**
-        * Test requesting an invalid output format.
-        * @expectedException TimestampException
-        * @covers MWTimestamp::getTimestamp
-        */
-       public function testInvalidOutput() {
-               $timestamp = new MWTimestamp( '1343761268' );
-               $timestamp->getTimestamp( 98 );
-       }
-
-       /**
-        * Returns a list of valid timestamps in the format:
-        * [ type, timestamp_of_type, timestamp_in_MW ]
-        */
-       public static function provideValidTimestamps() {
-               return [
-                       // Various formats
-                       [ TS_UNIX, '1343761268', '20120731190108' ],
-                       [ TS_MW, '20120731190108', '20120731190108' ],
-                       [ TS_DB, '2012-07-31 19:01:08', '20120731190108' ],
-                       [ TS_ISO_8601, '2012-07-31T19:01:08Z', '20120731190108' ],
-                       [ TS_ISO_8601_BASIC, '20120731T190108Z', '20120731190108' ],
-                       [ TS_EXIF, '2012:07:31 19:01:08', '20120731190108' ],
-                       [ TS_RFC2822, 'Tue, 31 Jul 2012 19:01:08 GMT', '20120731190108' ],
-                       [ TS_ORACLE, '31-07-2012 19:01:08.000000', '20120731190108' ],
-                       [ TS_POSTGRES, '2012-07-31 19:01:08 GMT', '20120731190108' ],
-                       // Some extremes and weird values
-                       [ TS_ISO_8601, '9999-12-31T23:59:59Z', '99991231235959' ],
-                       [ TS_UNIX, '-62135596801', '00001231235959' ]
-               ];
-       }
-
-       /**
-        * Returns a list of out of range timestamps in the format:
-        * [ type, timestamp_of_type ]
-        */
-       public static function provideOutOfRangeTimestamps() {
-               return [
-                       // Various formats
-                       [ TS_MW, '-62167219201' ], // -0001-12-31T23:59:59Z
-                       [ TS_MW, '253402300800' ], // 10000-01-01T00:00:00Z
-               ];
-       }
-
        /**
         * @dataProvider provideHumanTimestampTests
         * @covers MWTimestamp::getHumanTimestamp
index 0ec200c..bc43709 100644 (file)
@@ -2,8 +2,11 @@
 /**
  * @group Search
  * @group Database
+ * @covers PrefixSearch
  */
 class PrefixSearchTest extends MediaWikiLangTestCase {
+       const NS_NONCAP = 12346;
+
        private $originalHandlers;
 
        public function addDBDataOnce() {
@@ -31,6 +34,10 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
                $this->insertPage( 'Talk:Example' );
 
                $this->insertPage( 'User:Example' );
+
+               $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Bar' ) );
+               $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Upper' ) );
+               $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'sandbox' ) );
        }
 
        protected function setUp() {
@@ -44,11 +51,17 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
                $this->setMwGlobals( [
                        'wgSpecialPages' => [],
                        'wgHooks' => [],
+                       'wgExtraNamespaces' => [ self::NS_NONCAP => 'NonCap' ],
+                       'wgCapitalLinkOverrides' => [ self::NS_NONCAP => false ],
                ] );
 
                $this->originalHandlers = TestingAccessWrapper::newFromClass( 'Hooks' )->handlers;
                TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = [];
 
+               // Clear caches so that our new namespace appears
+               MWNamespace::getCanonicalNamespaces( true );
+               Language::factory( 'en' )->resetNamespaces();
+
                SpecialPageFactory::resetList();
        }
 
@@ -158,6 +171,29 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
                                        'Special:EditWatchlist/clear',
                                ],
                        ] ],
+                       [ [
+                               'Namespace with case sensitive first letter',
+                               'query' => 'NonCap:upper',
+                               'results' => []
+                       ] ],
+                       [ [
+                               'Multinamespace search',
+                               'query' => 'B',
+                               'results' => [
+                                       'Bar',
+                                       'NonCap:Bar',
+                               ],
+                               'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
+                       ] ],
+                       [ [
+                               'Multinamespace search with lowercase first letter',
+                               'query' => 'sand',
+                               'results' => [
+                                       'Sandbox',
+                                       'NonCap:sandbox',
+                               ],
+                               'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
+                       ] ],
                ];
        }
 
@@ -168,8 +204,11 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
         */
        public function testSearch( array $case ) {
                $this->searchProvision( null );
+
+               $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
+
                $searcher = new StringPrefixSearch;
-               $results = $searcher->search( $case['query'], 3 );
+               $results = $searcher->search( $case['query'], 3, $namespaces );
                $this->assertEquals(
                        $case['results'],
                        $results,
@@ -184,8 +223,11 @@ class PrefixSearchTest extends MediaWikiLangTestCase {
         */
        public function testSearchWithOffset( array $case ) {
                $this->searchProvision( null );
+
+               $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
+
                $searcher = new StringPrefixSearch;
-               $results = $searcher->search( $case['query'], 3, [], 1 );
+               $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
 
                // We don't expect the first result when offsetting
                array_shift( $case['results'] );
index 474a481..ebc2d10 100644 (file)
@@ -645,4 +645,66 @@ class StatusTest extends MediaWikiLangTestCase {
                ];
        }
 
+       /**
+        * @dataProvider provideErrorsWarningsOnly
+        * @covers Status::getErrorsOnlyStatus
+        * @covers Status::getWarningsOnlyStatus
+        */
+       public function testGetErrorsWarningsOnlyStatus( $errorText, $warningText, $type, $errorResult,
+               $warningResult
+       ) {
+               $status = Status::newGood();
+               if ( $errorText ) {
+                       $status->fatal( $errorText );
+               }
+               if ( $warningText ) {
+                       $status->warning( $warningText );
+               }
+               $testStatus = $status->splitByErrorType()[$type];
+               $this->assertEquals( $errorResult, $testStatus->getErrorsByType( 'error' ) );
+               $this->assertEquals( $warningResult, $testStatus->getErrorsByType( 'warning' ) );
+       }
+
+       public static function provideErrorsWarningsOnly() {
+               return [
+                       [
+                               'Just an error',
+                               'Just a warning',
+                               0,
+                               [
+                                       0 => [
+                                               'type' => 'error',
+                                               'message' => 'Just an error',
+                                               'params' => []
+                                       ],
+                               ],
+                               [],
+                       ], [
+                               'Just an error',
+                               'Just a warning',
+                               1,
+                               [],
+                               [
+                                       0 => [
+                                               'type' => 'warning',
+                                               'message' => 'Just a warning',
+                                               'params' => []
+                                       ],
+                               ],
+                       ], [
+                               null,
+                               null,
+                               1,
+                               [],
+                               [],
+                       ], [
+                               null,
+                               null,
+                               0,
+                               [],
+                               [],
+                       ]
+               ];
+       }
+
 }
index 7850f24..7925c6f 100644 (file)
@@ -90,6 +90,8 @@ class TitleTest extends MediaWikiTestCase {
                        [ 'A < B', 'title-invalid-characters' ],
                        [ 'A > B', 'title-invalid-characters' ],
                        [ 'A | B', 'title-invalid-characters' ],
+                       [ "A \t B", 'title-invalid-characters' ],
+                       [ "A \n B", 'title-invalid-characters' ],
                        // URL encoding
                        [ 'A%20B', 'title-invalid-characters' ],
                        [ 'A%23B', 'title-invalid-characters' ],
index c946689..aade490 100644 (file)
@@ -23,8 +23,11 @@ class WebRequestTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideDetectServer
         * @covers WebRequest::detectServer
+        * @covers WebRequest::detectProtocol
         */
        public function testDetectServer( $expected, $input, $description ) {
+               $this->setMwGlobals( 'wgAssumeProxiesUseDefaultProtocolPorts', true );
+
                $_SERVER = $input;
                $result = WebRequest::detectServer();
                $this->assertEquals( $expected, $result, $description );
@@ -63,6 +66,24 @@ class WebRequestTest extends MediaWikiTestCase {
                                ],
                                'Secure off'
                        ],
+                       [
+                               'https://x',
+                               [
+                                       'HTTP_HOST' => 'x',
+                                       'HTTP_X_FORWARDED_PROTO' => 'https',
+                               ],
+                               'Forwarded HTTPS'
+                       ],
+                       [
+                               'https://x',
+                               [
+                                       'HTTP_HOST' => 'x',
+                                       'HTTPS' => 'off',
+                                       'SERVER_PORT' => '81',
+                                       'HTTP_X_FORWARDED_PROTO' => 'https',
+                               ],
+                               'Forwarded HTTPS'
+                       ],
                        [
                                'http://y',
                                [
@@ -104,6 +125,241 @@ class WebRequestTest extends MediaWikiTestCase {
                ];
        }
 
+       protected function mockWebRequest( $data = [] ) {
+               // Cannot use PHPUnit getMockBuilder() as it does not support
+               // overriding protected properties afterwards
+               $reflection = new ReflectionClass( 'WebRequest' );
+               $req = $reflection->newInstanceWithoutConstructor();
+
+               $prop = $reflection->getProperty( 'data' );
+               $prop->setAccessible( true );
+               $prop->setValue( $req, $data );
+
+               $prop = $reflection->getProperty( 'requestTime' );
+               $prop->setAccessible( true );
+               $prop->setValue( $req, microtime( true ) );
+
+               return $req;
+       }
+
+       /**
+        * @covers WebRequest::getElapsedTime
+        */
+       public function testGetElapsedTime() {
+               $req = $this->mockWebRequest();
+               $this->assertGreaterThanOrEqual( 0.0, $req->getElapsedTime() );
+               $this->assertEquals( 0.0, $req->getElapsedTime(), '', /*delta*/ 0.2 );
+       }
+
+       /**
+        * @covers WebRequest::getVal
+        * @covers WebRequest::getGPCVal
+        * @covers WebRequest::normalizeUnicode
+        */
+       public function testGetValNormal() {
+               // Assert that WebRequest normalises GPC data using UtfNormal\Validator
+               $input = "a \x00 null";
+               $normal = "a \xef\xbf\xbd null";
+               $req = $this->mockWebRequest( [ 'x' => $input, 'y' => [ $input, $input ] ] );
+               $this->assertSame( $normal, $req->getVal( 'x' ) );
+               $this->assertNotSame( $input, $req->getVal( 'x' ) );
+               $this->assertSame( [ $normal, $normal ], $req->getArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getVal
+        * @covers WebRequest::getGPCVal
+        */
+       public function testGetVal() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a' ], 'crlf' => "A\r\nb" ] );
+               $this->assertSame( 'Value', $req->getVal( 'x' ), 'Simple value' );
+               $this->assertSame( null, $req->getVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers WebRequest::getRawVal
+        */
+       public function testGetRawVal() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'crlf' => "A\r\nb"
+               ] );
+               $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
+               $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers WebRequest::getArray
+        */
+       public function testGetArray() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a', 'b' ] ] );
+               $this->assertSame( [ 'Value' ], $req->getArray( 'x' ), 'Value becomes array' );
+               $this->assertSame( null, $req->getArray( 'z' ), 'Not found' );
+               $this->assertSame( [ 'a', 'b' ], $req->getArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getIntArray
+        */
+       public function testGetIntArray() {
+               $req = $this->mockWebRequest( [ 'x' => [ 'Value' ], 'y' => [ '0', '4.2', '-2' ] ] );
+               $this->assertSame( [ 0 ], $req->getIntArray( 'x' ), 'Text becomes 0' );
+               $this->assertSame( null, $req->getIntArray( 'z' ), 'Not found' );
+               $this->assertSame( [ 0, 4, -2 ], $req->getIntArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getInt
+        */
+       public function testGetInt() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( 0, $req->getInt( 'x' ), 'Text' );
+               $this->assertSame( 0, $req->getInt( 'y' ), 'Array' );
+               $this->assertSame( 0, $req->getInt( 'z' ), 'Not found' );
+               $this->assertSame( 0, $req->getInt( 'zero' ) );
+               $this->assertSame( 4, $req->getInt( 'answer' ) );
+               $this->assertSame( -2, $req->getInt( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getIntOrNull
+        */
+       public function testGetIntOrNull() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( null, $req->getIntOrNull( 'x' ), 'Text' );
+               $this->assertSame( null, $req->getIntOrNull( 'y' ), 'Array' );
+               $this->assertSame( null, $req->getIntOrNull( 'z' ), 'Not found' );
+               $this->assertSame( 0, $req->getIntOrNull( 'zero' ) );
+               $this->assertSame( 4, $req->getIntOrNull( 'answer' ) );
+               $this->assertSame( -2, $req->getIntOrNull( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getFloat
+        */
+       public function testGetFloat() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( 0.0, $req->getFloat( 'x' ), 'Text' );
+               $this->assertSame( 0.0, $req->getFloat( 'y' ), 'Array' );
+               $this->assertSame( 0.0, $req->getFloat( 'z' ), 'Not found' );
+               $this->assertSame( 0.0, $req->getFloat( 'zero' ) );
+               $this->assertSame( 4.2, $req->getFloat( 'answer' ) );
+               $this->assertSame( -2.0, $req->getFloat( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getBool
+        */
+       public function testGetBool() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'f' => 'false',
+                       't' => 'true',
+               ] );
+               $this->assertSame( true, $req->getBool( 'x' ), 'Text' );
+               $this->assertSame( false, $req->getBool( 'y' ), 'Array' );
+               $this->assertSame( false, $req->getBool( 'z' ), 'Not found' );
+               $this->assertSame( false, $req->getBool( 'zero' ) );
+               $this->assertSame( true, $req->getBool( 'f' ) );
+               $this->assertSame( true, $req->getBool( 't' ) );
+       }
+
+       public static function provideFuzzyBool() {
+               return [
+                       [ 'Text', true ],
+                       [ '', false, '(empty string)' ],
+                       [ '0', false ],
+                       [ '1', true ],
+                       [ 'false', false ],
+                       [ 'true', true ],
+                       [ 'False', false ],
+                       [ 'True', true ],
+                       [ 'FALSE', false ],
+                       [ 'TRUE', true ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideFuzzyBool
+        * @covers WebRequest::getFuzzyBool
+        */
+       public function testGetFuzzyBool( $value, $expected, $message = null ) {
+               $req = $this->mockWebRequest( [ 'x' => $value ] );
+               $this->assertSame( $expected, $req->getFuzzyBool( 'x' ), $message ?: "Value: '$value'" );
+       }
+
+       /**
+        * @covers WebRequest::getFuzzyBool
+        */
+       public function testGetFuzzyBoolDefault() {
+               $req = $this->mockWebRequest();
+               $this->assertSame( false, $req->getFuzzyBool( 'z' ), 'Not found' );
+       }
+
+       /**
+        * @covers WebRequest::getCheck
+        */
+       public function testGetCheck() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'zero' => '0' ] );
+               $this->assertSame( false, $req->getCheck( 'z' ), 'Not found' );
+               $this->assertSame( true, $req->getCheck( 'x' ), 'Text' );
+               $this->assertSame( true, $req->getCheck( 'zero' ) );
+       }
+
+       /**
+        * @covers WebRequest::getText
+        */
+       public function testGetText() {
+               // Avoid FauxRequest (overrides getText)
+               $req = $this->mockWebRequest( [ 'crlf' => "Va\r\nlue" ] );
+               $this->assertSame( "Va\nlue", $req->getText( 'crlf' ), 'CR stripped' );
+       }
+
+       /**
+        * @covers WebRequest::getValues
+        */
+       public function testGetValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+               // Avoid FauxRequest (overrides getValues)
+               $req = $this->mockWebRequest( $values );
+               $this->assertSame( $values, $req->getValues() );
+               $this->assertSame( [ 'x' => 'Value' ], $req->getValues( 'x' ), 'Specific keys' );
+       }
+
+       /**
+        * @covers WebRequest::getValueNames
+        */
+       public function testGetValueNames() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => '' ] );
+               $this->assertSame( [ 'x', 'y' ], $req->getValueNames() );
+               $this->assertSame( [ 'x' ], $req->getValueNames( [ 'y' ] ), 'Exclude keys' );
+       }
+
        /**
         * @dataProvider provideGetIP
         * @covers WebRequest::getIP
@@ -343,6 +599,7 @@ class WebRequestTest extends MediaWikiTestCase {
                                [ 'en-gb' => 1, 'en-us' => '1' ],
                                'Two equally prefered English variants'
                        ],
+                       [ '_', [], 'Invalid input' ],
                ];
        }
 
index 5d1ead0..8b75d56 100644 (file)
@@ -43,4 +43,87 @@ class ApiBaseTest extends ApiTestCase {
                );
        }
 
+       /**
+        * @dataProvider provideGetParameterFromSettings
+        * @param string|null $input
+        * @param array $paramSettings
+        * @param mixed $expected
+        * @param string[] $warnings
+        */
+       public function testGetParameterFromSettings( $input, $paramSettings, $expected, $warnings ) {
+               $mock = new MockApi();
+               $wrapper = TestingAccessWrapper::newFromObject( $mock );
+
+               $context = new DerivativeContext( $mock );
+               $context->setRequest( new FauxRequest( $input !== null ? [ 'foo' => $input ] : [] ) );
+               $wrapper->mMainModule = new ApiMain( $context );
+
+               if ( $expected instanceof UsageException ) {
+                       try {
+                               $wrapper->getParameterFromSettings( 'foo', $paramSettings, true );
+                       } catch ( UsageException $ex ) {
+                               $this->assertEquals( $expected, $ex );
+                       }
+               } else {
+                       $result = $wrapper->getParameterFromSettings( 'foo', $paramSettings, true );
+                       $this->assertSame( $expected, $result );
+                       $this->assertSame( $warnings, $mock->warnings );
+               }
+       }
+
+       public static function provideGetParameterFromSettings() {
+               $warnings = [
+                       'The value passed for \'foo\' contains invalid or non-normalized data. Textual data should ' .
+                       'be valid, NFC-normalized Unicode without C0 control characters other than ' .
+                       'HT (\\t), LF (\\n), and CR (\\r).'
+               ];
+
+               $c0 = '';
+               $enc = '';
+               for ( $i = 0; $i < 32; $i++ ) {
+                       $c0 .= chr( $i );
+                       $enc .= ( $i === 9 || $i === 10 || $i === 13 )
+                               ? chr( $i )
+                               : '�';
+               }
+
+               return [
+                       'Basic param' => [ 'bar', null, 'bar', [] ],
+                       'Basic param, C0 controls' => [ $c0, null, $enc, $warnings ],
+                       'String param' => [ 'bar', '', 'bar', [] ],
+                       'String param, defaulted' => [ null, '', '', [] ],
+                       'String param, empty' => [ '', 'default', '', [] ],
+                       'String param, required, empty' => [
+                               '',
+                               [ ApiBase::PARAM_DFLT => 'default', ApiBase::PARAM_REQUIRED => true ],
+                               new UsageException( 'The foo parameter must be set', 'nofoo' ),
+                               []
+                       ],
+                       'Multi-valued parameter' => [
+                               'a|b|c',
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ 'a', 'b', 'c' ],
+                               []
+                       ],
+                       'Multi-valued parameter, alternative separator' => [
+                               "\x1fa|b\x1fc|d",
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ 'a|b', 'c|d' ],
+                               []
+                       ],
+                       'Multi-valued parameter, other C0 controls' => [
+                               $c0,
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ $enc ],
+                               $warnings
+                       ],
+                       'Multi-valued parameter, other C0 controls (2)' => [
+                               "\x1f" . $c0,
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ substr( $enc, 0, -3 ), '' ],
+                               $warnings
+                       ],
+               ];
+       }
+
 }
index 7a8e208..02d0a0d 100644 (file)
@@ -513,4 +513,57 @@ class ApiEditPageTest extends ApiTestCase {
                $this->assertEquals( "testing-nontext", $page->getContentModel() );
                $this->assertEquals( $data, $page->getContent()->serialize() );
        }
+
+       /**
+        * This test verifies that after changing the content model
+        * of a page, undoing that edit via the API will also
+        * undo the content model change.
+        */
+       public function testUndoAfterContentModelChange() {
+               $name = 'Help:' . __FUNCTION__;
+               $uploader = self::$users['uploader']->getUser();
+               $sysop = self::$users['sysop']->getUser();
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'text' => 'some text',
+               ], null, $sysop )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               // Content model is wikitext
+               $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
+
+               // Convert the page to JSON
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'text' => '{}',
+                       'contentmodel' => 'json',
+               ], null, $uploader )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               $this->assertEquals( 'json', $apiResult['edit']['contentmodel'] );
+
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'undo' => $apiResult['edit']['newrevid']
+               ], null, $sysop )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               // Check that the contentmodel is back to wikitext now.
+               $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
+       }
 }
index 18da5af..d13b00b 100644 (file)
@@ -132,7 +132,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'err' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'error',
@@ -142,7 +142,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'string' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -154,7 +154,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'errWithData' => [
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
@@ -165,7 +165,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'messageWithData' => [
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
@@ -174,7 +174,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'message' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -182,12 +182,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'foo' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -199,12 +199,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'status' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        $I => 'error',
@@ -214,17 +214,17 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'status' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
index 487ab84..97681eb 100644 (file)
@@ -231,10 +231,11 @@ class ApiLoginTest extends ApiTestCase {
                $centralId = CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() );
                $this->assertNotEquals( 0, $centralId, 'sanity check' );
 
+               $password = 'ngfhmjm64hv0854493hsj5nncjud2clk';
                $passwordFactory = new PasswordFactory();
                $passwordFactory->init( RequestContext::getMain()->getConfig() );
                // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
-               $passwordHash = $passwordFactory->newFromPlaintext( 'foobaz' );
+               $passwordHash = $passwordFactory->newFromPlaintext( $password );
 
                $dbw = wfGetDB( DB_MASTER );
                $dbw->insert(
@@ -255,7 +256,7 @@ class ApiLoginTest extends ApiTestCase {
                $ret = $this->doApiRequest( [
                        'action' => 'login',
                        'lgname' => $lgName,
-                       'lgpassword' => 'foobaz',
+                       'lgpassword' => $password,
                ] );
 
                $result = $ret[0];
@@ -270,7 +271,7 @@ class ApiLoginTest extends ApiTestCase {
                        'action' => 'login',
                        'lgtoken' => $token,
                        'lgname' => $lgName,
-                       'lgpassword' => 'foobaz',
+                       'lgpassword' => $password,
                ], $ret[2] );
 
                $result = $ret[0];
index 367210a..ad1deee 100644 (file)
@@ -75,4 +75,25 @@ class ApiPageSetTest extends ApiTestCase {
 
                return [ $target, $pageSet ];
        }
+
+       public function testHandleNormalization() {
+               $context = new RequestContext();
+               $context->setRequest( new FauxRequest( [ 'titles' => "a|B|a\xcc\x8a" ] ) );
+               $main = new ApiMain( $context );
+               $pageSet = new ApiPageSet( $main );
+               $pageSet->execute();
+
+               $this->assertSame(
+                       [ 0 => [ 'A' => -1, 'B' => -2, 'Å' => -3 ] ],
+                       $pageSet->getAllTitlesByNamespace()
+               );
+               $this->assertSame(
+                       [
+                               [ 'fromencoded' => true, 'from' => 'a%CC%8A', 'to' => 'å' ],
+                               [ 'fromencoded' => false, 'from' => 'a', 'to' => 'A' ],
+                               [ 'fromencoded' => false, 'from' => 'å', 'to' => 'Å' ],
+                       ],
+                       $pageSet->getNormalizedTitlesAsResult()
+               );
+       }
 }
index 3b21ff8..7687236 100644 (file)
@@ -20,7 +20,11 @@ class ApiQueryAllPagesTest extends ApiTestCase {
        public function testPrefixNormalizationSearchBug() {
                $title = Title::newFromText( 'Category:Template:xyz' );
                $page = WikiPage::factory( $title );
-               $page->doEdit( 'Some text', 'inserting content' );
+
+               $page->doEditContent(
+                       ContentHandler::makeContent( 'Some text', $page->getTitle() ),
+                       'inserting content'
+               );
 
                $result = $this->doApiRequest( [
                        'action' => 'query',
index 9a64d08..d7db538 100644 (file)
@@ -1,12 +1,18 @@
 <?php
 
 class MockApi extends ApiBase {
+       public $warnings = [];
+
        public function execute() {
        }
 
        public function __construct() {
        }
 
+       public function setWarning( $warning ) {
+               $this->warnings[] = $warning;
+       }
+
        public function getAllowedParams() {
                return [
                        'filename' => null,
index a6f22b1..38a1d68 100644 (file)
@@ -15,7 +15,11 @@ class ApiQueryRevisionsTest extends ApiTestCase {
                $pageName = 'Help:' . __METHOD__;
                $title = Title::newFromText( $pageName );
                $page = WikiPage::factory( $title );
-               $page->doEdit( 'Some text', 'inserting content' );
+
+               $page->doEditContent(
+                       ContentHandler::makeContent( 'Some text', $page->getTitle() ),
+                       'inserting content'
+               );
 
                $apiResult = $this->doApiRequest( [
                        'action' => 'query',
index 504b16a..8cb2327 100644 (file)
@@ -43,6 +43,7 @@ class ApiQueryTest extends ApiTestCase {
 
                $this->assertEquals(
                        [
+                               'fromencoded' => false,
                                'from' => 'Project:articleA',
                                'to' => $to->getPrefixedText(),
                        ],
@@ -51,6 +52,7 @@ class ApiQueryTest extends ApiTestCase {
 
                $this->assertEquals(
                        [
+                               'fromencoded' => false,
                                'from' => 'article_B',
                                'to' => 'Article B'
                        ],
index 788d304..f679f63 100644 (file)
@@ -2709,9 +2709,11 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $session->clear();
                $user = $this->getMock( 'User', [ 'addToDatabase' ] );
                $user->expects( $this->once() )->method( 'addToDatabase' )
-                       ->will( $this->returnCallback( function () use ( $username ) {
-                               $status = \User::newFromName( $username )->addToDatabase();
+                       ->will( $this->returnCallback( function () use ( $username, &$user ) {
+                               $oldUser = \User::newFromName( $username );
+                               $status = $oldUser->addToDatabase();
                                $this->assertTrue( $status->isOK(), 'sanity check' );
+                               $user->setId( $oldUser->getId() );
                                return \Status::newFatal( 'userexists' );
                        } ) );
                $user->setName( $username );
index 477b161..194b49e 100644 (file)
@@ -16,6 +16,7 @@ class AuthenticationResponseTest extends \MediaWikiTestCase {
        public function testConstructors( $constructor, $args, $expect ) {
                if ( is_array( $expect ) ) {
                        $res = new AuthenticationResponse();
+                       $res->messageType = 'warning';
                        foreach ( $expect as $field => $value ) {
                                $res->$field = $value;
                        }
@@ -51,6 +52,7 @@ class AuthenticationResponseTest extends \MediaWikiTestCase {
                        [ 'newFail', [ $msg ], [
                                'status' => AuthenticationResponse::FAIL,
                                'message' => $msg,
+                               'messageType' => 'error',
                        ] ],
 
                        [ 'newRestart', [ $msg ], [
@@ -66,6 +68,21 @@ class AuthenticationResponseTest extends \MediaWikiTestCase {
                                'status' => AuthenticationResponse::UI,
                                'neededRequests' => [ $req ],
                                'message' => $msg,
+                               'messageType' => 'warning',
+                       ] ],
+
+                       [ 'newUI', [ [ $req ], $msg, 'warning' ], [
+                               'status' => AuthenticationResponse::UI,
+                               'neededRequests' => [ $req ],
+                               'message' => $msg,
+                               'messageType' => 'warning',
+                       ] ],
+
+                       [ 'newUI', [ [ $req ], $msg, 'error' ], [
+                               'status' => AuthenticationResponse::UI,
+                               'neededRequests' => [ $req ],
+                               'message' => $msg,
+                               'messageType' => 'error',
                        ] ],
                        [ 'newUI', [ [], $msg ],
                                new \InvalidArgumentException( '$reqs may not be empty' )
diff --git a/tests/phpunit/includes/content/JsonContentHandlerTest.php b/tests/phpunit/includes/content/JsonContentHandlerTest.php
new file mode 100644 (file)
index 0000000..abfb673
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+class JsonContentHandlerTest extends MediaWikiTestCase {
+
+       /**
+        * @covers JsonContentHandler::makeEmptyContent
+        */
+       public function testMakeEmptyContent() {
+               $handler = new JsonContentHandler();
+               $content = $handler->makeEmptyContent();
+               $this->assertInstanceOf( JsonContent::class, $content );
+               $this->assertTrue( $content->isValid() );
+       }
+}
index 4301fb8..49907c8 100644 (file)
@@ -49,6 +49,19 @@ END;
                $this->assertContains( "Wikitext in Heading and also html", $headings );
        }
 
+       public function testDefaultSort() {
+               $text = <<<END
+Louise Michel
+== Heading one ==
+Some text
+==== See also ====
+* Also things to see!
+{{DEFAULTSORT:Michel, Louise}}
+END;
+               $struct = $this->getStructure( $text );
+               $this->assertEquals( "Michel, Louise", $struct->getDefaultSort() );
+       }
+
        public function testHeadingsFirst() {
                $text = <<<END
 == Heading one ==
index bc7542a..f13ead4 100644 (file)
@@ -31,6 +31,8 @@
 class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        // From DatabaseBase
        function __construct() {
+               $this->profiler = new ProfilerStub( [] );
+               $this->trxProfiler = new TransactionProfiler();
        }
 
        protected function closeConnection() {
@@ -167,15 +169,11 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                        ->setMethods( [ 'fetchRow', 'query' ] )
                        ->getMock();
 
-               $db->expects( $this->any() )
-                       ->method( 'query' )
+               $db->method( 'query' )
                        ->with( $this->anything() )
-                       ->will(
-                               $this->returnValue( null )
-                       );
+                       ->willReturn( null );
 
-               $db->expects( $this->any() )
-                       ->method( 'fetchRow' )
+               $db->method( 'fetchRow' )
                        ->with( $this->anything() )
                        ->will( $this->onConsecutiveCalls(
                                [ 'Tables_in_' => 'view1' ],
@@ -359,13 +357,11 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                                'getLagDetectionMethod', 'getHeartbeatData', 'getMasterServerInfo' ] )
                        ->getMock();
 
-               $db->expects( $this->any() )
-                       ->method( 'getLagDetectionMethod' )
-                       ->will( $this->returnValue( 'pt-heartbeat' ) );
+               $db->method( 'getLagDetectionMethod' )
+                       ->willReturn( 'pt-heartbeat' );
 
-               $db->expects( $this->any() )
-                       ->method( 'getMasterServerInfo' )
-                       ->will( $this->returnValue( [ 'serverId' => 172, 'asOf' => time() ] ) );
+               $db->method( 'getMasterServerInfo' )
+                       ->willReturn( [ 'serverId' => 172, 'asOf' => time() ] );
 
                // Fake the current time.
                list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
@@ -379,10 +375,9 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                $ptTimeISO = $ptDateTime->format( 'Y-m-d\TH:i:s' );
                $ptTimeISO .= ltrim( number_format( $ptSecFrac, 6 ), '0' );
 
-               $db->expects( $this->any() )
-                       ->method( 'getHeartbeatData' )
+               $db->method( 'getHeartbeatData' )
                        ->with( [ 'server_id' => 172 ] )
-                       ->will( $this->returnValue( [ $ptTimeISO, $now ] ) );
+                       ->willReturn( [ $ptTimeISO, $now ] );
 
                $db->setLBInfo( 'clusterMasterHost', 'db1052' );
                $lagEst = $db->getLag();
index 5d5521c..0013685 100644 (file)
@@ -5,15 +5,12 @@
  * This is a non DBMS depending test.
  */
 class DatabaseSQLTest extends MediaWikiTestCase {
-
-       /**
-        * @var DatabaseTestHelper
-        */
+       /** @var DatabaseTestHelper */
        private $database;
 
        protected function setUp() {
                parent::setUp();
-               $this->database = new DatabaseTestHelper( __CLASS__ );
+               $this->database = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => true ] );
        }
 
        protected function assertLastSql( $sqlText ) {
@@ -23,6 +20,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                );
        }
 
+       protected function assertLastSqlDb( $sqlText, $db ) {
+               $this->assertEquals( $db->getLastSqls(), $sqlText );
+       }
+
        /**
         * @dataProvider provideSelect
         * @covers DatabaseBase::select
@@ -354,7 +355,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideInsertSelect
         * @covers DatabaseBase::insertSelect
         */
-       public function testInsertSelect( $sql, $sqlText ) {
+       public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
                $this->database->insertSelect(
                        $sql['destTable'],
                        $sql['srcTable'],
@@ -364,7 +365,22 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                        isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
                        isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
                );
-               $this->assertLastSql( $sqlText );
+               $this->assertLastSql( $sqlTextNative );
+
+               $dbWeb = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => false ] );
+               $dbWeb->forceNextResult( [
+                       array_flip( array_keys( $sql['varMap'] ) )
+               ] );
+               $dbWeb->insertSelect(
+                       $sql['destTable'],
+                       $sql['srcTable'],
+                       $sql['varMap'],
+                       $sql['conds'],
+                       __METHOD__,
+                       isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
+                       isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
+               );
+               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
        }
 
        public static function provideInsertSelect() {
@@ -379,7 +395,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                "INSERT INTO insert_table " .
                                        "(field_insert,field) " .
                                        "SELECT field_select,field2 " .
-                                       "FROM select_table"
+                                       "FROM select_table",
+                               "SELECT field_select AS field_insert,field2 AS field " .
+                               "FROM select_table WHERE *   FOR UPDATE",
+                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
                        ],
                        [
                                [
@@ -392,7 +411,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                        "(field_insert,field) " .
                                        "SELECT field_select,field2 " .
                                        "FROM select_table " .
-                                       "WHERE field = '2'"
+                                       "WHERE field = '2'",
+                               "SELECT field_select AS field_insert,field2 AS field FROM " .
+                               "select_table WHERE field = '2'   FOR UPDATE",
+                               "INSERT INTO insert_table (field_insert,field) VALUES ('0','1')"
                        ],
                        [
                                [
@@ -408,7 +430,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                        "SELECT field_select,field2 " .
                                        "FROM select_table " .
                                        "WHERE field = '2' " .
-                                       "ORDER BY field"
+                                       "ORDER BY field",
+                               "SELECT field_select AS field_insert,field2 AS field " .
+                               "FROM select_table WHERE field = '2' ORDER BY field  FOR UPDATE",
+                               "INSERT IGNORE INTO insert_table (field_insert,field) VALUES ('0','1')"
                        ],
                ];
        }
@@ -711,7 +736,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
        public function testDropTable() {
                $this->database->setExistingTables( [ 'table' ] );
                $this->database->dropTable( 'table', __METHOD__ );
-               $this->assertLastSql( 'DROP TABLE table' );
+               $this->assertLastSql( 'DROP TABLE table CASCADE' );
        }
 
        /**
index 0751409..134caf4 100644 (file)
@@ -25,6 +25,7 @@ class DatabaseTest extends MediaWikiTestCase {
                }
                $this->db->restoreFlags( IDatabase::RESTORE_INITIAL );
        }
+
        /**
         * @covers DatabaseBase::dropTable
         */
@@ -68,21 +69,26 @@ class DatabaseTest extends MediaWikiTestCase {
        }
 
        private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) {
-               global $wgSharedDB, $wgSharedTables, $wgSharedPrefix;
-
-               $oldName = $wgSharedDB;
-               $oldTables = $wgSharedTables;
-               $oldPrefix = $wgSharedPrefix;
+               global $wgSharedDB, $wgSharedTables, $wgSharedPrefix, $wgSharedSchema;
 
-               $wgSharedDB = $database;
-               $wgSharedTables = [ $table ];
-               $wgSharedPrefix = $prefix;
+               $this->db->setTableAliases( [
+                       $table => [
+                               'dbname' => $database,
+                               'schema' => null,
+                               'prefix' => $prefix
+                       ]
+               ] );
 
                $ret = $this->db->tableName( $table, $format );
 
-               $wgSharedDB = $oldName;
-               $wgSharedTables = $oldTables;
-               $wgSharedPrefix = $oldPrefix;
+               $this->db->setTableAliases( array_fill_keys(
+                       $wgSharedDB ? $wgSharedTables : [],
+                       [
+                               'dbname' => $wgSharedDB,
+                               'schema' => $wgSharedSchema,
+                               'prefix' => $wgSharedPrefix
+                       ]
+               ) );
 
                return $ret;
        }
@@ -226,7 +232,7 @@ class DatabaseTest extends MediaWikiTestCase {
 
        private function dropFunctions() {
                $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function'
-                               . ( $this->db->getType() == 'postgres' ? '()' : '' )
+                       . ( $this->db->getType() == 'postgres' ? '()' : '' )
                );
        }
 
@@ -242,26 +248,35 @@ class DatabaseTest extends MediaWikiTestCase {
                $db->setFlag( DBO_TRX );
                $called = false;
                $flagSet = null;
-               $db->onTransactionIdle( function() use ( $db, &$flagSet, &$called ) {
-                       $called = true;
-                       $flagSet = $db->getFlag( DBO_TRX );
-               } );
+               $db->onTransactionIdle(
+                       function () use ( $db, &$flagSet, &$called ) {
+                               $called = true;
+                               $flagSet = $db->getFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
                $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
                $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
                $this->assertTrue( $called, 'Callback reached' );
 
                $db->clearFlag( DBO_TRX );
                $flagSet = null;
-               $db->onTransactionIdle( function() use ( $db, &$flagSet ) {
-                       $flagSet = $db->getFlag( DBO_TRX );
-               } );
+               $db->onTransactionIdle(
+                       function () use ( $db, &$flagSet ) {
+                               $flagSet = $db->getFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
                $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
 
                $db->clearFlag( DBO_TRX );
-               $db->onTransactionIdle( function() use ( $db ) {
-                       $db->setFlag( DBO_TRX );
-               } );
+               $db->onTransactionIdle(
+                       function () use ( $db ) {
+                               $db->setFlag( DBO_TRX );
+                       },
+                       __METHOD__
+               );
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
        }
 
@@ -271,7 +286,7 @@ class DatabaseTest extends MediaWikiTestCase {
                $db->clearFlag( DBO_TRX );
                $db->begin( __METHOD__ );
                $called = false;
-               $db->onTransactionResolution( function() use ( $db, &$called ) {
+               $db->onTransactionResolution( function () use ( $db, &$called ) {
                        $called = true;
                        $db->setFlag( DBO_TRX );
                } );
@@ -282,7 +297,7 @@ class DatabaseTest extends MediaWikiTestCase {
                $db->clearFlag( DBO_TRX );
                $db->begin( __METHOD__ );
                $called = false;
-               $db->onTransactionResolution( function() use ( $db, &$called ) {
+               $db->onTransactionResolution( function () use ( $db, &$called ) {
                        $called = true;
                        $db->setFlag( DBO_TRX );
                } );
@@ -291,6 +306,56 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertTrue( $called, 'Callback reached' );
        }
 
+       /**
+        * @covers DatabaseBase::setTransactionListener()
+        */
+       public function testTransactionListener() {
+               $db = $this->db;
+
+               $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
+                       $called = true;
+               } );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Callback still reached' );
+
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->rollback( __METHOD__ );
+               $this->assertTrue( $called, 'Callback reached' );
+
+               $db->setTransactionListener( 'ping', null );
+               $called = false;
+               $db->begin( __METHOD__ );
+               $db->commit( __METHOD__ );
+               $this->assertFalse( $called, 'Callback not reached' );
+       }
+
+       /**
+        * @covers DatabaseBase::flushSnapshot()
+        */
+       public function testFlushSnapshot() {
+               $db = $this->db;
+
+               $db->flushSnapshot( __METHOD__ ); // ok
+               $db->flushSnapshot( __METHOD__ ); // ok
+
+               $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               $db->query( 'SELECT 1', __METHOD__ );
+               $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
+               $db->flushSnapshot( __METHOD__ ); // ok
+               $db->restoreFlags( $db::RESTORE_PRIOR );
+
+               $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
+       }
+
        public function testGetScopedLock() {
                $db = $this->db;
 
@@ -362,4 +427,26 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
                $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
        }
+
+       /**
+        * @covers DatabaseBase::tablePrefix()
+        * @covers DatabaseBase::dbSchema()
+        */
+       public function testMutators() {
+               $old = $this->db->tablePrefix();
+               $this->assertType( 'string', $old, 'Prefix is string' );
+               $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
+               $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
+               $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
+               $this->db->tablePrefix( $old );
+               $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
+
+               $old = $this->db->dbSchema();
+               $this->assertType( 'string', $old, 'Schema is string' );
+               $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
+               $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
+               $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
+               $this->db->dbSchema( $old );
+               $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
+       }
 }
index aa8b8e8..63322cc 100644 (file)
@@ -19,14 +19,21 @@ class DatabaseTestHelper extends DatabaseBase {
         */
        protected $lastSqls = [];
 
+       /** @var array List of row arrays */
+       protected $nextResult = [];
+
        /**
         * Array of tables to be considered as existing by tableExist()
         * Use setExistingTables() to alter.
         */
        protected $tablesExists;
 
-       public function __construct( $testName ) {
+       public function __construct( $testName, array $opts = [] ) {
                $this->testName = $testName;
+
+               $this->profiler = new ProfilerStub( [] );
+               $this->trxProfiler = new TransactionProfiler();
+               $this->cliMode = isset( $opts['cliMode'] ) ? $opts['cliMode'] : true;
        }
 
        /**
@@ -44,6 +51,13 @@ class DatabaseTestHelper extends DatabaseBase {
                $this->tablesExists = (array)$tablesExists;
        }
 
+       /**
+        * @param mixed $res Use an array of row arrays to set row result
+        */
+       public function forceNextResult( $res ) {
+               $this->nextResult = $res;
+       }
+
        protected function addSql( $sql ) {
                // clean up spaces before and after some words and the whole string
                $this->lastSqls[] = trim( preg_replace(
@@ -160,11 +174,19 @@ class DatabaseTestHelper extends DatabaseBase {
                return true;
        }
 
+       function ping( &$rtt = null ) {
+               $rtt = 0.0;
+               return true;
+       }
+
        protected function closeConnection() {
                return false;
        }
 
        protected function doQuery( $sql ) {
-               return [];
+               $res = $this->nextResult;
+               $this->nextResult = [];
+
+               return new FakeResultWrapper( $res );
        }
 }
index 3562ed8..cac2d6d 100644 (file)
@@ -43,7 +43,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                ];
 
                $this->hideDeprecated( '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details' );
-               $result = LBFactory::getLBFactoryClass( $config );
+               $result = LBFactoryMW::getLBFactoryClass( $config );
 
                $this->assertEquals( $expected, $result );
        }
@@ -54,7 +54,6 @@ class LBFactoryTest extends MediaWikiTestCase {
                        [ 'LBFactorySimple', 'LBFactory_Simple' ],
                        [ 'LBFactorySingle', 'LBFactory_Single' ],
                        [ 'LBFactoryMulti', 'LBFactory_Multi' ],
-                       [ 'LBFactoryFake', 'LBFactory_Fake' ],
                ];
        }
 
@@ -107,7 +106,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                        $wgDBserver, $dbw->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
                $dbr = $lb->getConnection( DB_SLAVE );
-               $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+               $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
                $this->assertEquals(
                        $wgDBserver, $dbr->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
@@ -145,7 +144,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
 
                $dbr = $lb->getConnection( DB_SLAVE );
-               $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+               $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
 
                $factory->shutdown();
                $lb->closeAll();
@@ -155,25 +154,31 @@ class LBFactoryTest extends MediaWikiTestCase {
                // (a) First HTTP request
                $mPos = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
 
+               $now = microtime( true );
                $mockDB = $this->getMockBuilder( 'DatabaseMysql' )
                        ->disableOriginalConstructor()
                        ->getMock();
-               $mockDB->expects( $this->any() )
-                       ->method( 'doneWrites' )->will( $this->returnValue( true ) );
-               $mockDB->expects( $this->any() )
-                       ->method( 'getMasterPos' )->will( $this->returnValue( $mPos ) );
+               $mockDB->method( 'writesOrCallbacksPending' )->willReturn( true );
+               $mockDB->method( 'lastDoneWrites' )->willReturn( $now );
+               $mockDB->method( 'getMasterPos' )->willReturn( $mPos );
 
                $lb = $this->getMockBuilder( 'LoadBalancer' )
                        ->disableOriginalConstructor()
                        ->getMock();
-               $lb->expects( $this->any() )
-                       ->method( 'getConnection' )->will( $this->returnValue( $mockDB ) );
-               $lb->expects( $this->any() )
-                       ->method( 'getServerCount' )->will( $this->returnValue( 2 ) );
-               $lb->expects( $this->any() )
-                       ->method( 'parentInfo' )->will( $this->returnValue( [ 'id' => "main-DEFAULT" ] ) );
-               $lb->expects( $this->any() )
-                       ->method( 'getAnyOpenConnection' )->will( $this->returnValue( $mockDB ) );
+               $lb->method( 'getConnection' )->willReturn( $mockDB );
+               $lb->method( 'getServerCount' )->willReturn( 2 );
+               $lb->method( 'parentInfo' )->willReturn( [ 'id' => "main-DEFAULT" ] );
+               $lb->method( 'getAnyOpenConnection' )->willReturn( $mockDB );
+               $lb->method( 'hasOrMadeRecentMasterChanges' )->will( $this->returnCallback(
+                               function () use ( $mockDB ) {
+                                       $p = 0;
+                                       $p |= call_user_func( [ $mockDB, 'writesOrCallbacksPending' ] );
+                                       $p |= call_user_func( [ $mockDB, 'lastDoneWrites' ] );
+
+                                       return (bool)$p;
+                               }
+                       ) );
+               $lb->method( 'getMasterPos' )->willReturn( $mPos );
 
                $bag = new HashBagOStuff();
                $cp = new ChronologyProtector(
@@ -184,7 +189,8 @@ class LBFactoryTest extends MediaWikiTestCase {
                        ]
                );
 
-               $mockDB->expects( $this->exactly( 2 ) )->method( 'doneWrites' );
+               $mockDB->expects( $this->exactly( 2 ) )->method( 'writesOrCallbacksPending' );
+               $mockDB->expects( $this->exactly( 2 ) )->method( 'lastDoneWrites' );
 
                // Nothing to wait for
                $cp->initLB( $lb );
@@ -210,4 +216,146 @@ class LBFactoryTest extends MediaWikiTestCase {
                $cp->shutdownLB( $lb );
                $cp->shutdown();
        }
+
+       private function newLBFactoryMulti( array $baseOverride = [], array $serverOverride = [] ) {
+               global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype;
+
+               return new LBFactoryMulti( $baseOverride + [
+                       'sectionsByDB' => [],
+                       'sectionLoads' => [
+                               'DEFAULT' => [
+                                       'test-db1' => 1,
+                               ],
+                       ],
+                       'serverTemplate' => $serverOverride + [
+                               'dbname' => $wgDBname,
+                               'user' => $wgDBuser,
+                               'password' => $wgDBpassword,
+                               'type' => $wgDBtype,
+                               'flags' => DBO_DEFAULT
+                       ],
+                       'hostsByName' => [
+                               'test-db1' => $wgDBserver,
+                       ],
+                       'loadMonitorClass' => 'LoadMonitorNull',
+                       'localDomain' => wfWikiID()
+               ] );
+       }
+
+       public function testNiceDomains() {
+               global $wgDBname;
+
+               $factory = $this->newLBFactoryMulti();
+               $lb = $factory->getMainLB();
+
+               $db = $lb->getConnectionRef( DB_MASTER );
+               $this->assertEquals(
+                       $wgDBname,
+                       $db->getDomainID()
+               );
+               unset( $db );
+
+               /** @var DatabaseBase $db */
+               $db = $lb->getConnection( DB_MASTER, [], '' );
+
+               $this->assertEquals(
+                       '',
+                       $db->getDomainID()
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'page' ),
+                       "Correct full table name"
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( $wgDBname ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( "$wgDBname.page" ),
+                       "Correct full table name"
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'nice_db' ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'nice_db.page' ),
+                       "Correct full table name"
+               );
+
+               $factory->setDomainPrefix( 'my_' );
+               $this->assertEquals(
+                       '',
+                       $db->getDomainID()
+               );
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'my_page' ),
+                       $db->tableName( 'page' ),
+                       "Correct full table name"
+               );
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'other_nice_db' ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'other_nice_db.page' ),
+                       "Correct full table name"
+               );
+
+               $factory->closeAll();
+               $factory->destroy();
+       }
+
+       public function testTrickyDomain() {
+               $dbname = 'unittest-domain';
+               $factory = $this->newLBFactoryMulti(
+                       [ 'localDomain' => $dbname ], [ 'dbname' => $dbname ] );
+               $lb = $factory->getMainLB();
+               /** @var DatabaseBase $db */
+               $db = $lb->getConnection( DB_MASTER, [], '' );
+
+               $this->assertEquals(
+                       '',
+                       $db->getDomainID()
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'page' ),
+                       "Correct full table name"
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( $dbname ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( "$dbname.page" ),
+                       "Correct full table name"
+               );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'nice_db' ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'nice_db.page' ),
+                       "Correct full table name"
+               );
+
+               $factory->setDomainPrefix( 'my_' );
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'my_page' ),
+                       $db->tableName( 'page' ),
+                       "Correct full table name"
+               );
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'other_nice_db' ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'other_nice_db.page' ),
+                       "Correct full table name"
+               );
+
+               \MediaWiki\suppressWarnings();
+               $this->assertFalse( $db->selectDB( 'garbage-db' ) );
+               \MediaWiki\restoreWarnings();
+
+               $this->assertEquals(
+                       $db->addIdentifierQuotes( 'garbage-db' ) . '.' . $db->addIdentifierQuotes( 'page' ),
+                       $db->tableName( 'garbage-db.page' ),
+                       "Correct full table name"
+               );
+
+               $factory->closeAll();
+               $factory->destroy();
+       }
 }
index ecc67b7..4227693 100644 (file)
@@ -5,10 +5,15 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                $this->setMwGlobals( 'wgCommandLineMode', false );
 
                $updates = [
-                       '1' => 'deferred update 1',
-                       '2' => 'deferred update 2',
-                       '3' => 'deferred update 3',
-                       '2-1' => 'deferred update 1 within deferred update 2',
+                       '1' => "deferred update 1;\n",
+                       '2' => "deferred update 2;\n",
+                       '2-1' => "deferred update 1 within deferred update 2;\n",
+                       '2-2' => "deferred update 2 within deferred update 2;\n",
+                       '3' => "deferred update 3;\n",
+                       '3-1' => "deferred update 1 within deferred update 3;\n",
+                       '3-2' => "deferred update 2 within deferred update 3;\n",
+                       '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+                       '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
                ];
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
@@ -23,14 +28,41 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                                                echo $updates['2-1'];
                                        }
                                );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['2-2'];
+                                       }
+                               );
                        }
                );
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
-                               echo $updates[3];
+                               echo $updates['3'];
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-1'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-1-1'];
+                                                       }
+                                               );
+                                       }
+                               );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-2'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-2-1'];
+                                                       }
+                                               );
+                                       }
+                               );
                        }
                );
 
+               $this->assertEquals( 3, DeferredUpdates::pendingUpdatesCount() );
+
                $this->expectOutputString( implode( '', $updates ) );
 
                DeferredUpdates::doUpdates();
@@ -63,13 +95,20 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
 
        public function testDoUpdatesCLI() {
                $this->setMwGlobals( 'wgCommandLineMode', true );
-
                $updates = [
-                       '1' => 'deferred update 1',
-                       '2' => 'deferred update 2',
-                       '2-1' => 'deferred update 1 within deferred update 2',
-                       '3' => 'deferred update 3',
+                       '1' => "deferred update 1;\n",
+                       '2' => "deferred update 2;\n",
+                       '2-1' => "deferred update 1 within deferred update 2;\n",
+                       '2-2' => "deferred update 2 within deferred update 2;\n",
+                       '3' => "deferred update 3;\n",
+                       '3-1' => "deferred update 1 within deferred update 3;\n",
+                       '3-2' => "deferred update 2 within deferred update 3;\n",
+                       '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+                       '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
                ];
+
+               wfGetLBFactory()->commitMasterChanges( __METHOD__ ); // clear anything
+
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
                                echo $updates['1'];
@@ -83,11 +122,36 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                                                echo $updates['2-1'];
                                        }
                                );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['2-2'];
+                                       }
+                               );
                        }
                );
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
-                               echo $updates[3];
+                               echo $updates['3'];
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-1'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-1-1'];
+                                                       }
+                                               );
+                                       }
+                               );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-2'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-2-1'];
+                                                       }
+                                               );
+                                       }
+                               );
                        }
                );
 
index 3309352..9cc3ffd 100644 (file)
@@ -369,10 +369,7 @@ class LinksUpdateTest extends MediaWikiLangTestCase {
        ) {
                $update = new LinksUpdate( $title, $parserOutput );
 
-               // NOTE: make sure LinksUpdate does not generate warnings when called inside a transaction.
-               $update->beginTransaction();
                $update->doUpdate();
-               $update->commitTransaction();
 
                $this->assertSelect( $table, $fields, $condition, $expectedRows );
                return $update;
index c5fd369..6520610 100644 (file)
@@ -206,7 +206,7 @@ class FileTest extends MediaWikiMediaTestCase {
                        ] ],
                        [ [
                                'supportsBucketing' => true,
-                               'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' + rand() ],
+                               'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' . rand() ],
                                'thumbnailBucket' => 1024,
                                'physicalWidth' => 2048,
                                'expectedPath' => 'fsFilePath',
index 1ebe551..9a48930 100644 (file)
@@ -32,12 +32,35 @@ class SamplingStatsdClientTest extends PHPUnit_Framework_TestCase {
 
                return [
                        // $data, $sampleRate, $seed, $expectWrite
-                       [ $unsampled, 1, 0 /*0.44*/, $unsampled ],
-                       [ $sampled, 1, 0 /*0.44*/, null ],
-                       [ $sampled, 1, 4 /*0.03*/, $sampled ],
-                       [ $unsampled, 0.1, 4 /*0.03*/, $sampled ],
-                       [ $sampled, 0.5, 0 /*0.44*/, null ],
-                       [ $sampled, 0.5, 4 /*0.03*/, $sampled ],
+                       [ $unsampled, 1, 0 /*0.44*/, true ],
+                       [ $sampled, 1, 0 /*0.44*/, false ],
+                       [ $sampled, 1, 4 /*0.03*/, true ],
+                       [ $unsampled, 0.1, 0 /*0.44*/, false ],
+                       [ $sampled, 0.5, 0 /*0.44*/, false ],
+                       [ $sampled, 0.5, 4 /*0.03*/, false ],
                ];
        }
+
+       public function testSetSamplingRates() {
+               $matching = new StatsdData();
+               $matching->setKey( 'foo.bar' );
+               $matching->setValue( 1 );
+
+               $nonMatching = new StatsdData();
+               $nonMatching->setKey( 'oof.bar' );
+               $nonMatching->setValue( 1 );
+
+               $sender = $this->getMock( 'Liuggio\StatsdClient\Sender\SenderInterface' );
+               $sender->expects( $this->any() )->method( 'open' )->will( $this->returnValue( true ) );
+               $sender->expects( $this->once() )->method( 'write' )->with( $this->anything(),
+                       $this->equalTo( $nonMatching ) );
+
+               $client = new SamplingStatsdClient( $sender );
+               $client->setSamplingRates( [ 'foo.*' => 0.2 ] );
+
+               mt_srand( 0 ); // next random is 0.44
+               $client->send( $matching );
+               mt_srand( 0 );
+               $client->send( $nonMatching );
+       }
 }
diff --git a/tests/phpunit/includes/libs/WaitConditionLoopTest.php b/tests/phpunit/includes/libs/WaitConditionLoopTest.php
new file mode 100644 (file)
index 0000000..9ce93d6
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+class WaitConditionLoopFakeTime extends WaitConditionLoop {
+       protected $wallClock = 1;
+
+       function __construct( callable $condition, $timeout, array $busyCallbacks ) {
+               parent::__construct( $condition, $timeout, $busyCallbacks );
+       }
+
+       function usleep( $microseconds ) {
+               $this->wallClock += $microseconds / 1e6;
+       }
+
+       function getCpuTime() {
+               return 0.0;
+       }
+
+       function getWallTime() {
+               return $this->wallClock;
+       }
+
+       public function setWallClock( &$timestamp ) {
+               $this->wallClock =& $timestamp;
+       }
+}
+
+class WaitConditionLoopTest extends PHPUnit_Framework_TestCase {
+       public function testCallbackReached() {
+               $wallClock = microtime( true );
+
+               $count = 0;
+               $status = new StatusValue();
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, $status ) {
+                               ++$count;
+                               $status->value = 'cookie';
+
+                               return WaitConditionLoop::CONDITION_REACHED;
+                       },
+                       10.0,
+                       $this->newBusyWork( $x, $y, $z )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+               $this->assertEquals( 1, $count );
+               $this->assertEquals( 'cookie', $status->value );
+               $this->assertEquals( [ 0, 0, 0 ], [ $x, $y, $z ], "No busy work done" );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 1;
+                               ++$count;
+
+                               return $count >= 2 ? WaitConditionLoop::CONDITION_REACHED : false;
+                       },
+                       7.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke(),
+                       "Busy work did not cause timeout" );
+               $this->assertEquals( [ 1, 0, 0 ], [ $x, $y, $z ] );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += .1;
+                               ++$count;
+
+                               return $count > 80 ? true : false;
+                       },
+                       50.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock, $dontCallMe, $badCalls )
+               );
+               $this->assertEquals( 0, $badCalls, "Callback exception not yet called" );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+               $this->assertEquals( 1, $badCalls, "Bad callback ran and was exception caught" );
+
+               try {
+                       $e = null;
+                       $dontCallMe();
+               } catch ( Exception $e ) {
+               }
+
+               $this->assertInstanceOf( 'RunTimeException', $e );
+               $this->assertEquals( 1, $badCalls, "Callback exception cached" );
+       }
+
+       public function testCallbackTimeout() {
+               $count = 0;
+               $wallClock = microtime( true );
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return $count > 300 ? true : false;
+                       },
+                       50.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $loop->setWallClock( $wallClock );
+               $this->assertEquals( $loop::CONDITION_TIMED_OUT, $loop->invoke() );
+               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return true;
+                       },
+                       0.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return $count > 10 ? true : false;
+                       },
+                       0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_FAILED, $loop->invoke() );
+       }
+
+       public function testCallbackAborted() {
+               $x = 0;
+               $wallClock = microtime( true );
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$x, &$wallClock ) {
+                               $wallClock += 2;
+                               ++$x;
+
+                               return $x > 2 ? WaitConditionLoop::CONDITION_ABORTED : false;
+                       },
+                       10.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $loop->setWallClock( $wallClock );
+               $this->assertEquals( $loop::CONDITION_ABORTED, $loop->invoke() );
+       }
+
+       private function newBusyWork(
+               &$x, &$y, &$z, &$wallClock = 1, &$dontCallMe = null, &$badCalls = 0
+       ) {
+               $x = $y = $z = 0;
+               $badCalls = 0;
+
+               $list = [];
+               $list[] = function () use ( &$x, &$wallClock ) {
+                       $wallClock += 1;
+
+                       return ++$x;
+               };
+               $dontCallMe = function () use ( &$badCalls ) {
+                       ++$badCalls;
+                       throw new RuntimeException( "TrollyMcTrollFace" );
+               };
+               $list[] =& $dontCallMe;
+               $list[] = function () use ( &$y, &$wallClock ) {
+                       $wallClock += 15;
+
+                       return ++$y;
+               };
+               $list[] = function () use ( &$z, &$wallClock ) {
+                       $wallClock += 0.1;
+
+                       return ++$z;
+               };
+
+               return $list;
+       }
+}
index 35005f5..99b959b 100644 (file)
@@ -105,6 +105,47 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
        }
 
+       public function testProcessCache() {
+               $hit = 0;
+               $callback = function () use ( &$hit ) {
+                       ++$hit;
+                       return 42;
+               };
+               $keys = [ wfRandomString(), wfRandomString(), wfRandomString() ];
+               $groups = [ 'thiscache:1', 'thatcache:1', 'somecache:1' ];
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 3, $hit );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 3, $hit, "Values cached" );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 6, $hit );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 6, $hit, "New values cached" );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->delete( $key );
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 9, $hit, "Values evicted" );
+       }
+
        /**
         * @dataProvider getWithSetCallback_provider
         * @covers WANObjectCache::getWithSetCallback()
@@ -120,9 +161,15 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $cKey1 = wfRandomString();
                $cKey2 = wfRandomString();
 
+               $priorValue = null;
+               $priorAsOf = null;
                $wasSet = 0;
-               $func = function( $old, &$ttl ) use ( &$wasSet, $value ) {
+               $func = function( $old, &$ttl, &$opts, $asOf )
+                       use ( &$wasSet, &$priorValue, &$priorAsOf, $value )
+               {
                        ++$wasSet;
+                       $priorValue = $old;
+                       $priorAsOf = $asOf;
                        $ttl = 20; // override with another value
                        return $value;
                };
@@ -131,6 +178,8 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $v = $cache->getWithSetCallback( $key, 30, $func, [ 'lockTSE' => 5 ] + $extOpts );
                $this->assertEquals( $value, $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated" );
+               $this->assertFalse( $priorValue, "No prior value" );
+               $this->assertNull( $priorAsOf, "No prior value" );
 
                $curTTL = null;
                $cache->get( $key, $curTTL );
@@ -153,6 +202,8 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                );
                $this->assertEquals( $value, $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated due to check keys" );
+               $this->assertEquals( $value, $priorValue, "Has prior value" );
+               $this->assertType( 'float', $priorAsOf, "Has prior value" );
                $t1 = $cache->getCheckKeyTime( $cKey1 );
                $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check keys generated on miss' );
                $t2 = $cache->getCheckKeyTime( $cKey2 );
@@ -722,4 +773,32 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $wanCache->getWithSetCallback( 'p', 30, $valFunc );
                $wanCache->getCheckKeyTime( 'zzz' );
        }
+
+       /**
+        * @dataProvider provideAdaptiveTTL
+        * @covers WANObjectCache::adaptiveTTL()
+        */
+       public function testAdaptiveTTL( $ago, $maxTTL, $minTTL, $factor, $adaptiveTTL ) {
+               $mtime = is_int( $ago ) ? time() - $ago : $ago;
+               $margin = 5;
+               $ttl = $this->cache->adaptiveTTL( $mtime, $maxTTL, $minTTL, $factor );
+
+               $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
+               $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
+
+               $ttl = $this->cache->adaptiveTTL( (string)$mtime, $maxTTL, $minTTL, $factor );
+
+               $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
+               $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
+       }
+
+       public static function provideAdaptiveTTL() {
+               return [
+                       [ 3600, 900, 30, .2, 720 ],
+                       [ 3600, 500, 30, .2, 500 ],
+                       [ 3600, 86400, 800, .2, 800 ],
+                       [ false, 86400, 800, .2, 800 ],
+                       [ null, 86400, 800, .2, 800 ]
+               ];
+       }
 }
diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php
new file mode 100644 (file)
index 0000000..d13fbf9
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @covers DatabaseDomain
+ */
+class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
+       public static function provideConstruct() {
+               return [
+                       // All strings
+                       [ 'foo', 'bar', 'baz', 'foo-bar-baz' ],
+                       // Nothing
+                       [ null, null, '', '' ],
+                       // Invalid $database
+                       [ 0, 'bar', '', '', true ],
+                       // - in one of the fields
+                       [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ],
+                       // ? in one of the fields
+                       [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideConstruct
+        */
+       public function testConstruct( $db, $schema, $prefix, $id, $exception = false ) {
+               if ( $exception ) {
+                       $this->setExpectedException( InvalidArgumentException::class );
+               }
+
+               $domain = new DatabaseDomain( $db, $schema, $prefix );
+               $this->assertInstanceOf( DatabaseDomain::class, $domain );
+               $this->assertEquals( $db, $domain->getDatabase() );
+               $this->assertEquals( $schema, $domain->getSchema() );
+               $this->assertEquals( $prefix, $domain->getTablePrefix() );
+               $this->assertEquals( $id, $domain->getId() );
+       }
+
+       public static function provideNewFromId() {
+               return [
+                       // basic
+                       [ 'foo', 'foo', null, '' ],
+                       // <database>-<prefix>
+                       [ 'foo-bar', 'foo', null, 'bar' ],
+                       [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
+                       // ?h -> -
+                       [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
+                       // ?? -> ?
+                       [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       // ? is left alone
+                       [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       // too many parts
+                       [ 'foo-bar-baz-baa', '', '', '', true ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideNewFromId
+        */
+       public function testNewFromId( $id, $db, $schema, $prefix, $exception = false ) {
+               if ( $exception ) {
+                       $this->setExpectedException( InvalidArgumentException::class );
+               }
+               $domain = DatabaseDomain::newFromId( $id );
+               $this->assertInstanceOf( DatabaseDomain::class, $domain );
+               $this->assertEquals( $db, $domain->getDatabase() );
+               $this->assertEquals( $schema, $domain->getSchema() );
+               $this->assertEquals( $prefix, $domain->getTablePrefix() );
+       }
+}
diff --git a/tests/phpunit/includes/libs/time/ConvertableTimestampTest.php b/tests/phpunit/includes/libs/time/ConvertableTimestampTest.php
new file mode 100644 (file)
index 0000000..88c2989
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * Tests timestamp parsing and output.
+ */
+class ConvertableTimestampTest extends PHPUnit_Framework_TestCase {
+       /**
+        * @covers ConvertableTimestamp::__construct
+        */
+       public function testConstructWithNoTimestamp() {
+               $timestamp = new ConvertableTimestamp();
+               $this->assertInternalType( 'string', $timestamp->getTimestamp() );
+               $this->assertNotEmpty( $timestamp->getTimestamp() );
+               $this->assertNotEquals( false, strtotime( $timestamp->getTimestamp( TS_MW ) ) );
+       }
+
+       /**
+        * @covers ConvertableTimestamp::__toString
+        */
+       public function testToString() {
+               $timestamp = new ConvertableTimestamp( '1406833268' ); // Equivalent to 20140731190108
+               $this->assertEquals( '1406833268', $timestamp->__toString() );
+       }
+
+       public static function provideValidTimestampDifferences() {
+               return [
+                       [ '1406833268', '1406833269', '00 00 00 01' ],
+                       [ '1406833268', '1406833329', '00 00 01 01' ],
+                       [ '1406833268', '1406836929', '00 01 01 01' ],
+                       [ '1406833268', '1406923329', '01 01 01 01' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideValidTimestampDifferences
+        * @covers ConvertableTimestamp::diff
+        */
+       public function testDiff( $timestamp1, $timestamp2, $expected ) {
+               $timestamp1 = new ConvertableTimestamp( $timestamp1 );
+               $timestamp2 = new ConvertableTimestamp( $timestamp2 );
+               $diff = $timestamp1->diff( $timestamp2 );
+               $this->assertEquals( $expected, $diff->format( '%D %H %I %S' ) );
+       }
+
+       /**
+        * Test parsing of valid timestamps and outputing to MW format.
+        * @dataProvider provideValidTimestamps
+        * @covers ConvertableTimestamp::getTimestamp
+        */
+       public function testValidParse( $format, $original, $expected ) {
+               $timestamp = new ConvertableTimestamp( $original );
+               $this->assertEquals( $expected, $timestamp->getTimestamp( TS_MW ) );
+       }
+
+       /**
+        * Test outputting valid timestamps to different formats.
+        * @dataProvider provideValidTimestamps
+        * @covers ConvertableTimestamp::getTimestamp
+        */
+       public function testValidOutput( $format, $expected, $original ) {
+               $timestamp = new ConvertableTimestamp( $original );
+               $this->assertEquals( $expected, (string)$timestamp->getTimestamp( $format ) );
+       }
+
+       /**
+        * Test an invalid timestamp.
+        * @expectedException TimestampException
+        * @covers ConvertableTimestamp
+        */
+       public function testInvalidParse() {
+               new ConvertableTimestamp( "This is not a timestamp." );
+       }
+
+       /**
+        * Test an out of range timestamp
+        * @dataProvider provideOutOfRangeTimestamps
+        * @expectedException TimestampException
+        * @covers ConvertableTimestamp
+        */
+       public function testOutOfRangeTimestamps( $format, $input ) {
+               $timestamp = new ConvertableTimestamp( $input );
+               $timestamp->getTimestamp( $format );
+       }
+
+       /**
+        * Test requesting an invalid output format.
+        * @expectedException TimestampException
+        * @covers ConvertableTimestamp::getTimestamp
+        */
+       public function testInvalidOutput() {
+               $timestamp = new ConvertableTimestamp( '1343761268' );
+               $timestamp->getTimestamp( 98 );
+       }
+
+       /**
+        * Returns a list of valid timestamps in the format:
+        * [ type, timestamp_of_type, timestamp_in_MW ]
+        */
+       public static function provideValidTimestamps() {
+               return [
+                       // Various formats
+                       [ TS_UNIX, '1343761268', '20120731190108' ],
+                       [ TS_MW, '20120731190108', '20120731190108' ],
+                       [ TS_DB, '2012-07-31 19:01:08', '20120731190108' ],
+                       [ TS_ISO_8601, '2012-07-31T19:01:08Z', '20120731190108' ],
+                       [ TS_ISO_8601_BASIC, '20120731T190108Z', '20120731190108' ],
+                       [ TS_EXIF, '2012:07:31 19:01:08', '20120731190108' ],
+                       [ TS_RFC2822, 'Tue, 31 Jul 2012 19:01:08 GMT', '20120731190108' ],
+                       [ TS_ORACLE, '31-07-2012 19:01:08.000000', '20120731190108' ],
+                       [ TS_POSTGRES, '2012-07-31 19:01:08 GMT', '20120731190108' ],
+                       // Some extremes and weird values
+                       [ TS_ISO_8601, '9999-12-31T23:59:59Z', '99991231235959' ],
+                       [ TS_UNIX, '-62135596801', '00001231235959' ]
+               ];
+       }
+
+       /**
+        * Returns a list of out of range timestamps in the format:
+        * [ type, timestamp_of_type ]
+        */
+       public static function provideOutOfRangeTimestamps() {
+               return [
+                       // Various formats
+                       [ TS_MW, '-62167219201' ], // -0001-12-31T23:59:59Z
+                       [ TS_MW, '253402300800' ], // 10000-01-01T00:00:00Z
+               ];
+       }
+}
index 91789c5..70c0ece 100644 (file)
@@ -135,8 +135,9 @@ class LinkRendererTest extends MediaWikiLangTestCase {
        }
 
        public function testGetLinkClasses() {
+               $wanCache = ObjectCache::getMainWANInstance();
                $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
-               $linkCache = new LinkCache( $titleFormatter );
+               $linkCache = new LinkCache( $titleFormatter, $wanCache );
                $foobarTitle = new TitleValue( NS_MAIN, 'FooBar' );
                $redirectTitle = new TitleValue( NS_MAIN, 'Redirect' );
                $userTitle = new TitleValue( NS_USER, 'Someuser' );
index b09e5b1..c289839 100644 (file)
@@ -50,7 +50,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase {
        private static function removeSomeHtml( $html ) {
                $html = str_replace( '&quot;', '"', $html );
                $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm
-               return trim( preg_replace( '/<(a|span)[^>]*>([^<]*)<\/\1>/', '$2', $html ) );
+               return trim( strip_tags( $html ) );
        }
 
        private static function removeApiMetaData( $val ) {
index 10e0f59..e55efee 100644 (file)
@@ -154,6 +154,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @covers WikiPage::doEdit
+        * @deprecated since 1.21. Should be removed when WikiPage::doEdit() gets removed
         */
        public function testDoEdit() {
                $this->hideDeprecated( "WikiPage::doEdit" );
diff --git a/tests/phpunit/includes/pager/ReverseChronologicalPagerTest.php b/tests/phpunit/includes/pager/ReverseChronologicalPagerTest.php
new file mode 100644 (file)
index 0000000..fc5d660
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Test class for ReverseChronologicalPagerTest methods.
+ *
+ * @group Pager
+ *
+ * @author Geoffrey Mon <geofbot@gmail.com>
+ */
+class ReverseChronologicalPagerTest extends MediaWikiLangTestCase {
+
+       /**
+        * @covers ReverseChronologicalPager::getDateCond
+        */
+       public function testGetDateCond() {
+               $pager = $this->getMockForAbstractClass( 'ReverseChronologicalPager' );
+               $timestamp = MWTimestamp::getInstance();
+               $db = wfGetDB( DB_MASTER );
+
+               $currYear = $timestamp->format( 'Y' );
+               $currMonth = $timestamp->format( 'n' );
+
+               // Test that getDateCond sets and returns mOffset
+               $this->assertEquals( $pager->getDateCond( 2006, 6 ), $pager->mOffset );
+
+               // Test year and month
+               $pager->getDateCond( 2006, 6 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20060701000000' ) );
+
+               // Test year, month, and day
+               $pager->getDateCond( 2006, 6, 5 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20060606000000' ) );
+
+               // Test month overflow into the next year
+               $pager->getDateCond( 2006, 12 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20070101000000' ) );
+
+               // Test day overflow to the next month
+               $pager->getDateCond( 2006, 6, 30 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20060701000000' ) );
+
+               // Test invalid month (should use end of year)
+               $pager->getDateCond( 2006, -1 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20070101000000' ) );
+
+               // Test invalid day (should use end of month)
+               $pager->getDateCond( 2006, 6, 1337 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20060701000000' ) );
+
+               // Test last day of year
+               $pager->getDateCond( 2006, 12, 31 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20070101000000' ) );
+
+               // Test invalid day that overflows to next year
+               $pager->getDateCond( 2006, 12, 32 );
+               $this->assertEquals( $pager->mOffset, $db->timestamp( '20070101000000' ) );
+
+               // Test month past current month (should use previous year)
+               if ( $currMonth < 5 ) {
+                       $pager->getDateCond( -1, 5 );
+                       $this->assertEquals( $pager->mOffset, $db->timestamp( $currYear - 1 . '0601000000' ) );
+               }
+               if ( $currMonth < 12 ) {
+                       $pager->getDateCond( -1, 12 );
+                       $this->assertEquals( $pager->mOffset, $db->timestamp( $currYear . '0101000000' ) );
+               }
+       }
+}
+
diff --git a/tests/phpunit/includes/parser/MediaWikiParserTest.php b/tests/phpunit/includes/parser/MediaWikiParserTest.php
deleted file mode 100644 (file)
index 173447f..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-require_once __DIR__ . '/NewParserTest.php';
-
-/**
- * The UnitTest must be either a class that inherits from MediaWikiTestCase
- * or a class that provides a public static suite() method which returns
- * an PHPUnit_Framework_Test object
- *
- * @group Parser
- * @group ParserTests
- * @group Database
- */
-class MediaWikiParserTest {
-
-       /**
-        * @defgroup filtering_constants Filtering constants
-        *
-        * Limit inclusion of parser tests files coming from MediaWiki core
-        * @{
-        */
-
-       /** Include files shipped with MediaWiki core */
-       const CORE_ONLY = 1;
-       /** Include non core files as set in $wgParserTestFiles */
-       const NO_CORE = 2;
-       /** Include anything set via $wgParserTestFiles */
-       const WITH_ALL = 3; # CORE_ONLY | NO_CORE
-
-       /** @} */
-
-       /**
-        * Get a PHPUnit test suite of parser tests. Optionally filtered with
-        * $flags.
-        *
-        * @par Examples:
-        * Get a suite of parser tests shipped by MediaWiki core:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::CORE_ONLY );
-        * @endcode
-        * Get a suite of various parser tests, like extensions:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::NO_CORE );
-        * @endcode
-        * Get any test defined via $wgParserTestFiles:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::WITH_ALL );
-        * @endcode
-        *
-        * @param int $flags Bitwise flag to filter out the $wgParserTestFiles that
-        * will be included.  Default: MediaWikiParserTest::CORE_ONLY
-        *
-        * @return PHPUnit_Framework_TestSuite
-        */
-       public static function suite( $flags = self::CORE_ONLY ) {
-               if ( is_string( $flags ) ) {
-                       $flags = self::CORE_ONLY;
-               }
-               global $wgParserTestFiles, $IP;
-
-               $mwTestDir = $IP . '/tests/';
-
-               # Human friendly helpers
-               $wantsCore = ( $flags & self::CORE_ONLY );
-               $wantsRest = ( $flags & self::NO_CORE );
-
-               # Will hold the .txt parser test files we will include
-               $filesToTest = [];
-
-               # Filter out .txt files
-               foreach ( $wgParserTestFiles as $parserTestFile ) {
-                       $isCore = ( 0 === strpos( $parserTestFile, $mwTestDir ) );
-
-                       if ( $isCore && $wantsCore ) {
-                               self::debug( "included core parser tests: $parserTestFile" );
-                               $filesToTest[] = $parserTestFile;
-                       } elseif ( !$isCore && $wantsRest ) {
-                               self::debug( "included non core parser tests: $parserTestFile" );
-                               $filesToTest[] = $parserTestFile;
-                       } else {
-                               self::debug( "skipped parser tests: $parserTestFile" );
-                       }
-               }
-               self::debug( 'parser tests files: '
-                       . implode( ' ', $filesToTest ) );
-
-               $suite = new PHPUnit_Framework_TestSuite;
-               $testList = [];
-               $counter = 0;
-               foreach ( $filesToTest as $fileName ) {
-                       // Call the highest level directory the extension name.
-                       // It may or may not actually be, but it should be close
-                       // enough to cause there to be separate names for different
-                       // things, which is good enough for our purposes.
-                       $extensionName = basename( dirname( $fileName ) );
-                       $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
-                       $escapedFileName = strtr( $fileName, [ "'" => "\\'", '\\' => '\\\\' ] );
-                       $parserTestClassName = ucfirst( $testsName );
-
-                       // Official spec for class names: http://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 );
-
-                       if ( isset( $testList[$parserTestClassName] ) ) {
-                               // If a conflict happens, gives a very unclear fatal.
-                               // So as a last ditch effort to prevent that eventuality, if there
-                               // is a conflict, append a number.
-                               $counter++;
-                               $parserTestClassName .= $counter;
-                       }
-                       $testList[$parserTestClassName] = true;
-                       $parserTestClassDefinition = <<<EOT
-/**
- * @group Database
- * @group Parser
- * @group ParserTests
- * @group ParserTests_$parserTestClassName
- */
-class $parserTestClassName extends NewParserTest {
-       protected \$file = '$escapedFileName';
-}
-EOT;
-
-                       eval( $parserTestClassDefinition );
-                       self::debug( "Adding test class $parserTestClassName" );
-                       $suite->addTestSuite( $parserTestClassName );
-               }
-               return $suite;
-       }
-
-       /**
-        * Write $msg under log group 'tests-parser'
-        * @param string $msg Message to log
-        */
-       protected static function debug( $msg ) {
-               return wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );
-       }
-}
diff --git a/tests/phpunit/includes/parser/NewParserTest.php b/tests/phpunit/includes/parser/NewParserTest.php
deleted file mode 100644 (file)
index ad84c20..0000000
+++ /dev/null
@@ -1,1138 +0,0 @@
-<?php
-
-use MediaWiki\MediaWikiServices;
-
-/**
- * Although marked as a stub, can work independently.
- *
- * @group Database
- * @group Parser
- * @group Stub
- *
- * @todo covers tags
- */
-class NewParserTest extends MediaWikiTestCase {
-       static protected $articles = []; // Array of test articles defined by the tests
-       /* The data provider is run on a different instance than the test, so it must be static
-        * When running tests from several files, all tests will see all articles.
-        */
-       static protected $backendToUse;
-
-       public $keepUploads = false;
-       public $runDisabled = false;
-       public $runParsoid = false;
-       public $regex = '';
-       public $showProgress = true;
-       public $savedWeirdGlobals = [];
-       public $savedGlobals = [];
-       public $hooks = [];
-       public $functionHooks = [];
-       public $transparentHooks = [];
-
-       // Fuzz test
-       public $maxFuzzTestLength = 300;
-       public $fuzzSeed = 0;
-       public $memoryLimit = 50;
-
-       /**
-        * @var DjVuSupport
-        */
-       private $djVuSupport;
-       /**
-        * @var TidySupport
-        */
-       private $tidySupport;
-
-       protected $file = false;
-
-       public static function setUpBeforeClass() {
-               // Inject ParserTest well-known interwikis
-               ParserTest::setupInterwikis();
-       }
-
-       protected function setUp() {
-               global $wgNamespaceAliases, $wgContLang;
-               global $wgHooks, $IP;
-
-               parent::setUp();
-
-               // Setup CLI arguments
-               if ( $this->getCliArg( 'regex' ) ) {
-                       $this->regex = $this->getCliArg( 'regex' );
-               } else {
-                       # Matches anything
-                       $this->regex = '';
-               }
-
-               $this->keepUploads = $this->getCliArg( 'keep-uploads' );
-
-               $tmpGlobals = [];
-
-               $tmpGlobals['wgLanguageCode'] = 'en';
-               $tmpGlobals['wgContLang'] = Language::factory( 'en' );
-               $tmpGlobals['wgSitename'] = 'MediaWiki';
-               $tmpGlobals['wgServer'] = 'http://example.org';
-               $tmpGlobals['wgServerName'] = 'example.org';
-               $tmpGlobals['wgScriptPath'] = '';
-               $tmpGlobals['wgScript'] = '/index.php';
-               $tmpGlobals['wgResourceBasePath'] = '';
-               $tmpGlobals['wgStylePath'] = '/skins';
-               $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
-               $tmpGlobals['wgArticlePath'] = '/wiki/$1';
-               $tmpGlobals['wgActionPaths'] = [];
-               $tmpGlobals['wgVariantArticlePath'] = false;
-               $tmpGlobals['wgEnableUploads'] = true;
-               $tmpGlobals['wgUploadNavigationUrl'] = false;
-               $tmpGlobals['wgThumbnailScriptPath'] = false;
-               $tmpGlobals['wgLocalFileRepo'] = [
-                       'class' => 'LocalRepo',
-                       'name' => 'local',
-                       'url' => 'http://example.com/images',
-                       'hashLevels' => 2,
-                       'transformVia404' => false,
-                       'backend' => 'local-backend'
-               ];
-               $tmpGlobals['wgForeignFileRepos'] = [];
-               $tmpGlobals['wgDefaultExternalStore'] = [];
-               $tmpGlobals['wgParserCacheType'] = CACHE_NONE;
-               $tmpGlobals['wgCapitalLinks'] = true;
-               $tmpGlobals['wgNoFollowLinks'] = true;
-               $tmpGlobals['wgNoFollowDomainExceptions'] = [ 'no-nofollow.org' ];
-               $tmpGlobals['wgExternalLinkTarget'] = false;
-               $tmpGlobals['wgThumbnailScriptPath'] = false;
-               $tmpGlobals['wgUseImageResize'] = true;
-               $tmpGlobals['wgAllowExternalImages'] = true;
-               $tmpGlobals['wgRawHtml'] = false;
-               $tmpGlobals['wgExperimentalHtmlIds'] = false;
-               $tmpGlobals['wgAdaptiveMessageCache'] = true;
-               $tmpGlobals['wgUseDatabaseMessages'] = true;
-               $tmpGlobals['wgLocaltimezone'] = 'UTC';
-               $tmpGlobals['wgGroupPermissions'] = [
-                       '*' => [
-                               'createaccount' => true,
-                               'read' => true,
-                               'edit' => true,
-                               'createpage' => true,
-                               'createtalk' => true,
-               ] ];
-               $tmpGlobals['wgNamespaceProtection'] = [ NS_MEDIAWIKI => 'editinterface' ];
-
-               $tmpGlobals['wgParser'] = new StubObject(
-                       'wgParser', $GLOBALS['wgParserConf']['class'],
-                       [ $GLOBALS['wgParserConf'] ] );
-
-               $tmpGlobals['wgFileExtensions'][] = 'svg';
-               $tmpGlobals['wgSVGConverter'] = 'rsvg';
-               $tmpGlobals['wgSVGConverters']['rsvg'] =
-                       '$path/rsvg-convert -w $width -h $height -o $output $input';
-
-               if ( $GLOBALS['wgStyleDirectory'] === false ) {
-                       $tmpGlobals['wgStyleDirectory'] = "$IP/skins";
-               }
-
-               $tmpHooks = $wgHooks;
-               $tmpHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
-               $tmpHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
-               $tmpGlobals['wgHooks'] = $tmpHooks;
-               # add a namespace shadowing a interwiki link, to test
-               # proper precedence when resolving links. (bug 51680)
-               $tmpGlobals['wgExtraNamespaces'] = [
-                       100 => 'MemoryAlpha',
-                       101 => 'MemoryAlpha_talk'
-               ];
-
-               $tmpGlobals['wgLocalInterwikis'] = [ 'local', 'mi' ];
-               # "extra language links"
-               # see https://gerrit.wikimedia.org/r/111390
-               $tmpGlobals['wgExtraInterlanguageLinkPrefixes'] = [ 'mul' ];
-
-               // DjVu support
-               $this->djVuSupport = new DjVuSupport();
-               // Tidy support
-               $this->tidySupport = new TidySupport();
-               $tmpGlobals['wgTidyConfig'] = $this->tidySupport->getConfig();
-               $tmpGlobals['wgUseTidy'] = false;
-
-               $this->setMwGlobals( $tmpGlobals );
-
-               $this->savedWeirdGlobals['image_alias'] = $wgNamespaceAliases['Image'];
-               $this->savedWeirdGlobals['image_talk_alias'] = $wgNamespaceAliases['Image_talk'];
-
-               $wgNamespaceAliases['Image'] = NS_FILE;
-               $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
-
-               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
-               $wgContLang->resetNamespaces(); # reset namespace cache
-               ParserTest::resetTitleServices();
-               MediaWikiServices::getInstance()->disableService( 'MediaHandlerFactory' );
-               MediaWikiServices::getInstance()->redefineService(
-                       'MediaHandlerFactory',
-                       function() {
-                               return new MockMediaHandlerFactory();
-                       }
-               );
-       }
-
-       protected function tearDown() {
-               global $wgNamespaceAliases, $wgContLang;
-
-               $wgNamespaceAliases['Image'] = $this->savedWeirdGlobals['image_alias'];
-               $wgNamespaceAliases['Image_talk'] = $this->savedWeirdGlobals['image_talk_alias'];
-
-               MWTidy::destroySingleton();
-
-               // Restore backends
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-
-               // Remove temporary pages from the link cache
-               LinkCache::singleton()->clear();
-
-               // Restore message cache (temporary pages and $wgUseDatabaseMessages)
-               MessageCache::destroyInstance();
-               MediaWikiServices::getInstance()->resetServiceForTesting( 'MediaHandlerFactory' );
-
-               parent::tearDown();
-
-               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
-               $wgContLang->resetNamespaces(); # reset namespace cache
-       }
-
-       public static function tearDownAfterClass() {
-               ParserTest::tearDownInterwikis();
-               parent::tearDownAfterClass();
-       }
-
-       function addDBDataOnce() {
-               # disabled for performance
-               # $this->tablesUsed[] = 'image';
-
-               # Update certain things in site_stats
-               $this->db->insert( 'site_stats',
-                       [ 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ],
-                       __METHOD__,
-                       [ 'IGNORE' ]
-               );
-
-               $user = User::newFromId( 0 );
-               LinkCache::singleton()->clear(); # Avoids the odd failure at creating the nullRevision
-
-               # Upload DB table entries for files.
-               # We will upload the actual files later. Note that if anything causes LocalFile::load()
-               # to be triggered before then, it will break via maybeUpgrade() setting the fileExists
-               # member to false and storing it in cache.
-               # note that the size/width/height/bits/etc of the file
-               # are actually set by inspecting the file itself; the arguments
-               # to recordUpload2 have no effect.  That said, we try to make things
-               # match up so it is less confusing to readers of the code & tests.
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'Upload of some lame file',
-                               'Some lame file',
-                               [
-                                       'size' => 7881,
-                                       'width' => 1941,
-                                       'height' => 220,
-                                       'bits' => 8,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/jpeg',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20010115123500' ), $user
-                       );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'Upload of some lame thumbnail',
-                               'Some lame thumbnail',
-                               [
-                                       'size' => 22589,
-                                       'width' => 135,
-                                       'height' => 135,
-                                       'bits' => 8,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/png',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20130225203040' ), $user
-                       );
-               }
-
-               # This image will be blacklisted in [[MediaWiki:Bad image list]]
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'zomgnotcensored',
-                               'Borderline image',
-                               [
-                                       'size' => 12345,
-                                       'width' => 320,
-                                       'height' => 240,
-                                       'bits' => 24,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/jpeg',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20010115123500' ), $user
-                       );
-               }
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
-                                       'size'        => 12345,
-                                       'width'       => 240,
-                                       'height'      => 180,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_DRAWING,
-                                       'mime'        => 'image/svg+xml',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
-                                       'size'        => 12345,
-                                       'width'       => 320,
-                                       'height'      => 240,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_VIDEO,
-                                       'mime'        => 'application/ogg',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 32 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'An awesome hitsong ', 'Will it play', [
-                                       'size'        => 12345,
-                                       'width'       => 0,
-                                       'height'      => 0,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_AUDIO,
-                                       'mime'        => 'application/ogg',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 32 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               # A DjVu file
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
-                               'size' => 3249,
-                               'width' => 2480,
-                               'height' => 3508,
-                               'bits' => 0,
-                               'media_type' => MEDIATYPE_BITMAP,
-                               'mime' => 'image/vnd.djvu',
-                               'metadata' => '<?xml version="1.0" ?>
-<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
-<DjVuXML>
-<HEAD></HEAD>
-<BODY><OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-</BODY>
-</DjVuXML>',
-                               'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                               'fileExists' => true
-                       ], $this->db->timestamp( '20140115123600' ), $user );
-               }
-       }
-
-       // ParserTest setup/teardown functions
-
-       /**
-        * Set up the global variables for a consistent environment for each test.
-        * Ideally this should replace the global configuration entirely.
-        * @param array $opts
-        * @param string $config
-        * @return RequestContext
-        */
-       protected function setupGlobals( $opts = [], $config = '' ) {
-               global $wgFileBackends;
-               # Find out values for some special options.
-               $lang =
-                       self::getOptionValue( 'language', $opts, 'en' );
-               $variant =
-                       self::getOptionValue( 'variant', $opts, false );
-               $maxtoclevel =
-                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
-               $linkHolderBatchSize =
-                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
-
-               $uploadDir = $this->getUploadDir();
-               if ( $this->getCliArg( 'use-filebackend' ) ) {
-                       if ( self::$backendToUse ) {
-                               $backend = self::$backendToUse;
-                       } else {
-                               $name = $this->getCliArg( 'use-filebackend' );
-                               $useConfig = [];
-                               foreach ( $wgFileBackends as $conf ) {
-                                       if ( $conf['name'] == $name ) {
-                                               $useConfig = $conf;
-                                       }
-                               }
-                               $useConfig['name'] = 'local-backend'; // swap name
-                               unset( $useConfig['lockManager'] );
-                               unset( $useConfig['fileJournal'] );
-                               $class = $useConfig['class'];
-                               self::$backendToUse = new $class( $useConfig );
-                               $backend = self::$backendToUse;
-                       }
-               } else {
-                       # Replace with a mock. We do not care about generating real
-                       # files on the filesystem, just need to expose the file
-                       # informations.
-                       $backend = new MockFileBackend( [
-                               'name' => 'local-backend',
-                               'wikiId' => wfWikiID()
-                       ] );
-               }
-
-               $settings = [
-                       'wgLocalFileRepo' => [
-                               'class' => 'LocalRepo',
-                               'name' => 'local',
-                               'url' => 'http://example.com/images',
-                               'hashLevels' => 2,
-                               'transformVia404' => false,
-                               'backend' => $backend
-                       ],
-                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
-                       'wgLanguageCode' => $lang,
-                       'wgDBprefix' => $this->db->getType() != 'oracle' ? 'unittest_' : 'ut_',
-                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
-                       'wgNamespacesWithSubpages' => [ NS_MAIN => isset( $opts['subpage'] ) ],
-                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
-                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
-                       'wgMaxTocLevel' => $maxtoclevel,
-                       'wgUseTeX' => isset( $opts['math'] ) || isset( $opts['texvc'] ),
-                       'wgMathDirectory' => $uploadDir . '/math',
-                       'wgDefaultLanguageVariant' => $variant,
-                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
-                       'wgUseTidy' => false,
-                       'wgTidyConfig' => isset( $opts['tidy'] ) ? $this->tidySupport->getConfig() : null
-               ];
-
-               if ( $config ) {
-                       $configLines = explode( "\n", $config );
-
-                       foreach ( $configLines as $line ) {
-                               list( $var, $value ) = explode( '=', $line, 2 );
-
-                               $settings[$var] = eval( "return $value;" ); // ???
-                       }
-               }
-
-               $this->savedGlobals = [];
-
-               /** @since 1.20 */
-               Hooks::run( 'ParserTestGlobals', [ &$settings ] );
-
-               $langObj = Language::factory( $lang );
-               $settings['wgContLang'] = $langObj;
-               $settings['wgLang'] = $langObj;
-
-               $context = new RequestContext();
-               $settings['wgOut'] = $context->getOutput();
-               $settings['wgUser'] = $context->getUser();
-               $settings['wgRequest'] = $context->getRequest();
-
-               // We (re)set $wgThumbLimits to a single-element array above.
-               $context->getUser()->setOption( 'thumbsize', 0 );
-
-               foreach ( $settings as $var => $val ) {
-                       if ( array_key_exists( $var, $GLOBALS ) ) {
-                               $this->savedGlobals[$var] = $GLOBALS[$var];
-                       }
-
-                       $GLOBALS[$var] = $val;
-               }
-
-               MWTidy::destroySingleton();
-               MagicWord::clearCache();
-
-               # The entries saved into RepoGroup cache with previous globals will be wrong.
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-
-               # Create dummy files in storage
-               $this->setupUploads();
-
-               # Publish the articles after we have the final language set
-               $this->publishTestArticles();
-
-               MessageCache::destroyInstance();
-
-               return $context;
-       }
-
-       /**
-        * Get an FS upload directory (only applies to FSFileBackend)
-        *
-        * @return string The directory
-        */
-       protected function getUploadDir() {
-               if ( $this->keepUploads ) {
-                       // Don't use getNewTempDirectory() as this is meant to persist
-                       $dir = wfTempDir() . '/mwParser-images';
-
-                       if ( is_dir( $dir ) ) {
-                               return $dir;
-                       }
-               } else {
-                       $dir = $this->getNewTempDirectory();
-               }
-
-               if ( file_exists( $dir ) ) {
-                       wfDebug( "Already exists!\n" );
-
-                       return $dir;
-               }
-
-               return $dir;
-       }
-
-       /**
-        * Create a dummy uploads directory which will contain a couple
-        * of files in order to pass existence tests.
-        *
-        * @return string The directory
-        */
-       protected function setupUploads() {
-               global $IP;
-
-               $base = $this->getBaseDir();
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               $backend->prepare( [ 'dir' => "$base/local-public/3/3a" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
-                       'dst' => "$base/local-public/3/3a/Foobar.jpg"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/e/ea" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/wiki.png",
-                       'dst' => "$base/local-public/e/ea/Thumb.png"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/0/09" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
-                       'dst' => "$base/local-public/0/09/Bad.jpg"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/5/5f" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/LoremIpsum.djvu",
-                       'dst' => "$base/local-public/5/5f/LoremIpsum.djvu"
-               ] );
-
-               // No helpful SVG file to copy, so make one ourselves
-               $data = '<?xml version="1.0" encoding="utf-8"?>' .
-                       '<svg xmlns="http://www.w3.org/2000/svg"' .
-                       ' version="1.1" width="240" height="180"/>';
-
-               $backend->prepare( [ 'dir' => "$base/local-public/f/ff" ] );
-               $backend->quickCreate( [
-                       'content' => $data, 'dst' => "$base/local-public/f/ff/Foobar.svg"
-               ] );
-       }
-
-       /**
-        * Restore default values and perform any necessary clean-up
-        * after each test runs.
-        */
-       protected function teardownGlobals() {
-               $this->teardownUploads();
-
-               foreach ( $this->savedGlobals as $var => $val ) {
-                       $GLOBALS[$var] = $val;
-               }
-       }
-
-       /**
-        * Remove the dummy uploads directory
-        */
-       private function teardownUploads() {
-               if ( $this->keepUploads ) {
-                       return;
-               }
-
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               if ( $backend instanceof MockFileBackend ) {
-                       # In memory backend, so dont bother cleaning them up.
-                       return;
-               }
-
-               $base = $this->getBaseDir();
-               // delete the files first, then the dirs.
-               self::deleteFiles(
-                       [
-                               "$base/local-public/3/3a/Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/100px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1280px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/137px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1500px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/177px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/206px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/20px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/220px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/265px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/270px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/274px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/300px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/30px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/330px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/353px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/360px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/400px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/40px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/440px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/442px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/450px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/50px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/600px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/75px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
-
-                               "$base/local-public/e/ea/Thumb.png",
-
-                               "$base/local-public/0/09/Bad.jpg",
-
-                               "$base/local-public/5/5f/LoremIpsum.djvu",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-2480px-LoremIpsum.djvu.jpg",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-3720px-LoremIpsum.djvu.jpg",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-4960px-LoremIpsum.djvu.jpg",
-
-                               "$base/local-public/f/ff/Foobar.svg",
-                               "$base/local-thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/3000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/4000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
-
-                               "$base/local-public/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
-                       ]
-               );
-       }
-
-       /**
-        * Delete the specified files, if they exist.
-        * @param array $files Full paths to files to delete.
-        */
-       private static function deleteFiles( $files ) {
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               foreach ( $files as $file ) {
-                       $backend->delete( [ 'src' => $file ], [ 'force' => 1 ] );
-               }
-               foreach ( $files as $file ) {
-                       $tmp = FileBackend::parentStoragePath( $file );
-                       while ( $tmp ) {
-                               if ( !$backend->clean( [ 'dir' => $tmp ] )->isOK() ) {
-                                       break;
-                               }
-                               $tmp = FileBackend::parentStoragePath( $tmp );
-                       }
-               }
-       }
-
-       protected function getBaseDir() {
-               return 'mwstore://local-backend';
-       }
-
-       public function parserTestProvider() {
-               if ( $this->file === false ) {
-                       global $wgParserTestFiles;
-                       $this->file = $wgParserTestFiles[0];
-               }
-
-               return new TestFileDataProvider( $this->file, $this );
-       }
-
-       /**
-        * Set the file from whose tests will be run by this instance
-        * @param string $filename
-        */
-       public function setParserTestFile( $filename ) {
-               $this->file = $filename;
-       }
-
-       /**
-        * @group medium
-        * @group ParserTests
-        * @dataProvider parserTestProvider
-        * @param string $desc
-        * @param string $input
-        * @param string $result
-        * @param array $opts
-        * @param array $config
-        */
-       public function testParserTest( $desc, $input, $result, $opts, $config ) {
-               if ( $this->regex != '' && !preg_match( '/' . $this->regex . '/', $desc ) ) {
-                       $this->assertTrue( true ); // XXX: don't flood output with "test made no assertions"
-                       // $this->markTestSkipped( 'Filtered out by the user' );
-                       $this->teardownGlobals();
-                       return;
-               }
-
-               if ( !$this->isWikitextNS( NS_MAIN ) ) {
-                       // parser tests frequently assume that the main namespace contains wikitext.
-                       // @todo When setting up pages, force the content model. Only skip if
-                       //        $wgtContentModelUseDB is false.
-                       $this->teardownGlobals();
-                       $this->markTestSkipped( "Main namespace does not support wikitext,"
-                               . "skipping parser test: $desc" );
-               }
-
-               wfDebug( "Running parser test: $desc\n" );
-
-               $opts = $this->parseOptions( $opts );
-               $context = $this->setupGlobals( $opts, $config );
-
-               $user = $context->getUser();
-               $options = ParserOptions::newFromContext( $context );
-
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
-               }
-
-               $local = isset( $opts['local'] );
-               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
-               $parser = $this->getParser( $preprocessor );
-
-               $title = Title::newFromText( $titleText );
-
-               # Parser test requiring math. Make sure texvc is executable
-               # or just skip such tests.
-               if ( isset( $opts['math'] ) || isset( $opts['texvc'] ) ) {
-                       global $wgTexvc;
-
-                       if ( !isset( $wgTexvc ) ) {
-                               $this->teardownGlobals();
-                               $this->markTestSkipped( "SKIPPED: \$wgTexvc is not set" );
-                       } elseif ( !is_executable( $wgTexvc ) ) {
-                               $this->teardownGlobals();
-                               $this->markTestSkipped( "SKIPPED: texvc binary does not exist"
-                                       . " or is not executable.\n"
-                                       . "Current configuration is:\n\$wgTexvc = '$wgTexvc'" );
-                       }
-               }
-
-               if ( isset( $opts['djvu'] ) ) {
-                       if ( !$this->djVuSupport->isEnabled() ) {
-                               $this->teardownGlobals();
-                               $this->markTestSkipped( "SKIPPED: djvu binaries do not exist or are not executable.\n" );
-                       }
-               }
-
-               if ( isset( $opts['tidy'] ) ) {
-                       if ( !$this->tidySupport->isEnabled() ) {
-                               $this->teardownGlobals();
-                               $this->markTestSkipped( "SKIPPED: tidy extension is not installed.\n" );
-                       } else {
-                               $options->setTidy( true );
-                       }
-               }
-
-               if ( isset( $opts['pst'] ) ) {
-                       $out = $parser->preSaveTransform( $input, $title, $user, $options );
-               } elseif ( isset( $opts['msg'] ) ) {
-                       $out = $parser->transformMsg( $input, $options, $title );
-               } elseif ( isset( $opts['section'] ) ) {
-                       $section = $opts['section'];
-                       $out = $parser->getSection( $input, $section );
-               } elseif ( isset( $opts['replace'] ) ) {
-                       $section = $opts['replace'][0];
-                       $replace = $opts['replace'][1];
-                       $out = $parser->replaceSection( $input, $section, $replace );
-               } elseif ( isset( $opts['comment'] ) ) {
-                       $out = Linker::formatComment( $input, $title, $local );
-               } elseif ( isset( $opts['preload'] ) ) {
-                       $out = $parser->getPreloadText( $input, $title, $options );
-               } else {
-                       $output = $parser->parse( $input, $title, $options, true, true, 1337 );
-                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
-                       $out = $output->getText();
-                       if ( isset( $opts['tidy'] ) ) {
-                               $out = preg_replace( '/\s+$/', '', $out );
-                       }
-
-                       if ( isset( $opts['showtitle'] ) ) {
-                               if ( $output->getTitleText() ) {
-                                       $title = $output->getTitleText();
-                               }
-
-                               $out = "$title\n$out";
-                       }
-
-                       if ( isset( $opts['showindicators'] ) ) {
-                               $indicators = '';
-                               foreach ( $output->getIndicators() as $id => $content ) {
-                                       $indicators .= "$id=$content\n";
-                               }
-                               $out = $indicators . $out;
-                       }
-
-                       if ( isset( $opts['ill'] ) ) {
-                               $out = implode( ' ', $output->getLanguageLinks() );
-                       } elseif ( isset( $opts['cat'] ) ) {
-                               $outputPage = $context->getOutput();
-                               $outputPage->addCategoryLinks( $output->getCategories() );
-                               $cats = $outputPage->getCategoryLinks();
-
-                               if ( isset( $cats['normal'] ) ) {
-                                       $out = implode( ' ', $cats['normal'] );
-                               } else {
-                                       $out = '';
-                               }
-                       }
-                       $parser->mPreprocessor = null;
-               }
-
-               $this->teardownGlobals();
-
-               $this->assertEquals( $result, $out, $desc );
-       }
-
-       /**
-        * Run a fuzz test series
-        * Draw input from a set of test files
-        *
-        * @todo fixme Needs some work to not eat memory until the world explodes
-        *
-        * @group ParserFuzz
-        */
-       public function testFuzzTests() {
-               global $wgParserTestFiles;
-
-               $files = $wgParserTestFiles;
-
-               if ( $this->getCliArg( 'file' ) ) {
-                       $files = [ $this->getCliArg( 'file' ) ];
-               }
-
-               $dict = $this->getFuzzInput( $files );
-               $dictSize = strlen( $dict );
-               $logMaxLength = log( $this->maxFuzzTestLength );
-
-               ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
-
-               $user = new User;
-               $opts = ParserOptions::newFromUser( $user );
-               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
-
-               $id = 1;
-
-               while ( true ) {
-
-                       // Generate test input
-                       mt_srand( ++$this->fuzzSeed );
-                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
-                       $input = '';
-
-                       while ( strlen( $input ) < $totalLength ) {
-                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
-                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
-                               $offset = mt_rand( 0, $dictSize - $hairLength );
-                               $input .= substr( $dict, $offset, $hairLength );
-                       }
-
-                       $this->setupGlobals();
-                       $parser = $this->getParser();
-
-                       // Run the test
-                       try {
-                               $parser->parse( $input, $title, $opts );
-                               $this->assertTrue( true, "Test $id, fuzz seed {$this->fuzzSeed}" );
-                       } catch ( Exception $exception ) {
-                               $input_dump = sprintf( "string(%d) \"%s\"\n", strlen( $input ), $input );
-
-                               $this->assertTrue( false, "Test $id, fuzz seed {$this->fuzzSeed}. \n\n" .
-                                       "Input: $input_dump\n\nError: {$exception->getMessage()}\n\n" .
-                                       "Backtrace: {$exception->getTraceAsString()}" );
-                       }
-
-                       $this->teardownGlobals();
-                       $parser->__destruct();
-
-                       if ( $id % 100 == 0 ) {
-                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
-                               // echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
-                               if ( $usage > 90 ) {
-                                       $ret = "Out of memory:\n";
-                                       $memStats = $this->getMemoryBreakdown();
-
-                                       foreach ( $memStats as $name => $usage ) {
-                                               $ret .= "$name: $usage\n";
-                                       }
-
-                                       throw new MWException( $ret );
-                               }
-                       }
-
-                       $id++;
-               }
-       }
-
-       // Various getter functions
-
-       /**
-        * Get an input dictionary from a set of parser test files
-        * @param array $filenames
-        * @return string
-        */
-       function getFuzzInput( $filenames ) {
-               $dict = '';
-
-               foreach ( $filenames as $filename ) {
-                       $contents = file_get_contents( $filename );
-                       preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
-
-                       foreach ( $matches[1] as $match ) {
-                               $dict .= $match . "\n";
-                       }
-               }
-
-               return $dict;
-       }
-
-       /**
-        * Get a memory usage breakdown
-        * @return array
-        */
-       function getMemoryBreakdown() {
-               $memStats = [];
-
-               foreach ( $GLOBALS as $name => $value ) {
-                       $memStats['$' . $name] = strlen( serialize( $value ) );
-               }
-
-               $classes = get_declared_classes();
-
-               foreach ( $classes as $class ) {
-                       $rc = new ReflectionClass( $class );
-                       $props = $rc->getStaticProperties();
-                       $memStats[$class] = strlen( serialize( $props ) );
-                       $methods = $rc->getMethods();
-
-                       foreach ( $methods as $method ) {
-                               $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
-                       }
-               }
-
-               $functions = get_defined_functions();
-
-               foreach ( $functions['user'] as $function ) {
-                       $rf = new ReflectionFunction( $function );
-                       $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
-               }
-
-               asort( $memStats );
-
-               return $memStats;
-       }
-
-       /**
-        * Get a Parser object
-        * @param Preprocessor $preprocessor
-        * @return Parser
-        */
-       function getParser( $preprocessor = null ) {
-               global $wgParserConf;
-
-               $class = $wgParserConf['class'];
-               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
-
-               Hooks::run( 'ParserTestParser', [ &$parser ] );
-
-               return $parser;
-       }
-
-       // Various action functions
-
-       public function addArticle( $name, $text, $line ) {
-               self::$articles[$name] = [ $text, $line ];
-       }
-
-       public function publishTestArticles() {
-               if ( empty( self::$articles ) ) {
-                       return;
-               }
-
-               foreach ( self::$articles as $name => $info ) {
-                       list( $text, $line ) = $info;
-                       ParserTest::addArticle( $name, $text, $line, 'ignoreduplicate' );
-               }
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if tag hook is present
-        */
-       public function requireHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mTagHooks[$name] );
-       }
-
-       public function requireFunctionHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mFunctionHooks[$name] );
-       }
-
-       public function requireTransparentHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mTransparentTagHooks[$name] );
-       }
-
-       // Various "cleanup" functions
-
-       /**
-        * Remove last character if it is a newline
-        * @param string $s
-        * @return string
-        */
-       public function removeEndingNewline( $s ) {
-               if ( substr( $s, -1 ) === "\n" ) {
-                       return substr( $s, 0, -1 );
-               } else {
-                       return $s;
-               }
-       }
-
-       // Test options parser functions
-
-       protected function parseOptions( $instring ) {
-               $opts = [];
-               // foo
-               // foo=bar
-               // foo="bar baz"
-               // foo=[[bar baz]]
-               // foo=bar,"baz quux"
-               $regex = '/\b
-                       ([\w-]+)                                                # Key
-                       \b
-                       (?:\s*
-                               =                                               # First sub-value
-                               \s*
-                               (
-                                       "
-                                               [^"]*                   # Quoted val
-                                       "
-                               |
-                                       \[\[
-                                               [^]]*                   # Link target
-                                       \]\]
-                               |
-                                       [\w-]+                          # Plain word
-                               )
-                               (?:\s*
-                                       ,                                       # Sub-vals 1..N
-                                       \s*
-                                       (
-                                               "[^"]*"                 # Quoted val
-                                       |
-                                               \[\[[^]]*\]\]   # Link target
-                                       |
-                                               [\w-]+                  # Plain word
-                                       )
-                               )*
-                       )?
-                       /x';
-
-               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
-                       foreach ( $matches as $bits ) {
-                               array_shift( $bits );
-                               $key = strtolower( array_shift( $bits ) );
-                               if ( count( $bits ) == 0 ) {
-                                       $opts[$key] = true;
-                               } elseif ( count( $bits ) == 1 ) {
-                                       $opts[$key] = $this->cleanupOption( array_shift( $bits ) );
-                               } else {
-                                       // Array!
-                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $bits );
-                               }
-                       }
-               }
-
-               return $opts;
-       }
-
-       protected function cleanupOption( $opt ) {
-               if ( substr( $opt, 0, 1 ) == '"' ) {
-                       return substr( $opt, 1, -1 );
-               }
-
-               if ( substr( $opt, 0, 2 ) == '[[' ) {
-                       return substr( $opt, 2, -2 );
-               }
-
-               return $opt;
-       }
-
-       /**
-        * Use a regex to find out the value of an option
-        * @param string $key Name of option val to retrieve
-        * @param array $opts Options array to look in
-        * @param mixed $default Default value returned if not found
-        * @return mixed
-        */
-       protected static function getOptionValue( $key, $opts, $default ) {
-               $key = strtolower( $key );
-
-               if ( isset( $opts[$key] ) ) {
-                       return $opts[$key];
-               } else {
-                       return $default;
-               }
-       }
-}
diff --git a/tests/phpunit/includes/parser/ParserIntegrationTest.php b/tests/phpunit/includes/parser/ParserIntegrationTest.php
new file mode 100644 (file)
index 0000000..b38c98d
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * This is the TestCase subclass for running a single parser test via the
+ * ParserTestRunner integration test system.
+ *
+ * Note: the following groups are not used by PHPUnit.
+ * The list in ParserTestFileSuite::__construct() is used instead.
+ *
+ * @group Database
+ * @group Parser
+ * @group ParserTests
+ *
+ * @todo covers tags
+ */
+class ParserIntegrationTest extends PHPUnit_Framework_TestCase {
+       /** @var array */
+       private $ptTest;
+
+       /** @var ParserTestRunner */
+       private $ptRunner;
+
+       /** @var ScopedCallback */
+       private $ptTeardownScope;
+
+       public function __construct( $runner, $fileName, $test ) {
+               parent::__construct( 'testParse', [ '[details omitted]' ],
+                       basename( $fileName ) . ': ' . $test['desc'] );
+               $this->ptTest = $test;
+               $this->ptRunner = $runner;
+       }
+
+       public function testParse() {
+               $this->ptRunner->getRecorder()->setTestCase( $this );
+               $result = $this->ptRunner->runTest( $this->ptTest );
+               $this->assertEquals( $result->expected, $result->actual );
+       }
+
+       public function setUp() {
+               $this->ptTeardownScope = $this->ptRunner->staticSetup();
+       }
+
+       public function tearDown() {
+               if ( $this->ptTeardownScope ) {
+                       ScopedCallback::consume( $this->ptTeardownScope );
+               }
+       }
+}
index 90c9f38..0be04ef 100644 (file)
@@ -2,11 +2,11 @@
 
 /**
  * @group ResourceLoader
+ * @covers DerivativeResourceLoaderContext
  */
 class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
 
-       protected static function getResourceLoaderContext() {
-               $resourceLoader = new ResourceLoader();
+       protected static function getContext() {
                $request = new FauxRequest( [
                                'lang' => 'zh',
                                'modules' => 'test.context',
@@ -14,42 +14,76 @@ class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
                                'skin' => 'fallback',
                                'target' => 'test',
                ] );
-               return new ResourceLoaderContext( $resourceLoader, $request );
+               return new ResourceLoaderContext( new ResourceLoader(), $request );
        }
 
-       public function testGet() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testGetInherited() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
+               // Request parameters
+               $this->assertEquals( $derived->getDebug(), false );
                $this->assertEquals( $derived->getLanguage(), 'zh' );
                $this->assertEquals( $derived->getModules(), [ 'test.context' ] );
                $this->assertEquals( $derived->getOnly(), 'scripts' );
                $this->assertEquals( $derived->getSkin(), 'fallback' );
+               $this->assertEquals( $derived->getUser(), null );
+
+               // Misc
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
                $this->assertEquals( $derived->getHash(), 'zh|fallback|||scripts|||||' );
        }
 
-       public function testSetLanguage() {
-               $context = self::getResourceLoaderContext();
+       public function testModules() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setModules( [ 'test.override' ] );
+               $this->assertEquals( $derived->getModules(), [ 'test.override' ] );
+       }
+
+       public function testLanguage() {
+               $context = self::getContext();
                $derived = new DerivativeResourceLoaderContext( $context );
 
                $derived->setLanguage( 'nl' );
                $this->assertEquals( $derived->getLanguage(), 'nl' );
+       }
+
+       public function testDirection() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setLanguage( 'nl' );
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
 
                $derived->setLanguage( 'he' );
                $this->assertEquals( $derived->getDirection(), 'rtl' );
+
+               $derived->setDirection( 'ltr' );
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
        }
 
-       public function testSetModules() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testSkin() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
-               $derived->setModules( [ 'test.override' ] );
-               $this->assertEquals( $derived->getModules(), [ 'test.override' ] );
+               $derived->setSkin( 'override' );
+               $this->assertEquals( $derived->getSkin(), 'override' );
        }
 
-       public function testSetOnly() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testUser() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setUser( 'Example' );
+               $this->assertEquals( $derived->getUser(), 'Example' );
+       }
+
+       public function testDebug() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setDebug( true );
+               $this->assertEquals( $derived->getDebug(), true );
+       }
+
+       public function testOnly() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
                $derived->setOnly( 'styles' );
                $this->assertEquals( $derived->getOnly(), 'styles' );
@@ -58,21 +92,35 @@ class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $derived->getOnly(), null );
        }
 
-       public function testSetSkin() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testVersion() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
-               $derived->setSkin( 'override' );
-               $this->assertEquals( $derived->getSkin(), 'override' );
+               $derived->setVersion( 'hw1' );
+               $this->assertEquals( $derived->getVersion(), 'hw1' );
+       }
+
+       public function testRaw() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setRaw( true );
+               $this->assertEquals( $derived->getRaw(), true );
        }
 
        public function testGetHash() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $this->assertEquals( $derived->getHash(), 'zh|fallback|||scripts|||||' );
 
                $derived->setLanguage( 'nl' );
+               $derived->setUser( 'Example' );
                // Assert that subclass is able to clear parent class "hash" member
-               $this->assertEquals( $derived->getHash(), 'nl|fallback|||scripts|||||' );
+               $this->assertEquals( $derived->getHash(), 'nl|fallback||Example|scripts|||||' );
        }
 
+       public function testAccessors() {
+               $context = self::getContext();
+               $derived = new DerivativeResourceLoaderContext( $context );
+               $this->assertSame( $derived->getRequest(), $context->getRequest() );
+               $this->assertSame( $derived->getResourceLoader(), $context->getResourceLoader() );
+       }
 }
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php
new file mode 100644 (file)
index 0000000..1093039
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * See also:
+ * - ResourceLoaderTest::testExpandModuleNames
+ * - ResourceLoaderImageModuleTest::testContext
+ *
+ * @group Cache
+ * @covers ResourceLoaderContext
+ */
+class ResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
+       protected static function getResourceLoader() {
+               return new EmptyResourceLoader( new HashConfig( [
+                       'ResourceLoaderDebug' => false,
+                       'DefaultSkin' => 'fallback',
+                       'LanguageCode' => 'nl',
+               ] ) );
+       }
+
+       public function testEmpty() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+
+               // Request parameters
+               $this->assertEquals( [], $ctx->getModules() );
+               $this->assertEquals( 'nl', $ctx->getLanguage() );
+               $this->assertEquals( false, $ctx->getDebug() );
+               $this->assertEquals( null, $ctx->getOnly() );
+               $this->assertEquals( 'fallback', $ctx->getSkin() );
+               $this->assertEquals( null, $ctx->getUser() );
+
+               // Misc
+               $this->assertEquals( 'ltr', $ctx->getDirection() );
+               $this->assertEquals( 'nl|fallback||||||||', $ctx->getHash() );
+               $this->assertInstanceOf( User::class, $ctx->getUserObj() );
+       }
+
+       public function testDummy() {
+               $this->assertInstanceOf(
+                       ResourceLoaderContext::class,
+                       ResourceLoaderContext::newDummyContext()
+               );
+       }
+
+       public function testAccessors() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertInstanceOf( WebRequest::class, $ctx->getRequest() );
+               $this->assertInstanceOf( \Psr\Log\LoggerInterface::class, $ctx->getLogger() );
+       }
+
+       public function testTypicalRequest() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'debug' => 'false',
+                       'lang' => 'zh',
+                       'modules' => 'foo|foo.quux,baz,bar|baz.quux',
+                       'only' => 'styles',
+                       'skin' => 'fallback',
+               ] ) );
+
+               // Request parameters
+               $this->assertEquals(
+                       $ctx->getModules(),
+                       [ 'foo', 'foo.quux', 'foo.baz', 'foo.bar', 'baz.quux' ]
+               );
+               $this->assertEquals( false, $ctx->getDebug() );
+               $this->assertEquals( 'zh', $ctx->getLanguage() );
+               $this->assertEquals( 'styles', $ctx->getOnly() );
+               $this->assertEquals( 'fallback', $ctx->getSkin() );
+               $this->assertEquals( null, $ctx->getUser() );
+
+               // Misc
+               $this->assertEquals( 'ltr', $ctx->getDirection() );
+               $this->assertEquals( 'zh|fallback|||styles|||||', $ctx->getHash() );
+       }
+
+       public function testShouldInclude() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertTrue( $ctx->shouldIncludeScripts(), 'Scripts in combined' );
+               $this->assertTrue( $ctx->shouldIncludeStyles(), 'Styles in combined' );
+               $this->assertTrue( $ctx->shouldIncludeMessages(), 'Messages in combined' );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'only' => 'styles'
+               ] ) );
+               $this->assertFalse( $ctx->shouldIncludeScripts(), 'Scripts not in styles-only' );
+               $this->assertTrue( $ctx->shouldIncludeStyles(), 'Styles in styles-only' );
+               $this->assertFalse( $ctx->shouldIncludeMessages(), 'Messages not in styles-only' );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'only' => 'scripts'
+               ] ) );
+               $this->assertTrue( $ctx->shouldIncludeScripts(), 'Scripts in scripts-only' );
+               $this->assertFalse( $ctx->shouldIncludeStyles(), 'Styles not in scripts-only' );
+               $this->assertFalse( $ctx->shouldIncludeMessages(), 'Messages not in scripts-only' );
+       }
+
+       public function testGetUser() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertSame( null, $ctx->getUser() );
+               $this->assertTrue( $ctx->getUserObj()->isAnon() );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'user' => 'Example'
+               ] ) );
+               $this->assertSame( 'Example', $ctx->getUser() );
+               $this->assertEquals( 'Example', $ctx->getUserObj()->getName() );
+       }
+}
index 2114e0a..8b29983 100644 (file)
@@ -102,6 +102,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
         */
        public function testTemplateDependencies( $module, $expected ) {
                $rl = new ResourceLoaderFileModule( $module );
+               $rl->setName( 'testing' );
                $this->assertEquals( $rl->getDependencies(), $expected );
        }
 
@@ -164,6 +165,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                ];
 
                $module = new ResourceLoaderFileModule( $baseParams );
+               $module->setName( 'testing' );
 
                $this->assertEquals(
                        [
@@ -201,10 +203,12 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                        'localBasePath' => $basePath,
                        'styles' => [ 'test.css' ],
                ] );
+               $testModule->setName( 'testing' );
                $expectedModule = new ResourceLoaderFileModule( [
                        'localBasePath' => $basePath,
                        'styles' => [ 'expected.css' ],
                ] );
+               $expectedModule->setName( 'testing' );
 
                $contextLtr = $this->getResourceLoaderContext( 'en', 'ltr' );
                $contextRtl = $this->getResourceLoaderContext( 'he', 'rtl' );
@@ -260,6 +264,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
         */
        public function testGetTemplates( $module, $expected ) {
                $rl = new ResourceLoaderFileModule( $module );
+               $rl->setName( 'testing' );
 
                $this->assertEquals( $rl->getTemplates(), $expected );
        }
@@ -270,6 +275,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                        'localBasePath' => $basePath,
                        'styles' => [ 'bom.css' ],
                        ] );
+               $testModule->setName( 'testing' );
                $this->assertEquals(
                        substr( file_get_contents( "$basePath/bom.css" ), 0, 10 ),
                        "\xef\xbb\xbf.efbbbf",
index f61540d..aeb82d1 100644 (file)
@@ -154,6 +154,49 @@ class ResourceLoaderImageModuleTest extends ResourceLoaderTestCase {
                $styles = $module->getStyles( $this->getResourceLoaderContext() );
                $this->assertEquals( $expected, $styles['all'] );
        }
+
+       /**
+        * @covers ResourceLoaderContext::getImageObj
+        */
+       public function testContext() {
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
+               $this->assertFalse( $context->getImageObj(), 'Missing image parameter' );
+
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'image' => 'example',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Missing module parameter' );
+
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'modules' => 'unknown',
+                       'image' => 'example',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Not an image module' );
+
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [
+                       'class' => ResourceLoaderImageModule::class,
+                       'prefix' => 'test',
+                       'images' => [ 'example' => 'example.png' ],
+               ] );
+               $context = new ResourceLoaderContext( $rl, new FauxRequest( [
+                       'modules' => 'test',
+                       'image' => 'unknown',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Unknown image' );
+
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [
+                       'class' => ResourceLoaderImageModule::class,
+                       'prefix' => 'test',
+                       'images' => [ 'example' => 'example.png' ],
+               ] );
+               $context = new ResourceLoaderContext( $rl, new FauxRequest( [
+                       'modules' => 'test',
+                       'image' => 'example',
+               ] ) );
+               $this->assertInstanceOf( ResourceLoaderImage::class, $context->getImageObj() );
+       }
 }
 
 class ResourceLoaderImageModuleTestable extends ResourceLoaderImageModule {
index 53c9926..c24a321 100644 (file)
@@ -163,6 +163,12 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
                                [ 'single.module', 'foobar', 'foobaz' ],
                                'single.module|foobar,foobaz',
                        ],
+                       [
+                               'Ordering',
+                               [ 'foo', 'foo.baz', 'baz.quux', 'foo.bar' ],
+                               'foo|foo.baz,bar|baz.quux',
+                               [ 'foo', 'foo.baz', 'foo.bar', 'baz.quux' ],
+                       ]
                ];
        }
 
@@ -178,8 +184,12 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
         * @dataProvider providePackedModules
         * @covers ResourceLoaderContext::expandModuleNames
         */
-       public function testexpandModuleNames( $desc, $modules, $packed ) {
-               $this->assertEquals( $modules, ResourceLoaderContext::expandModuleNames( $packed ), $desc );
+       public function testExpandModuleNames( $desc, $modules, $packed, $unpacked = null ) {
+               $this->assertEquals(
+                       $unpacked ?: $modules,
+                       ResourceLoaderContext::expandModuleNames( $packed ),
+                       $desc
+               );
        }
 
        public static function provideAddSource() {
@@ -271,7 +281,6 @@ mw.example();
                                'name' => 'test.example',
                                'scripts' => [],
                                'styles' => [ 'css' => [ '.mw-example {}' ] ],
-                               'messages' => new XmlJsCode( '{}' ),
 
                                'expected' => 'mw.loader.implement( "test.example", [], {
     "css": [
@@ -310,18 +319,10 @@ mw.example();
 
                                'name' => 'user',
                                'scripts' => 'mw.example( 1 );',
+                               'wrap' => false,
 
                                'expected' => 'mw.loader.implement( "user", "mw.example( 1 );" );',
                        ] ],
-                       [ [
-                               'title' => 'Implement unwrapped user script',
-                               'debug' => false,
-
-                               'name' => 'user',
-                               'scripts' => 'mw.example( 1 );',
-
-                               'expected' => 'mw.loader.implement("user","mw.example(1);");',
-                       ] ],
                ];
        }
 
@@ -332,17 +333,20 @@ mw.example();
         */
        public function testMakeLoaderImplementScript( $case ) {
                $case += [
-                       'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' ),
-                       'debug' => true
+                       'wrap' => true,
+                       'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' )
                ];
                ResourceLoader::clearCache();
-               $this->setMwGlobals( 'wgResourceLoaderDebug', $case['debug'] );
+               $this->setMwGlobals( 'wgResourceLoaderDebug', true );
 
+               $rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
                $this->assertEquals(
                        $case['expected'],
-                       ResourceLoader::makeLoaderImplementScript(
+                       $rl->makeLoaderImplementScript(
                                $case['name'],
-                               $case['scripts'],
+                               ( $case['wrap'] && is_string( $case['scripts'] ) )
+                                       ? new XmlJsCode( $case['scripts'] )
+                                       : $case['scripts'],
                                $case['styles'],
                                $case['messages'],
                                $case['templates']
@@ -355,7 +359,8 @@ mw.example();
         */
        public function testMakeLoaderImplementScriptInvalid() {
                $this->setExpectedException( 'MWException', 'Invalid scripts error' );
-               ResourceLoader::makeLoaderImplementScript(
+               $rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
+               $rl->makeLoaderImplementScript(
                        'test', // name
                        123, // scripts
                        null, // styles
index 85834d7..b12d235 100644 (file)
@@ -114,28 +114,107 @@ class ResourceLoaderWikiModuleTest extends ResourceLoaderTestCase {
                        [ [], 'test1', true ],
                        // 'site' module with a non-empty page
                        [
-                               [ 'MediaWiki:Common.js' => [ 'rev_sha1' => 'dmh6qn', 'rev_len' => 1234 ] ],
+                               [ 'MediaWiki:Common.js' => [ 'page_len' => 1234 ] ],
                                'site',
                                false,
                        ],
                        // 'site' module with an empty page
                        [
-                               [ 'MediaWiki:Foo.js' => [ 'rev_sha1' => 'phoi', 'rev_len' => 0 ] ],
+                               [ 'MediaWiki:Foo.js' => [ 'page_len' => 0 ] ],
                                'site',
                                false,
                        ],
                        // 'user' module with a non-empty page
                        [
-                               [ 'User:Example/common.js' => [ 'rev_sha1' => 'j7ssba', 'rev_len' => 25 ] ],
+                               [ 'User:Example/common.js' => [ 'page_len' => 25 ] ],
                                'user',
                                false,
                        ],
                        // 'user' module with an empty page
                        [
-                               [ 'User:Example/foo.js' => [ 'rev_sha1' => 'phoi', 'rev_len' => 0 ] ],
+                               [ 'User:Example/foo.js' => [ 'page_len' => 0 ] ],
                                'user',
                                true,
                        ],
                ];
        }
+
+       /**
+        * @covers ResourceLoaderWikiModule::getTitleInfo
+        */
+       public function testGetTitleInfo() {
+               $pages = [
+                       'MediaWiki:Common.css' => [ 'type' => 'styles' ],
+                       'mediawiki: fallback.css' => [ 'type' => 'styles' ],
+               ];
+               $titleInfo = [
+                       'MediaWiki:Common.css' => [ 'page_len' => 1234 ],
+                       'MediaWiki:Fallback.css' => [ 'page_len' => 0 ],
+               ];
+               $expected = $titleInfo;
+
+               $module = $this->getMockBuilder( 'TestResourceLoaderWikiModule' )
+                       ->setMethods( [ 'getPages' ] )
+                       ->getMock();
+               $module->method( 'getPages' )->willReturn( $pages );
+               // Can't mock static methods
+               $module::$returnFetchTitleInfo = $titleInfo;
+
+               $context = $this->getMockBuilder( 'ResourceLoaderContext' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $module = TestingAccessWrapper::newFromObject( $module );
+               $this->assertEquals( $expected, $module->getTitleInfo( $context ), 'Title info' );
+       }
+
+       /**
+        * @covers ResourceLoaderWikiModule::getTitleInfo
+        * @covers ResourceLoaderWikiModule::setTitleInfo
+        * @covers ResourceLoaderWikiModule::preloadTitleInfo
+        */
+       public function testGetPreloadedTitleInfo() {
+               $pages = [
+                       'MediaWiki:Common.css' => [ 'type' => 'styles' ],
+                       // Regression against T145673. It's impossible to statically declare page names in
+                       // a canonical way since the canonical prefix is localised. As such, the preload
+                       // cache computed the right cache key, but failed to find the results when
+                       // doing an intersect on the canonical result, producing an empty array.
+                       'mediawiki: fallback.css' => [ 'type' => 'styles' ],
+               ];
+               $titleInfo = [
+                       'MediaWiki:Common.css' => [ 'page_len' => 1234 ],
+                       'MediaWiki:Fallback.css' => [ 'page_len' => 0 ],
+               ];
+               $expected = $titleInfo;
+
+               $module = $this->getMockBuilder( 'TestResourceLoaderWikiModule' )
+                       ->setMethods( [ 'getPages' ] )
+                       ->getMock();
+               $module->method( 'getPages' )->willReturn( $pages );
+               // Can't mock static methods
+               $module::$returnFetchTitleInfo = $titleInfo;
+
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'testmodule', $module );
+               $context = new ResourceLoaderContext( $rl, new FauxRequest() );
+
+               TestResourceLoaderWikiModule::preloadTitleInfo(
+                       $context,
+                       wfGetDB( DB_REPLICA ),
+                       [ 'testmodule' ]
+               );
+
+               $module = TestingAccessWrapper::newFromObject( $module );
+               $this->assertEquals( $expected, $module->getTitleInfo( $context ), 'Title info' );
+       }
+}
+
+class TestResourceLoaderWikiModule extends ResourceLoaderWikiModule {
+       public static $returnFetchTitleInfo = null;
+       protected static function fetchTitleInfo( IDatabase $db, array $pages, $fname = null ) {
+               $ret = self::$returnFetchTitleInfo;
+               self::$returnFetchTitleInfo = null;
+               return $ret;
+       }
 }
index 0b34b6f..3fb4bbb 100644 (file)
@@ -30,7 +30,7 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                        $this->markTestSkipped( "MySQL or SQLite with FTS3 only" );
                }
 
-               $searchType = $this->db->getSearchEngine();
+               $searchType = SearchEngineFactory::getSearchEngineClass( $this->db );
                $this->setMwGlobals( [
                        'wgSearchType' => $searchType
                ] );
@@ -207,4 +207,50 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                $this->assertArrayHasKey( 'testData', $mapping );
                $this->assertEquals( 'test', $mapping['testData'] );
        }
+
+       public function hookSearchIndexFields( $mockFieldBuilder, &$fields, SearchEngine $engine ) {
+               $fields['testField'] = $mockFieldBuilder( "testField", SearchIndexField::INDEX_TYPE_TEXT );
+               return true;
+       }
+
+       public function testAugmentorSearch() {
+               $this->search->setNamespaces( [ 0, 1, 4 ] );
+               $resultSet = $this->search->searchText( 'smithee' );
+               // Not using mock since PHPUnit mocks do not work properly with references in params
+               $this->mergeMwGlobalArrayValue( 'wgHooks',
+                       [ 'SearchResultsAugment' => [ [ $this, 'addAugmentors' ] ] ] );
+               $this->search->augmentSearchResults( $resultSet );
+               for ( $result = $resultSet->next(); $result; $result = $resultSet->next() ) {
+                       $id = $result->getTitle()->getArticleID();
+                       $augmentData = "Result:$id:" . $result->getTitle()->getText();
+                       $augmentData2 = "Result2:$id:" . $result->getTitle()->getText();
+                       $this->assertEquals( [ 'testSet' => $augmentData, 'testRow' => $augmentData2 ],
+                               $result->getExtensionData() );
+               }
+       }
+
+       public function addAugmentors( &$setAugmentors, &$rowAugmentors ) {
+               $setAugmentor = $this->getMock( 'ResultSetAugmentor' );
+               $setAugmentor->expects( $this->once() )
+                       ->method( 'augmentAll' )
+                       ->willReturnCallback( function ( SearchResultSet $resultSet ) {
+                               $data = [];
+                               for ( $result = $resultSet->next(); $result; $result = $resultSet->next() ) {
+                                       $id = $result->getTitle()->getArticleID();
+                                       $data[$id] = "Result:$id:" . $result->getTitle()->getText();
+                               }
+                               $resultSet->rewind();
+                               return $data;
+                       } );
+               $setAugmentors['testSet'] = $setAugmentor;
+
+               $rowAugmentor = $this->getMock( 'ResultAugmentor' );
+               $rowAugmentor->expects( $this->exactly( 2 ) )
+                       ->method( 'augment' )
+                       ->willReturnCallback( function ( SearchResult $result ) {
+                               $id = $result->getTitle()->getArticleID();
+                               return "Result2:$id:" . $result->getTitle()->getText();
+                       } );
+               $rowAugmentors['testRow'] = $rowAugmentor;
+       }
 }
index cfd5f78..cb27fde 100644 (file)
@@ -228,6 +228,34 @@ class BotPasswordTest extends MediaWikiTestCase {
                $this->assertNotNull( BotPassword::newFromCentralId( 43, 'BotPassword' ) );
        }
 
+       /**
+        * @dataProvider provideCanonicalizeLoginData
+        */
+       public function testCanonicalizeLoginData( $username, $password, $expectedResult ) {
+               $result = BotPassword::canonicalizeLoginData( $username, $password );
+               if ( is_array( $expectedResult ) ) {
+                       $this->assertArrayEquals( $expectedResult, $result, true, true );
+               } else {
+                       $this->assertSame( $expectedResult, $result );
+               }
+       }
+
+       public function provideCanonicalizeLoginData() {
+               return [
+                       [ 'user', 'pass', false ],
+                       [ 'user', 'abc@def', false ],
+                       [ 'legacy@user', 'pass', false ],
+                       [ 'user@bot', '12345678901234567890123456789012',
+                               [ 'user@bot', '12345678901234567890123456789012', true ] ],
+                       [ 'user', 'bot@12345678901234567890123456789012',
+                               [ 'user@bot', '12345678901234567890123456789012', true ] ],
+                       [ 'user', 'bot@12345678901234567890123456789012345',
+                               [ 'user@bot', '12345678901234567890123456789012345', true ] ],
+                       [ 'user', 'bot@x@12345678901234567890123456789012',
+                               [ 'user@bot@x', '12345678901234567890123456789012', true ] ],
+               ];
+       }
+
        public function testLogin() {
                // Test failure when bot passwords aren't enabled
                $this->setMwGlobals( 'wgEnableBotPasswords', false );
index 985554b..34548c0 100644 (file)
@@ -269,7 +269,14 @@ class UserTest extends MediaWikiTestCase {
                // let the user have a few (3) edits
                $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) );
                for ( $i = 0; $i < 3; $i++ ) {
-                       $page->doEdit( (string)$i, 'test', 0, false, $user );
+
+                       $page->doEditContent(
+                               ContentHandler::makeContent( (string)$i, $page->getTitle() ),
+                               'test',
+                               0,
+                               false,
+                               $user
+                       );
                }
 
                $this->assertEquals(
index 2de4bff..0ee4c13 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers FileContentsHasherTest
  */
-class FileContentsHasherTest extends MediaWikiTestCase {
+class FileContentsHasherTest extends PHPUnit_Framework_TestCase {
 
        public function provideSingleFile() {
                return array_map( function ( $file ) {
index 4c85c3d..905d14c 100644 (file)
@@ -4,7 +4,7 @@
  * @group Hash
  */
 
-class MWCryptHashTest extends MediaWikiTestCase {
+class MWCryptHashTest extends PHPUnit_Framework_TestCase {
 
        public function testHashLength() {
                if ( MWCryptHash::hashAlgo() !== 'whirlpool' ) {
index a70946a..d817104 100755 (executable)
 // through this entry point or not.
 define( 'MW_PHPUNIT_TEST', true );
 
-$wgPhpUnitClass = 'PHPUnit_TextUI_Command';
-
 // Start up MediaWiki in command-line mode
 require_once dirname( dirname( __DIR__ ) ) . "/maintenance/Maintenance.php";
 
 class PHPUnitMaintClass extends Maintenance {
 
        public static $additionalOptions = [
-               'regex' => false,
                'file' => false,
                'use-filebackend' => false,
                'use-bagostuff' => false,
                'use-jobqueue' => false,
-               'keep-uploads' => false,
                'use-normal-tables' => false,
                'reuse-db' => false,
                'wiki' => false,
+               'profiler' => false,
        ];
 
        public function __construct() {
@@ -43,22 +40,10 @@ class PHPUnitMaintClass extends Maintenance {
                        false, # not required
                        false # no arg needed
                );
-               $this->addOption(
-                       'regex',
-                       'Only run parser tests that match the given regex.',
-                       false,
-                       true
-               );
                $this->addOption( 'file', 'File describing parser tests.', false, true );
                $this->addOption( 'use-filebackend', 'Use filebackend', false, true );
                $this->addOption( 'use-bagostuff', 'Use bagostuff', false, true );
                $this->addOption( 'use-jobqueue', 'Use jobqueue', false, true );
-               $this->addOption(
-                       'keep-uploads',
-                       'Re-use the same upload directory for each test, don\'t delete it.',
-                       false,
-                       false
-               );
                $this->addOption( 'use-normal-tables', 'Use normal DB tables.', false, false );
                $this->addOption(
                        'reuse-db', 'Init DB only if tables are missing and keep after finish.',
@@ -70,104 +55,10 @@ class PHPUnitMaintClass extends Maintenance {
        public function finalSetup() {
                parent::finalSetup();
 
-               global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgMainWANCache;
-               global $wgMainStash;
-               global $wgLanguageConverterCacheType, $wgUseDatabaseMessages;
-               global $wgLocaltimezone, $wgLocalisationCacheConf;
-               global $wgDevelopmentWarnings;
-               global $wgSessionProviders, $wgSessionPbkdf2Iterations;
-               global $wgJobTypeConf;
-               global $wgAuthManagerConfig, $wgAuth;
-
                // Inject test autoloader
-               require_once __DIR__ . '/../TestsAutoLoader.php';
-
-               // wfWarn should cause tests to fail
-               $wgDevelopmentWarnings = true;
-
-               // Make sure all caches and stashes are either disabled or use
-               // in-process cache only to prevent tests from using any preconfigured
-               // cache meant for the local wiki from outside the test run.
-               // See also MediaWikiTestCase::run() which mocks CACHE_DB and APC.
-
-               // Disabled in DefaultSettings, override local settings
-               $wgMainWANCache =
-               $wgMainCacheType = CACHE_NONE;
-               // Uses CACHE_ANYTHING in DefaultSettings, use hash instead of db
-               $wgMessageCacheType =
-               $wgParserCacheType =
-               $wgSessionCacheType =
-               $wgLanguageConverterCacheType = 'hash';
-               // Uses db-replicated in DefaultSettings
-               $wgMainStash = 'hash';
-               // Use memory job queue
-               $wgJobTypeConf = [
-                       'default' => [ 'class' => 'JobQueueMemory', 'order' => 'fifo' ],
-               ];
-
-               $wgUseDatabaseMessages = false; # Set for future resets
-
-               // Assume UTC for testing purposes
-               $wgLocaltimezone = 'UTC';
-
-               $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull';
-
-               // Generic MediaWiki\Session\SessionManager configuration for tests
-               // We use CookieSessionProvider because things might be expecting
-               // cookies to show up in a FauxRequest somewhere.
-               $wgSessionProviders = [
-                       [
-                               'class' => MediaWiki\Session\CookieSessionProvider::class,
-                               'args' => [ [
-                                       'priority' => 30,
-                                       'callUserSetCookiesHook' => true,
-                               ] ],
-                       ],
-               ];
-
-               // Single-iteration PBKDF2 session secret derivation, for speed.
-               $wgSessionPbkdf2Iterations = 1;
+               self::requireTestsAutoloader();
 
-               // Generic AuthManager configuration for testing
-               $wgAuthManagerConfig = [
-                       'preauth' => [],
-                       'primaryauth' => [
-                               [
-                                       'class' => MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class,
-                                       'args' => [ [
-                                               'authoritative' => false,
-                                       ] ],
-                               ],
-                               [
-                                       'class' => MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class,
-                                       'args' => [ [
-                                               'authoritative' => true,
-                                       ] ],
-                               ],
-                       ],
-                       'secondaryauth' => [],
-               ];
-               $wgAuth = new MediaWiki\Auth\AuthManagerAuthPlugin();
-
-               // Bug 44192 Do not attempt to send a real e-mail
-               Hooks::clear( 'AlternateUserMailer' );
-               Hooks::register(
-                       'AlternateUserMailer',
-                       function () {
-                               return false;
-                       }
-               );
-               // xdebug's default of 100 is too low for MediaWiki
-               ini_set( 'xdebug.max_nesting_level', 1000 );
-
-               // Bug T116683 serialize_precision of 100
-               // may break testing against floating point values
-               // treated with PHP's serialize()
-               ini_set( 'serialize_precision', 17 );
-
-               // TODO: we should call MediaWikiTestCase::prepareServices( new GlobalVarConfig() ) here.
-               // But PHPUnit may not be loaded yet, so we have to wait until just
-               // before PHPUnit_TextUI_Command::main() is executed at the end of this file.
+               TestSetup::applyInitialConfig();
        }
 
        public function execute() {
@@ -188,9 +79,10 @@ class PHPUnitMaintClass extends Maintenance {
                                [ '--configuration', $IP . '/tests/phpunit/suite.xml' ] );
                }
 
+               $phpUnitClass = 'PHPUnit_TextUI_Command';
+
                if ( $this->hasOption( 'with-phpunitclass' ) ) {
-                       global $wgPhpUnitClass;
-                       $wgPhpUnitClass = $this->getOption( 'with-phpunitclass' );
+                       $phpUnitClass = $this->getOption( 'with-phpunitclass' );
 
                        # Cleanup $args array so the option and its value do not
                        # pollute PHPUnit
@@ -220,6 +112,25 @@ class PHPUnitMaintClass extends Maintenance {
                        }
                }
 
+               if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
+                       echo "PHPUnit not found. Please install it and other dev dependencies by
+               running `composer install` in MediaWiki root directory.\n";
+                       exit( 1 );
+               }
+               if ( !class_exists( $phpUnitClass ) ) {
+                       echo "PHPUnit entry point '" . $phpUnitClass . "' not found. Please make sure you installed
+               the containing component and check the spelling of the class name.\n";
+                       exit( 1 );
+               }
+
+               echo defined( 'HHVM_VERSION' ) ?
+                       'Using HHVM ' . HHVM_VERSION . ' (' . PHP_VERSION . ")\n" :
+                       'Using PHP ' . PHP_VERSION . "\n";
+
+               // Prepare global services for unit tests.
+               MediaWikiTestCase::prepareServices( new GlobalVarConfig() );
+
+               $phpUnitClass::main();
        }
 
        public function getDbType() {
@@ -250,25 +161,3 @@ class PHPUnitMaintClass extends Maintenance {
 
 $maintClass = 'PHPUnitMaintClass';
 require RUN_MAINTENANCE_IF_MAIN;
-
-if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
-       echo "PHPUnit not found. Please install it and other dev dependencies by
-running `composer install` in MediaWiki root directory.\n";
-       exit( 1 );
-}
-if ( !class_exists( $wgPhpUnitClass ) ) {
-       echo "PHPUnit entry point '" . $wgPhpUnitClass . "' not found. Please make sure you installed
-the containing component and check the spelling of the class name.\n";
-       exit( 1 );
-}
-
-echo defined( 'HHVM_VERSION' ) ?
-       'Using HHVM ' . HHVM_VERSION . ' (' . PHP_VERSION . ")\n" :
-       'Using PHP ' . PHP_VERSION . "\n";
-
-// Prepare global services for unit tests.
-// FIXME: this should be done in the finalSetup() method,
-// but PHPUnit may not have been loaded at that point.
-MediaWikiTestCase::prepareServices( new GlobalVarConfig() );
-
-$wgPhpUnitClass::main();
diff --git a/tests/phpunit/structure/ContentHandlerSanityTest.php b/tests/phpunit/structure/ContentHandlerSanityTest.php
new file mode 100644 (file)
index 0000000..98a0fbb
--- /dev/null
@@ -0,0 +1,53 @@
+<?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
+ */
+
+class ContentHandlerSanityTest extends MediaWikiTestCase {
+
+       public static function provideHandlers() {
+               $models = ContentHandler::getContentModels();
+               $handlers = [];
+               foreach ( $models as $model ) {
+                       $handlers[] = [ ContentHandler::getForModelID( $model ) ];
+               }
+
+               return $handlers;
+       }
+
+       /**
+        * @dataProvider provideHandlers
+        * @param ContentHandler $handler
+        */
+       public function testMakeEmptyContent( ContentHandler $handler ) {
+               $content = $handler->makeEmptyContent();
+               $this->assertInstanceOf( Content::class, $content );
+               if ( $handler instanceof TextContentHandler ) {
+                       // TextContentHandler::getContentClass() is protected, so bypass
+                       // that restriction
+                       $testingWrapper = TestingAccessWrapper::newFromObject( $handler );
+                       $this->assertInstanceOf( $testingWrapper->getContentClass(), $content );
+               }
+
+               $handlerClass = get_class( $handler );
+               $contentClass = get_class( $content );
+
+               $this->assertTrue(
+                       $content->isValid(),
+                       "$handlerClass::makeEmptyContent() did not return a valid content ($contentClass::isValid())"
+               );
+       }
+}
index ed18205..16299aa 100644 (file)
        <testsuites>
                <testsuite name="includes">
                        <directory>includes</directory>
+                       <!-- Parser tests must be invoked via their suite -->
+                       <exclude>includes/parser/ParserIntegrationTest.php</exclude>
                </testsuite>
                <testsuite name="languages">
                        <directory>languages</directory>
                </testsuite>
                <testsuite name="parsertests">
-                       <file>includes/parser/MediaWikiParserTest.php</file>
+                       <file>suites/CoreParserTestSuite.php</file>
                        <file>suites/ExtensionsParserTestSuite.php</file>
                </testsuite>
                <testsuite name="skins">
@@ -55,7 +57,6 @@
                <exclude>
                        <group>Utility</group>
                        <group>Broken</group>
-                       <group>ParserFuzz</group>
                        <group>Stub</group>
                </exclude>
        </groups>
diff --git a/tests/phpunit/suites/CoreParserTestSuite.php b/tests/phpunit/suites/CoreParserTestSuite.php
new file mode 100644 (file)
index 0000000..e48a116
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+class CoreParserTestSuite extends PHPUnit_Framework_TestSuite {
+
+       public static function suite() {
+               return ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
+       }
+
+}
+
index 3d68b24..8d6ee07 100644 (file)
@@ -2,7 +2,7 @@
 class ExtensionsParserTestSuite extends PHPUnit_Framework_TestSuite {
 
        public static function suite() {
-               return MediaWikiParserTest::suite( MediaWikiParserTest::NO_CORE );
+               return ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
        }
 
 }
diff --git a/tests/phpunit/suites/ParserTestFileSuite.php b/tests/phpunit/suites/ParserTestFileSuite.php
new file mode 100644 (file)
index 0000000..dbee894
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * This is the suite class for running tests within a single .txt source file.
+ * It is not invoked directly. Use --filter to select files, or
+ * use parserTests.php.
+ */
+class ParserTestFileSuite extends PHPUnit_Framework_TestSuite {
+       private $ptRunner;
+       private $ptFileName;
+       private $ptFileInfo;
+
+       function __construct( $runner, $name, $fileName ) {
+               parent::__construct( $name );
+               $this->ptRunner = $runner;
+               $this->ptFileName = $fileName;
+               $this->ptFileInfo = TestFileReader::read( $this->ptFileName );
+
+               foreach ( $this->ptFileInfo['tests'] as $test ) {
+                       $this->addTest( new ParserIntegrationTest( $runner, $fileName, $test ),
+                               [ 'Database', 'Parser', 'ParserTests' ] );
+               }
+       }
+
+       function setUp() {
+               $this->ptRunner->addArticles( $this->ptFileInfo[ 'articles'] );
+       }
+}
diff --git a/tests/phpunit/suites/ParserTestTopLevelSuite.php b/tests/phpunit/suites/ParserTestTopLevelSuite.php
new file mode 100644 (file)
index 0000000..4284a77
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * The UnitTest must be either a class that inherits from MediaWikiTestCase
+ * or a class that provides a public static suite() method which returns
+ * an PHPUnit_Framework_Test object
+ *
+ * @group Parser
+ * @group ParserTests
+ * @group Database
+ */
+class ParserTestTopLevelSuite extends PHPUnit_Framework_TestSuite {
+       /** @var ParserTestRunner */
+       private $ptRunner;
+
+       /** @var ScopedCallback */
+       private $ptTeardownScope;
+
+       /**
+        * @defgroup filtering_constants Filtering constants
+        *
+        * Limit inclusion of parser tests files coming from MediaWiki core
+        * @{
+        */
+
+       /** Include files shipped with MediaWiki core */
+       const CORE_ONLY = 1;
+       /** Include non core files as set in $wgParserTestFiles */
+       const NO_CORE = 2;
+       /** Include anything set via $wgParserTestFiles */
+       const WITH_ALL = 3; # CORE_ONLY | NO_CORE
+
+       /** @} */
+
+       /**
+        * Get a PHPUnit test suite of parser tests. Optionally filtered with
+        * $flags.
+        *
+        * @par Examples:
+        * Get a suite of parser tests shipped by MediaWiki core:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
+        * @endcode
+        * Get a suite of various parser tests, like extensions:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
+        * @endcode
+        * Get any test defined via $wgParserTestFiles:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::WITH_ALL );
+        * @endcode
+        *
+        * @param int $flags Bitwise flag to filter out the $wgParserTestFiles that
+        * will be included.  Default: ParserTestTopLevelSuite::CORE_ONLY
+        *
+        * @return PHPUnit_Framework_TestSuite
+        */
+       public static function suite( $flags = self::CORE_ONLY ) {
+               return new self( $flags );
+       }
+
+       function __construct( $flags ) {
+               parent::__construct();
+
+               $this->ptRecorder = new PhpunitTestRecorder;
+               $this->ptRunner = new ParserTestRunner( $this->ptRecorder );
+
+               if ( is_string( $flags ) ) {
+                       $flags = self::CORE_ONLY;
+               }
+               global $wgParserTestFiles, $IP;
+
+               $mwTestDir = $IP . '/tests/';
+
+               # Human friendly helpers
+               $wantsCore = ( $flags & self::CORE_ONLY );
+               $wantsRest = ( $flags & self::NO_CORE );
+
+               # Will hold the .txt parser test files we will include
+               $filesToTest = [];
+
+               # Filter out .txt files
+               foreach ( $wgParserTestFiles as $parserTestFile ) {
+                       $isCore = ( 0 === strpos( $parserTestFile, $mwTestDir ) );
+
+                       if ( $isCore && $wantsCore ) {
+                               self::debug( "included core parser tests: $parserTestFile" );
+                               $filesToTest[] = $parserTestFile;
+                       } elseif ( !$isCore && $wantsRest ) {
+                               self::debug( "included non core parser tests: $parserTestFile" );
+                               $filesToTest[] = $parserTestFile;
+                       } else {
+                               self::debug( "skipped parser tests: $parserTestFile" );
+                       }
+               }
+               self::debug( 'parser tests files: '
+                       . implode( ' ', $filesToTest ) );
+
+               $testList = [];
+               $counter = 0;
+               foreach ( $filesToTest as $fileName ) {
+                       // Call the highest level directory the extension name.
+                       // It may or may not actually be, but it should be close
+                       // enough to cause there to be separate names for different
+                       // things, which is good enough for our purposes.
+                       $extensionName = basename( dirname( $fileName ) );
+                       $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
+                       $parserTestClassName = ucfirst( $testsName );
+
+                       // Official spec for class names: http://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 );
+
+                       if ( isset( $testList[$parserTestClassName] ) ) {
+                               // If there is a conflict, append a number.
+                               $counter++;
+                               $parserTestClassName .= $counter;
+                       }
+                       $testList[$parserTestClassName] = true;
+
+                       // Previously we actually created a class here, with eval(). We now
+                       // just override the name.
+
+                       self::debug( "Adding test class $parserTestClassName" );
+                       $this->addTest( new ParserTestFileSuite(
+                               $this->ptRunner, $parserTestClassName, $fileName ) );
+               }
+       }
+
+       public function setUp() {
+               wfDebug( __METHOD__ );
+               $db = wfGetDB( DB_MASTER );
+               $type = $db->getType();
+               $prefix = $type === 'oracle' ?
+                       MediaWikiTestCase::ORA_DB_PREFIX : MediaWikiTestCase::DB_PREFIX;
+               MediaWikiTestCase::setupTestDB( $db, $prefix );
+               $teardown = $this->ptRunner->setDatabase( $db );
+               $teardown = $this->ptRunner->setupUploads( $teardown );
+               $this->ptTeardownScope = $teardown;
+       }
+
+       public function tearDown() {
+               wfDebug( __METHOD__ );
+               if ( $this->ptTeardownScope ) {
+                       ScopedCallback::consume( $this->ptTeardownScope );
+               }
+       }
+
+       /**
+        * Write $msg under log group 'tests-parser'
+        * @param string $msg Message to log
+        */
+       protected static function debug( $msg ) {
+               return wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );
+       }
+}
index a480671..0797f32 100644 (file)
                assert.deepEqual( stub.getCall( 0 ).args, [ { foo: 'bar' } ], '#saveOptions called correctly' );
        } );
 
-       QUnit.test( 'saveOptions', function ( assert ) {
+       QUnit.test( 'saveOptions without Unit Separator', function ( assert ) {
                QUnit.expect( 13 );
 
-               var api = new mw.Api();
+               var api = new mw.Api( { useUS: false } );
 
                // We need to respond to the request for token first, otherwise the other requests won't be sent
                // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
                        }
                } );
        } );
+
+       QUnit.test( 'saveOptions with Unit Separator', function ( assert ) {
+               QUnit.expect( 14 );
+
+               var api = new mw.Api( { useUS: true } );
+
+               // We need to respond to the request for token first, otherwise the other requests won't be sent
+               // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
+               api.getToken( 'options' );
+               this.server.respond(
+                       /meta=tokens&type=csrf/,
+                       [ 200, { 'Content-Type': 'application/json' },
+                               '{ "query": { "tokens": { "csrftoken": "+\\\\" } } }' ]
+               );
+
+               api.saveOptions( {} ).done( function () {
+                       assert.ok( true, 'Request completed: empty case' );
+               } );
+               api.saveOptions( { foo: 'bar' } ).done( function () {
+                       assert.ok( true, 'Request completed: simple' );
+               } );
+               api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: two options' );
+               } );
+               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: bundleable with unit separator' );
+               } );
+               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', 'baz=baz': 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: not bundleable with unit separator' );
+               } );
+               api.saveOptions( { foo: null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option' );
+               } );
+               api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option, not bundleable' );
+               } );
+
+               // Requests are POST, match requestBody instead of url
+               this.server.respond( function ( request ) {
+                       switch ( request.requestBody ) {
+                               // simple
+                               case 'action=options&format=json&formatversion=2&change=foo%3Dbar&token=%2B%5C':
+                               // two options
+                               case 'action=options&format=json&formatversion=2&change=foo%3Dbar%7Cbaz%3Dquux&token=%2B%5C':
+                               // bundleable with unit separator
+                               case 'action=options&format=json&formatversion=2&change=%1Ffoo%3Dbar%7Cquux%1Fbar%3Da%7Cb%7Cc%1Fbaz%3Dquux&token=%2B%5C':
+                               // not bundleable with unit separator
+                               case 'action=options&format=json&formatversion=2&optionname=baz%3Dbaz&optionvalue=quux&token=%2B%5C':
+                               case 'action=options&format=json&formatversion=2&change=%1Ffoo%3Dbar%7Cquux%1Fbar%3Da%7Cb%7Cc&token=%2B%5C':
+                               // reset an option
+                               case 'action=options&format=json&formatversion=2&change=foo&token=%2B%5C':
+                               // reset an option, not bundleable
+                               case 'action=options&format=json&formatversion=2&optionname=foo%7Cbar%3Dquux&token=%2B%5C':
+                                       assert.ok( true, 'Repond to ' + request.requestBody );
+                                       request.respond( 200, { 'Content-Type': 'application/json' },
+                                               '{ "options": "success" }' );
+                                       break;
+                               default:
+                                       assert.ok( false, 'Unexpected request: ' + request.requestBody );
+                       }
+               } );
+       } );
 }( mediaWiki ) );
index 991725b..886e2b6 100644 (file)
@@ -38,6 +38,8 @@
                        'A < B',
                        'A > B',
                        'A | B',
+                       'A \t B',
+                       'A \n B',
                        // URL encoding
                        'A%20B',
                        'A%23B',
                assert.equal( title.getPrefixedText(), '.foo' );
        } );
 
-       QUnit.test( 'Transformation', 11, function ( assert ) {
+       QUnit.test( 'Transformation', 12, function ( assert ) {
                var title;
 
                title = new mw.Title( 'File:quux pif.jpg' );
                assert.equal( title.toText(), 'User:HAshAr' );
                assert.equal( title.getNamespaceId(), 2, 'Case-insensitive namespace prefix' );
 
-               // Don't ask why, it's the way the backend works. One space is kept of each set.
-               title = new mw.Title( 'Foo  __  \t __ bar' );
+               title = new mw.Title( 'Foo \u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000 bar' );
                assert.equal( title.getMain(), 'Foo_bar', 'Merge multiple types of whitespace/underscores into a single underscore' );
 
+               title = new mw.Title( 'Foo\u200E\u200F\u202A\u202B\u202C\u202D\u202Ebar' );
+               assert.equal( title.getMain(), 'Foobar', 'Strip Unicode bidi override characters' );
+
                // Regression test: Previously it would only detect an extension if there is no space after it
                title = new mw.Title( 'Example.js  ' );
                assert.equal( title.getExtension(), 'js', 'Space after an extension is stripped' );
index 3332c08..5122dcd 100644 (file)
                }
        } ) );
 
-       QUnit.test( 'options', 1, function ( assert ) {
+       QUnit.test( 'options', function ( assert ) {
                assert.ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
        } );
 
-       QUnit.test( 'user status', 7, function ( assert ) {
-
+       QUnit.test( 'getters (anonymous)', function ( assert ) {
                // Forge an anonymous user
                mw.config.set( 'wgUserName', null );
                delete mw.config.values.wgUserId;
 
-               assert.strictEqual( mw.user.getName(), null, 'user.getName() returns null when anonymous' );
-               assert.assertTrue( mw.user.isAnon(), 'user.isAnon() returns true when anonymous' );
-               assert.strictEqual( mw.user.getId(), 0, 'user.getId() returns 0 when anonymous' );
+               assert.strictEqual( mw.user.getName(), null, 'getName()' );
+               assert.strictEqual( mw.user.isAnon(), true, 'isAnon()' );
+               assert.strictEqual( mw.user.getId(), 0, 'getId()' );
+       } );
 
-               // Not part of startUp module
+       QUnit.test( 'getters (logged-in)', function ( assert ) {
                mw.config.set( 'wgUserName', 'John' );
                mw.config.set( 'wgUserId', 123 );
 
-               assert.equal( mw.user.getName(), 'John', 'user.getName() returns username when logged-in' );
-               assert.assertFalse( mw.user.isAnon(), 'user.isAnon() returns false when logged-in' );
-               assert.strictEqual( mw.user.getId(), 123, 'user.getId() returns correct ID when logged-in' );
+               assert.equal( mw.user.getName(), 'John', 'getName()' );
+               assert.strictEqual( mw.user.isAnon(), false, 'isAnon()' );
+               assert.strictEqual( mw.user.getId(), 123, 'getId()' );
 
-               assert.equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
+               assert.equal( mw.user.id(), 'John', 'user.id()' );
        } );
 
-       QUnit.test( 'getUserInfos', 3, function ( assert ) {
+       QUnit.test( 'getUserInfo', function ( assert ) {
                mw.config.set( 'wgUserGroups', [ '*', 'user' ] );
 
                mw.user.getGroups( function ( groups ) {
@@ -64,7 +64,7 @@
                this.server.respond();
        } );
 
-       QUnit.test( 'generateRandomSessionId', 4, function ( assert ) {
+       QUnit.test( 'generateRandomSessionId', function ( assert ) {
                var result, result2;
 
                result = mw.user.generateRandomSessionId();
@@ -77,7 +77,7 @@
 
        } );
 
-       QUnit.test( 'generateRandomSessionId (fallback)', 4, function ( assert ) {
+       QUnit.test( 'generateRandomSessionId (fallback)', function ( assert ) {
                var result, result2;
 
                // Pretend crypto API is not there to test the Math.random fallback
index d697507..4eac362 100644 (file)
                assert.strictEqual( mw.util.getParamValue( 'TEST', url ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
        } );
 
-       QUnit.test( 'tooltipAccessKey', 4, function ( assert ) {
-               this.suppressWarnings();
-
-               assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'tooltipAccessKeyPrefix must be a string' );
-               assert.equal( $.type( mw.util.tooltipAccessKeyRegexp ), 'regexp', 'tooltipAccessKeyRegexp is a regexp' );
-               assert.ok( mw.util.updateTooltipAccessKeys, 'updateTooltipAccessKeys is non-empty' );
-
-               'Example [a]'.replace( mw.util.tooltipAccessKeyRegexp, function ( sub, m1, m2, m3, m4, m5, m6 ) {
-                       assert.equal( m6, 'a', 'tooltipAccessKeyRegexp finds the accesskey hint' );
-               } );
-
-               this.restoreWarnings();
-       } );
-
        QUnit.test( '$content', 2, function ( assert ) {
                assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
                assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
diff --git a/tests/testHelpers.inc b/tests/testHelpers.inc
deleted file mode 100644 (file)
index 1369406..0000000
+++ /dev/null
@@ -1,908 +0,0 @@
-<?php
-/**
- * Recording for passing/failing tests.
- *
- * 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 Testing
- */
-
-/**
- * Interface to record parser test results.
- *
- * The ITestRecorder is a very simple interface to record the result of
- * MediaWiki parser tests. One should call start() before running the
- * full parser tests and end() once all the tests have been finished.
- * After each test, you should use record() to keep track of your tests
- * results. Finally, report() is used to generate a summary of your
- * test run, one could dump it to the console for human consumption or
- * register the result in a database for tracking purposes.
- *
- * @since 1.22
- */
-interface ITestRecorder {
-
-       /**
-        * Called at beginning of the parser test run
-        */
-       public function start();
-
-       /**
-        * Called after each test
-        * @param string $test
-        * @param integer $subtest
-        * @param bool $result
-        */
-       public function record( $test, $subtest, $result );
-
-       /**
-        * Called before finishing the test run
-        */
-       public function report();
-
-       /**
-        * Called at the end of the parser test run
-        */
-       public function end();
-
-}
-
-class TestRecorder implements ITestRecorder {
-       public $parent;
-       public $term;
-
-       function __construct( $parent ) {
-               $this->parent = $parent;
-               $this->term = $parent->term;
-       }
-
-       function start() {
-               $this->total = 0;
-               $this->success = 0;
-       }
-
-       function record( $test, $subtest, $result ) {
-               $this->total++;
-               $this->success += ( $result ? 1 : 0 );
-       }
-
-       function end() {
-               // dummy
-       }
-
-       function report() {
-               if ( $this->total > 0 ) {
-                       $this->reportPercentage( $this->success, $this->total );
-               } else {
-                       throw new MWException( "No tests found.\n" );
-               }
-       }
-
-       function reportPercentage( $success, $total ) {
-               $ratio = wfPercent( 100 * $success / $total );
-               print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)... ";
-
-               if ( $success == $total ) {
-                       print $this->term->color( 32 ) . "ALL TESTS PASSED!";
-               } else {
-                       $failed = $total - $success;
-                       print $this->term->color( 31 ) . "$failed tests failed!";
-               }
-
-               print $this->term->reset() . "\n";
-
-               return ( $success == $total );
-       }
-}
-
-class DbTestPreviewer extends TestRecorder {
-       protected $lb; // /< Database load balancer
-       protected $db; // /< Database connection to the main DB
-       protected $curRun; // /< run ID number for the current run
-       protected $prevRun; // /< run ID number for the previous run, if any
-       protected $results; // /< Result array
-
-       /**
-        * This should be called before the table prefix is changed
-        * @param TestRecorder $parent
-        */
-       function __construct( $parent ) {
-               parent::__construct( $parent );
-
-               $this->lb = wfGetLBFactory()->newMainLB();
-               // This connection will have the wiki's table prefix, not parsertest_
-               $this->db = $this->lb->getConnection( DB_MASTER );
-       }
-
-       /**
-        * Set up result recording; insert a record for the run with the date
-        * and all that fun stuff
-        */
-       function start() {
-               parent::start();
-
-               if ( !$this->db->tableExists( 'testrun', __METHOD__ )
-                       || !$this->db->tableExists( 'testitem', __METHOD__ )
-               ) {
-                       print "WARNING> `testrun` table not found in database.\n";
-                       $this->prevRun = false;
-               } else {
-                       // We'll make comparisons against the previous run later...
-                       $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' );
-               }
-
-               $this->results = [];
-       }
-
-       function getName( $test, $subtest ) {
-               if ( $subtest ) {
-                       return "$test subtest #$subtest";
-               } else {
-                       return $test;
-               }
-       }
-
-       function record( $test, $subtest, $result ) {
-               parent::record( $test, $subtest, $result );
-               $this->results[ $this->getName( $test, $subtest ) ] = $result;
-       }
-
-       function report() {
-               if ( $this->prevRun ) {
-                       // f = fail, p = pass, n = nonexistent
-                       // codes show before then after
-                       $table = [
-                               'fp' => 'previously failing test(s) now PASSING! :)',
-                               'pn' => 'previously PASSING test(s) removed o_O',
-                               'np' => 'new PASSING test(s) :)',
-
-                               'pf' => 'previously passing test(s) now FAILING! :(',
-                               'fn' => 'previously FAILING test(s) removed O_o',
-                               'nf' => 'new FAILING test(s) :(',
-                               'ff' => 'still FAILING test(s) :(',
-                       ];
-
-                       $prevResults = [];
-
-                       $res = $this->db->select( 'testitem', [ 'ti_name', 'ti_success' ],
-                               [ 'ti_run' => $this->prevRun ], __METHOD__ );
-
-                       foreach ( $res as $row ) {
-                               if ( !$this->parent->regex
-                                       || preg_match( "/{$this->parent->regex}/i", $row->ti_name )
-                               ) {
-                                       $prevResults[$row->ti_name] = $row->ti_success;
-                               }
-                       }
-
-                       $combined = array_keys( $this->results + $prevResults );
-
-                       # Determine breakdown by change type
-                       $breakdown = [];
-                       foreach ( $combined as $test ) {
-                               if ( !isset( $prevResults[$test] ) ) {
-                                       $before = 'n';
-                               } elseif ( $prevResults[$test] == 1 ) {
-                                       $before = 'p';
-                               } else /* if ( $prevResults[$test] == 0 )*/ {
-                                       $before = 'f';
-                               }
-
-                               if ( !isset( $this->results[$test] ) ) {
-                                       $after = 'n';
-                               } elseif ( $this->results[$test] == 1 ) {
-                                       $after = 'p';
-                               } else /*if ( $this->results[$test] == 0 ) */ {
-                                       $after = 'f';
-                               }
-
-                               $code = $before . $after;
-
-                               if ( isset( $table[$code] ) ) {
-                                       $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after );
-                               }
-                       }
-
-                       # Write out results
-                       foreach ( $table as $code => $label ) {
-                               if ( !empty( $breakdown[$code] ) ) {
-                                       $count = count( $breakdown[$code] );
-                                       printf( "\n%4d %s\n", $count, $label );
-
-                                       foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) {
-                                               print "      * $differing_test_name  [$statusInfo]\n";
-                                       }
-                               }
-                       }
-               } else {
-                       print "No previous test runs to compare against.\n";
-               }
-
-               print "\n";
-               parent::report();
-       }
-
-       /**
-        * Returns a string giving information about when a test last had a status change.
-        * Could help to track down when regressions were introduced, as distinct from tests
-        * which have never passed (which are more change requests than regressions).
-        * @param string $testname
-        * @param string $after
-        * @return string
-        */
-       private function getTestStatusInfo( $testname, $after ) {
-               // If we're looking at a test that has just been removed, then say when it first appeared.
-               if ( $after == 'n' ) {
-                       $changedRun = $this->db->selectField( 'testitem',
-                               'MIN(ti_run)',
-                               [ 'ti_name' => $testname ],
-                               __METHOD__ );
-                       $appear = $this->db->selectRow( 'testrun',
-                               [ 'tr_date', 'tr_mw_version' ],
-                               [ 'tr_id' => $changedRun ],
-                               __METHOD__ );
-
-                       return "First recorded appearance: "
-                               . date( "d-M-Y H:i:s", strtotime( $appear->tr_date ) )
-                               . ", " . $appear->tr_mw_version;
-               }
-
-               // Otherwise, this test has previous recorded results.
-               // See when this test last had a different result to what we're seeing now.
-               $conds = [
-                       'ti_name' => $testname,
-                       'ti_success' => ( $after == 'f' ? "1" : "0" ) ];
-
-               if ( $this->curRun ) {
-                       $conds[] = "ti_run != " . $this->db->addQuotes( $this->curRun );
-               }
-
-               $changedRun = $this->db->selectField( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ );
-
-               // If no record of ever having had a different result.
-               if ( is_null( $changedRun ) ) {
-                       if ( $after == "f" ) {
-                               return "Has never passed";
-                       } else {
-                               return "Has never failed";
-                       }
-               }
-
-               // Otherwise, we're looking at a test whose status has changed.
-               // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.)
-               // In this situation, give as much info as we can as to when it changed status.
-               $pre = $this->db->selectRow( 'testrun',
-                       [ 'tr_date', 'tr_mw_version' ],
-                       [ 'tr_id' => $changedRun ],
-                       __METHOD__ );
-               $post = $this->db->selectRow( 'testrun',
-                       [ 'tr_date', 'tr_mw_version' ],
-                       [ "tr_id > " . $this->db->addQuotes( $changedRun ) ],
-                       __METHOD__,
-                       [ "LIMIT" => 1, "ORDER BY" => 'tr_id' ]
-               );
-
-               if ( $post ) {
-                       $postDate = date( "d-M-Y H:i:s", strtotime( $post->tr_date ) ) . ", {$post->tr_mw_version}";
-               } else {
-                       $postDate = 'now';
-               }
-
-               return ( $after == "f" ? "Introduced" : "Fixed" ) . " between "
-                       . date( "d-M-Y H:i:s", strtotime( $pre->tr_date ) ) . ", " . $pre->tr_mw_version
-                       . " and $postDate";
-       }
-
-       /**
-        * Close the DB connection
-        */
-       function end() {
-               $this->lb->closeAll();
-               parent::end();
-       }
-}
-
-class DbTestRecorder extends DbTestPreviewer {
-       public $version;
-
-       /**
-        * Set up result recording; insert a record for the run with the date
-        * and all that fun stuff
-        */
-       function start() {
-               $this->db->begin( __METHOD__ );
-
-               if ( !$this->db->tableExists( 'testrun' )
-                       || !$this->db->tableExists( 'testitem' )
-               ) {
-                       print "WARNING> `testrun` table not found in database. Trying to create table.\n";
-                       $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) );
-                       echo "OK, resuming.\n";
-               }
-
-               parent::start();
-
-               $this->db->insert( 'testrun',
-                       [
-                               'tr_date' => $this->db->timestamp(),
-                               'tr_mw_version' => $this->version,
-                               'tr_php_version' => PHP_VERSION,
-                               'tr_db_version' => $this->db->getServerVersion(),
-                               'tr_uname' => php_uname()
-                       ],
-                       __METHOD__ );
-               if ( $this->db->getType() === 'postgres' ) {
-                       $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' );
-               } else {
-                       $this->curRun = $this->db->insertId();
-               }
-       }
-
-       /**
-        * Record an individual test item's success or failure to the db
-        *
-        * @param string $test
-        * @param bool $result
-        */
-       function record( $test, $subtest, $result ) {
-               parent::record( $test, $subtest, $result );
-
-               $this->db->insert( 'testitem',
-                       [
-                               'ti_run' => $this->curRun,
-                               'ti_name' => $this->getName( $test, $subtest ),
-                               'ti_success' => $result ? 1 : 0,
-                       ],
-                       __METHOD__ );
-       }
-
-       /**
-        * Commit transaction and clean up for result recording
-        */
-       function end() {
-               $this->db->commit( __METHOD__ );
-               parent::end();
-       }
-}
-
-class TestFileIterator implements Iterator {
-       private $file;
-       private $fh;
-       /**
-        * @var ParserTest|MediaWikiParserTest An instance of ParserTest (parserTests.php)
-        *  or MediaWikiParserTest (phpunit)
-        */
-       private $parserTest;
-       private $index = 0;
-       private $test;
-       private $section = null;
-       /** String|null: current test section being analyzed */
-       private $sectionData = [];
-       private $lineNum;
-       private $eof;
-       # Create a fake parser tests which never run anything unless
-       # asked to do so. This will avoid running hooks for a disabled test
-       private $delayedParserTest;
-       private $nextSubTest = 0;
-
-       function __construct( $file, $parserTest ) {
-               $this->file = $file;
-               $this->fh = fopen( $this->file, "rt" );
-
-               if ( !$this->fh ) {
-                       throw new MWException( "Couldn't open file '$file'\n" );
-               }
-
-               $this->parserTest = $parserTest;
-               $this->delayedParserTest = new DelayedParserTest();
-
-               $this->lineNum = $this->index = 0;
-       }
-
-       function rewind() {
-               if ( fseek( $this->fh, 0 ) ) {
-                       throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
-               }
-
-               $this->index = -1;
-               $this->lineNum = 0;
-               $this->eof = false;
-               $this->next();
-
-               return true;
-       }
-
-       function current() {
-               return $this->test;
-       }
-
-       function key() {
-               return $this->index;
-       }
-
-       function next() {
-               if ( $this->readNextTest() ) {
-                       $this->index++;
-                       return true;
-               } else {
-                       $this->eof = true;
-               }
-       }
-
-       function valid() {
-               return $this->eof != true;
-       }
-
-       function setupCurrentTest() {
-               // "input" and "result" are old section names allowed
-               // for backwards-compatibility.
-               $input = $this->checkSection( [ 'wikitext', 'input' ], false );
-               $result = $this->checkSection( [ 'html/php', 'html/*', 'html', 'result' ], false );
-               // some tests have "with tidy" and "without tidy" variants
-               $tidy = $this->checkSection( [ 'html/php+tidy', 'html+tidy' ], false );
-               if ( $tidy != false ) {
-                       if ( $this->nextSubTest == 0 ) {
-                               if ( $result != false ) {
-                                       $this->nextSubTest = 1; // rerun non-tidy variant later
-                               }
-                               $result = $tidy;
-                       } else {
-                               $this->nextSubTest = 0; // go on to next test after this
-                               $tidy = false;
-                       }
-               }
-
-               if ( !isset( $this->sectionData['options'] ) ) {
-                       $this->sectionData['options'] = '';
-               }
-
-               if ( !isset( $this->sectionData['config'] ) ) {
-                       $this->sectionData['config'] = '';
-               }
-
-               $isDisabled = preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) &&
-                       !$this->parserTest->runDisabled;
-               $isParsoidOnly = preg_match( '/\\bparsoid\\b/i', $this->sectionData['options'] ) &&
-                       $result == 'html' &&
-                       !$this->parserTest->runParsoid;
-               $isFiltered = !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] );
-               if ( $input == false || $result == false || $isDisabled || $isParsoidOnly || $isFiltered ) {
-                       # disabled test
-                       return false;
-               }
-
-               # We are really going to run the test, run pending hooks and hooks function
-               wfDebug( __METHOD__ . " unleashing delayed test for: {$this->sectionData['test']}" );
-               $hooksResult = $this->delayedParserTest->unleash( $this->parserTest );
-               if ( !$hooksResult ) {
-                       # Some hook reported an issue. Abort.
-                       throw new MWException( "Problem running requested parser hook from the test file" );
-               }
-
-               $this->test = [
-                       'test' => ParserTest::chomp( $this->sectionData['test'] ),
-                       'subtest' => $this->nextSubTest,
-                       'input' => ParserTest::chomp( $this->sectionData[$input] ),
-                       'result' => ParserTest::chomp( $this->sectionData[$result] ),
-                       'options' => ParserTest::chomp( $this->sectionData['options'] ),
-                       'config' => ParserTest::chomp( $this->sectionData['config'] ),
-               ];
-               if ( $tidy != false ) {
-                       $this->test['options'] .= " tidy";
-               }
-               return true;
-       }
-
-       function readNextTest() {
-               # Run additional subtests of previous test
-               while ( $this->nextSubTest > 0 ) {
-                       if ( $this->setupCurrentTest() ) {
-                               return true;
-                       }
-               }
-
-               $this->clearSection();
-               # Reset hooks for the delayed test object
-               $this->delayedParserTest->reset();
-
-               while ( false !== ( $line = fgets( $this->fh ) ) ) {
-                       $this->lineNum++;
-                       $matches = [];
-
-                       if ( preg_match( '/^!!\s*(\S+)/', $line, $matches ) ) {
-                               $this->section = strtolower( $matches[1] );
-
-                               if ( $this->section == 'endarticle' ) {
-                                       $this->checkSection( 'text' );
-                                       $this->checkSection( 'article' );
-
-                                       $this->parserTest->addArticle(
-                                               ParserTest::chomp( $this->sectionData['article'] ),
-                                               $this->sectionData['text'], $this->lineNum );
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endhooks' ) {
-                                       $this->checkSection( 'hooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endfunctionhooks' ) {
-                                       $this->checkSection( 'functionhooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireFunctionHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endtransparenthooks' ) {
-                                       $this->checkSection( 'transparenthooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['transparenthooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireTransparentHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'end' ) {
-                                       $this->checkSection( 'test' );
-                                       do {
-                                               if ( $this->setupCurrentTest() ) {
-                                                       return true;
-                                               }
-                                       } while ( $this->nextSubTest > 0 );
-                                       # go on to next test (since this was disabled)
-                                       $this->clearSection();
-                                       $this->delayedParserTest->reset();
-                                       continue;
-                               }
-
-                               if ( isset( $this->sectionData[$this->section] ) ) {
-                                       throw new MWException( "duplicate section '$this->section' "
-                                               . "at line {$this->lineNum} of $this->file\n" );
-                               }
-
-                               $this->sectionData[$this->section] = '';
-
-                               continue;
-                       }
-
-                       if ( $this->section ) {
-                               $this->sectionData[$this->section] .= $line;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Clear section name and its data
-        */
-       private function clearSection() {
-               $this->sectionData = [];
-               $this->section = null;
-
-       }
-
-       /**
-        * Verify the current section data has some value for the given token
-        * name(s) (first parameter).
-        * Throw an exception if it is not set, referencing current section
-        * and adding the current file name and line number
-        *
-        * @param string|array $tokens Expected token(s) that should have been
-        * mentioned before closing this section
-        * @param bool $fatal True iff an exception should be thrown if
-        * the section is not found.
-        * @return bool|string
-        * @throws MWException
-        */
-       private function checkSection( $tokens, $fatal = true ) {
-               if ( is_null( $this->section ) ) {
-                       throw new MWException( __METHOD__ . " can not verify a null section!\n" );
-               }
-               if ( !is_array( $tokens ) ) {
-                       $tokens = [ $tokens ];
-               }
-               if ( count( $tokens ) == 0 ) {
-                       throw new MWException( __METHOD__ . " can not verify zero sections!\n" );
-               }
-
-               $data = $this->sectionData;
-               $tokens = array_filter( $tokens, function ( $token ) use ( $data ) {
-                       return isset( $data[$token] );
-               } );
-
-               if ( count( $tokens ) == 0 ) {
-                       if ( !$fatal ) {
-                               return false;
-                       }
-                       throw new MWException( sprintf(
-                               "'%s' without '%s' at line %s of %s\n",
-                               $this->section,
-                               implode( ',', $tokens ),
-                               $this->lineNum,
-                               $this->file
-                       ) );
-               }
-               if ( count( $tokens ) > 1 ) {
-                       throw new MWException( sprintf(
-                               "'%s' with unexpected tokens '%s' at line %s of %s\n",
-                               $this->section,
-                               implode( ',', $tokens ),
-                               $this->lineNum,
-                               $this->file
-                       ) );
-               }
-
-               return array_values( $tokens )[0];
-       }
-}
-
-/**
- * An iterator for use as a phpunit data provider. Provides the test arguments
- * in the order expected by NewParserTest::testParserTest().
- */
-class TestFileDataProvider extends TestFileIterator {
-       function current() {
-               $test = parent::current();
-               if ( $test ) {
-                       return [
-                               $test['test'],
-                               $test['input'],
-                               $test['result'],
-                               $test['options'],
-                               $test['config'],
-                       ];
-               } else {
-                       return $test;
-               }
-       }
-}
-
-/**
- * A class to delay execution of a parser test hooks.
- */
-class DelayedParserTest {
-
-       /** Initialized on construction */
-       private $hooks;
-       private $fnHooks;
-       private $transparentHooks;
-
-       public function __construct() {
-               $this->reset();
-       }
-
-       /**
-        * Init/reset or forgot about the current delayed test.
-        * Call to this will erase any hooks function that were pending.
-        */
-       public function reset() {
-               $this->hooks = [];
-               $this->fnHooks = [];
-               $this->transparentHooks = [];
-       }
-
-       /**
-        * Called whenever we actually want to run the hook.
-        * Should be the case if we found the parserTest is not disabled
-        * @param ParserTest|NewParserTest $parserTest
-        * @return bool
-        * @throws MWException
-        */
-       public function unleash( &$parserTest ) {
-               if ( !( $parserTest instanceof ParserTest || $parserTest instanceof NewParserTest ) ) {
-                       throw new MWException( __METHOD__ . " must be passed an instance of ParserTest or "
-                               . "NewParserTest classes\n" );
-               }
-
-               # Trigger delayed hooks. Any failure will make us abort
-               foreach ( $this->hooks as $hook ) {
-                       $ret = $parserTest->requireHook( $hook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Trigger delayed function hooks. Any failure will make us abort
-               foreach ( $this->fnHooks as $fnHook ) {
-                       $ret = $parserTest->requireFunctionHook( $fnHook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Trigger delayed transparent hooks. Any failure will make us abort
-               foreach ( $this->transparentHooks as $hook ) {
-                       $ret = $parserTest->requireTransparentHook( $hook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Delayed execution was successful.
-               return true;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook
-        * @param string $hook
-        */
-       public function requireHook( $hook ) {
-               $this->hooks[] = $hook;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook function
-        * @param string $fnHook
-        */
-       public function requireFunctionHook( $fnHook ) {
-               $this->fnHooks[] = $fnHook;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook function
-        * @param string $hook
-        */
-       public function requireTransparentHook( $hook ) {
-               $this->transparentHooks[] = $hook;
-       }
-
-}
-
-/**
- * Initialize and detect the DjVu files support
- */
-class DjVuSupport {
-
-       /**
-        * Initialises DjVu tools global with default values
-        */
-       public function __construct() {
-               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgFileExtensions, $wgDjvuTxt;
-
-               $wgDjvuRenderer = $wgDjvuRenderer ? $wgDjvuRenderer : '/usr/bin/ddjvu';
-               $wgDjvuDump = $wgDjvuDump ? $wgDjvuDump : '/usr/bin/djvudump';
-               $wgDjvuToXML = $wgDjvuToXML ? $wgDjvuToXML : '/usr/bin/djvutoxml';
-               $wgDjvuTxt = $wgDjvuTxt ? $wgDjvuTxt : '/usr/bin/djvutxt';
-
-               if ( !in_array( 'djvu', $wgFileExtensions ) ) {
-                       $wgFileExtensions[] = 'djvu';
-               }
-       }
-
-       /**
-        * Returns true if the DjVu tools are usable
-        *
-        * @return bool
-        */
-       public function isEnabled() {
-               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgDjvuTxt;
-
-               return is_executable( $wgDjvuRenderer )
-                       && is_executable( $wgDjvuDump )
-                       && is_executable( $wgDjvuToXML )
-                       && is_executable( $wgDjvuTxt );
-       }
-}
-
-/**
- * Initialize and detect the tidy support
- */
-class TidySupport {
-       private $enabled;
-       private $config;
-
-       /**
-        * Determine if there is a usable tidy.
-        */
-       public function __construct( $useConfiguration = false ) {
-               global $IP, $wgUseTidy, $wgTidyBin, $wgTidyInternal, $wgTidyConfig,
-                       $wgTidyConf, $wgTidyOpts;
-
-               $this->enabled = true;
-               if ( $useConfiguration ) {
-                       if ( $wgTidyConfig !== null ) {
-                               $this->config = $wgTidyConfig;
-                       } elseif ( $wgUseTidy ) {
-                               $this->config = [
-                                       'tidyConfigFile' => $wgTidyConf,
-                                       'debugComment' => false,
-                                       'tidyBin' => $wgTidyBin,
-                                       'tidyCommandLine' => $wgTidyOpts
-                               ];
-                               if ( $wgTidyInternal ) {
-                                       $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
-                               } else {
-                                       $this->config['driver'] = 'RaggettExternal';
-                               }
-                       } else {
-                               $this->enabled = false;
-                       }
-               } else {
-                       $this->config = [
-                               'tidyConfigFile' => "$IP/includes/tidy/tidy.conf",
-                               'tidyCommandLine' => '',
-                       ];
-                       if ( extension_loaded( 'tidy' ) && class_exists( 'tidy' ) ) {
-                               $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
-                       } else {
-                               if ( is_executable( $wgTidyBin ) ) {
-                                       $this->config['driver'] = 'RaggettExternal';
-                                       $this->config['tidyBin'] = $wgTidyBin;
-                               } else {
-                                       $path = Installer::locateExecutableInDefaultPaths( $wgTidyBin );
-                                       if ( $path !== false ) {
-                                               $this->config['driver'] = 'RaggettExternal';
-                                               $this->config['tidyBin'] = $wgTidyBin;
-                                       } else {
-                                               $this->enabled = false;
-                                       }
-                               }
-                       }
-               }
-               if ( !$this->enabled ) {
-                       $this->config = [ 'driver' => 'disabled' ];
-               }
-       }
-
-       /**
-        * Returns true if tidy is usable
-        *
-        * @return bool
-        */
-       public function isEnabled() {
-               return $this->enabled;
-       }
-
-       public function getConfig() {
-               return $this->config;
-       }
-}