summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-12-17 09:15:42 +0100
committerPierre Schmitz <pierre@archlinux.de>2015-12-17 09:44:51 +0100
commita1789ddde42033f1b05cc4929491214ee6e79383 (patch)
tree63615735c4ddffaaabf2428946bb26f90899f7bf /includes
parent9e06a62f265e3a2aaabecc598d4bc617e06fa32d (diff)
Update to MediaWiki 1.26.0
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php12
-rw-r--r--includes/AjaxResponse.php30
-rw-r--r--includes/AuthPlugin.php16
-rw-r--r--includes/Block.php158
-rw-r--r--includes/CategoryFinder.php4
-rw-r--r--includes/CategoryViewer.php2
-rw-r--r--includes/Collation.php8
-rw-r--r--includes/DefaultSettings.php563
-rw-r--r--includes/Defines.php7
-rw-r--r--includes/EditPage.php137
-rw-r--r--includes/Export.php4
-rw-r--r--includes/FileDeleteForm.php4
-rw-r--r--includes/GitInfo.php4
-rw-r--r--includes/GlobalFunctions.php265
-rw-r--r--includes/HistoryBlob.php8
-rw-r--r--includes/Hooks.php21
-rw-r--r--includes/Html.php111
-rw-r--r--includes/HttpFunctions.php6
-rw-r--r--includes/Import.php69
-rw-r--r--includes/Linker.php88
-rw-r--r--includes/MWNamespace.php12
-rw-r--r--includes/MWTimestamp.php42
-rw-r--r--includes/MagicWord.php13
-rw-r--r--includes/MediaWiki.php282
-rw-r--r--includes/Message.php81
-rw-r--r--includes/MimeMagic.php14
-rw-r--r--includes/MovePage.php67
-rw-r--r--includes/OutputPage.php525
-rw-r--r--includes/PHPVersionCheck.php146
-rw-r--r--includes/Preferences.php135
-rw-r--r--includes/PrefixSearch.php6
-rw-r--r--includes/ProtectionForm.php57
-rw-r--r--includes/Revision.php30
-rw-r--r--includes/RevisionList.php4
-rw-r--r--includes/Sanitizer.php111
-rw-r--r--includes/Setup.php74
-rw-r--r--includes/SiteStats.php4
-rw-r--r--includes/SquidPurgeClient.php24
-rw-r--r--includes/Status.php10
-rw-r--r--includes/StreamFile.php8
-rw-r--r--includes/StubObject.php2
-rw-r--r--includes/TemplateParser.php9
-rw-r--r--includes/Title.php270
-rw-r--r--includes/User.php528
-rw-r--r--includes/UserRightsProxy.php4
-rw-r--r--includes/WatchedItem.php79
-rw-r--r--includes/WebRequest.php113
-rw-r--r--includes/WebResponse.php19
-rw-r--r--includes/WebStart.php6
-rw-r--r--includes/WikiMap.php62
-rw-r--r--includes/Xml.php125
-rw-r--r--includes/XmlSelect.php132
-rw-r--r--includes/ZhConversion.php1949
-rw-r--r--includes/actions/Action.php10
-rw-r--r--includes/actions/DeleteAction.php2
-rw-r--r--includes/actions/EditAction.php2
-rw-r--r--includes/actions/HistoryAction.php33
-rw-r--r--includes/actions/InfoAction.php95
-rw-r--r--includes/actions/RawAction.php52
-rw-r--r--includes/actions/RevertAction.php2
-rw-r--r--includes/actions/RollbackAction.php3
-rw-r--r--includes/actions/UnprotectAction.php1
-rw-r--r--includes/actions/WatchAction.php2
-rw-r--r--includes/api/ApiBase.php169
-rw-r--r--includes/api/ApiBlock.php16
-rw-r--r--includes/api/ApiCreateAccount.php16
-rw-r--r--includes/api/ApiDelete.php4
-rw-r--r--includes/api/ApiEditPage.php86
-rw-r--r--includes/api/ApiEmailUser.php2
-rw-r--r--includes/api/ApiExpandTemplates.php32
-rw-r--r--includes/api/ApiFeedRecentChanges.php12
-rw-r--r--includes/api/ApiFeedWatchlist.php17
-rw-r--r--includes/api/ApiFileRevert.php2
-rw-r--r--includes/api/ApiFormatBase.php23
-rw-r--r--includes/api/ApiFormatFeedWrapper.php9
-rw-r--r--includes/api/ApiFormatJson.php16
-rw-r--r--includes/api/ApiFormatPhp.php3
-rw-r--r--includes/api/ApiFormatRaw.php28
-rw-r--r--includes/api/ApiFormatWddx.php162
-rw-r--r--includes/api/ApiFormatXml.php11
-rw-r--r--includes/api/ApiHelp.php188
-rw-r--r--includes/api/ApiImageRotate.php4
-rw-r--r--includes/api/ApiImport.php7
-rw-r--r--includes/api/ApiLogin.php19
-rw-r--r--includes/api/ApiMain.php209
-rw-r--r--includes/api/ApiMessage.php30
-rw-r--r--includes/api/ApiMove.php2
-rw-r--r--includes/api/ApiOptions.php14
-rw-r--r--includes/api/ApiPageSet.php69
-rw-r--r--includes/api/ApiParamInfo.php49
-rw-r--r--includes/api/ApiParse.php126
-rw-r--r--includes/api/ApiProtect.php9
-rw-r--r--includes/api/ApiQuery.php23
-rw-r--r--includes/api/ApiQueryAllCategories.php3
-rw-r--r--includes/api/ApiQueryAllDeletedRevisions.php45
-rw-r--r--includes/api/ApiQueryAllLinks.php1
-rw-r--r--includes/api/ApiQueryAllMessages.php8
-rw-r--r--includes/api/ApiQueryAllPages.php12
-rw-r--r--includes/api/ApiQueryAllUsers.php11
-rw-r--r--includes/api/ApiQueryBacklinksprop.php8
-rw-r--r--includes/api/ApiQueryBase.php21
-rw-r--r--includes/api/ApiQueryBlocks.php9
-rw-r--r--includes/api/ApiQueryCategories.php5
-rw-r--r--includes/api/ApiQueryCategoryInfo.php2
-rw-r--r--includes/api/ApiQueryCategoryMembers.php11
-rw-r--r--includes/api/ApiQueryContributors.php6
-rw-r--r--includes/api/ApiQueryDeletedRevisions.php8
-rw-r--r--includes/api/ApiQueryDeletedrevs.php2
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php2
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php3
-rw-r--r--includes/api/ApiQueryExternalLinks.php2
-rw-r--r--includes/api/ApiQueryFileRepoInfo.php24
-rw-r--r--includes/api/ApiQueryFilearchive.php5
-rw-r--r--includes/api/ApiQueryIWBacklinks.php1
-rw-r--r--includes/api/ApiQueryIWLinks.php3
-rw-r--r--includes/api/ApiQueryImageInfo.php6
-rw-r--r--includes/api/ApiQueryImages.php2
-rw-r--r--includes/api/ApiQueryInfo.php19
-rw-r--r--includes/api/ApiQueryLangBacklinks.php1
-rw-r--r--includes/api/ApiQueryLangLinks.php5
-rw-r--r--includes/api/ApiQueryLinks.php4
-rw-r--r--includes/api/ApiQueryLogEvents.php3
-rw-r--r--includes/api/ApiQueryPageProps.php4
-rw-r--r--includes/api/ApiQueryPagesWithProp.php3
-rw-r--r--includes/api/ApiQueryProtectedTitles.php5
-rw-r--r--includes/api/ApiQueryRandom.php181
-rw-r--r--includes/api/ApiQueryRecentChanges.php5
-rw-r--r--includes/api/ApiQueryRevisions.php101
-rw-r--r--includes/api/ApiQueryRevisionsBase.php38
-rw-r--r--includes/api/ApiQuerySearch.php32
-rw-r--r--includes/api/ApiQuerySiteinfo.php18
-rw-r--r--includes/api/ApiQueryStashImageInfo.php4
-rw-r--r--includes/api/ApiQueryTags.php3
-rw-r--r--includes/api/ApiQueryTokens.php4
-rw-r--r--includes/api/ApiQueryUserContributions.php5
-rw-r--r--includes/api/ApiQueryUserInfo.php52
-rw-r--r--includes/api/ApiQueryUsers.php7
-rw-r--r--includes/api/ApiQueryWatchlist.php5
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php31
-rw-r--r--includes/api/ApiResult.php164
-rw-r--r--includes/api/ApiRevisionDelete.php4
-rw-r--r--includes/api/ApiRollback.php2
-rw-r--r--includes/api/ApiSetNotificationTimestamp.php4
-rw-r--r--includes/api/ApiStashEdit.php7
-rw-r--r--includes/api/ApiUnblock.php8
-rw-r--r--includes/api/ApiUndelete.php9
-rw-r--r--includes/api/ApiUpload.php24
-rw-r--r--includes/api/i18n/ar.json4
-rw-r--r--includes/api/i18n/ast.json10
-rw-r--r--includes/api/i18n/ba.json13
-rw-r--r--includes/api/i18n/bcl.json10
-rw-r--r--includes/api/i18n/be-tarask.json4
-rw-r--r--includes/api/i18n/br.json13
-rw-r--r--includes/api/i18n/bs.json5
-rw-r--r--includes/api/i18n/ca.json17
-rw-r--r--includes/api/i18n/ce.json3
-rw-r--r--includes/api/i18n/ckb.json8
-rw-r--r--includes/api/i18n/cs.json105
-rw-r--r--includes/api/i18n/de.json107
-rw-r--r--includes/api/i18n/el.json89
-rw-r--r--includes/api/i18n/en-gb.json2
-rw-r--r--includes/api/i18n/en.json457
-rw-r--r--includes/api/i18n/es.json564
-rw-r--r--includes/api/i18n/et.json42
-rw-r--r--includes/api/i18n/eu.json11
-rw-r--r--includes/api/i18n/fa.json7
-rw-r--r--includes/api/i18n/fi.json8
-rw-r--r--includes/api/i18n/fo.json39
-rw-r--r--includes/api/i18n/fr.json421
-rw-r--r--includes/api/i18n/gl.json414
-rw-r--r--includes/api/i18n/he.json1185
-rw-r--r--includes/api/i18n/ht.json8
-rw-r--r--includes/api/i18n/hu.json13
-rw-r--r--includes/api/i18n/ia.json3
-rw-r--r--includes/api/i18n/is.json10
-rw-r--r--includes/api/i18n/it.json90
-rw-r--r--includes/api/i18n/ja.json374
-rw-r--r--includes/api/i18n/ko.json78
-rw-r--r--includes/api/i18n/ksh.json669
-rw-r--r--includes/api/i18n/ku-latn.json23
-rw-r--r--includes/api/i18n/ky.json19
-rw-r--r--includes/api/i18n/lb.json65
-rw-r--r--includes/api/i18n/lv.json7
-rw-r--r--includes/api/i18n/mk.json241
-rw-r--r--includes/api/i18n/mr.json12
-rw-r--r--includes/api/i18n/nap.json12
-rw-r--r--includes/api/i18n/nb.json91
-rw-r--r--includes/api/i18n/ne.json13
-rw-r--r--includes/api/i18n/nl.json42
-rw-r--r--includes/api/i18n/oc.json73
-rw-r--r--includes/api/i18n/olo.json12
-rw-r--r--includes/api/i18n/or.json14
-rw-r--r--includes/api/i18n/pl.json256
-rw-r--r--includes/api/i18n/ps.json21
-rw-r--r--includes/api/i18n/pt-br.json276
-rw-r--r--includes/api/i18n/pt.json15
-rw-r--r--includes/api/i18n/qqq.json366
-rw-r--r--includes/api/i18n/ru.json197
-rw-r--r--includes/api/i18n/shn.json8
-rw-r--r--includes/api/i18n/si.json1
-rw-r--r--includes/api/i18n/sq.json12
-rw-r--r--includes/api/i18n/sv.json75
-rw-r--r--includes/api/i18n/ta.json13
-rw-r--r--includes/api/i18n/tr.json12
-rw-r--r--includes/api/i18n/uk.json1286
-rw-r--r--includes/api/i18n/vi.json75
-rw-r--r--includes/api/i18n/wuu.json8
-rw-r--r--includes/api/i18n/yi.json10
-rw-r--r--includes/api/i18n/zh-hans.json743
-rw-r--r--includes/api/i18n/zh-hant.json42
-rw-r--r--includes/cache/BacklinkCache.php6
-rw-r--r--includes/cache/CacheDependency.php8
-rw-r--r--includes/cache/FileCacheBase.php4
-rw-r--r--includes/cache/HTMLFileCache.php1
-rw-r--r--includes/cache/LCStoreStaticArray.php140
-rw-r--r--includes/cache/LinkBatch.php2
-rw-r--r--includes/cache/LinkCache.php87
-rw-r--r--includes/cache/LocalisationCache.php13
-rw-r--r--includes/cache/MessageBlobStore.php (renamed from includes/MessageBlobStore.php)45
-rw-r--r--includes/cache/MessageCache.php557
-rw-r--r--includes/cache/ResourceFileCache.php4
-rw-r--r--includes/cache/UserCache.php4
-rw-r--r--includes/changes/ChangesFeed.php4
-rw-r--r--includes/changes/ChangesList.php39
-rw-r--r--includes/changes/EnhancedChangesList.php262
-rw-r--r--includes/changes/RecentChange.php113
-rw-r--r--includes/changetags/ChangeTags.php (renamed from includes/ChangeTags.php)318
-rw-r--r--includes/changetags/ChangeTagsLogList.php3
-rw-r--r--includes/changetags/ChangeTagsRevisionList.php3
-rw-r--r--includes/clientpool/RedisConnectionPool.php108
-rw-r--r--includes/compat/CdbCompat.php (renamed from includes/CdbCompat.php)0
-rw-r--r--includes/compat/IPSetCompat.php28
-rw-r--r--includes/compat/normal/UtfNormal.php (renamed from includes/libs/normal/UtfNormal.php)0
-rw-r--r--includes/compat/normal/UtfNormalDefines.php (renamed from includes/libs/normal/UtfNormalDefines.php)0
-rw-r--r--includes/compat/normal/UtfNormalUtil.php (renamed from includes/libs/normal/UtfNormalUtil.php)1
-rw-r--r--includes/config/ConfigFactory.php3
-rw-r--r--includes/content/ContentHandler.php54
-rw-r--r--includes/content/CssContent.php44
-rw-r--r--includes/content/CssContentHandler.php19
-rw-r--r--includes/content/JavaScriptContent.php47
-rw-r--r--includes/content/JavaScriptContentHandler.php18
-rw-r--r--includes/content/TextContentHandler.php9
-rw-r--r--includes/content/WikitextContent.php4
-rw-r--r--includes/context/ContextSource.php1
-rw-r--r--includes/context/DerivativeContext.php7
-rw-r--r--includes/context/IContextSource.php3
-rw-r--r--includes/context/MutableContext.php82
-rw-r--r--includes/context/RequestContext.php20
-rw-r--r--includes/dao/DBAccessObjectUtils.php59
-rw-r--r--includes/db/DBConnRef.php524
-rw-r--r--includes/db/Database.php310
-rw-r--r--includes/db/DatabaseError.php19
-rw-r--r--includes/db/DatabaseMssql.php8
-rw-r--r--includes/db/DatabaseMysql.php40
-rw-r--r--includes/db/DatabaseMysqlBase.php82
-rw-r--r--includes/db/DatabaseMysqli.php43
-rw-r--r--includes/db/DatabaseOracle.php38
-rw-r--r--includes/db/DatabasePostgres.php25
-rw-r--r--includes/db/DatabaseSqlite.php49
-rw-r--r--includes/db/IDatabase.php1513
-rw-r--r--includes/db/LBFactory.php11
-rw-r--r--includes/db/LBFactoryMulti.php2
-rw-r--r--includes/db/LoadBalancer.php147
-rw-r--r--includes/db/LoadMonitor.php87
-rw-r--r--includes/db/LoadMonitorMySQL.php124
-rw-r--r--includes/debug/MWDebug.php6
-rw-r--r--includes/debug/logger/LegacyLogger.php101
-rw-r--r--includes/debug/logger/LegacySpi.php4
-rw-r--r--includes/debug/logger/LoggerFactory.php14
-rw-r--r--includes/debug/logger/MonologSpi.php35
-rw-r--r--includes/debug/logger/NullSpi.php8
-rw-r--r--includes/debug/logger/Spi.php8
-rw-r--r--includes/debug/logger/monolog/AvroFormatter.php139
-rw-r--r--includes/debug/logger/monolog/BufferHandler.php (renamed from includes/api/ApiFormatDump.php)55
-rw-r--r--includes/debug/logger/monolog/KafkaHandler.php224
-rw-r--r--includes/debug/logger/monolog/LegacyFormatter.php4
-rw-r--r--includes/debug/logger/monolog/LineFormatter.php177
-rw-r--r--includes/deferred/DeferredUpdates.php68
-rw-r--r--includes/deferred/HTMLCacheUpdate.php3
-rw-r--r--includes/deferred/LinksDeletionUpdate.php105
-rw-r--r--includes/deferred/LinksUpdate.php85
-rw-r--r--includes/deferred/SiteStatsUpdate.php14
-rw-r--r--includes/diff/DifferenceEngine.php27
-rw-r--r--includes/diff/TableDiffFormatter.php2
-rw-r--r--includes/diff/UnifiedDiffFormatter.php10
-rw-r--r--includes/exception/BadTitleError.php16
-rw-r--r--includes/exception/HttpError.php20
-rw-r--r--includes/exception/MWException.php8
-rw-r--r--includes/exception/MWExceptionHandler.php367
-rw-r--r--includes/filebackend/FSFile.php9
-rw-r--r--includes/filebackend/FileBackend.php15
-rw-r--r--includes/filebackend/FileBackendGroup.php2
-rw-r--r--includes/filebackend/FileBackendMultiWrite.php176
-rw-r--r--includes/filebackend/FileBackendStore.php80
-rw-r--r--includes/filebackend/FileOp.php4
-rw-r--r--includes/filebackend/MemoryFileBackend.php8
-rw-r--r--includes/filebackend/SwiftFileBackend.php151
-rw-r--r--includes/filebackend/TempFSFile.php15
-rw-r--r--includes/filebackend/lockmanager/DBLockManager.php2
-rw-r--r--includes/filebackend/lockmanager/FSLockManager.php4
-rw-r--r--includes/filebackend/lockmanager/LockManager.php1
-rw-r--r--includes/filerepo/FileBackendDBRepoWrapper.php356
-rw-r--r--includes/filerepo/FileRepo.php43
-rw-r--r--includes/filerepo/ForeignAPIRepo.php4
-rw-r--r--includes/filerepo/ForeignDBRepo.php32
-rw-r--r--includes/filerepo/ForeignDBViaLBRepo.php10
-rw-r--r--includes/filerepo/LocalRepo.php113
-rw-r--r--includes/filerepo/file/ArchivedFile.php4
-rw-r--r--includes/filerepo/file/File.php65
-rw-r--r--includes/filerepo/file/ForeignAPIFile.php19
-rw-r--r--includes/filerepo/file/LocalFile.php289
-rw-r--r--includes/gallery/PackedImageGallery.php4
-rw-r--r--includes/gallery/PackedOverlayImageGallery.php2
-rw-r--r--includes/gallery/TraditionalImageGallery.php4
-rw-r--r--includes/htmlform/HTMLAutoCompleteSelectField.php14
-rw-r--r--includes/htmlform/HTMLButtonField.php36
-rw-r--r--includes/htmlform/HTMLCheckField.php62
-rw-r--r--includes/htmlform/HTMLCheckMatrix.php42
-rw-r--r--includes/htmlform/HTMLForm.php185
-rw-r--r--includes/htmlform/HTMLFormField.php193
-rw-r--r--includes/htmlform/HTMLFormFieldWithButton.php73
-rw-r--r--includes/htmlform/HTMLInfoField.php10
-rw-r--r--includes/htmlform/HTMLMultiSelectField.php60
-rw-r--r--includes/htmlform/HTMLRadioField.php17
-rw-r--r--includes/htmlform/HTMLSelectAndOtherField.php5
-rw-r--r--includes/htmlform/HTMLSelectField.php22
-rw-r--r--includes/htmlform/HTMLSelectNamespace.php16
-rw-r--r--includes/htmlform/HTMLSelectNamespaceWithButton.php17
-rw-r--r--includes/htmlform/HTMLSelectOrOtherField.php4
-rw-r--r--includes/htmlform/HTMLSubmitField.php2
-rw-r--r--includes/htmlform/HTMLTextAreaField.php44
-rw-r--r--includes/htmlform/HTMLTextField.php61
-rw-r--r--includes/htmlform/HTMLTextFieldWithButton.php17
-rw-r--r--includes/htmlform/HTMLTitleTextField.php81
-rw-r--r--includes/htmlform/HTMLUserTextField.php47
-rw-r--r--includes/htmlform/OOUIHTMLForm.php221
-rw-r--r--includes/htmlform/VFormHTMLForm.php6
-rw-r--r--includes/installer/DatabaseInstaller.php4
-rw-r--r--includes/installer/DatabaseUpdater.php3
-rw-r--r--includes/installer/Installer.php51
-rw-r--r--includes/installer/LocalSettingsGenerator.php11
-rw-r--r--includes/installer/MssqlInstaller.php1
-rw-r--r--includes/installer/MssqlUpdater.php2
-rw-r--r--includes/installer/MysqlInstaller.php10
-rw-r--r--includes/installer/MysqlUpdater.php5
-rw-r--r--includes/installer/PostgresInstaller.php11
-rw-r--r--includes/installer/PostgresUpdater.php10
-rw-r--r--includes/installer/SqliteInstaller.php4
-rw-r--r--includes/installer/WebInstaller.php29
-rw-r--r--includes/installer/WebInstallerPage.php37
-rw-r--r--includes/installer/i18n/ar.json9
-rw-r--r--includes/installer/i18n/ast.json8
-rw-r--r--includes/installer/i18n/azb.json30
-rw-r--r--includes/installer/i18n/ba.json7
-rw-r--r--includes/installer/i18n/bcl.json1
-rw-r--r--includes/installer/i18n/be-tarask.json3
-rw-r--r--includes/installer/i18n/bg.json43
-rw-r--r--includes/installer/i18n/bn.json17
-rw-r--r--includes/installer/i18n/bs.json27
-rw-r--r--includes/installer/i18n/ca.json3
-rw-r--r--includes/installer/i18n/ce.json9
-rw-r--r--includes/installer/i18n/ckb.json1
-rw-r--r--includes/installer/i18n/cs.json3
-rw-r--r--includes/installer/i18n/cu.json4
-rw-r--r--includes/installer/i18n/da.json11
-rw-r--r--includes/installer/i18n/de-ch.json6
-rw-r--r--includes/installer/i18n/de.json2
-rw-r--r--includes/installer/i18n/el.json63
-rw-r--r--includes/installer/i18n/en-gb.json1
-rw-r--r--includes/installer/i18n/en.json2
-rw-r--r--includes/installer/i18n/eo.json10
-rw-r--r--includes/installer/i18n/es.json56
-rw-r--r--includes/installer/i18n/et.json7
-rw-r--r--includes/installer/i18n/eu.json4
-rw-r--r--includes/installer/i18n/fa.json19
-rw-r--r--includes/installer/i18n/fo.json15
-rw-r--r--includes/installer/i18n/fr.json3
-rw-r--r--includes/installer/i18n/fy.json1
-rw-r--r--includes/installer/i18n/gl.json7
-rw-r--r--includes/installer/i18n/he.json3
-rw-r--r--includes/installer/i18n/hi.json8
-rw-r--r--includes/installer/i18n/hu.json19
-rw-r--r--includes/installer/i18n/hy.json35
-rw-r--r--includes/installer/i18n/ia.json7
-rw-r--r--includes/installer/i18n/id.json1
-rw-r--r--includes/installer/i18n/it.json17
-rw-r--r--includes/installer/i18n/ja.json11
-rw-r--r--includes/installer/i18n/jut.json5
-rw-r--r--includes/installer/i18n/km.json3
-rw-r--r--includes/installer/i18n/kn.json4
-rw-r--r--includes/installer/i18n/ko.json76
-rw-r--r--includes/installer/i18n/ksh.json99
-rw-r--r--includes/installer/i18n/ku-latn.json48
-rw-r--r--includes/installer/i18n/lb.json3
-rw-r--r--includes/installer/i18n/lrc.json4
-rw-r--r--includes/installer/i18n/lv.json15
-rw-r--r--includes/installer/i18n/mai.json4
-rw-r--r--includes/installer/i18n/mg.json12
-rw-r--r--includes/installer/i18n/mk.json6
-rw-r--r--includes/installer/i18n/mr.json5
-rw-r--r--includes/installer/i18n/nan.json11
-rw-r--r--includes/installer/i18n/nap.json102
-rw-r--r--includes/installer/i18n/nb.json1
-rw-r--r--includes/installer/i18n/ne.json55
-rw-r--r--includes/installer/i18n/nl.json6
-rw-r--r--includes/installer/i18n/nn.json4
-rw-r--r--includes/installer/i18n/olo.json59
-rw-r--r--includes/installer/i18n/or.json17
-rw-r--r--includes/installer/i18n/pl.json19
-rw-r--r--includes/installer/i18n/pms.json4
-rw-r--r--includes/installer/i18n/ps.json22
-rw-r--r--includes/installer/i18n/pt-br.json19
-rw-r--r--includes/installer/i18n/pt.json6
-rw-r--r--includes/installer/i18n/qqq.json6
-rw-r--r--includes/installer/i18n/ro.json14
-rw-r--r--includes/installer/i18n/ru.json3
-rw-r--r--includes/installer/i18n/sah.json9
-rw-r--r--includes/installer/i18n/sco.json3
-rw-r--r--includes/installer/i18n/sd.json8
-rw-r--r--includes/installer/i18n/sk.json5
-rw-r--r--includes/installer/i18n/sq.json40
-rw-r--r--includes/installer/i18n/sr-ec.json5
-rw-r--r--includes/installer/i18n/su.json7
-rw-r--r--includes/installer/i18n/sv.json5
-rw-r--r--includes/installer/i18n/tokipona.json8
-rw-r--r--includes/installer/i18n/tr.json53
-rw-r--r--includes/installer/i18n/tt-cyrl.json7
-rw-r--r--includes/installer/i18n/udm.json12
-rw-r--r--includes/installer/i18n/uk.json13
-rw-r--r--includes/installer/i18n/vi.json5
-rw-r--r--includes/installer/i18n/wuu.json4
-rw-r--r--includes/installer/i18n/xmf.json33
-rw-r--r--includes/installer/i18n/yi.json15
-rw-r--r--includes/installer/i18n/zh-hans.json5
-rw-r--r--includes/installer/i18n/zh-hant.json12
-rw-r--r--includes/interwiki/Interwiki.php21
-rw-r--r--includes/jobqueue/Job.php46
-rw-r--r--includes/jobqueue/JobQueue.php81
-rw-r--r--includes/jobqueue/JobQueueDB.php98
-rw-r--r--includes/jobqueue/JobQueueFederated.php35
-rw-r--r--includes/jobqueue/JobQueueGroup.php143
-rw-r--r--includes/jobqueue/JobQueueRedis.php151
-rw-r--r--includes/jobqueue/JobRunner.php171
-rw-r--r--includes/jobqueue/JobSpecification.php71
-rw-r--r--includes/jobqueue/aggregator/JobQueueAggregator.php2
-rw-r--r--includes/jobqueue/aggregator/JobQueueAggregatorRedis.php8
-rw-r--r--includes/jobqueue/jobs/ActivityUpdateJob.php75
-rw-r--r--includes/jobqueue/jobs/AssembleUploadChunksJob.php2
-rw-r--r--includes/jobqueue/jobs/DoubleRedirectJob.php20
-rw-r--r--includes/jobqueue/jobs/DuplicateJob.php2
-rw-r--r--includes/jobqueue/jobs/EmaillingJob.php4
-rw-r--r--includes/jobqueue/jobs/EnotifNotifyJob.php2
-rw-r--r--includes/jobqueue/jobs/EnqueueJob.php21
-rw-r--r--includes/jobqueue/jobs/HTMLCacheUpdateJob.php2
-rw-r--r--includes/jobqueue/jobs/NullJob.php2
-rw-r--r--includes/jobqueue/jobs/PublishStashedFileJob.php2
-rw-r--r--includes/jobqueue/jobs/RecentChangesUpdateJob.php8
-rw-r--r--includes/jobqueue/jobs/RefreshLinksJob.php51
-rw-r--r--includes/jobqueue/jobs/ThumbnailRenderJob.php14
-rw-r--r--includes/jobqueue/jobs/UploadFromUrlJob.php6
-rw-r--r--includes/json/FormatJson.php2
-rw-r--r--includes/libs/BufferingStatsdDataFactory.php34
-rw-r--r--includes/libs/CSSMin.php73
-rw-r--r--includes/libs/HashRing.php4
-rw-r--r--includes/libs/HttpStatus.php28
-rw-r--r--includes/libs/IPSet.php276
-rw-r--r--includes/libs/JavaScriptMinifier.php8
-rw-r--r--includes/libs/MapCacheLRU.php7
-rw-r--r--includes/libs/MultiHttpClient.php55
-rw-r--r--includes/libs/ObjectFactory.php49
-rw-r--r--includes/libs/ProcessCacheLRU.php7
-rw-r--r--includes/libs/ReplacementArray.php9
-rw-r--r--includes/libs/RiffExtractor.php100
-rw-r--r--includes/libs/SamplingStatsdClient.php133
-rw-r--r--includes/libs/ScopedPHPTimeout.php84
-rw-r--r--includes/libs/XmlTypeCheck.php35
-rw-r--r--includes/libs/composer/ComposerLock.php3
-rw-r--r--includes/libs/eventrelayer/EventRelayer.php65
-rw-r--r--includes/libs/eventrelayer/EventRelayerMCRD.php66
-rw-r--r--includes/libs/objectcache/APCBagOStuff.php31
-rw-r--r--includes/libs/objectcache/BagOStuff.php201
-rw-r--r--includes/libs/objectcache/EmptyBagOStuff.php2
-rw-r--r--includes/libs/objectcache/HashBagOStuff.php14
-rw-r--r--includes/libs/objectcache/ReplicatedBagOStuff.php129
-rw-r--r--includes/libs/objectcache/WANObjectCache.php746
-rw-r--r--includes/libs/objectcache/WinCacheBagOStuff.php33
-rw-r--r--includes/libs/objectcache/XCacheBagOStuff.php23
-rw-r--r--includes/libs/virtualrest/ParsoidVirtualRESTService.php221
-rw-r--r--includes/libs/virtualrest/RestbaseVirtualRESTService.php242
-rw-r--r--includes/libs/virtualrest/SwiftVirtualRESTService.php6
-rw-r--r--includes/libs/virtualrest/VirtualRESTService.php15
-rw-r--r--includes/libs/virtualrest/VirtualRESTServiceClient.php12
-rw-r--r--includes/logging/ContentModelLogFormatter.php34
-rw-r--r--includes/logging/LogEntry.php34
-rw-r--r--includes/logging/LogEventsList.php8
-rw-r--r--includes/logging/LogFormatter.php10
-rw-r--r--includes/logging/LogPager.php5
-rw-r--r--includes/logging/PatrolLogFormatter.php6
-rw-r--r--includes/logging/ProtectLogFormatter.php70
-rw-r--r--includes/mail/EmailNotification.php61
-rw-r--r--includes/mail/UserMailer.php140
-rw-r--r--includes/media/Bitmap.php7
-rw-r--r--includes/media/BitmapMetadataHandler.php8
-rw-r--r--includes/media/DjVu.php96
-rw-r--r--includes/media/DjVuImage.php10
-rw-r--r--includes/media/Exif.php12
-rw-r--r--includes/media/ExifBitmap.php78
-rw-r--r--includes/media/FormatMetadata.php35
-rw-r--r--includes/media/GIF.php8
-rw-r--r--includes/media/GIFMetadataExtractor.php4
-rw-r--r--includes/media/IPTC.php4
-rw-r--r--includes/media/ImageHandler.php4
-rw-r--r--includes/media/Jpeg.php2
-rw-r--r--includes/media/JpegMetadataExtractor.php4
-rw-r--r--includes/media/MediaHandler.php4
-rw-r--r--includes/media/MediaTransformInvalidParametersException.php3
-rw-r--r--includes/media/PNG.php8
-rw-r--r--includes/media/PNGMetadataExtractor.php16
-rw-r--r--includes/media/SVG.php15
-rw-r--r--includes/media/SVGMetadataExtractor.php17
-rw-r--r--includes/media/TransformationalImageHandler.php2
-rw-r--r--includes/media/WebP.php306
-rw-r--r--includes/media/XCF.php6
-rw-r--r--includes/media/XMP.php245
-rw-r--r--includes/media/XMPInfo.php11
-rw-r--r--includes/media/XMPValidate.php60
-rw-r--r--includes/media/tinyrgb.iccbin0 -> 524 bytes
-rw-r--r--includes/mime.info2
-rw-r--r--includes/objectcache/MemcachedBagOStuff.php15
-rw-r--r--includes/objectcache/MemcachedClient.php15
-rw-r--r--includes/objectcache/MemcachedPeclBagOStuff.php22
-rw-r--r--includes/objectcache/MemcachedPhpBagOStuff.php14
-rw-r--r--includes/objectcache/MultiWriteBagOStuff.php88
-rw-r--r--includes/objectcache/ObjectCache.php189
-rw-r--r--includes/objectcache/ObjectCacheSessionHandler.php45
-rw-r--r--includes/objectcache/RedisBagOStuff.php130
-rw-r--r--includes/objectcache/SqlBagOStuff.php78
-rw-r--r--includes/page/Article.php129
-rw-r--r--includes/page/ImagePage.php35
-rw-r--r--includes/page/WikiPage.php417
-rw-r--r--includes/pager/ReverseChronologicalPager.php6
-rw-r--r--includes/pager/TablePager.php7
-rw-r--r--includes/parser/CacheTime.php2
-rw-r--r--includes/parser/CoreParserFunctions.php25
-rw-r--r--includes/parser/LinkHolderArray.php1
-rw-r--r--includes/parser/MWTidy.php323
-rw-r--r--includes/parser/Parser.php221
-rw-r--r--includes/parser/ParserCache.php51
-rw-r--r--includes/parser/ParserDiffTest.php21
-rw-r--r--includes/parser/ParserOptions.php99
-rw-r--r--includes/parser/ParserOutput.php37
-rw-r--r--includes/parser/Preprocessor_DOM.php26
-rw-r--r--includes/parser/Preprocessor_Hash.php24
-rw-r--r--includes/parser/StripState.php27
-rw-r--r--includes/password/EncryptedPassword.php2
-rw-r--r--includes/password/PasswordPolicyChecks.php115
-rw-r--r--includes/password/UserPasswordPolicy.php201
-rw-r--r--includes/poolcounter/PoolCounter.php9
-rw-r--r--includes/poolcounter/PoolCounterRedis.php7
-rw-r--r--includes/poolcounter/PoolWorkArticleView.php8
-rw-r--r--includes/profiler/ProfileSection.php5
-rw-r--r--includes/profiler/Profiler.php26
-rw-r--r--includes/profiler/ProfilerFunctions.php2
-rw-r--r--includes/profiler/ProfilerStub.php3
-rw-r--r--includes/profiler/ProfilerXhprof.php45
-rw-r--r--includes/profiler/SectionProfiler.php9
-rw-r--r--includes/profiler/TransactionProfiler.php56
-rw-r--r--includes/profiler/output/ProfilerOutputDump.php6
-rw-r--r--includes/profiler/output/ProfilerOutputStats.php6
-rw-r--r--includes/profiler/output/ProfilerOutputText.php2
-rw-r--r--includes/profiler/output/ProfilerOutputUdp.php2
-rw-r--r--includes/rcfeed/MachineReadableRCFeedFormatter.php4
-rw-r--r--includes/rcfeed/RCFeedFormatter.php3
-rw-r--r--includes/registration/CoreVersionChecker.php68
-rw-r--r--includes/registration/ExtensionProcessor.php27
-rw-r--r--includes/registration/ExtensionRegistry.php52
-rw-r--r--includes/registration/Processor.php20
-rw-r--r--includes/resourceloader/DerivativeResourceLoaderContext.php76
-rw-r--r--includes/resourceloader/ResourceLoader.php553
-rw-r--r--includes/resourceloader/ResourceLoaderContext.php33
-rw-r--r--includes/resourceloader/ResourceLoaderEditToolbarModule.php31
-rw-r--r--includes/resourceloader/ResourceLoaderFileModule.php122
-rw-r--r--includes/resourceloader/ResourceLoaderForeignApiModule.php33
-rw-r--r--includes/resourceloader/ResourceLoaderImage.php25
-rw-r--r--includes/resourceloader/ResourceLoaderImageModule.php193
-rw-r--r--includes/resourceloader/ResourceLoaderJqueryMsgModule.php66
-rw-r--r--includes/resourceloader/ResourceLoaderLanguageDataModule.php16
-rw-r--r--includes/resourceloader/ResourceLoaderLanguageNamesModule.php18
-rw-r--r--includes/resourceloader/ResourceLoaderModule.php479
-rw-r--r--includes/resourceloader/ResourceLoaderOOUIImageModule.php86
-rw-r--r--includes/resourceloader/ResourceLoaderRawFileModule.php52
-rw-r--r--includes/resourceloader/ResourceLoaderSiteModule.php9
-rw-r--r--includes/resourceloader/ResourceLoaderSkinModule.php13
-rw-r--r--includes/resourceloader/ResourceLoaderSpecialCharacterDataModule.php19
-rw-r--r--includes/resourceloader/ResourceLoaderStartUpModule.php191
-rw-r--r--includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php18
-rw-r--r--includes/resourceloader/ResourceLoaderUserDefaultsModule.php19
-rw-r--r--includes/resourceloader/ResourceLoaderUserOptionsModule.php21
-rw-r--r--includes/resourceloader/ResourceLoaderWikiModule.php198
-rw-r--r--includes/revisiondelete/RevDelItem.php2
-rw-r--r--includes/revisiondelete/RevDelList.php24
-rw-r--r--includes/revisiondelete/RevDelLogItem.php27
-rw-r--r--includes/revisiondelete/RevDelRevisionItem.php2
-rw-r--r--includes/revisiondelete/RevisionDeleter.php4
-rw-r--r--includes/search/SearchEngine.php4
-rw-r--r--includes/search/SearchHighlighter.php2
-rw-r--r--includes/search/SearchMySQL.php6
-rw-r--r--includes/search/SearchPostgres.php2
-rw-r--r--includes/search/SearchResultSet.php35
-rw-r--r--includes/search/SearchSqlite.php4
-rw-r--r--includes/site/CachingSiteStore.php6
-rw-r--r--includes/site/DBSiteStore.php6
-rw-r--r--includes/site/SiteExporter.php20
-rw-r--r--includes/site/SiteSQLStore.php2
-rw-r--r--includes/skins/MediaWikiI18N.php4
-rw-r--r--includes/skins/Skin.php48
-rw-r--r--includes/skins/SkinFallbackTemplate.php6
-rw-r--r--includes/skins/SkinTemplate.php23
-rw-r--r--includes/specialpage/ChangesListSpecialPage.php3
-rw-r--r--includes/specialpage/FormSpecialPage.php6
-rw-r--r--includes/specialpage/QueryPage.php49
-rw-r--r--includes/specialpage/RedirectSpecialPage.php33
-rw-r--r--includes/specialpage/SpecialPage.php27
-rw-r--r--includes/specialpage/SpecialPageFactory.php61
-rw-r--r--includes/specials/SpecialActiveusers.php29
-rw-r--r--includes/specials/SpecialAllMessages.php18
-rw-r--r--includes/specials/SpecialAllPages.php83
-rw-r--r--includes/specials/SpecialAncientpages.php6
-rw-r--r--includes/specials/SpecialBlock.php39
-rw-r--r--includes/specials/SpecialBlockList.php77
-rw-r--r--includes/specials/SpecialBrokenRedirects.php4
-rw-r--r--includes/specials/SpecialChangeContentModel.php223
-rw-r--r--includes/specials/SpecialChangeEmail.php13
-rw-r--r--includes/specials/SpecialChangePassword.php3
-rw-r--r--includes/specials/SpecialComparePages.php3
-rw-r--r--includes/specials/SpecialConfirmemail.php8
-rw-r--r--includes/specials/SpecialContributions.php8
-rw-r--r--includes/specials/SpecialDeletedContributions.php8
-rw-r--r--includes/specials/SpecialDiff.php8
-rw-r--r--includes/specials/SpecialDoubleRedirects.php5
-rw-r--r--includes/specials/SpecialEditTags.php20
-rw-r--r--includes/specials/SpecialEditWatchlist.php7
-rw-r--r--includes/specials/SpecialEmailuser.php7
-rw-r--r--includes/specials/SpecialExport.php207
-rw-r--r--includes/specials/SpecialFewestrevisions.php4
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php6
-rw-r--r--includes/specials/SpecialFilepath.php11
-rw-r--r--includes/specials/SpecialImport.php144
-rw-r--r--includes/specials/SpecialJavaScriptTest.php108
-rw-r--r--includes/specials/SpecialLinkSearch.php68
-rw-r--r--includes/specials/SpecialListDuplicatedFiles.php4
-rw-r--r--includes/specials/SpecialListfiles.php14
-rw-r--r--includes/specials/SpecialListredirects.php4
-rw-r--r--includes/specials/SpecialListusers.php16
-rw-r--r--includes/specials/SpecialLockdb.php4
-rw-r--r--includes/specials/SpecialMIMEsearch.php7
-rw-r--r--includes/specials/SpecialMediaStatistics.php12
-rw-r--r--includes/specials/SpecialMergeHistory.php2
-rw-r--r--includes/specials/SpecialMostcategories.php4
-rw-r--r--includes/specials/SpecialMostinterwikis.php4
-rw-r--r--includes/specials/SpecialMostlinked.php4
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php2
-rw-r--r--includes/specials/SpecialMovepage.php338
-rw-r--r--includes/specials/SpecialMyLanguage.php26
-rw-r--r--includes/specials/SpecialMyRedirectPages.php55
-rw-r--r--includes/specials/SpecialNewpages.php4
-rw-r--r--includes/specials/SpecialPageLanguage.php7
-rw-r--r--includes/specials/SpecialPagesWithProp.php8
-rw-r--r--includes/specials/SpecialPasswordReset.php2
-rw-r--r--includes/specials/SpecialPermanentLink.php8
-rw-r--r--includes/specials/SpecialPreferences.php9
-rw-r--r--includes/specials/SpecialProtectedtitles.php12
-rw-r--r--includes/specials/SpecialRandomInCategory.php16
-rw-r--r--includes/specials/SpecialRandompage.php20
-rw-r--r--includes/specials/SpecialRecentchanges.php23
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php1
-rw-r--r--includes/specials/SpecialResetTokens.php5
-rw-r--r--includes/specials/SpecialRevisiondelete.php36
-rw-r--r--includes/specials/SpecialRunJobs.php15
-rw-r--r--includes/specials/SpecialSearch.php152
-rw-r--r--includes/specials/SpecialShortpages.php2
-rw-r--r--includes/specials/SpecialSpecialpages.php27
-rw-r--r--includes/specials/SpecialStatistics.php43
-rw-r--r--includes/specials/SpecialTags.php97
-rw-r--r--includes/specials/SpecialUndelete.php15
-rw-r--r--includes/specials/SpecialUnlockdb.php4
-rw-r--r--includes/specials/SpecialUnusedcategories.php4
-rw-r--r--includes/specials/SpecialUnusedtemplates.php4
-rw-r--r--includes/specials/SpecialUnwatchedpages.php4
-rw-r--r--includes/specials/SpecialUpload.php14
-rw-r--r--includes/specials/SpecialUploadStash.php4
-rw-r--r--includes/specials/SpecialUserlogin.php115
-rw-r--r--includes/specials/SpecialUserrights.php8
-rw-r--r--includes/specials/SpecialVersion.php76
-rw-r--r--includes/specials/SpecialWantedfiles.php2
-rw-r--r--includes/specials/SpecialWantedtemplates.php5
-rw-r--r--includes/specials/SpecialWatchlist.php1
-rw-r--r--includes/specials/SpecialWhatlinkshere.php53
-rw-r--r--includes/templates/Usercreate.php8
-rw-r--r--includes/templates/Userlogin.php5
-rw-r--r--includes/tidy/Html5Depurate.php45
-rw-r--r--includes/tidy/RaggettBase.php47
-rw-r--r--includes/tidy/RaggettExternal.php73
-rw-r--r--includes/tidy/RaggettInternalHHVM.php29
-rw-r--r--includes/tidy/RaggettInternalPHP.php52
-rw-r--r--includes/tidy/RaggettWrapper.php89
-rw-r--r--includes/tidy/TidyDriverBase.php40
-rw-r--r--includes/tidy/tidy.conf (renamed from includes/tidy.conf)0
-rw-r--r--includes/title/MalformedTitleException.php54
-rw-r--r--includes/title/MediaWikiTitleCodec.php34
-rw-r--r--includes/title/TitleValue.php24
-rw-r--r--includes/upload/UploadBase.php93
-rw-r--r--includes/upload/UploadFromUrl.php4
-rw-r--r--includes/utils/AutoloadGenerator.php74
-rw-r--r--includes/utils/AvroValidator.php184
-rw-r--r--includes/utils/BatchRowIterator.php278
-rw-r--r--includes/utils/BatchRowUpdate.php133
-rw-r--r--includes/utils/BatchRowWriter.php71
-rw-r--r--includes/utils/IP.php6
-rw-r--r--includes/utils/MWCryptHKDF.php8
-rw-r--r--includes/utils/MWCryptRand.php8
-rw-r--r--includes/utils/RowUpdateGenerator.php39
-rw-r--r--includes/utils/UIDGenerator.php21
-rw-r--r--includes/utils/iterators/IteratorDecorator.php50
-rw-r--r--includes/utils/iterators/NotRecursiveIterator.php (renamed from includes/utils/MWFunction.php)29
-rw-r--r--includes/widget/AUTHORS.txt11
-rw-r--r--includes/widget/ComplexNamespaceInputWidget.php110
-rw-r--r--includes/widget/ComplexTitleInputWidget.php67
-rw-r--r--includes/widget/LICENSE.txt25
-rw-r--r--includes/widget/NamespaceInputWidget.php66
-rw-r--r--includes/widget/TitleInputWidget.php60
-rw-r--r--includes/widget/UserInputWidget.php29
732 files changed, 32314 insertions, 11064 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index b14114d7..96892d71 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -124,9 +124,9 @@ class AjaxDispatcher {
$result = call_user_func_array( $this->func_name, $this->args );
if ( $result === false || $result === null ) {
- wfDebug( __METHOD__ . ' ERROR while dispatching '
- . $this->func_name . "(" . var_export( $this->args, true ) . "): "
- . "no data returned\n" );
+ wfDebug( __METHOD__ . ' ERROR while dispatching ' .
+ $this->func_name . "(" . var_export( $this->args, true ) . "): " .
+ "no data returned\n" );
wfHttpError( 500, 'Internal Error',
"{$this->func_name} returned no data" );
@@ -141,9 +141,9 @@ class AjaxDispatcher {
wfDebug( __METHOD__ . ' dispatch complete for ' . $this->func_name . "\n" );
}
} catch ( Exception $e ) {
- wfDebug( __METHOD__ . ' ERROR while dispatching '
- . $this->func_name . "(" . var_export( $this->args, true ) . "): "
- . get_class( $e ) . ": " . $e->getMessage() . "\n" );
+ wfDebug( __METHOD__ . ' ERROR while dispatching ' .
+ $this->func_name . "(" . var_export( $this->args, true ) . "): " .
+ get_class( $e ) . ": " . $e->getMessage() . "\n" );
if ( !headers_sent() ) {
wfHttpError( 500, 'Internal Error',
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 8e9f490f..6c2efc29 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -86,7 +86,7 @@ class AjaxResponse {
$this->mDisabled = false;
$this->mText = '';
- $this->mResponseCode = '200 OK';
+ $this->mResponseCode = 200;
$this->mLastModified = false;
$this->mContentType = 'application/x-wiki';
@@ -158,16 +158,20 @@ class AjaxResponse {
*/
function sendHeaders() {
if ( $this->mResponseCode ) {
- $n = preg_replace( '/^ *(\d+)/', '\1', $this->mResponseCode );
- header( "Status: " . $this->mResponseCode, true, (int)$n );
+ // For back-compat, it is supported that mResponseCode be a string like " 200 OK"
+ // (with leading space and the status message after). Cast response code to an integer
+ // to take advantage of PHP's conversion rules which will turn " 200 OK" into 200.
+ // http://php.net/string#language.types.string.conversion
+ $n = intval( trim( $this->mResponseCode ) );
+ HttpStatus::header( $n );
}
- header ( "Content-Type: " . $this->mContentType );
+ header( "Content-Type: " . $this->mContentType );
if ( $this->mLastModified ) {
- header ( "Last-Modified: " . $this->mLastModified );
+ header( "Last-Modified: " . $this->mLastModified );
} else {
- header ( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
+ header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
}
if ( $this->mCacheDuration ) {
@@ -189,20 +193,20 @@ class AjaxResponse {
} else {
# Let the client do the caching. Cache is not purged.
- header ( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" );
- header ( "Cache-Control: s-maxage={$this->mCacheDuration}," .
+ header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" );
+ header( "Cache-Control: s-maxage={$this->mCacheDuration}," .
"public,max-age={$this->mCacheDuration}" );
}
} else {
# always expired, always modified
- header ( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); // Date in the past
- header ( "Cache-Control: no-cache, must-revalidate" ); // HTTP/1.1
- header ( "Pragma: no-cache" ); // HTTP/1.0
+ header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); // Date in the past
+ header( "Cache-Control: no-cache, must-revalidate" ); // HTTP/1.1
+ header( "Pragma: no-cache" ); // HTTP/1.0
}
if ( $this->mVary ) {
- header ( "Vary: " . $this->mVary );
+ header( "Vary: " . $this->mVary );
}
}
@@ -246,7 +250,7 @@ class AjaxResponse {
$ismodsince >= $wgCacheEpoch
) {
ini_set( 'zlib.output_compression', 0 );
- $this->setResponseCode( "304 Not Modified" );
+ $this->setResponseCode( 304 );
$this->disable();
$this->mLastModified = $lastmod;
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 45ad4d1b..badf47c3 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -120,6 +120,8 @@ class AuthPlugin {
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
+ * @deprecated since 1.26, use the UserLoggedIn hook instead. And assigning
+ * a different User object to $user is no longer supported.
* @param User $user
* @return bool
*/
@@ -204,6 +206,7 @@ class AuthPlugin {
* Update user information in the external authentication database.
* Return true if successful.
*
+ * @deprecated since 1.26, use the UserSaveSettings hook instead.
* @param User $user
* @return bool
*/
@@ -215,6 +218,7 @@ class AuthPlugin {
* Update user groups in the external authentication database.
* Return true if successful.
*
+ * @deprecated since 1.26, use the UserGroupsChanged hook instead.
* @param User $user
* @param array $addgroups Groups to add.
* @param array $delgroups Groups to remove.
@@ -278,6 +282,8 @@ class AuthPlugin {
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
+ * @deprecated since 1.26, use the UserLoggedIn hook instead. And assigning
+ * a different User object to $user is no longer supported.
* @param User $user
* @param bool $autocreate True if user is being autocreated on login
*/
@@ -326,11 +332,21 @@ class AuthPluginUser {
return -1;
}
+ /**
+ * Indicate whether the user is locked
+ * @deprecated since 1.26, use the UserIsLocked hook instead.
+ * @return bool
+ */
public function isLocked() {
# Override this!
return false;
}
+ /**
+ * Indicate whether the user is hidden
+ * @deprecated since 1.26, use the UserIsHidden hook instead.
+ * @return bool
+ */
public function isHidden() {
# Override this!
return false;
diff --git a/includes/Block.php b/includes/Block.php
index 873a26d8..c5a16fce 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -23,15 +23,16 @@ class Block {
/** @var string */
public $mReason;
- /** @var bool|string */
+ /** @var string */
public $mTimestamp;
- /** @var int */
+ /** @var bool */
public $mAuto;
- /** @var bool|string */
+ /** @var string */
public $mExpiry;
+ /** @var bool */
public $mHideName;
/** @var int */
@@ -65,10 +66,10 @@ class Block {
protected $blocker;
/** @var bool */
- protected $isHardblock = true;
+ protected $isHardblock;
/** @var bool */
- protected $isAutoblocking = true;
+ protected $isAutoblocking;
# TYPE constants
const TYPE_USER = 1;
@@ -78,59 +79,84 @@ class Block {
const TYPE_ID = 5;
/**
- * @todo FIXME: Don't know what the best format to have for this constructor
- * is, but fourteen optional parameters certainly isn't it.
- * @param string $address
- * @param int $user
- * @param int $by
- * @param string $reason
- * @param mixed $timestamp
- * @param int $auto
- * @param string $expiry
- * @param int $anonOnly
- * @param int $createAccount
- * @param int $enableAutoblock
- * @param int $hideName
- * @param int $blockEmail
- * @param int $allowUsertalk
- * @param string $byText
+ * Create a new block with specified parameters on a user, IP or IP range.
+ *
+ * @param array $options Parameters of the block:
+ * address string|User Target user name, User object, IP address or IP range
+ * user int Override target user ID (for foreign users)
+ * by int User ID of the blocker
+ * reason string Reason of the block
+ * timestamp string The time at which the block comes into effect
+ * auto bool Is this an automatic block?
+ * expiry string Timestamp of expiration of the block or 'infinity'
+ * anonOnly bool Only disallow anonymous actions
+ * createAccount bool Disallow creation of new accounts
+ * enableAutoblock bool Enable automatic blocking
+ * hideName bool Hide the target user name
+ * blockEmail bool Disallow sending emails
+ * allowUsertalk bool Allow the target to edit its own talk page
+ * byText string Username of the blocker (for foreign users)
+ *
+ * @since 1.26 accepts $options array instead of individual parameters; order
+ * of parameters above reflects the original order
*/
- function __construct( $address = '', $user = 0, $by = 0, $reason = '',
- $timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
- $hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byText = ''
- ) {
- if ( $timestamp === 0 ) {
- $timestamp = wfTimestampNow();
- }
+ function __construct( $options = array() ) {
+ $defaults = array(
+ 'address' => '',
+ 'user' => null,
+ 'by' => null,
+ 'reason' => '',
+ 'timestamp' => '',
+ 'auto' => false,
+ 'expiry' => '',
+ 'anonOnly' => false,
+ 'createAccount' => false,
+ 'enableAutoblock' => false,
+ 'hideName' => false,
+ 'blockEmail' => false,
+ 'allowUsertalk' => false,
+ 'byText' => '',
+ );
- if ( count( func_get_args() ) > 0 ) {
- # Soon... :D
- # wfDeprecated( __METHOD__ . " with arguments" );
+ if ( func_num_args() > 1 || !is_array( $options ) ) {
+ $options = array_combine(
+ array_slice( array_keys( $defaults ), 0, func_num_args() ),
+ func_get_args()
+ );
+ wfDeprecated( __METHOD__ . ' with multiple arguments', '1.26' );
}
- $this->setTarget( $address );
- if ( $this->target instanceof User && $user ) {
- $this->forcedTargetID = $user; // needed for foreign users
- }
- if ( $by ) { // local user
- $this->setBlocker( User::newFromId( $by ) );
- } else { // foreign user
- $this->setBlocker( $byText );
+ $options += $defaults;
+
+ $this->setTarget( $options['address'] );
+
+ if ( $this->target instanceof User && $options['user'] ) {
+ # Needed for foreign users
+ $this->forcedTargetID = $options['user'];
}
- $this->mReason = $reason;
- $this->mTimestamp = wfTimestamp( TS_MW, $timestamp );
- $this->mAuto = $auto;
- $this->isHardblock( !$anonOnly );
- $this->prevents( 'createaccount', $createAccount );
- if ( $expiry == 'infinity' || $expiry == wfGetDB( DB_SLAVE )->getInfinity() ) {
- $this->mExpiry = 'infinity';
+
+ if ( $options['by'] ) {
+ # Local user
+ $this->setBlocker( User::newFromID( $options['by'] ) );
} else {
- $this->mExpiry = wfTimestamp( TS_MW, $expiry );
+ # Foreign user
+ $this->setBlocker( $options['byText'] );
}
- $this->isAutoblocking( $enableAutoblock );
- $this->mHideName = $hideName;
- $this->prevents( 'sendemail', $blockEmail );
- $this->prevents( 'editownusertalk', !$allowUsertalk );
+
+ $this->mReason = $options['reason'];
+ $this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
+ $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $options['expiry'] );
+
+ # Boolean settings
+ $this->mAuto = (bool)$options['auto'];
+ $this->mHideName = (bool)$options['hideName'];
+ $this->isHardblock( !$options['anonOnly'] );
+ $this->isAutoblocking( (bool)$options['enableAutoblock'] );
+
+ # Prevention measures
+ $this->prevents( 'sendemail', (bool)$options['blockEmail'] );
+ $this->prevents( 'editownusertalk', !$options['allowUsertalk'] );
+ $this->prevents( 'createaccount', (bool)$options['createAccount'] );
$this->mFromMaster = false;
}
@@ -375,16 +401,11 @@ class Block {
$this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
$this->mAuto = $row->ipb_auto;
$this->mHideName = $row->ipb_deleted;
- $this->mId = $row->ipb_id;
+ $this->mId = (int)$row->ipb_id;
$this->mParentBlockId = $row->ipb_parent_block_id;
// I wish I didn't have to do this
- $db = wfGetDB( DB_SLAVE );
- if ( $row->ipb_expiry == $db->getInfinity() ) {
- $this->mExpiry = 'infinity';
- } else {
- $this->mExpiry = wfTimestamp( TS_MW, $row->ipb_expiry );
- }
+ $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $row->ipb_expiry );
$this->isHardblock( !$row->ipb_anon_only );
$this->isAutoblocking( $row->ipb_enable_autoblock );
@@ -452,11 +473,15 @@ class Block {
$dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
$affected = $dbw->affectedRows();
+ $this->mId = $dbw->insertId();
# Don't collide with expired blocks.
- # Do this after trying to insert to avoid pointless gap locks.
+ # Do this after trying to insert to avoid locking.
if ( !$affected ) {
- $dbw->delete( 'ipblocks',
+ # T96428: The ipb_address index uses a prefix on a field, so
+ # use a standard SELECT + DELETE to avoid annoying gap locks.
+ $ids = $dbw->selectFieldValues( 'ipblocks',
+ 'ipb_id',
array(
'ipb_address' => $row['ipb_address'],
'ipb_user' => $row['ipb_user'],
@@ -464,13 +489,14 @@ class Block {
),
__METHOD__
);
-
- $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
- $affected = $dbw->affectedRows();
+ if ( $ids ) {
+ $dbw->delete( 'ipblocks', array( 'ipb_id' => $ids ), __METHOD__ );
+ $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
+ $affected = $dbw->affectedRows();
+ $this->mId = $dbw->insertId();
+ }
}
- $this->mId = $dbw->insertId();
-
if ( $affected ) {
$auto_ipd_ids = $this->doRetroactiveAutoblock();
return array( 'id' => $this->mId, 'autoIds' => $auto_ipd_ids );
@@ -1113,7 +1139,7 @@ class Block {
$blocks = array();
foreach ( $rows as $row ) {
$block = self::newFromRow( $row );
- if ( !$block->deleteIfExpired() ) {
+ if ( !$block->deleteIfExpired() ) {
$blocks[] = $block;
}
}
diff --git a/includes/CategoryFinder.php b/includes/CategoryFinder.php
index 33de7404..77c43bf0 100644
--- a/includes/CategoryFinder.php
+++ b/includes/CategoryFinder.php
@@ -27,7 +27,7 @@
* articles are in one or all of a given subset of categories.
*
* Example use :
- * <code>
+ * @code
* # Determines whether the article with the page_id 12345 is in both
* # "Category 1" and "Category 2" or their subcategories, respectively
*
@@ -39,7 +39,7 @@
* );
* $a = $cf->run();
* print implode( ',' , $a );
- * </code>
+ * @endcode
*
*/
class CategoryFinder {
diff --git a/includes/CategoryViewer.php b/includes/CategoryViewer.php
index 66079c01..e2c31a66 100644
--- a/includes/CategoryViewer.php
+++ b/includes/CategoryViewer.php
@@ -329,7 +329,7 @@ class CategoryViewer extends ContextSource {
'category' => array( 'LEFT JOIN', array(
'cat_title = page_title',
'page_namespace' => NS_CATEGORY
- ))
+ ) )
)
);
diff --git a/includes/Collation.php b/includes/Collation.php
index 481d8e70..c1f0b388 100644
--- a/includes/Collation.php
+++ b/includes/Collation.php
@@ -320,16 +320,16 @@ class IcuCollation extends Collation {
// intl extension produces non null-terminated
// strings. Appending '' fixes it so that it doesn't generate
// a warning on each access in debug php.
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$key = $this->mainCollator->getSortKey( $string ) . '';
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $key;
}
function getPrimarySortKey( $string ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$key = $this->primaryCollator->getSortKey( $string ) . '';
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $key;
}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index c13aa5f4..268a8d19 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -75,7 +75,7 @@ $wgConfigRegistry = array(
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.25.3';
+$wgVersion = '1.26.0';
/**
* Name of the site. It must be changed in LocalSettings.php
@@ -83,6 +83,14 @@ $wgVersion = '1.25.3';
$wgSitename = 'MediaWiki';
/**
+ * When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes
+ * the wiki on the standard ports (443 for https and 80 for http).
+ * @var bool
+ * @since 1.26
+ */
+$wgAssumeProxiesUseDefaultProtocolPorts = true;
+
+/**
* URL of the server.
*
* @par Example:
@@ -203,7 +211,7 @@ $wgLoadScript = false;
/**
* The URL path of the skins directory.
- * Defaults to "{$wgScriptPath}/skins".
+ * Defaults to "{$wgResourceBasePath}/skins".
* @since 1.3
*/
$wgStylePath = false;
@@ -218,7 +226,7 @@ $wgLocalStylePath = false;
/**
* The URL path of the extensions directory.
- * Defaults to "{$wgScriptPath}/extensions".
+ * Defaults to "{$wgResourceBasePath}/extensions".
* @since 1.16
*/
$wgExtensionAssetsPath = false;
@@ -472,13 +480,13 @@ $wgImgAuthUrlPathMap = array();
*
* These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
* for local repositories:
- * - descBaseUrl URL of image description pages, e.g. http://en.wikipedia.org/wiki/File:
+ * - descBaseUrl URL of image description pages, e.g. https://en.wikipedia.org/wiki/File:
* - scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
- * http://en.wikipedia.org/w
+ * https://en.wikipedia.org/w
* - scriptExtension Script extension of the MediaWiki installation, equivalent to
* $wgScriptExtension, e.g. .php5 defaults to .php
*
- * - articleUrl Equivalent to $wgArticlePath, e.g. http://en.wikipedia.org/wiki/$1
+ * - articleUrl Equivalent to $wgArticlePath, e.g. https://en.wikipedia.org/wiki/$1
* - fetchDescription Fetch the text of the remote file description page. Equivalent to
* $wgFetchCommonsDescriptions.
* - abbrvThreshold File names over this size will use the short form of thumbnail names.
@@ -518,6 +526,16 @@ $wgForeignFileRepos = array();
$wgUseInstantCommons = false;
/**
+ * Array of foreign file repo names (set in $wgForeignFileRepos above) that
+ * are allowable upload targets. These wikis must have some method of
+ * authentication (i.e. CentralAuth), and be CORS-enabled for this wiki.
+ *
+ * Example:
+ * $wgForeignUploadTargets = array( 'shared' );
+ */
+$wgForeignUploadTargets = array();
+
+/**
* File backend structure configuration.
*
* This is an array of file backend configuration arrays.
@@ -717,7 +735,7 @@ $wgMinUploadChunkSize = 1024; # 1KB
*
* @par Example:
* @code
- * $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload';
+ * $wgUploadNavigationUrl = 'https://commons.wikimedia.org/wiki/Special:Upload';
* @endcode
*/
$wgUploadNavigationUrl = false;
@@ -777,7 +795,7 @@ $wgHashedSharedUploadDirectory = true;
*
* Please specify the namespace, as in the example below.
*/
-$wgRepositoryBaseUrl = "http://commons.wikimedia.org/wiki/File:";
+$wgRepositoryBaseUrl = "https://commons.wikimedia.org/wiki/File:";
/**
* This is the list of preferred extensions for uploading files. Uploading files
@@ -884,6 +902,7 @@ $wgMediaHandlers = array(
'image/png' => 'PNGHandler',
'image/gif' => 'GIFHandler',
'image/tiff' => 'TiffHandler',
+ 'image/webp' => 'WebPHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/x-bmp' => 'BmpHandler',
'image/x-xcf' => 'XCFHandler',
@@ -978,6 +997,14 @@ $wgJpegTran = '/usr/bin/jpegtran';
*/
$wgExiv2Command = '/usr/bin/exiv2';
+
+/**
+ * Path to exiftool binary. Used for lossless ICC profile swapping.
+ *
+ * @since 1.26
+ */
+$wgExiftool = '/usr/bin/exiftool';
+
/**
* Scalable Vector Graphics (SVG) may be uploaded as images.
* Since SVG support is not yet standard in browsers, it is
@@ -1012,7 +1039,7 @@ $wgSVGConverterPath = '';
/**
* Don't scale a SVG larger than this
*/
-$wgSVGMaxSize = 2048;
+$wgSVGMaxSize = 5120;
/**
* Don't read SVG metadata beyond this point.
@@ -1331,6 +1358,14 @@ $wgUploadThumbnailRenderHttpCustomHost = false;
$wgUploadThumbnailRenderHttpCustomDomain = false;
/**
+ * When this variable is true and JPGs use the sRGB ICC profile, swaps it for the more lightweight
+ * (and free) TinyRGB profile when generating thumbnails.
+ *
+ * @since 1.26
+ */
+$wgUseTinyRGBForJPGThumbnails = false;
+
+/**
* Default parameters for the "<gallery>" tag
*/
$wgGalleryOptions = array(
@@ -1580,7 +1615,8 @@ $wgEnotifRevealEditorAddress = false;
/**
* Send notification mails on minor edits to watchlist pages. This is enabled
- * by default. Does not affect user talk notifications.
+ * by default. User talk notifications are affected by this, $wgEnotifUserTalk, and
+ * the nominornewtalk user right.
*/
$wgEnotifMinorEdits = true;
@@ -1843,12 +1879,6 @@ $wgDBservers = false;
$wgLBFactoryConf = array( 'class' => 'LBFactorySimple' );
/**
- * How long to wait for a slave to catch up to the master
- * @deprecated since 1.24
- */
-$wgMasterWaitTimeout = 10;
-
-/**
* File to log database errors to
*/
$wgDBerrorLog = false;
@@ -1862,11 +1892,11 @@ $wgDBerrorLog = false;
*
* @par Examples:
* @code
- * $wgLocaltimezone = 'UTC';
- * $wgLocaltimezone = 'GMT';
- * $wgLocaltimezone = 'PST8PDT';
- * $wgLocaltimezone = 'Europe/Sweden';
- * $wgLocaltimezone = 'CET';
+ * $wgDBerrorLogTZ = 'UTC';
+ * $wgDBerrorLogTZ = 'GMT';
+ * $wgDBerrorLogTZ = 'PST8PDT';
+ * $wgDBerrorLogTZ = 'Europe/Sweden';
+ * $wgDBerrorLogTZ = 'CET';
* @endcode
*
* @since 1.20
@@ -1874,13 +1904,6 @@ $wgDBerrorLog = false;
$wgDBerrorLogTZ = false;
/**
- * Scale load balancer polling time so that under overload conditions, the
- * database server receives a SHOW STATUS query at an average interval of this
- * many microseconds
- */
-$wgDBAvgStatusPoll = 2000;
-
-/**
* Set to true to engage MySQL 4.1/5.0 charset-related features;
* for now will just cause sending of 'SET NAMES=utf8' on connect.
*
@@ -2067,6 +2090,14 @@ $wgMaxArticleSize = 2048;
*/
$wgMemoryLimit = "50M";
+/**
+ * The minimum amount of time that MediaWiki needs for "slow" write request,
+ * particularly ones with multiple non-atomic writes that *should* be as
+ * transactional as possible; MediaWiki will call set_time_limit() if needed.
+ * @since 1.26
+ */
+$wgTransactionalTimeLimit = 120;
+
/** @} */ # end performance hacks }
/************************************************************************//**
@@ -2086,8 +2117,8 @@ $wgCacheDirectory = false;
/**
* Main cache type. This should be a cache with fast access, but it may have
- * limited space. By default, it is disabled, since the database is not fast
- * enough to make it worthwhile.
+ * limited space. By default, it is disabled, since the stock database cache
+ * is not fast enough to make it worthwhile.
*
* The options are:
*
@@ -2157,6 +2188,19 @@ $wgObjectCaches = array(
CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached', 'loggroup' => 'memcached' ),
+ 'db-replicated' => array(
+ 'class' => 'ReplicatedBagOStuff',
+ 'readFactory' => array(
+ 'class' => 'SqlBagOStuff',
+ 'args' => array( array( 'slaveOnly' => true ) )
+ ),
+ 'writeFactory' => array(
+ 'class' => 'SqlBagOStuff',
+ 'args' => array( array( 'slaveOnly' => false ) )
+ ),
+ 'loggroup' => 'SQLBagOStuff'
+ ),
+
'apc' => array( 'class' => 'APCBagOStuff' ),
'xcache' => array( 'class' => 'XCacheBagOStuff' ),
'wincache' => array( 'class' => 'WinCacheBagOStuff' ),
@@ -2166,6 +2210,71 @@ $wgObjectCaches = array(
);
/**
+ * Main Wide-Area-Network cache type. This should be a cache with fast access,
+ * but it may have limited space. By default, it is disabled, since the basic stock
+ * cache is not fast enough to make it worthwhile. For single data-center setups, this can
+ * simply be pointed to a cache in $wgWANObjectCaches that uses a local $wgObjectCaches
+ * cache with a relayer of type EventRelayerNull.
+ *
+ * The options are:
+ * - false: Configure the cache using $wgMainCacheType, without using
+ * a relayer (only matters if there are multiple data-centers)
+ * - CACHE_NONE: Do not cache
+ * - (other): A string may be used which identifies a cache
+ * configuration in $wgWANObjectCaches
+ * @since 1.26
+ */
+$wgMainWANCache = false;
+
+/**
+ * Advanced WAN object cache configuration.
+ *
+ * Each WAN cache wraps a registered object cache (for the local cluster)
+ * and it must also be configured to point to a PubSub instance. Subscribers
+ * must be configured to relay purges to the actual cache servers.
+ *
+ * The format is an associative array where the key is a cache identifier, and
+ * the value is an associative array of parameters. The "cacheId" parameter is
+ * a cache identifier from $wgObjectCaches. The "relayerConfig" parameter is an
+ * array used to construct an EventRelayer object. The "pool" parameter is a
+ * string that is used as a PubSub channel prefix.
+ *
+ * @since 1.26
+ */
+$wgWANObjectCaches = array(
+ CACHE_NONE => array(
+ 'class' => 'WANObjectCache',
+ 'cacheId' => CACHE_NONE,
+ 'pool' => 'mediawiki-main-none',
+ 'relayerConfig' => array( 'class' => 'EventRelayerNull' )
+ )
+ /* Example of a simple single data-center cache:
+ 'memcached-php' => array(
+ 'class' => 'WANObjectCache',
+ 'cacheId' => 'memcached-php',
+ 'pool' => 'mediawiki-main-memcached',
+ 'relayerConfig' => array( 'class' => 'EventRelayerNull' )
+ )
+ */
+);
+
+/**
+ * Main object stash type. This should be a fast storage system for storing
+ * lightweight data like hit counters and user activity. Sites with multiple
+ * data-centers should have this use a store that replicates all writes. The
+ * store should have enough consistency for CAS operations to be usable.
+ * Reads outside of those needed for merge() may be eventually consistent.
+ *
+ * The options are:
+ * - db: Store cache objects in the DB
+ * - (other): A string may be used which identifies a cache
+ * configuration in $wgObjectCaches
+ *
+ * @since 1.26
+ */
+$wgMainStash = 'db-replicated';
+
+/**
* The expiry time for the parser cache, in seconds.
* The default is 86400 (one day).
*/
@@ -2239,11 +2348,13 @@ $wgAdaptiveMessageCache = false;
* Localisation cache configuration. Associative array with keys:
* class: The class to use. May be overridden by extensions.
*
- * store: The location to store cache data. May be 'files', 'db' or
+ * store: The location to store cache data. May be 'files', 'array', 'db' or
* 'detect'. If set to "files", data will be in CDB files. If set
* to "db", data will be stored to the database. If set to
* "detect", files will be used if $wgCacheDirectory is set,
* otherwise the database will be used.
+ * "array" is an experimental option that uses PHP files that
+ * store static arrays.
*
* storeClass: The class name for the underlying storage. If set to a class
* name, it overrides the "store" setting.
@@ -2311,13 +2422,8 @@ $wgUseFileCache = false;
$wgFileCacheDepth = 2;
/**
- * Keep parsed pages in a cache (objectcache table or memcached)
- * to speed up output of the same page viewed by another user with the
- * same options.
- *
- * This can provide a significant speedup for medium to large pages,
- * so you probably want to keep it on. Extensions that conflict with the
- * parser cache should disable the cache on a per-page basis instead.
+ * Kept for extension compatibility; see $wgParserCacheType
+ * @deprecated 1.26
*/
$wgEnableParserCache = true;
@@ -2447,13 +2553,16 @@ $wgInternalServer = false;
/**
* Cache timeout for the squid, will be sent as s-maxage (without ESI) or
* Surrogate-Control (with ESI). Without ESI, you should strip out s-maxage in
- * the Squid config. 18000 seconds = 5 hours, more cache hits with 2678400 = 31
- * days
+ * the Squid config.
+ *
+* 18000 seconds = 5 hours, more cache hits with 2678400 = 31 days.
*/
$wgSquidMaxage = 18000;
/**
* Default maximum age for raw CSS/JS accesses
+ *
+ * 300 seconds = 5 minutes.
*/
$wgForcedRawSMaxage = 300;
@@ -2742,14 +2851,14 @@ $wgBrowserBlackList = array(
* - Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)
* - [...]
*
- * @link http://en.wikipedia.org/w/index.php?diff=12356041&oldid=12355864
- * @link http://en.wikipedia.org/wiki/Template%3AOS9
+ * @link https://en.wikipedia.org/w/index.php?diff=12356041&oldid=12355864
+ * @link https://en.wikipedia.org/wiki/Template%3AOS9
*/
'/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
/**
* Google wireless transcoder, seems to eat a lot of chars alive
- * http://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
+ * https://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
*/
'/^Mozilla\/4\.0 \(compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;\)/'
);
@@ -3382,8 +3491,8 @@ $wgResourceModuleSkinStyles = array();
$wgResourceLoaderSources = array();
/**
- * Default 'remoteBasePath' value for instances of ResourceLoaderFileModule.
- * If not set, then $wgScriptPath will be used as a fallback.
+ * The default 'remoteBasePath' value for instances of ResourceLoaderFileModule.
+ * Defaults to $wgScriptPath.
*/
$wgResourceBasePath = null;
@@ -3422,13 +3531,6 @@ $wgResourceLoaderMaxage = array(
$wgResourceLoaderDebug = false;
/**
- * Enable embedding of certain resources using Edge Side Includes. This will
- * improve performance but only works if there is something in front of the
- * web server (e..g a Squid or Varnish server) configured to process the ESI.
- */
-$wgResourceLoaderUseESI = false;
-
-/**
* Put each statement on its own line when minifying JavaScript. This makes
* debugging in non-debug mode a bit easier.
*/
@@ -3442,28 +3544,27 @@ $wgResourceLoaderMinifierStatementsOnOwnLine = false;
$wgResourceLoaderMinifierMaxLineLength = 1000;
/**
- * Whether to include the mediawiki.legacy JS library (old wikibits.js), and its
- * dependencies.
+ * Whether to ensure the mediawiki.legacy library is loaded before other modules.
+ *
+ * @deprecated since 1.26: Always declare dependencies.
*/
$wgIncludeLegacyJavaScript = true;
/**
- * Whether to preload the mediawiki.util module as blocking module in the top
- * queue.
+ * Whether to ensure the mediawiki.util is loaded before other modules.
*
- * Before MediaWiki 1.19, modules used to load slower/less asynchronous which
- * allowed modules to lack dependencies on 'popular' modules that were likely
- * loaded already.
+ * Before MediaWiki 1.19, modules used to load less asynchronous which allowed
+ * modules to lack dependencies on 'popular' modules that were likely loaded already.
*
* This setting is to aid scripts during migration by providing mediawiki.util
- * unconditionally (which was the most commonly missed dependency).
- * It doesn't cover all missing dependencies obviously but should fix most of
- * them.
+ * unconditionally (which was the most commonly missed dependency). It doesn't
+ * cover all missing dependencies obviously but should fix most of them.
*
* This should be removed at some point after site/user scripts have been fixed.
* Enable this if your wiki has a large amount of user/site scripts that are
* lacking dependencies.
- * @todo Deprecate
+ *
+ * @deprecated since 1.26: Always declare dependencies.
*/
$wgPreloadJavaScriptMwUtil = false;
@@ -3529,13 +3630,6 @@ $wgResourceLoaderValidateJS = true;
$wgResourceLoaderValidateStaticJS = false;
/**
- * If set to true, asynchronous loading of bottom-queue scripts in the "<head>"
- * will be enabled. This is an experimental feature that's supposed to make
- * JavaScript load faster.
- */
-$wgResourceLoaderExperimentalAsyncLoading = false;
-
-/**
* Global LESS variables. An associative array binding variable names to
* LESS code snippets representing their values.
*
@@ -3561,18 +3655,6 @@ $wgResourceLoaderExperimentalAsyncLoading = false;
$wgResourceLoaderLESSVars = array();
/**
- * Custom LESS functions. An associative array mapping function name to PHP
- * callable.
- *
- * Changes to LESS functions do not trigger cache invalidation.
- *
- * @since 1.22
- * @deprecated since 1.24 Questionable usefulness and problematic to support,
- * will be removed in the future.
- */
-$wgResourceLoaderLESSFunctions = array();
-
-/**
* Default import paths for LESS modules. LESS files referenced in @import
* statements will be looked up here first, and relative to the importing file
* second. To avoid collisions, it's important for the LESS files in these
@@ -3881,6 +3963,15 @@ $wgTrackingCategories = array();
$wgContentNamespaces = array( NS_MAIN );
/**
+ * Array of namespaces, in addition to the talk namespaces, where signatures
+ * (~~~~) are likely to be used. This determines whether to display the
+ * Signature button on the edit toolbar, and may also be used by extensions.
+ * For example, "traditional" style wikis, where content and discussion are
+ * intermixed, could place NS_MAIN and NS_PROJECT namespaces in this array.
+ */
+$wgExtraSignatureNamespaces = array();
+
+/**
* Max number of redirects to follow when resolving redirects.
* 1 means only the first redirect is followed (default behavior).
* 0 or less means no redirects are followed.
@@ -4031,44 +4122,55 @@ $wgEnableImageWhitelist = true;
$wgAllowImageTag = false;
/**
- * $wgUseTidy: use tidy to make sure HTML output is sane.
- * Tidy is a free tool that fixes broken HTML.
- * See http://www.w3.org/People/Raggett/tidy/
+ * Configuration for HTML postprocessing tool. Set this to a configuration
+ * array to enable an external tool. Dave Raggett's "HTML Tidy" is typically
+ * used. See http://www.w3.org/People/Raggett/tidy/
*
- * - $wgTidyBin should be set to the path of the binary and
- * - $wgTidyConf to the path of the configuration file.
- * - $wgTidyOpts can include any number of parameters.
- * - $wgTidyInternal controls the use of the PECL extension or the
- * libtidy (PHP >= 5) extension to use an in-process tidy library instead
- * of spawning a separate program.
- * Normally you shouldn't need to override the setting except for
- * debugging. To install, use 'pear install tidy' and add a line
- * 'extension=tidy.so' to php.ini.
+ * If this is null and $wgUseTidy is true, the deprecated configuration
+ * parameters will be used instead.
+ *
+ * If this is null and $wgUseTidy is false, a pure PHP fallback will be used.
+ *
+ * Keys are:
+ * - driver: May be:
+ * - RaggettInternalHHVM: Use the limited-functionality HHVM extension
+ * - RaggettInternalPHP: Use the PECL extension
+ * - RaggettExternal: Shell out to an external binary (tidyBin)
+ *
+ * - tidyConfigFile: Path to configuration file for any of the Raggett drivers
+ * - debugComment: True to add a comment to the output with warning messages
+ * - tidyBin: For RaggettExternal, the path to the tidy binary.
+ * - tidyCommandLine: For RaggettExternal, additional command line options.
*/
-$wgUseTidy = false;
+$wgTidyConfig = null;
/**
- * @see $wgUseTidy
+ * Set this to true to use the deprecated tidy configuration parameters.
+ * @deprecated use $wgTidyConfig
*/
-$wgAlwaysUseTidy = false;
+$wgUseTidy = false;
/**
- * @see $wgUseTidy
+ * The path to the tidy binary.
+ * @deprecated Use $wgTidyConfig['tidyBin']
*/
$wgTidyBin = 'tidy';
/**
- * @see $wgUseTidy
+ * The path to the tidy config file
+ * @deprecated Use $wgTidyConfig['tidyConfigFile']
*/
-$wgTidyConf = $IP . '/includes/tidy.conf';
+$wgTidyConf = $IP . '/includes/tidy/tidy.conf';
/**
- * @see $wgUseTidy
+ * The command line options to the tidy binary
+ * @deprecated Use $wgTidyConfig['tidyCommandLine']
*/
$wgTidyOpts = '';
/**
- * @see $wgUseTidy
+ * Set this to true to use the tidy extension
+ * @deprecated Use $wgTidyConfig['driver']
*/
$wgTidyInternal = extension_loaded( 'tidy' );
@@ -4198,6 +4300,59 @@ $wgActiveUserDays = 30;
*/
/**
+ * Password policy for local wiki users. A user's effective policy
+ * is the superset of all policy statements from the policies for the
+ * groups where the user is a member. If more than one group policy
+ * include the same policy statement, the value is the max() of the
+ * values. Note true > false. The 'default' policy group is required,
+ * and serves as the minimum policy for all users. New statements can
+ * be added by appending to $wgPasswordPolicy['checks'].
+ * Statements:
+ * - MinimalPasswordLength - minimum length a user can set
+ * - MinimumPasswordLengthToLogin - passwords shorter than this will
+ * not be allowed to login, regardless if it is correct.
+ * - MaximalPasswordLength - maximum length password a user is allowed
+ * to attempt. Prevents DoS attacks with pbkdf2.
+ * - PasswordCannotMatchUsername - Password cannot match username to
+ * - PasswordCannotMatchBlacklist - Username/password combination cannot
+ * match a specific, hardcoded blacklist.
+ * @since 1.26
+ */
+$wgPasswordPolicy = array(
+ 'policies' => array(
+ 'bureaucrat' => array(
+ 'MinimalPasswordLength' => 8,
+ 'MinimumPasswordLengthToLogin' => 1,
+ 'PasswordCannotMatchUsername' => true,
+ ),
+ 'sysop' => array(
+ 'MinimalPasswordLength' => 8,
+ 'MinimumPasswordLengthToLogin' => 1,
+ 'PasswordCannotMatchUsername' => true,
+ ),
+ 'bot' => array(
+ 'MinimalPasswordLength' => 8,
+ 'MinimumPasswordLengthToLogin' => 1,
+ 'PasswordCannotMatchUsername' => true,
+ ),
+ 'default' => array(
+ 'MinimalPasswordLength' => 1,
+ 'PasswordCannotMatchUsername' => true,
+ 'PasswordCannotMatchBlacklist' => true,
+ 'MaximalPasswordLength' => 4096,
+ ),
+ ),
+ 'checks' => array(
+ 'MinimalPasswordLength' => 'PasswordPolicyChecks::checkMinimalPasswordLength',
+ 'MinimumPasswordLengthToLogin' => 'PasswordPolicyChecks::checkMinimumPasswordLengthToLogin',
+ 'PasswordCannotMatchUsername' => 'PasswordPolicyChecks::checkPasswordCannotMatchUsername',
+ 'PasswordCannotMatchBlacklist' => 'PasswordPolicyChecks::checkPasswordCannotMatchBlacklist',
+ 'MaximalPasswordLength' => 'PasswordPolicyChecks::checkMaximalPasswordLength',
+ ),
+);
+
+
+/**
* For compatibility with old installations set to false
* @deprecated since 1.24 will be removed in future
*/
@@ -4206,8 +4361,9 @@ $wgPasswordSalt = true;
/**
* Specifies the minimal length of a user password. If set to 0, empty pass-
* words are allowed.
+ * @deprecated since 1.26, use $wgPasswordPolicy's MinimalPasswordLength.
*/
-$wgMinimalPasswordLength = 1;
+$wgMinimalPasswordLength = false;
/**
* Specifies the maximal length of a user password (T64685).
@@ -4218,8 +4374,9 @@ $wgMinimalPasswordLength = 1;
*
* @warning Unlike other password settings, user with passwords greater than
* the maximum will not be able to log in.
+ * @deprecated since 1.26, use $wgPasswordPolicy's MaximalPasswordLength.
*/
-$wgMaximalPasswordLength = 4096;
+$wgMaximalPasswordLength = false;
/**
* Specifies if users should be sent to a password-reset form on login, if their
@@ -4295,7 +4452,7 @@ $wgPasswordConfig = array(
*/
$wgPasswordResetRoutes = array(
'username' => true,
- 'email' => false,
+ 'email' => true,
);
/**
@@ -4322,6 +4479,7 @@ $wgReservedUsernames = array(
'msg:double-redirect-fixer', // Automatic double redirect fix
'msg:usermessage-editor', // Default user for leaving user messages
'msg:proxyblocker', // For $wgProxyList and Special:Blockme (removed in 1.22)
+ 'msg:spambot_username', // Used by cleanupSpam.php
);
/**
@@ -4397,7 +4555,7 @@ $wgHiddenPrefs = array();
* This is used in a regular expression character class during
* registration (regex metacharacters like / are escaped).
*/
-$wgInvalidUsernameCharacters = '@';
+$wgInvalidUsernameCharacters = '@:';
/**
* Character used as a delimiter when testing for interwiki userrights
@@ -4413,7 +4571,7 @@ $wgUserrightsInterwikiDelimiter = '@';
/**
* This is to let user authenticate using https when they come from http.
* Based on an idea by George Herbert on wikitech-l:
- * http://lists.wikimedia.org/pipermail/wikitech-l/2010-October/050039.html
+ * https://lists.wikimedia.org/pipermail/wikitech-l/2010-October/050039.html
* @since 1.17
*/
$wgSecureLogin = false;
@@ -4433,7 +4591,7 @@ $wgAutoblockExpiry = 86400;
/**
* Set this to true to allow blocked users to edit their own user talk page.
*/
-$wgBlockAllowsUTEdit = false;
+$wgBlockAllowsUTEdit = true;
/**
* Allow sysops to ban users from accessing Emailuser
@@ -4940,7 +5098,7 @@ $wgAccountCreationThrottle = 0;
* There's no administrator override on-wiki, so be careful what you set. :)
* May be an array of regexes or a single string for backwards compatibility.
*
- * @see http://en.wikipedia.org/wiki/Regular_expression
+ * @see https://en.wikipedia.org/wiki/Regular_expression
*
* @note Each regex needs a beginning/end delimiter, eg: # or /
*/
@@ -5145,6 +5303,22 @@ $wgProxyList = array();
$wgCookieExpiration = 180 * 86400;
/**
+ * The identifiers of the login cookies that can have their lifetimes
+ * extended independently of all other login cookies.
+ *
+ * @var string[]
+ */
+$wgExtendedLoginCookies = array( 'UserID', 'Token' );
+
+/**
+ * Default login cookie lifetime, in seconds. Setting
+ * $wgExtendLoginCookieExpiration to null will use $wgCookieExpiration to
+ * calculate the cookie lifetime. As with $wgCookieExpiration, 0 will make
+ * login cookies session-only.
+ */
+$wgExtendedLoginCookieExpiration = null;
+
+/**
* Set to set an explicit domain on the login cookies eg, "justthis.domain.org"
* or ".any.subdomain.net"
*/
@@ -5282,6 +5456,36 @@ $wgDebugDumpSql = false;
$wgDebugDumpSqlLength = 500;
/**
+ * Performance expectations for DB usage
+ *
+ * @since 1.26
+ */
+$wgTrxProfilerLimits = array(
+ // Basic GET and POST requests
+ 'GET' => array(
+ 'masterConns' => 0,
+ 'writes' => 0,
+ 'readQueryTime' => 5
+ ),
+ 'POST' => array(
+ 'readQueryTime' => 5,
+ 'writeQueryTime' => 1,
+ 'maxAffected' => 500
+ ),
+ // Background job runner
+ 'JobRunner' => array(
+ 'readQueryTime' => 30,
+ 'writeQueryTime' => 5,
+ 'maxAffected' => 500
+ ),
+ // Command-line scripts
+ 'Maintenance' => array(
+ 'writeQueryTime' => 5,
+ 'maxAffected' => 1000
+ )
+);
+
+/**
* Map of string log group names to log destinations.
*
* If set, wfDebugLog() output for that group will go to that file instead
@@ -5447,7 +5651,7 @@ $wgProfilePerHost = null;
*
* The host should be running a daemon which can be obtained from MediaWiki
* Git at:
- * http://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile
+ * https://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile
*
* @deprecated set $wgProfiler['udphost'] instead
*/
@@ -5504,6 +5708,29 @@ $wgAggregateStatsID = false;
$wgStatsFormatString = "stats/%s - %s 1 1 1 1 %s\n";
/**
+ * Destination of statsd metrics.
+ *
+ * A host or host:port of a statsd server. Port defaults to 8125.
+ *
+ * If not set, statsd metrics will not be collected.
+ *
+ * @see wfLogProfilingData
+ * @since 1.25
+ */
+$wgStatsdServer = false;
+
+/**
+ * Prefix for metric names sent to wgStatsdServer.
+ *
+ * Defaults to "MediaWiki".
+ *
+ * @see RequestContext::getStats
+ * @see BufferingStatsdDataFactory
+ * @since 1.25
+ */
+$wgStatsdMetricPrefix = false;
+
+/**
* 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
* templates.
@@ -5836,6 +6063,21 @@ $wgGitRepositoryViewers = array(
$wgRCMaxAge = 90 * 24 * 3600;
/**
+ * Page watchers inactive for more than this many seconds are considered inactive.
+ * Used mainly by action=info. Default: 180 days = about six months.
+ * @since 1.26
+ */
+$wgWatchersMaxAge = 180 * 24 * 3600;
+
+/**
+ * If active watchers (per above) are this number or less, do not disclose it.
+ * Left to 1, prevents unprivileged users from knowing for sure that there are 0.
+ * Set to -1 if you want to always complement watchers count with this info.
+ * @since 1.26
+ */
+$wgUnwatchedPageSecret = 1;
+
+/**
* Filter $wgRCLinkDays by $wgRCMaxAge to avoid showing links for numbers
* higher than what will be stored. Note that this is disabled by default
* because we sometimes do have RC data which is beyond the limit for some
@@ -6453,6 +6695,7 @@ $wgJobClasses = array(
'ThumbnailRender' => 'ThumbnailRenderJob',
'recentChangesUpdate' => 'RecentChangesUpdateJob',
'refreshLinksPrioritized' => 'RefreshLinksJob', // for cascading protection
+ 'activityUpdateJob' => 'ActivityUpdateJob',
'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
'null' => 'NullJob'
);
@@ -6482,13 +6725,28 @@ $wgJobTypesExcludedFromDefaultQueue = array( 'AssembleUploadChunks', 'PublishSta
$wgJobBackoffThrottling = array();
/**
+ * 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.
+ * 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
+ * several DBs at once they may have a smaller chance of being atomic due to the
+ * possibility of connection loss while queueing up to commit. Affected jobs may
+ * also fail due to the commit lock acquisition timeout.
+ *
+ * @var float|bool
+ * @since 1.26
+ */
+$wgJobSerialCommitThreshold = false;
+
+/**
* Map of job types to configuration arrays.
* This determines which queue class and storage system is used for each job type.
* Job types that do not have explicit configuration will use the 'default' config.
* These settings should be global to all wikis.
*/
$wgJobTypeConf = array(
- 'default' => array( 'class' => 'JobQueueDB', 'order' => 'random' ),
+ 'default' => array( 'class' => 'JobQueueDB', 'order' => 'random', 'claimTTL' => 3600 ),
);
/**
@@ -6604,6 +6862,7 @@ $wgLogTypes = array(
'suppress',
'tag',
'managetags',
+ 'contentmodel',
);
/**
@@ -6679,15 +6938,15 @@ $wgLogNames = array(
$wgLogHeaders = array(
'' => 'alllogstext',
'block' => 'blocklogtext',
- 'protect' => 'protectlogtext',
- 'rights' => 'rightslogtext',
'delete' => 'dellogpagetext',
- 'upload' => 'uploadlogpagetext',
- 'move' => 'movelogpagetext',
'import' => 'importlogpagetext',
- 'patrol' => 'patrol-log-header',
'merge' => 'mergelogpagetext',
+ 'move' => 'movelogpagetext',
+ 'patrol' => 'patrol-log-header',
+ 'protect' => 'protectlogtext',
+ 'rights' => 'rightslogtext',
'suppress' => 'suppressionlogtext',
+ 'upload' => 'uploadlogpagetext',
);
/**
@@ -6697,10 +6956,9 @@ $wgLogHeaders = array(
* Extensions with custom log types may add to this array.
*/
$wgLogActions = array(
- 'protect/protect' => 'protectedarticle',
'protect/modify' => 'modifiedarticleprotection',
+ 'protect/protect' => 'protectedarticle',
'protect/unprotect' => 'unprotectedarticle',
- 'protect/move_prot' => 'movedarticleprotection',
);
/**
@@ -6710,34 +6968,36 @@ $wgLogActions = array(
* @see LogFormatter
*/
$wgLogActionsHandlers = array(
- 'move/move' => 'MoveLogFormatter',
- 'move/move_redir' => 'MoveLogFormatter',
+ 'block/block' => 'BlockLogFormatter',
+ 'block/reblock' => 'BlockLogFormatter',
+ 'block/unblock' => 'BlockLogFormatter',
+ 'contentmodel/change' => 'ContentModelLogFormatter',
'delete/delete' => 'DeleteLogFormatter',
+ 'delete/event' => 'DeleteLogFormatter',
'delete/restore' => 'DeleteLogFormatter',
'delete/revision' => 'DeleteLogFormatter',
- 'delete/event' => 'DeleteLogFormatter',
- 'suppress/revision' => 'DeleteLogFormatter',
- 'suppress/event' => 'DeleteLogFormatter',
- 'suppress/delete' => 'DeleteLogFormatter',
- 'patrol/patrol' => 'PatrolLogFormatter',
- 'rights/rights' => 'RightsLogFormatter',
- 'rights/autopromote' => 'RightsLogFormatter',
- 'upload/upload' => 'UploadLogFormatter',
- 'upload/overwrite' => 'UploadLogFormatter',
- 'upload/revert' => 'UploadLogFormatter',
- 'merge/merge' => 'MergeLogFormatter',
- 'tag/update' => 'TagLogFormatter',
- 'managetags/create' => 'LogFormatter',
- 'managetags/delete' => 'LogFormatter',
+ 'import/interwiki' => 'LogFormatter',
+ 'import/upload' => 'LogFormatter',
'managetags/activate' => 'LogFormatter',
+ 'managetags/create' => 'LogFormatter',
'managetags/deactivate' => 'LogFormatter',
- 'block/block' => 'BlockLogFormatter',
- 'block/unblock' => 'BlockLogFormatter',
- 'block/reblock' => 'BlockLogFormatter',
+ 'managetags/delete' => 'LogFormatter',
+ 'merge/merge' => 'MergeLogFormatter',
+ 'move/move' => 'MoveLogFormatter',
+ 'move/move_redir' => 'MoveLogFormatter',
+ 'patrol/patrol' => 'PatrolLogFormatter',
+ 'protect/move_prot' => 'ProtectLogFormatter',
+ 'rights/autopromote' => 'RightsLogFormatter',
+ 'rights/rights' => 'RightsLogFormatter',
'suppress/block' => 'BlockLogFormatter',
+ 'suppress/delete' => 'DeleteLogFormatter',
+ 'suppress/event' => 'DeleteLogFormatter',
'suppress/reblock' => 'BlockLogFormatter',
- 'import/upload' => 'LogFormatter',
- 'import/interwiki' => 'LogFormatter',
+ 'suppress/revision' => 'DeleteLogFormatter',
+ 'tag/update' => 'TagLogFormatter',
+ 'upload/overwrite' => 'UploadLogFormatter',
+ 'upload/revert' => 'UploadLogFormatter',
+ 'upload/upload' => 'UploadLogFormatter',
);
/**
@@ -6764,14 +7024,6 @@ $wgAllowSpecialInclusion = true;
$wgDisableQueryPageUpdate = false;
/**
- * List of special pages, followed by what subtitle they should go under
- * at Special:SpecialPages
- *
- * @deprecated since 1.21 Override SpecialPage::getGroupName instead
- */
-$wgSpecialPageGroups = array();
-
-/**
* On Special:Unusedimages, consider images "used", if they are put
* into a category. Default (false) is not to count those as used.
*/
@@ -7009,12 +7261,6 @@ $wgAPIPropModules = array();
$wgAPIListModules = array();
/**
- * This variable is ignored. To add your module to the API, please add it to $wgAPI*Modules
- * @deprecated since 1.21
- */
-$wgAPIGeneratorModules = array();
-
-/**
* Maximum amount of rows to scan in a DB query in the API
* The default value is generally fine
*/
@@ -7464,6 +7710,7 @@ $wgUseLinkNamespaceDBFields = true;
* $wgVirtualRestConfig['modules']['parsoid'] = array(
* 'url' => 'http://localhost:8000',
* 'prefix' => 'enwiki',
+ * 'domain' => 'en.wikipedia.org',
* );
*
* @var array
@@ -7474,12 +7721,22 @@ $wgVirtualRestConfig = array(
'global' => array(
# Timeout in seconds
'timeout' => 360,
+ # 'domain' is set to $wgCanonicalServer in Setup.php
'forwardCookies' => false,
'HTTPProxy' => null
)
);
/**
+ * Controls whether zero-result search queries with suggestions should display results for
+ * these suggestions.
+ *
+ * @var bool
+ * @since 1.26
+ */
+$wgSearchRunSuggestedQuery = true;
+
+/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
* @}
diff --git a/includes/Defines.php b/includes/Defines.php
index c9263da9..d55bbcf8 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -24,11 +24,6 @@
* @defgroup Constants MediaWiki constants
*/
-/**
- * Version constants for the benefit of extensions
- */
-define( 'MW_SPECIALPAGE_VERSION', 2 );
-
/**@{
* Database related constants
*/
@@ -203,7 +198,7 @@ define( 'LIST_OR', 4 );
/**
* Unicode and normalisation related
*/
-require_once __DIR__ . '/libs/normal/UtfNormalDefines.php';
+require_once __DIR__ . '/compat/normal/UtfNormalDefines.php';
/**@{
* Hook support constants
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 8d27eac8..05e0ac0e 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -168,6 +168,12 @@ class EditPage {
const AS_PARSE_ERROR = 240;
/**
+ * Status: when changing the content model is disallowed due to
+ * $wgContentHandlerUseDB being false
+ */
+ const AS_CANNOT_USE_CUSTOM_MODEL = 241;
+
+ /**
* HTML id and name for the beginning of the edit form.
*/
const EDITFORM_ID = 'editform';
@@ -380,13 +386,15 @@ class EditPage {
public $suppressIntro = false;
- /** @var bool Set to true to allow editing of non-text content types. */
- public $allowNonTextContent = false;
-
/** @var bool */
protected $edit;
/**
+ * @var bool Set in ApiEditPage, based on ContentHandler::allowsDirectApiEditing
+ */
+ private $enableApiEditOverride = false;
+
+ /**
* @param Article $article
*/
public function __construct( Article $article ) {
@@ -447,8 +455,18 @@ class EditPage {
* @throws MWException If $modelId has no known handler
*/
public function isSupportedContentModel( $modelId ) {
- return $this->allowNonTextContent ||
- ContentHandler::getForModelID( $modelId ) instanceof TextContentHandler;
+ return $this->enableApiEditOverride === true ||
+ ContentHandler::getForModelID( $modelId )->supportsDirectEditing();
+ }
+
+ /**
+ * Allow editing of content that supports API direct editing, but not general
+ * direct editing. Set to false by default.
+ *
+ * @param bool $enableOverride
+ */
+ public function setApiEditOverride( $enableOverride ) {
+ $this->enableApiEditOverride = $enableOverride;
}
function submit() {
@@ -509,7 +527,10 @@ class EditPage {
if ( $permErrors ) {
wfDebug( __METHOD__ . ": User can't edit\n" );
// Auto-block user's IP if the account was "hard" blocked
- $wgUser->spreadAnyEditBlock();
+ $user = $wgUser;
+ DeferredUpdates::addCallableUpdate( function() use ( $user ) {
+ $user->spreadAnyEditBlock();
+ } );
$this->displayPermissionsError( $permErrors );
@@ -634,6 +655,9 @@ class EditPage {
$this->getContextTitle()->getPrefixedText()
) );
$wgOut->addBacklinkSubtitle( $this->getContextTitle() );
+ $wgOut->addHTML( $this->editFormPageTop );
+ $wgOut->addHTML( $this->editFormTextTop );
+
$wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' ) );
$wgOut->addHTML( "<hr />\n" );
@@ -647,13 +671,16 @@ class EditPage {
$wgOut->addWikiMsg( 'viewsourcetext' );
}
+ $wgOut->addHTML( $this->editFormTextBeforeContent );
$this->showTextbox( $text, 'wpTextbox1', array( 'readonly' ) );
+ $wgOut->addHTML( $this->editFormTextAfterContent );
$wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'templatesUsed' ),
Linker::formatTemplates( $this->getTemplates() ) ) );
$wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
+ $wgOut->addHTML( $this->editFormTextBottom );
if ( $this->mTitle->exists() ) {
$wgOut->returnToMain( null, $this->mTitle );
}
@@ -1025,7 +1052,6 @@ class EditPage {
$undo = $wgRequest->getInt( 'undo' );
if ( $undo > 0 && $undoafter > 0 ) {
-
$undorev = Revision::newFromId( $undo );
$oldrev = Revision::newFromId( $undoafter );
@@ -1034,8 +1060,8 @@ class EditPage {
# Otherwise, $content will be left as-is.
if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
- !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
-
+ !$oldrev->isDeleted( Revision::DELETED_TEXT )
+ ) {
$content = $this->mArticle->getUndoContent( $undorev, $oldrev );
if ( $content === false ) {
@@ -1230,9 +1256,9 @@ class EditPage {
if ( !$converted ) {
//TODO: somehow show a warning to the user!
- wfDebug( "Attempt to preload incompatible content: "
- . "can't convert " . $content->getModel()
- . " to " . $handler->getModelID() );
+ wfDebug( "Attempt to preload incompatible content: " .
+ "can't convert " . $content->getModel() .
+ " to " . $handler->getModelID() );
return $handler->makeEmptyContent();
}
@@ -1350,6 +1376,7 @@ class EditPage {
case self::AS_HOOK_ERROR:
return false;
+ case self::AS_CANNOT_USE_CUSTOM_MODEL:
case self::AS_PARSE_ERROR:
$wgOut->addWikiText( '<div class="error">' . $status->getWikiText() . '</div>' );
return true;
@@ -1532,6 +1559,7 @@ class EditPage {
*/
function internalAttemptSave( &$result, $bot = false ) {
global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
+ global $wgContentHandlerUseDB;
$status = Status::newGood();
@@ -1652,11 +1680,19 @@ class EditPage {
}
}
- if ( $this->contentModel !== $this->mTitle->getContentModel()
- && !$wgUser->isAllowed( 'editcontentmodel' )
- ) {
- $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
- return $status;
+ $changingContentModel = false;
+ if ( $this->contentModel !== $this->mTitle->getContentModel() ) {
+ if ( !$wgContentHandlerUseDB ) {
+ $status->fatal( 'editpage-cannot-use-custom-model' );
+ $status->value = self::AS_CANNOT_USE_CUSTOM_MODEL;
+ return $status;
+ } elseif ( !$wgUser->isAllowed( 'editcontentmodel' ) ) {
+ $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
+ return $status;
+
+ }
+ $changingContentModel = true;
+ $oldContentModel = $this->mTitle->getContentModel();
}
if ( $this->changeTags ) {
@@ -1916,7 +1952,7 @@ class EditPage {
$this->summary,
$flags,
false,
- null,
+ $wgUser,
$content->getDefaultFormat()
);
@@ -1946,10 +1982,22 @@ class EditPage {
if ( $this->changeTags && isset( $doEditStatus->value['revision'] ) ) {
// If a revision was created, apply any change tags that were requested
- ChangeTags::addTags(
- $this->changeTags,
- isset( $doEditStatus->value['rc'] ) ? $doEditStatus->value['rc']->mAttribs['rc_id'] : null,
- $doEditStatus->value['revision']->getId()
+ $addTags = $this->changeTags;
+ $revId = $doEditStatus->value['revision']->getId();
+ // Defer this both for performance and so that addTags() sees the rc_id
+ // since the recentchange entry addition is deferred first (bug T100248)
+ DeferredUpdates::addCallableUpdate( function() use ( $addTags, $revId ) {
+ ChangeTags::addTags( $addTags, null, $revId );
+ } );
+ }
+
+ // If the content model changed, add a log entry
+ if ( $changingContentModel ) {
+ $this->addContentModelChangeLogEntry(
+ $wgUser,
+ $oldContentModel,
+ $this->contentModel,
+ $this->summary
);
}
@@ -1957,6 +2005,26 @@ class EditPage {
}
/**
+ * @param Title $title
+ * @param string $oldModel
+ * @param string $newModel
+ * @param string $reason
+ */
+ protected function addContentModelChangeLogEntry( User $user, $oldModel, $newModel, $reason ) {
+ $log = new ManualLogEntry( 'contentmodel', 'change' );
+ $log->setPerformer( $user );
+ $log->setTarget( $this->mTitle );
+ $log->setComment( $reason );
+ $log->setParameters( array(
+ '4::oldmodel' => $oldModel,
+ '5::newmodel' => $newModel
+ ) );
+ $logid = $log->insert();
+ $log->publish( $logid );
+ }
+
+
+ /**
* Register the change of watch status
*/
protected function updateWatchlist() {
@@ -2486,7 +2554,7 @@ class EditPage {
$wgOut->addHTML( $this->editFormTextBeforeContent );
if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
- $wgOut->addHTML( EditPage::getEditToolbar() );
+ $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
}
if ( $this->blankArticle ) {
@@ -3386,7 +3454,7 @@ HTML
$this->deletedSinceEdit = false;
- if ( $this->mTitle->isDeletedQuick() ) {
+ if ( !$this->mTitle->exists() && $this->mTitle->isDeletedQuick() ) {
$this->lastDelete = $this->getLastDelete();
if ( $this->lastDelete ) {
$deleteTime = wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
@@ -3450,6 +3518,8 @@ HTML
global $wgOut, $wgUser, $wgRawHtml, $wgLang;
global $wgAllowUserCss, $wgAllowUserJs;
+ $stats = $wgOut->getContext()->getStats();
+
if ( $wgRawHtml && !$this->mTokenOk ) {
// Could be an offsite preview attempt. This is very unsafe if
// HTML is enabled, as it could be an attack.
@@ -3461,6 +3531,7 @@ HTML
$parsedNote = $wgOut->parse( "<div class='previewnote'>" .
wfMessage( 'session_fail_preview_html' )->text() . "</div>", true, /* interface */true );
}
+ $stats->increment( 'edit.failures.session_loss' );
return $parsedNote;
}
@@ -3484,11 +3555,16 @@ HTML
if ( $this->mTriedSave && !$this->mTokenOk ) {
if ( $this->mTokenOkExceptSuffix ) {
$note = wfMessage( 'token_suffix_mismatch' )->plain();
+ $stats->increment( 'edit.failures.bad_token' );
} else {
$note = wfMessage( 'session_fail_preview' )->plain();
+ $stats->increment( 'edit.failures.session_loss' );
}
} elseif ( $this->incompleteForm ) {
$note = wfMessage( 'edit_form_incomplete' )->plain();
+ if ( $this->mTriedSave ) {
+ $stats->increment( 'edit.failures.incomplete_form' );
+ }
} else {
$note = wfMessage( 'previewnote' )->plain() . ' ' . $continueEditing;
}
@@ -3619,13 +3695,18 @@ HTML
* Shows a bulletin board style toolbar for common editing functions.
* It can be disabled in the user preferences.
*
+ * @param $title Title object for the page being edited (optional)
* @return string
*/
- static function getEditToolbar() {
+ static function getEditToolbar( $title = null ) {
global $wgContLang, $wgOut;
global $wgEnableUploads, $wgForeignFileRepos;
$imagesAvailable = $wgEnableUploads || count( $wgForeignFileRepos );
+ $showSignature = true;
+ if ( $title ) {
+ $showSignature = MWNamespace::wantSignatures( $title->getNamespace() );
+ }
/**
* $toolarray is an array of arrays each of which includes the
@@ -3693,13 +3774,13 @@ HTML
'sample' => wfMessage( 'nowiki_sample' )->text(),
'tip' => wfMessage( 'nowiki_tip' )->text(),
),
- array(
+ $showSignature ? array(
'id' => 'mw-editbutton-signature',
'open' => '--~~~~',
'close' => '',
'sample' => '',
'tip' => wfMessage( 'sig_tip' )->text(),
- ),
+ ) : false,
array(
'id' => 'mw-editbutton-hr',
'open' => "\n----\n",
@@ -3737,7 +3818,7 @@ HTML
}
$script .= '});';
- $wgOut->addScript( Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( $script ) ) );
+ $wgOut->addScript( ResourceLoader::makeInlineScript( $script ) );
$toolbar = '<div id="toolbar"></div>';
diff --git a/includes/Export.php b/includes/Export.php
index 4600feb5..adab21c3 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -874,7 +874,7 @@ class XmlDumpWriter {
}
global $wgContLang;
- $prefix = str_replace( '_', ' ', $wgContLang->getNsText( $title->getNamespace() ) );
+ $prefix = $wgContLang->getFormattedNsText( $title->getNamespace() );
if ( $prefix !== '' ) {
$prefix .= ':';
@@ -1191,7 +1191,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
* @return string
*/
function setup7zCommand( $file ) {
- $command = "7za a -bd -si " . wfEscapeShellArg( $file );
+ $command = "7za a -bd -si -mx=4 " . wfEscapeShellArg( $file );
// Suppress annoying useless crap from p7zip
// Unfortunately this could suppress real error messages too
$command .= ' >' . wfGetNull() . ' 2>&1';
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index c1d14db0..bcd6db20 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -296,8 +296,8 @@ class FileDeleteForm {
Xml::closeElement( 'form' );
if ( $wgUser->isAllowed( 'editinterface' ) ) {
- $title = Title::makeTitle( NS_MEDIAWIKI, 'Filedelete-reason-dropdown' );
- $link = Linker::link(
+ $title = wfMessage( 'filedelete-reason-dropdown' )->inContentLanguage()->getTitle();
+ $link = Linker::linkKnown(
$title,
wfMessage( 'filedelete-edit-reasonlist' )->escaped(),
array(),
diff --git a/includes/GitInfo.php b/includes/GitInfo.php
index fb298cfe..7f05bb0f 100644
--- a/includes/GitInfo.php
+++ b/includes/GitInfo.php
@@ -281,9 +281,9 @@ class GitInfo {
$config = "{$this->basedir}/config";
$url = false;
if ( is_readable( $config ) ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$configArray = parse_ini_file( $config, true );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
$remote = false;
// Use the "origin" remote repo if available or any other repo if not.
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index ab3f019f..64aa87ec 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -24,7 +24,6 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point" );
}
-use Liuggio\StatsdClient\StatsdClient;
use Liuggio\StatsdClient\Sender\SocketSender;
use MediaWiki\Logger\LoggerFactory;
@@ -172,6 +171,7 @@ if ( !function_exists( 'hash_equals' ) ) {
*
* @param string $ext Name of the extension to load
* @param string|null $path Absolute path of where to find the extension.json file
+ * @since 1.25
*/
function wfLoadExtension( $ext, $path = null ) {
if ( !$path ) {
@@ -192,6 +192,7 @@ function wfLoadExtension( $ext, $path = null ) {
*
* @see wfLoadExtension
* @param string[] $exts Array of extension names to load
+ * @since 1.25
*/
function wfLoadExtensions( array $exts ) {
global $wgExtensionDirectory;
@@ -207,6 +208,7 @@ function wfLoadExtensions( array $exts ) {
* @see wfLoadExtension
* @param string $skin Name of the extension to load
* @param string|null $path Absolute path of where to find the skin.json file
+ * @since 1.25
*/
function wfLoadSkin( $skin, $path = null ) {
if ( !$path ) {
@@ -221,6 +223,7 @@ function wfLoadSkin( $skin, $path = null ) {
*
* @see wfLoadExtensions
* @param string[] $skins Array of extension names to load
+ * @since 1.25
*/
function wfLoadSkins( array $skins ) {
global $wgStyleDirectory;
@@ -402,12 +405,17 @@ function wfRandomString( $length = 32 ) {
*
* ;:@&=$-_.+!*'(),
*
+ * RFC 1738 says ~ is unsafe, however RFC 3986 considers it an unreserved
+ * character which should not be encoded. More importantly, google chrome
+ * always converts %7E back to ~, and converting it in this function can
+ * cause a redirect loop (T105265).
+ *
* But + is not safe because it's used to indicate a space; &= are only safe in
* paths and not in queries (and we don't distinguish here); ' seems kind of
* scary; and urlencode() doesn't touch -_. to begin with. Plus, although /
* is reserved, we don't care. So the list we unescape is:
*
- * ;:@$!*(),/
+ * ;:@$!*(),/~
*
* However, IIS7 redirects fail when the url contains a colon (Bug 22709),
* so no fancy : for IIS7.
@@ -426,7 +434,7 @@ function wfUrlencode( $s ) {
}
if ( is_null( $needle ) ) {
- $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
+ $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F', '%7E' );
if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
) {
@@ -437,7 +445,7 @@ function wfUrlencode( $s ) {
$s = urlencode( $s );
$s = str_ireplace(
$needle,
- array( ';', '@', '$', '!', '*', '(', ')', ',', '/', ':' ),
+ array( ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ),
$s
);
@@ -860,9 +868,9 @@ function wfParseUrl( $url ) {
if ( $wasRelative ) {
$url = "http:$url";
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$bits = parse_url( $url );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
// parse_url() returns an array without scheme for some invalid URLs, e.g.
// parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
if ( !$bits || !isset( $bits['scheme'] ) ) {
@@ -1248,13 +1256,17 @@ function wfLogProfilingData() {
$profiler->logData();
$config = $context->getConfig();
- if ( $config->has( 'StatsdServer' ) ) {
- $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
- $statsdHost = $statsdServer[0];
- $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
- $statsdSender = new SocketSender( $statsdHost, $statsdPort );
- $statsdClient = new StatsdClient( $statsdSender );
- $statsdClient->send( $context->getStats()->getBuffer() );
+ if ( $config->get( 'StatsdServer' ) ) {
+ try {
+ $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
+ $statsdHost = $statsdServer[0];
+ $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
+ $statsdSender = new SocketSender( $statsdHost, $statsdPort );
+ $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
+ $statsdClient->send( $context->getStats()->getBuffer() );
+ } catch ( Exception $ex ) {
+ MWExceptionHandler::logException( $ex );
+ }
}
# Profiling must actually be enabled...
@@ -1344,6 +1356,17 @@ function wfReadOnlyReason() {
} else {
$wgReadOnly = false;
}
+ // Callers use this method to be aware that data presented to a user
+ // may be very stale and thus allowing submissions can be problematic.
+ try {
+ if ( $wgReadOnly === false && wfGetLB()->getLaggedSlaveMode() ) {
+ $wgReadOnly = 'The database has been automatically locked ' .
+ 'while the slave database servers catch up to the master';
+ }
+ } catch ( DBConnectionError $e ) {
+ $wgReadOnly = 'The database has been automatically locked ' .
+ 'until the slave database servers become available';
+ }
}
return $wgReadOnly;
@@ -1405,7 +1428,7 @@ function wfGetLangObj( $langcode = false ) {
*
* This function replaces all old wfMsg* functions.
*
- * @param string|string[] $key Message key, or array of keys
+ * @param string|string[]|MessageSpecifier $key Message key, or array of keys, or a MessageSpecifier
* @param mixed $params,... Normal message parameters
* @return Message
*
@@ -1745,7 +1768,7 @@ function wfMsgExt( $key, $options ) {
}
if ( in_array( 'escape', $options, true ) ) {
- $string = htmlspecialchars ( $string );
+ $string = htmlspecialchars( $string );
} elseif ( in_array( 'escapenoentities', $options, true ) ) {
$string = Sanitizer::escapeHtmlAllowEntities( $string );
}
@@ -2118,15 +2141,14 @@ function wfVarDump( $var ) {
*/
function wfHttpError( $code, $label, $desc ) {
global $wgOut;
- header( "HTTP/1.0 $code $label" );
- header( "Status: $code $label" );
+ HttpStatus::header( $code );
if ( $wgOut ) {
$wgOut->disable();
$wgOut->sendCacheControl();
}
header( 'Content-type: text/html; charset=utf-8' );
- print "<!doctype html>" .
+ print '<!DOCTYPE html>' .
'<html><head><title>' .
htmlspecialchars( $label ) .
'</title></head><body><h1>' .
@@ -2161,14 +2183,24 @@ function wfResetOutputBuffers( $resetGzipEncoding = true ) {
$wgDisableOutputCompression = true;
}
while ( $status = ob_get_status() ) {
- if ( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
- // Probably from zlib.output_compression or other
- // PHP-internal setting which can't be removed.
- //
+ if ( isset( $status['flags'] ) ) {
+ $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
+ $deleteable = ( $status['flags'] & $flags ) === $flags;
+ } elseif ( isset( $status['del'] ) ) {
+ $deleteable = $status['del'];
+ } else {
+ // Guess that any PHP-internal setting can't be removed.
+ $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
+ }
+ if ( !$deleteable ) {
// Give up, and hope the result doesn't break
// output behavior.
break;
}
+ if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
+ // Unit testing barrier to prevent this function from breaking PHPUnit.
+ break;
+ }
if ( !ob_end_clean() ) {
// Could not remove output buffer handler; abort now
// to avoid getting in some kind of infinite loop.
@@ -2312,40 +2344,19 @@ function wfNegotiateType( $cprefs, $sprefs ) {
/**
* Reference-counted warning suppression
*
+ * @deprecated since 1.26, use MediaWiki\suppressWarnings() directly
* @param bool $end
*/
function wfSuppressWarnings( $end = false ) {
- static $suppressCount = 0;
- static $originalLevel = false;
-
- if ( $end ) {
- if ( $suppressCount ) {
- --$suppressCount;
- if ( !$suppressCount ) {
- error_reporting( $originalLevel );
- }
- }
- } else {
- if ( !$suppressCount ) {
- $originalLevel = error_reporting( E_ALL & ~(
- E_WARNING |
- E_NOTICE |
- E_USER_WARNING |
- E_USER_NOTICE |
- E_DEPRECATED |
- E_USER_DEPRECATED |
- E_STRICT
- ) );
- }
- ++$suppressCount;
- }
+ MediaWiki\suppressWarnings( $end );
}
/**
+ * @deprecated since 1.26, use MediaWiki\restoreWarnings() directly
* Restore error level to previous value
*/
function wfRestoreWarnings() {
- wfSuppressWarnings( true );
+ MediaWiki\suppressWarnings( true );
}
# Autodetect, convert and provide timestamps of various types
@@ -2453,7 +2464,7 @@ function wfTimestampNow() {
function wfIsWindows() {
static $isWindows = null;
if ( $isWindows === null ) {
- $isWindows = substr( php_uname(), 0, 7 ) == 'Windows';
+ $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
}
return $isWindows;
}
@@ -2515,7 +2526,7 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
wfDebug( "$caller: called wfMkdirParents($dir)\n" );
}
- if ( strval( $dir ) === '' || ( file_exists( $dir ) && is_dir( $dir ) ) ) {
+ if ( strval( $dir ) === '' || is_dir( $dir ) ) {
return true;
}
@@ -2526,9 +2537,9 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
}
// Turn off the normal warning, we're doing our own below
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ok = mkdir( $dir, $mode, true ); // PHP5 <3
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$ok ) {
//directory may have been created on another request since we last checked
@@ -2769,7 +2780,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
$useLogPipe = false;
if ( is_executable( '/bin/bash' ) ) {
- $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
+ $time = intval( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
if ( isset( $limits['walltime'] ) ) {
$wallTime = intval( $limits['walltime'] );
} elseif ( isset( $limits['time'] ) ) {
@@ -2777,8 +2788,8 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
} else {
$wallTime = intval( $wgMaxShellWallClockTime );
}
- $mem = intval ( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
- $filesize = intval ( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
+ $mem = intval( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
+ $filesize = intval( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
$cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
@@ -3023,9 +3034,9 @@ function wfMerge( $old, $mine, $yours, &$result ) {
# This check may also protect against code injection in
# case of broken installations.
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$haveDiff3 ) {
wfDebug( "diff3 not found\n" );
@@ -3102,9 +3113,9 @@ function wfDiff( $before, $after, $params = '-u' ) {
}
global $wgDiff;
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$haveDiff = $wgDiff && file_exists( $wgDiff );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
# This check may also protect against code injection in
# case of broken installations.
@@ -3205,6 +3216,7 @@ function wfUsePHP( $req_ver ) {
*
* @see perldoc -f use
*
+ * @deprecated since 1.26, use the "requires' property of extension.json
* @param string|int|float $req_ver The version to check, can be a string, an integer, or a float
* @throws MWException
*/
@@ -3455,7 +3467,6 @@ function wfResetSessionID() {
$_SESSION = $tmp;
}
$newSessionId = session_id();
- Hooks::run( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
}
/**
@@ -3464,15 +3475,17 @@ function wfResetSessionID() {
* @param bool $sessionId
*/
function wfSetupSession( $sessionId = false ) {
- global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain,
- $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler;
- if ( $wgSessionsInObjectCache || $wgSessionsInMemcached ) {
+ global $wgSessionsInObjectCache, $wgSessionHandler;
+ global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly;
+
+ if ( $wgSessionsInObjectCache ) {
ObjectCacheSessionHandler::install();
} elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
# Only set this if $wgSessionHandler isn't null and session.save_handler
# hasn't already been set to the desired value (that causes errors)
ini_set( 'session.save_handler', $wgSessionHandler );
}
+
session_set_cookie_params(
0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly );
session_cache_limiter( 'private, must-revalidate' );
@@ -3481,9 +3494,14 @@ function wfSetupSession( $sessionId = false ) {
} else {
wfFixSessionID();
}
- wfSuppressWarnings();
+
+ MediaWiki\suppressWarnings();
session_start();
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
+
+ if ( $wgSessionsInObjectCache ) {
+ ObjectCacheSessionHandler::renewCurrentSession();
+ }
}
/**
@@ -3506,7 +3524,7 @@ function wfGetPrecompiledData( $name ) {
}
/**
- * Get a cache key
+ * Make a cache key for the local wiki.
*
* @param string $args,...
* @return string
@@ -3516,12 +3534,13 @@ function wfMemcKey( /*...*/ ) {
$prefix = $wgCachePrefix === false ? wfWikiID() : $wgCachePrefix;
$args = func_get_args();
$key = $prefix . ':' . implode( ':', $args );
- $key = str_replace( ' ', '_', $key );
- return $key;
+ return strtr( $key, ' ', '_' );
}
/**
- * Get a cache key for a foreign DB
+ * Make a cache key for a foreign DB.
+ *
+ * Must match what wfMemcKey() would produce in context of the foreign wiki.
*
* @param string $db
* @param string $prefix
@@ -3531,11 +3550,29 @@ function wfMemcKey( /*...*/ ) {
function wfForeignMemcKey( $db, $prefix /*...*/ ) {
$args = array_slice( func_get_args(), 2 );
if ( $prefix ) {
+ // Match wfWikiID() logic
$key = "$db-$prefix:" . implode( ':', $args );
} else {
$key = $db . ':' . implode( ':', $args );
}
- return str_replace( ' ', '_', $key );
+ return strtr( $key, ' ', '_' );
+}
+
+/**
+ * Make a cache key with database-agnostic prefix.
+ *
+ * Doesn't have a wiki-specific namespace. Uses a generic 'global' prefix
+ * instead. Must have a prefix as otherwise keys that use a database name
+ * in the first segment will clash with wfMemcKey/wfForeignMemcKey.
+ *
+ * @since 1.26
+ * @param string $args,...
+ * @return string
+ */
+function wfGlobalCacheKey( /*...*/ ) {
+ $args = func_get_args();
+ $key = 'global:' . implode( ':', $args );
+ return strtr( $key, ' ', '_' );
}
/**
@@ -3744,6 +3781,7 @@ function wfWaitForSlaves(
}
// Figure out which clusters need to be checked
+ /** @var LoadBalancer[] $lbs */
$lbs = array();
if ( $cluster === '*' ) {
wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
@@ -3760,20 +3798,14 @@ function wfWaitForSlaves(
// time needed to wait on the next clusters.
$masterPositions = array_fill( 0, count( $lbs ), false );
foreach ( $lbs as $i => $lb ) {
- // bug 27975 - Don't try to wait for slaves if there are none
- // Prevents permission error when getting master position
- if ( $lb->getServerCount() > 1 ) {
- if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
- continue; // assume no writes done
- }
- // Use the empty string to not trigger selectDB() since the connection
- // may have been to a server that does not have a DB for the current wiki.
- $dbw = $lb->getConnection( DB_MASTER, array(), '' );
- if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
- continue; // no writes since the last wait
- }
- $masterPositions[$i] = $dbw->getMasterPos();
+ 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 ( $ifWritesSince && $lb->lastMasterChangeTimestamp() < $ifWritesSince ) {
+ continue; // no writes since the last wait
}
+ $masterPositions[$i] = $lb->getMasterPos();
}
$ok = true;
@@ -3830,9 +3862,9 @@ function wfStripIllegalFilenameChars( $name ) {
}
/**
- * Set PHP's memory limit to the larger of php.ini or $wgMemoryLimit;
+ * Set PHP's memory limit to the larger of php.ini or $wgMemoryLimit
*
- * @return int Value the memory limit was set to.
+ * @return int Resulting value of the memory limit.
*/
function wfMemoryLimit() {
global $wgMemoryLimit;
@@ -3841,15 +3873,15 @@ function wfMemoryLimit() {
$conflimit = wfShorthandToInteger( $wgMemoryLimit );
if ( $conflimit == -1 ) {
wfDebug( "Removing PHP's memory limit\n" );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
ini_set( 'memory_limit', $conflimit );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $conflimit;
} elseif ( $conflimit > $memlimit ) {
wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
ini_set( 'memory_limit', $conflimit );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $conflimit;
}
}
@@ -3857,6 +3889,26 @@ function wfMemoryLimit() {
}
/**
+ * Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit
+ *
+ * @return int Prior time limit
+ * @since 1.26
+ */
+function wfTransactionalTimeLimit() {
+ global $wgTransactionalTimeLimit;
+
+ $timeLimit = ini_get( 'max_execution_time' );
+ // Note that CLI scripts use 0
+ if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
+ set_time_limit( $wgTransactionalTimeLimit );
+ }
+
+ ignore_user_abort( true ); // ignore client disconnects
+
+ return $timeLimit;
+}
+
+/**
* Converts shorthand byte notation to integer form
*
* @param string $string
@@ -3917,13 +3969,13 @@ function wfBCP47( $code ) {
}
/**
- * Get a cache object.
+ * Get a specific cache object.
*
- * @param int $inputType Cache type, one of the CACHE_* constants.
+ * @param int|string $cacheType A CACHE_* constants, or other key in $wgObjectCaches
* @return BagOStuff
*/
-function wfGetCache( $inputType ) {
- return ObjectCache::getInstance( $inputType );
+function wfGetCache( $cacheType ) {
+ return ObjectCache::getInstance( $cacheType );
}
/**
@@ -3995,9 +4047,9 @@ function wfUnpack( $format, $data, $length = false ) {
}
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$result = unpack( $format, $data );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $result === false ) {
// If it cannot extract the packed data.
@@ -4236,3 +4288,28 @@ function wfThumbIsStandard( File $file, array $params ) {
return true;
}
+
+/**
+ * Merges two (possibly) 2 dimensional arrays into the target array ($baseArray).
+ *
+ * Values that exist in both values will be combined with += (all values of the array
+ * of $newValues will be added to the values of the array of $baseArray, while values,
+ * that exists in both, the value of $baseArray will be used).
+ *
+ * @param array $baseArray The array where you want to add the values of $newValues to
+ * @param array $newValues An array with new values
+ * @return array The combined array
+ * @since 1.26
+ */
+function wfArrayPlus2d( array $baseArray, array $newValues ) {
+ // First merge items that are in both arrays
+ foreach ( $baseArray as $name => &$groupVal ) {
+ if ( isset( $newValues[$name] ) ) {
+ $groupVal += $newValues[$name];
+ }
+ }
+ // Now add items that didn't exist yet
+ $baseArray += $newValues;
+
+ return $baseArray;
+}
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 69f1120d..494cbfaf 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -522,9 +522,9 @@ class DiffHistoryBlob implements HistoryBlob {
function diff( $t1, $t2 ) {
# Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
# "String is not zero-terminated"
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$diff = xdiff_string_rabdiff( $t1, $t2 ) . '';
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $diff;
}
@@ -535,9 +535,9 @@ class DiffHistoryBlob implements HistoryBlob {
*/
function patch( $base, $diff ) {
if ( function_exists( 'xdiff_string_bpatch' ) ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$text = xdiff_string_bpatch( $base, $diff ) . '';
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $text;
}
diff --git a/includes/Hooks.php b/includes/Hooks.php
index dffc7bcf..a4145624 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -135,9 +135,6 @@ class Hooks {
* returning null) is equivalent to returning true.
*/
public static function run( $event, array $args = array(), $deprecatedVersion = null ) {
- $profiler = Profiler::instance();
- $eventPS = $profiler->scopedProfileIn( 'hook: ' . $event );
-
foreach ( self::getHandlers( $event ) as $hook ) {
// Turn non-array values into an array. (Can't use casting because of objects.)
if ( !is_array( $hook ) ) {
@@ -196,8 +193,6 @@ class Hooks {
$badhookmsg = null;
$hook_args = array_merge( $hook, $args );
- // Profile first in case the Profiler causes errors
- $funcPS = $profiler->scopedProfileIn( $func );
set_error_handler( 'Hooks::hookErrorHandler' );
// mark hook as deprecated, if deprecation version is specified
@@ -215,7 +210,6 @@ class Hooks {
}
restore_error_handler();
- $profiler->scopedProfileOut( $funcPS );
// Process the return value.
if ( is_string( $retval ) ) {
@@ -237,22 +231,25 @@ class Hooks {
}
/**
- * Handle PHP errors issued inside a hook. Catch errors that have to do with
- * a function expecting a reference, and let all others pass through.
- *
- * This REALLY should be protected... but it's public for compatibility
+ * Handle PHP errors issued inside a hook. Catch errors that have to do
+ * with a function expecting a reference, and pass all others through to
+ * MWExceptionHandler::handleError() for default processing.
*
* @since 1.18
*
* @param int $errno Error number (unused)
* @param string $errstr Error message
* @throws MWHookException If the error has to do with the function signature
- * @return bool Always returns false
+ * @return bool
*/
public static function hookErrorHandler( $errno, $errstr ) {
if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) {
throw new MWHookException( $errstr, $errno );
}
- return false;
+
+ // Delegate unhandled errors to the default MW handler
+ return call_user_func_array(
+ 'MWExceptionHandler::handleError', func_get_args()
+ );
}
}
diff --git a/includes/Html.php b/includes/Html.php
index d312e0a6..62ae0b85 100644
--- a/includes/Html.php
+++ b/includes/Html.php
@@ -104,27 +104,26 @@ class Html {
/**
* Modifies a set of attributes meant for button elements
* and apply a set of default attributes when $wgUseMediaWikiUIEverywhere enabled.
- * @param array $attrs
- * @param string[] $modifiers to add to the button
+ * @param array $attrs HTML attributes in an associative array
+ * @param string[] $modifiers classes to add to the button
* @see https://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
* @return array $attrs A modified attribute array
*/
- public static function buttonAttributes( $attrs, $modifiers = array() ) {
+ public static function buttonAttributes( array $attrs, array $modifiers = array() ) {
global $wgUseMediaWikiUIEverywhere;
if ( $wgUseMediaWikiUIEverywhere ) {
if ( isset( $attrs['class'] ) ) {
if ( is_array( $attrs['class'] ) ) {
$attrs['class'][] = 'mw-ui-button';
- $attrs = array_merge( $attrs, $modifiers );
+ $attrs['class'] = array_merge( $attrs['class'], $modifiers );
// ensure compatibility with Xml
$attrs['class'] = implode( ' ', $attrs['class'] );
} else {
$attrs['class'] .= ' mw-ui-button ' . implode( ' ', $modifiers );
}
} else {
- $attrs['class'] = array( 'mw-ui-button' );
// ensure compatibility with Xml
- $attrs['class'] = implode( ' ', array_merge( $attrs['class'], $modifiers ) );
+ $attrs['class'] = 'mw-ui-button ' . implode( ' ', $modifiers );
}
}
return $attrs;
@@ -137,11 +136,8 @@ class Html {
* @param array $attrs An attribute array.
* @return array $attrs A modified attribute array
*/
- public static function getTextInputAttributes( $attrs ) {
+ public static function getTextInputAttributes( array $attrs ) {
global $wgUseMediaWikiUIEverywhere;
- if ( !$attrs ) {
- $attrs = array();
- }
if ( $wgUseMediaWikiUIEverywhere ) {
if ( isset( $attrs['class'] ) ) {
if ( is_array( $attrs['class'] ) ) {
@@ -165,11 +161,11 @@ class Html {
* @param array $attrs Associative array of attributes, e.g., array(
* 'href' => 'http://www.mediawiki.org/' ). See expandAttributes() for
* further documentation.
- * @param string[] $modifiers to add to the button
+ * @param string[] $modifiers classes to add to the button
* @see http://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
* @return string Raw HTML
*/
- public static function linkButton( $contents, $attrs, $modifiers = array() ) {
+ public static function linkButton( $contents, array $attrs, array $modifiers = array() ) {
return self::element( 'a',
self::buttonAttributes( $attrs, $modifiers ),
$contents
@@ -185,11 +181,11 @@ class Html {
* @param array $attrs Associative array of attributes, e.g., array(
* 'href' => 'http://www.mediawiki.org/' ). See expandAttributes() for
* further documentation.
- * @param string[] $modifiers to add to the button
+ * @param string[] $modifiers classes to add to the button
* @see http://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
* @return string Raw HTML
*/
- public static function submitButton( $contents, $attrs, $modifiers = array() ) {
+ public static function submitButton( $contents, array $attrs, array $modifiers = array() ) {
$attrs['type'] = 'submit';
$attrs['value'] = $contents;
return self::element( 'input', self::buttonAttributes( $attrs, $modifiers ) );
@@ -337,8 +333,7 @@ class Html {
* further documentation.
* @return array An array of attributes functionally identical to $attribs
*/
- private static function dropDefaults( $element, $attribs ) {
-
+ private static function dropDefaults( $element, array $attribs ) {
// Whenever altering this array, please provide a covering test case
// in HtmlTest::provideElementsWithAttributesHavingDefaultValues
static $attribDefaults = array(
@@ -485,11 +480,10 @@ class Html {
* @return string HTML fragment that goes between element name and '>'
* (starting with a space if at least one attribute is output)
*/
- public static function expandAttributes( $attribs ) {
+ public static function expandAttributes( array $attribs ) {
global $wgWellFormedXml;
$ret = '';
- $attribs = (array)$attribs;
foreach ( $attribs as $key => $value ) {
// Support intuitive array( 'checked' => true/false ) form
if ( $value === false || is_null( $value ) ) {
@@ -714,13 +708,16 @@ class Html {
* attributes, passed to Html::element()
* @return string Raw HTML
*/
- public static function input( $name, $value = '', $type = 'text', $attribs = array() ) {
+ public static function input( $name, $value = '', $type = 'text', array $attribs = array() ) {
$attribs['type'] = $type;
$attribs['value'] = $value;
$attribs['name'] = $name;
if ( in_array( $type, array( 'text', 'search', 'email', 'password', 'number' ) ) ) {
$attribs = self::getTextInputAttributes( $attribs );
}
+ if ( in_array( $type, array( 'button', 'reset', 'submit' ) ) ) {
+ $attribs = self::buttonAttributes( $attribs );
+ }
return self::element( 'input', $attribs );
}
@@ -794,7 +791,7 @@ class Html {
* attributes, passed to Html::element()
* @return string Raw HTML
*/
- public static function hidden( $name, $value, $attribs = array() ) {
+ public static function hidden( $name, $value, array $attribs = array() ) {
return self::input( $name, $value, 'hidden', $attribs );
}
@@ -810,7 +807,7 @@ class Html {
* attributes, passed to Html::element()
* @return string Raw HTML
*/
- public static function textarea( $name, $value = '', $attribs = array() ) {
+ public static function textarea( $name, $value = '', array $attribs = array() ) {
$attribs['name'] = $name;
if ( substr( $value, 0, 1 ) == "\n" ) {
@@ -826,6 +823,47 @@ class Html {
}
/**
+ * Helper for Html::namespaceSelector().
+ * @param array $params See Html::namespaceSelector()
+ * @return array
+ */
+ public static function namespaceSelectorOptions( array $params = array() ) {
+ global $wgContLang;
+
+ $options = array();
+
+ if ( !isset( $params['exclude'] ) || !is_array( $params['exclude'] ) ) {
+ $params['exclude'] = array();
+ }
+
+ if ( isset( $params['all'] ) ) {
+ // add an option that would let the user select all namespaces.
+ // Value is provided by user, the name shown is localized for the user.
+ $options[$params['all']] = wfMessage( 'namespacesall' )->text();
+ }
+ // Add all namespaces as options (in the content language)
+ $options += $wgContLang->getFormattedNamespaces();
+
+ $optionsOut = array();
+ // Filter out namespaces below 0 and massage labels
+ foreach ( $options as $nsId => $nsName ) {
+ if ( $nsId < NS_MAIN || in_array( $nsId, $params['exclude'] ) ) {
+ continue;
+ }
+ if ( $nsId === NS_MAIN ) {
+ // For other namespaces use the namespace prefix as label, but for
+ // main we don't use "" but the user message describing it (e.g. "(Main)" or "(Article)")
+ $nsName = wfMessage( 'blanknamespace' )->text();
+ } elseif ( is_int( $nsId ) ) {
+ $nsName = $wgContLang->convertNamespace( $nsId );
+ }
+ $optionsOut[ $nsId ] = $nsName;
+ }
+
+ return $optionsOut;
+ }
+
+ /**
* Build a drop-down box for selecting a namespace
*
* @param array $params Params to set.
@@ -844,8 +882,6 @@ class Html {
public static function namespaceSelector( array $params = array(),
array $selectAttribs = array()
) {
- global $wgContLang;
-
ksort( $selectAttribs );
// Is a namespace selected?
@@ -862,37 +898,16 @@ class Html {
$params['selected'] = '';
}
- if ( !isset( $params['exclude'] ) || !is_array( $params['exclude'] ) ) {
- $params['exclude'] = array();
- }
if ( !isset( $params['disable'] ) || !is_array( $params['disable'] ) ) {
$params['disable'] = array();
}
// Associative array between option-values and option-labels
- $options = array();
-
- if ( isset( $params['all'] ) ) {
- // add an option that would let the user select all namespaces.
- // Value is provided by user, the name shown is localized for the user.
- $options[$params['all']] = wfMessage( 'namespacesall' )->text();
- }
- // Add all namespaces as options (in the content language)
- $options += $wgContLang->getFormattedNamespaces();
+ $options = self::namespaceSelectorOptions( $params );
- // Convert $options to HTML and filter out namespaces below 0
+ // Convert $options to HTML
$optionsHtml = array();
foreach ( $options as $nsId => $nsName ) {
- if ( $nsId < NS_MAIN || in_array( $nsId, $params['exclude'] ) ) {
- continue;
- }
- if ( $nsId === NS_MAIN ) {
- // For other namespaces use the namespace prefix as label, but for
- // main we don't use "" but the user message describing it (e.g. "(Main)" or "(Article)")
- $nsName = wfMessage( 'blanknamespace' )->text();
- } elseif ( is_int( $nsId ) ) {
- $nsName = $wgContLang->convertNamespace( $nsId );
- }
$optionsHtml[] = self::element(
'option', array(
'disabled' => in_array( $nsId, $params['disable'] ),
@@ -937,7 +952,7 @@ class Html {
* attributes, passed to Html::element() of html tag.
* @return string Raw HTML
*/
- public static function htmlHeader( $attribs = array() ) {
+ public static function htmlHeader( array $attribs = array() ) {
$ret = '';
global $wgHtml5Version, $wgMimeType, $wgXhtmlNamespaces;
@@ -1047,7 +1062,7 @@ class Html {
* @param string[] $urls
* @return string
*/
- static function srcSet( $urls ) {
+ static function srcSet( array $urls ) {
$candidates = array();
foreach ( $urls as $density => $url ) {
// Cast density to float to strip 'x'.
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index fa54487a..bc5a9570 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -257,7 +257,7 @@ class MWHttpRequest {
$this->parsedUrl = wfParseUrl( $this->url );
if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) {
- $this->status = Status::newFatal( 'http-invalid-url' );
+ $this->status = Status::newFatal( 'http-invalid-url', $url );
} else {
$this->status = Status::newGood( 100 ); // continue
}
@@ -797,14 +797,14 @@ class CurlHttpRequest extends MWHttpRequest {
}
if ( $this->followRedirects && $this->canFollowRedirects() ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
if ( !curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
"Probably safe_mode or open_basedir is set.\n" );
// Continue the processing. If it were in curl_setopt_array,
// processing would have halted on its entry
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
if ( $this->profiler ) {
diff --git a/includes/Import.php b/includes/Import.php
index d31be43b..6a0bfd09 100644
--- a/includes/Import.php
+++ b/includes/Import.php
@@ -34,7 +34,7 @@ class WikiImporter {
private $reader = null;
private $foreignNamespaces = null;
private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
- private $mSiteInfoCallback, $mTargetNamespace, $mPageOutCallback;
+ private $mSiteInfoCallback, $mPageOutCallback;
private $mNoticeCallback, $mDebug;
private $mImportUploads, $mImageBasePath;
private $mNoUpdates = false;
@@ -49,8 +49,13 @@ class WikiImporter {
* Creates an ImportXMLReader drawing from the source provided
* @param ImportSource $source
* @param Config $config
+ * @throws Exception
*/
function __construct( ImportSource $source, Config $config = null ) {
+ if ( !class_exists( 'XMLReader' ) ) {
+ throw new Exception( 'Import requires PHP to have been compiled with libxml support' );
+ }
+
$this->reader = new XMLReader();
if ( !$config ) {
wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
@@ -62,11 +67,22 @@ class WikiImporter {
stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
}
$id = UploadSourceAdapter::registerSource( $source );
+
+ // Enable the entity loader, as it is needed for loading external URLs via
+ // XMLReader::open (T86036)
+ $oldDisable = libxml_disable_entity_loader( false );
if ( defined( 'LIBXML_PARSEHUGE' ) ) {
- $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
+ $status = $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
} else {
- $this->reader->open( "uploadsource://$id" );
+ $status = $this->reader->open( "uploadsource://$id" );
}
+ if ( !$status ) {
+ $error = libxml_get_last_error();
+ libxml_disable_entity_loader( $oldDisable );
+ throw new MWException( 'Encountered an internal error while initializing WikiImporter object: ' .
+ $error->message );
+ }
+ libxml_disable_entity_loader( $oldDisable );
// Default callbacks
$this->setPageCallback( array( $this, 'beforeImportPage' ) );
@@ -224,7 +240,6 @@ class WikiImporter {
public function setTargetNamespace( $namespace ) {
if ( is_null( $namespace ) ) {
// Don't override namespaces
- $this->mTargetNamespace = null;
$this->setImportTitleFactory( new NaiveImportTitleFactory() );
return true;
} elseif (
@@ -232,7 +247,6 @@ class WikiImporter {
MWNamespace::exists( intval( $namespace ) )
) {
$namespace = intval( $namespace );
- $this->mTargetNamespace = $namespace;
$this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
return true;
} else {
@@ -252,10 +266,7 @@ class WikiImporter {
$this->setImportTitleFactory( new NaiveImportTitleFactory() );
} elseif ( $rootpage !== '' ) {
$rootpage = rtrim( $rootpage, '/' ); //avoid double slashes
- $title = Title::newFromText( $rootpage, !is_null( $this->mTargetNamespace )
- ? $this->mTargetNamespace
- : NS_MAIN
- );
+ $title = Title::newFromText( $rootpage );
if ( !$title || $title->isExternal() ) {
$status->fatal( 'import-rootpage-invalid' );
@@ -383,9 +394,9 @@ class WikiImporter {
$countKey = 'title_' . $title->getPrefixedText();
$countable = $page->isCountable( $editInfo );
if ( array_key_exists( $countKey, $this->countableCache ) &&
- $countable != $this->countableCache[ $countKey ] ) {
+ $countable != $this->countableCache[$countKey] ) {
DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
- 'articles' => ( (int)$countable - (int)$this->countableCache[ $countKey ] )
+ 'articles' => ( (int)$countable - (int)$this->countableCache[$countKey] )
) ) );
}
}
@@ -528,10 +539,10 @@ class WikiImporter {
$oldDisable = libxml_disable_entity_loader( true );
$this->reader->read();
- if ( $this->reader->name != 'mediawiki' ) {
+ if ( $this->reader->localName != 'mediawiki' ) {
libxml_disable_entity_loader( $oldDisable );
throw new MWException( "Expected <mediawiki> tag, got " .
- $this->reader->name );
+ $this->reader->localName );
}
$this->debug( "<mediawiki> tag is correct." );
@@ -542,7 +553,7 @@ class WikiImporter {
$rethrow = null;
try {
while ( $keepReading ) {
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
$type = $this->reader->nodeType;
if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
@@ -593,14 +604,14 @@ class WikiImporter {
while ( $this->reader->read() ) {
if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
- $this->reader->name == 'siteinfo' ) {
+ $this->reader->localName == 'siteinfo' ) {
break;
}
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( $tag == 'namespace' ) {
- $this->foreignNamespaces[ $this->nodeAttribute( 'key' ) ] =
+ $this->foreignNamespaces[$this->nodeAttribute( 'key' )] =
$this->nodeContents();
} elseif ( in_array( $tag, $normalFields ) ) {
$siteInfo[$tag] = $this->nodeContents();
@@ -621,11 +632,11 @@ class WikiImporter {
while ( $this->reader->read() ) {
if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
- $this->reader->name == 'logitem' ) {
+ $this->reader->localName == 'logitem' ) {
break;
}
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
$this, $logInfo
@@ -685,13 +696,13 @@ class WikiImporter {
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
- $this->reader->name == 'page' ) {
+ $this->reader->localName == 'page' ) {
break;
}
$skip = false;
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( $badTitle ) {
// The title is invalid, bail out of this page
@@ -758,11 +769,11 @@ class WikiImporter {
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
- $this->reader->name == 'revision' ) {
+ $this->reader->localName == 'revision' ) {
break;
}
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
$this, $pageInfo, $revisionInfo
@@ -850,11 +861,11 @@ class WikiImporter {
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
- $this->reader->name == 'upload' ) {
+ $this->reader->localName == 'upload' ) {
break;
}
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
$this, $pageInfo
@@ -948,11 +959,11 @@ class WikiImporter {
while ( $this->reader->read() ) {
if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
- $this->reader->name == 'contributor' ) {
+ $this->reader->localName == 'contributor' ) {
break;
}
- $tag = $this->reader->name;
+ $tag = $this->reader->localName;
if ( in_array( $tag, $fields ) ) {
$info[$tag] = $this->nodeContents();
@@ -1846,9 +1857,9 @@ class ImportStreamSource implements ImportSource {
* @return Status
*/
static function newFromFile( $filename ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$file = fopen( $filename, 'rt' );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$file ) {
return Status::newFatal( "importcantopen" );
}
diff --git a/includes/Linker.php b/includes/Linker.php
index b58dabab..9b5ff27b 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -77,7 +77,7 @@ class Linker {
wfDeprecated( __METHOD__, '1.25' );
$title = urldecode( $title );
- $title = str_replace( '_', ' ', $title );
+ $title = strtr( $title, '_', ' ' );
return self::getLinkAttributesInternal( $title, $class );
}
@@ -1276,9 +1276,11 @@ class Linker {
* @param string $comment
* @param Title|null $title Title object (to generate link to the section in autocomment) or null
* @param bool $local Whether section links should refer to local page
+ * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes.
+ *
* @return mixed|string
*/
- public static function formatComment( $comment, $title = null, $local = false ) {
+ public static function formatComment( $comment, $title = null, $local = false, $wikiId = null ) {
# Sanitize text a bit:
$comment = str_replace( "\n", " ", $comment );
@@ -1286,8 +1288,8 @@ class Linker {
$comment = Sanitizer::escapeHtmlAllowEntities( $comment );
# Render autocomments and make links:
- $comment = self::formatAutocomments( $comment, $title, $local );
- $comment = self::formatLinksInComment( $comment, $title, $local );
+ $comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
+ $comment = self::formatLinksInComment( $comment, $title, $local, $wikiId );
return $comment;
}
@@ -1304,9 +1306,11 @@ class Linker {
* @param string $comment Comment text
* @param Title|null $title An optional title object used to links to sections
* @param bool $local Whether section links should refer to local page
- * @return string Formatted comment
+ * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
+ *
+ * @return string Formatted comment (wikitext)
*/
- private static function formatAutocomments( $comment, $title = null, $local = false ) {
+ private static function formatAutocomments( $comment, $title = null, $local = false, $wikiId = null ) {
// @todo $append here is something of a hack to preserve the status
// quo. Someone who knows more about bidi and such should decide
// (1) what sane rendering even *is* for an LTR edit summary on an RTL
@@ -1320,7 +1324,7 @@ class Linker {
// zero-width assertions optional, so wrap them in a non-capturing
// group.
'!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
- function ( $match ) use ( $title, $local, &$append ) {
+ function ( $match ) use ( $title, $local, $wikiId, &$append ) {
global $wgLang;
// Ensure all match positions are defined
@@ -1330,7 +1334,7 @@ class Linker {
$auto = $match[2];
$post = $match[3] !== '';
$comment = null;
- Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local ) );
+ Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local, $wikiId ) );
if ( $comment === null ) {
$link = '';
if ( $title ) {
@@ -1349,9 +1353,7 @@ class Linker {
$title->getDBkey(), $section );
}
if ( $sectionTitle ) {
- $link = Linker::link( $sectionTitle,
- $wgLang->getArrow(), array(), array(),
- 'noclasses' );
+ $link = Linker::makeCommentLink( $sectionTitle, $wgLang->getArrow(), $wikiId, 'noclasses' );
} else {
$link = '';
}
@@ -1384,7 +1386,7 @@ class Linker {
* @param string $comment Text to format links in
* @param Title|null $title An optional title object used to links to sections
* @param bool $local Whether section links should refer to local page
- * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap
+ * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
*
* @return string
*/
@@ -1414,10 +1416,9 @@ class Linker {
# fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
if ( strpos( $match[1], '%' ) !== false ) {
- $match[1] = str_replace(
- array( '<', '>' ),
- array( '&lt;', '&gt;' ),
- rawurldecode( $match[1] )
+ $match[1] = strtr(
+ rawurldecode( $match[1] ),
+ array( '<' => '&lt;', '>' => '&gt;' )
);
}
@@ -1460,22 +1461,9 @@ class Linker {
$newTarget = clone ( $title );
$newTarget->setFragment( '#' . $target->getFragment() );
$target = $newTarget;
-
- }
-
- if ( $wikiId !== null ) {
- $thelink = Linker::makeExternalLink(
- WikiMap::getForeignURL( $wikiId, $target->getFullText() ),
- $linkText . $inside,
- /* escape = */ false // Already escaped
- ) . $trail;
- } else {
- $thelink = Linker::link(
- $target,
- $linkText . $inside
- ) . $trail;
}
+ $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
}
}
if ( $thelink ) {
@@ -1495,6 +1483,32 @@ class Linker {
}
/**
+ * Generates a link to the given Title
+ *
+ * @note This is only public for technical reasons. It's not intended for use outside Linker.
+ *
+ * @param Title $title
+ * @param string $text
+ * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
+ * @param string|string[] $options See the $options parameter in Linker::link.
+ *
+ * @return string HTML link
+ */
+ public static function makeCommentLink( Title $title, $text, $wikiId = null, $options = array() ) {
+ if ( $wikiId !== null && !$title->isExternal() ) {
+ $link = Linker::makeExternalLink(
+ WikiMap::getForeignURL( $wikiId, $title->getPrefixedText(), $title->getFragment() ),
+ $text,
+ /* escape = */ false // Already escaped
+ );
+ } else {
+ $link = Linker::link( $title, $text, array(), array(), $options );
+ }
+
+ return $link;
+ }
+
+ /**
* @param Title $contextTitle
* @param string $target
* @param string $text
@@ -1580,17 +1594,18 @@ class Linker {
* @param string $comment
* @param Title|null $title Title object (to generate link to section in autocomment) or null
* @param bool $local Whether section links should refer to local page
+ * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes.
*
* @return string
*/
- public static function commentBlock( $comment, $title = null, $local = false ) {
+ public static function commentBlock( $comment, $title = null, $local = false, $wikiId = null ) {
// '*' used to be the comment inserted by the software way back
// in antiquity in case none was provided, here for backwards
// compatibility, acc. to brion -ævar
if ( $comment == '' || $comment == '*' ) {
return '';
} else {
- $formatted = self::formatComment( $comment, $title, $local );
+ $formatted = self::formatComment( $comment, $title, $local, $wikiId );
$formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
return " <span class=\"comment\">$formatted</span>";
}
@@ -1705,13 +1720,13 @@ class Linker {
}
/**
- * Generate a table of contents from a section tree
- * Currently unused.
+ * Generate a table of contents from a section tree.
*
* @param array $tree Return value of ParserOutput::getSections()
+ * @param string|Language|bool $lang Language for the toc title, defaults to user language
* @return string HTML fragment
*/
- public static function generateTOC( $tree ) {
+ public static function generateTOC( $tree, $lang = false ) {
$toc = '';
$lastLevel = 0;
foreach ( $tree as $section ) {
@@ -1730,7 +1745,7 @@ class Linker {
$lastLevel = $section['toclevel'];
}
$toc .= self::tocLineEnd();
- return self::tocList( $toc );
+ return self::tocList( $toc, $lang );
}
/**
@@ -2383,6 +2398,7 @@ class Linker {
'title' => $tooltip
) );
}
+
}
/**
diff --git a/includes/MWNamespace.php b/includes/MWNamespace.php
index e370bf10..8ca205ab 100644
--- a/includes/MWNamespace.php
+++ b/includes/MWNamespace.php
@@ -299,6 +299,18 @@ class MWNamespace {
}
/**
+ * Might pages in this namespace require the use of the Signature button on
+ * the edit toolbar?
+ *
+ * @param int $index Index to check
+ * @return bool
+ */
+ public static function wantSignatures( $index ) {
+ global $wgExtraSignatureNamespaces;
+ return self::isTalk( $index ) || in_array( $index, $wgExtraSignatureNamespaces );
+ }
+
+ /**
* Can pages in a namespace be watched?
*
* @param int $index
diff --git a/includes/MWTimestamp.php b/includes/MWTimestamp.php
index ea91470e..d28f88e5 100644
--- a/includes/MWTimestamp.php
+++ b/includes/MWTimestamp.php
@@ -56,7 +56,7 @@ class MWTimestamp {
*
* @since 1.20
*
- * @param bool|string $timestamp Timestamp to set, or false for current time
+ * @param bool|string|int|float $timestamp Timestamp to set, or false for current time
*/
public function __construct( $timestamp = false ) {
$this->setTimestamp( $timestamp );
@@ -74,6 +74,7 @@ class MWTimestamp {
* @throws TimestampException
*/
public function setTimestamp( $ts = false ) {
+ $m = array();
$da = array();
$strtime = '';
@@ -87,9 +88,9 @@ class MWTimestamp {
# 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', $ts ) ) {
+ } elseif ( preg_match( '/^(-?\d{1,13})(\.\d+)?$/D', $ts, $m ) ) {
# TS_UNIX
- $strtime = "@$ts"; // http://php.net/manual/en/datetime.formats.compound.php
+ $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",
@@ -199,42 +200,19 @@ class MWTimestamp {
*
* @since 1.20
* @since 1.22 Uses Language::getHumanTimestamp to produce the timestamp
+ * @deprecated since 1.26 Use Language::getHumanTimestamp directly
*
- * @param MWTimestamp|null $relativeTo The base timestamp to compare to
- * (defaults to now).
- * @param User|null $user User the timestamp is being generated for (or null
- * to use main context's user).
- * @param Language|null $lang Language to use to make the human timestamp
- * (or null to use main context's language).
+ * @param MWTimestamp|null $relativeTo The base timestamp to compare to (defaults to now)
+ * @param User|null $user User the timestamp is being generated for (or null to use main context's user)
+ * @param Language|null $lang Language to use to make the human timestamp (or null to use main context's language)
* @return string Formatted timestamp
*/
- public function getHumanTimestamp( MWTimestamp $relativeTo = null,
- User $user = null, Language $lang = null
- ) {
- if ( $relativeTo === null ) {
- $relativeTo = new self();
- }
- if ( $user === null ) {
- $user = RequestContext::getMain()->getUser();
- }
+ public function getHumanTimestamp( MWTimestamp $relativeTo = null, User $user = null, Language $lang = null ) {
if ( $lang === null ) {
$lang = RequestContext::getMain()->getLanguage();
}
- // Adjust for the user's timezone.
- $offsetThis = $this->offsetForUser( $user );
- $offsetRel = $relativeTo->offsetForUser( $user );
-
- $ts = '';
- if ( Hooks::run( 'GetHumanTimestamp', array( &$ts, $this, $relativeTo, $user, $lang ) ) ) {
- $ts = $lang->getHumanTimestamp( $this, $relativeTo, $user );
- }
-
- // Reset the timezone on the objects.
- $this->timestamp->sub( $offsetThis );
- $relativeTo->timestamp->sub( $offsetRel );
-
- return $ts;
+ return $lang->getHumanTimestamp( $this, $relativeTo, $user );
}
/**
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index 186821de..2c7ba91b 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -718,9 +718,6 @@ class MagicWordArray {
private $regex;
- /** @todo Unused? */
- private $matches;
-
/**
* @param array $names
*/
@@ -953,10 +950,12 @@ class MagicWordArray {
if ( $regex === '' ) {
continue;
}
- preg_match_all( $regex, $text, $matches, PREG_SET_ORDER );
- foreach ( $matches as $m ) {
- list( $name, $param ) = $this->parseMatch( $m );
- $found[$name] = $param;
+ $matches = array();
+ if ( preg_match_all( $regex, $text, $matches, PREG_SET_ORDER ) ) {
+ foreach ( $matches as $m ) {
+ list( $name, $param ) = $this->parseMatch( $m );
+ $found[$name] = $param;
+ }
}
$text = preg_replace( $regex, '', $text );
}
diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php
index ec2f40f6..fbacb250 100644
--- a/includes/MediaWiki.php
+++ b/includes/MediaWiki.php
@@ -51,6 +51,7 @@ class MediaWiki {
/**
* Parse the request to get the Title object
*
+ * @throws MalformedTitleException If a title has been provided by the user, but is invalid.
* @return Title Title object to be $wgTitle
*/
private function parseTitle() {
@@ -110,7 +111,10 @@ class MediaWiki {
}
if ( $ret === null || ( $ret->getDBkey() == '' && !$ret->isExternal() ) ) {
- $ret = SpecialPage::getTitleFor( 'Badtitle' );
+ // If we get here, we definitely don't have a valid title; throw an exception.
+ // Try to get detailed invalid title exception first, fall back to MalformedTitleException.
+ Title::newFromTextThrow( $title );
+ throw new MalformedTitleException( 'badtitletext', $title );
}
return $ret;
@@ -122,7 +126,11 @@ class MediaWiki {
*/
public function getTitle() {
if ( !$this->context->hasTitle() ) {
- $this->context->setTitle( $this->parseTitle() );
+ try {
+ $this->context->setTitle( $this->parseTitle() );
+ } catch ( MalformedTitleException $ex ) {
+ $this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
+ }
}
return $this->context->getTitle();
}
@@ -174,6 +182,11 @@ class MediaWiki {
|| $title->isSpecial( 'Badtitle' )
) {
$this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
+ try {
+ $this->parseTitle();
+ } catch ( MalformedTitleException $ex ) {
+ throw new BadTitleError( $ex );
+ }
throw new BadTitleError();
}
@@ -219,65 +232,116 @@ class MediaWiki {
$output->redirect( $url, 301 );
} else {
$this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
- throw new BadTitleError();
- }
- // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
- } elseif ( $request->getVal( 'action', 'view' ) == 'view' && !$request->wasPosted()
- && ( $request->getVal( 'title' ) === null
- || $title->getPrefixedDBkey() != $request->getVal( 'title' ) )
- && !count( $request->getValueNames( array( 'action', 'title' ) ) )
- && Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
- ) {
- if ( $title->isSpecialPage() ) {
- list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
- if ( $name ) {
- $title = SpecialPage::getTitleFor( $name, $subpage );
+ try {
+ $this->parseTitle();
+ } catch ( MalformedTitleException $ex ) {
+ throw new BadTitleError( $ex );
}
+ throw new BadTitleError();
}
- $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
- // Redirect to canonical url, make it a 301 to allow caching
- if ( $targetUrl == $request->getFullRequestURL() ) {
- $message = "Redirect loop detected!\n\n" .
- "This means the wiki got confused about what page was " .
- "requested; this sometimes happens when moving a wiki " .
- "to a new server or changing the server configuration.\n\n";
-
- if ( $this->config->get( 'UsePathInfo' ) ) {
- $message .= "The wiki is trying to interpret the page " .
- "title from the URL path portion (PATH_INFO), which " .
- "sometimes fails depending on the web server. Try " .
- "setting \"\$wgUsePathInfo = false;\" in your " .
- "LocalSettings.php, or check that \$wgArticlePath " .
- "is correct.";
+ // Handle any other redirects.
+ // Redirect loops, titleless URL, $wgUsePathInfo URLs, and URLs with a variant
+ } elseif ( !$this->tryNormaliseRedirect( $title ) ) {
+
+ // Special pages
+ if ( NS_SPECIAL == $title->getNamespace() ) {
+ // Actions that need to be made when we have a special pages
+ SpecialPageFactory::executePath( $title, $this->context );
+ } else {
+ // ...otherwise treat it as an article view. The article
+ // may still be a wikipage redirect to another article or URL.
+ $article = $this->initializeArticle();
+ if ( is_object( $article ) ) {
+ $this->performAction( $article, $requestTitle );
+ } elseif ( is_string( $article ) ) {
+ $output->redirect( $article );
} else {
- $message .= "Your web server was detected as possibly not " .
- "supporting URL path components (PATH_INFO) correctly; " .
- "check your LocalSettings.php for a customized " .
- "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
- "to true.";
+ throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
+ . " returned neither an object nor a URL" );
}
- throw new HttpError( 500, $message );
- } else {
- $output->setSquidMaxage( 1200 );
- $output->redirect( $targetUrl, '301' );
}
- // Special pages
- } elseif ( NS_SPECIAL == $title->getNamespace() ) {
- // Actions that need to be made when we have a special pages
- SpecialPageFactory::executePath( $title, $this->context );
- } else {
- // ...otherwise treat it as an article view. The article
- // may be a redirect to another article or URL.
- $article = $this->initializeArticle();
- if ( is_object( $article ) ) {
- $this->performAction( $article, $requestTitle );
- } elseif ( is_string( $article ) ) {
- $output->redirect( $article );
+ }
+ }
+
+ /**
+ * Handle redirects for uncanonical title requests.
+ *
+ * Handles:
+ * - Redirect loops.
+ * - No title in URL.
+ * - $wgUsePathInfo URLs.
+ * - URLs with a variant.
+ * - Other non-standard URLs (as long as they have no extra query parameters).
+ *
+ * Behaviour:
+ * - Normalise title values:
+ * /wiki/Foo%20Bar -> /wiki/Foo_Bar
+ * - Normalise empty title:
+ * /wiki/ -> /wiki/Main
+ * /w/index.php?title= -> /wiki/Main
+ * - Normalise non-standard title urls:
+ * /w/index.php?title=Foo_Bar -> /wiki/Foo_Bar
+ * - Don't redirect anything with query parameters other than 'title' or 'action=view'.
+ *
+ * @param Title $title
+ * @return bool True if a redirect was set.
+ * @throws HttpError
+ */
+ private function tryNormaliseRedirect( Title $title ) {
+ $request = $this->context->getRequest();
+ $output = $this->context->getOutput();
+
+ if ( $request->getVal( 'action', 'view' ) != 'view'
+ || $request->wasPosted()
+ || count( $request->getValueNames( array( 'action', 'title' ) ) )
+ || !Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
+ ) {
+ return false;
+ }
+
+ if ( $title->isSpecialPage() ) {
+ list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
+ if ( $name ) {
+ $title = SpecialPage::getTitleFor( $name, $subpage );
+ }
+ }
+ // Redirect to canonical url, make it a 301 to allow caching
+ $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
+
+ if ( $targetUrl != $request->getFullRequestURL() ) {
+ $output->setSquidMaxage( 1200 );
+ $output->redirect( $targetUrl, '301' );
+ return true;
+ }
+
+ // If there is no title, or the title is in a non-standard encoding, we demand
+ // a redirect. If cgi somehow changed the 'title' query to be non-standard while
+ // the url is standard, the server is misconfigured.
+ if ( $request->getVal( 'title' ) === null
+ || $title->getPrefixedDBkey() != $request->getVal( 'title' )
+ ) {
+ $message = "Redirect loop detected!\n\n" .
+ "This means the wiki got confused about what page was " .
+ "requested; this sometimes happens when moving a wiki " .
+ "to a new server or changing the server configuration.\n\n";
+
+ if ( $this->config->get( 'UsePathInfo' ) ) {
+ $message .= "The wiki is trying to interpret the page " .
+ "title from the URL path portion (PATH_INFO), which " .
+ "sometimes fails depending on the web server. Try " .
+ "setting \"\$wgUsePathInfo = false;\" in your " .
+ "LocalSettings.php, or check that \$wgArticlePath " .
+ "is correct.";
} else {
- throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
- . " returned neither an object nor a URL" );
+ $message .= "Your web server was detected as possibly not " .
+ "supporting URL path components (PATH_INFO) correctly; " .
+ "check your LocalSettings.php for a customized " .
+ "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
+ "to true.";
}
+ throw new HttpError( 500, $message );
}
+ return false;
}
/**
@@ -301,9 +365,8 @@ class MediaWiki {
$this->context->setWikiPage( $article->getPage() );
}
- // NS_MEDIAWIKI has no redirects.
- // It is also used for CSS/JS, so performance matters here...
- if ( $title->getNamespace() == NS_MEDIAWIKI ) {
+ // Skip some unnecessary code if the content model doesn't support redirects
+ if ( !ContentHandler::getForTitle( $title )->supportsRedirects() ) {
return $article;
}
@@ -404,8 +467,7 @@ class MediaWiki {
}
/**
- * Run the current MediaWiki instance
- * index.php just calls this
+ * Run the current MediaWiki instance; index.php just calls this
*/
public function run() {
try {
@@ -416,16 +478,71 @@ class MediaWiki {
// Bug 62091: while exceptions are convenient to bubble up GUI errors,
// they are not internal application faults. As with normal requests, this
// should commit, print the output, do deferred updates, jobs, and profiling.
- wfGetLBFactory()->commitMasterChanges();
+ $this->doPreOutputCommit();
$e->report(); // display the GUI error
}
+ } catch ( Exception $e ) {
+ MWExceptionHandler::handleException( $e );
+ }
+
+ $this->doPostOutputShutdown( 'normal' );
+ }
+
+ /**
+ * This function commits all DB changes as needed before
+ * the user can receive a response (in case commit fails)
+ *
+ * @since 1.26
+ */
+ public function doPreOutputCommit() {
+ // Either all DBs should commit or none
+ ignore_user_abort( true );
+
+ // Commit all changes and record ChronologyProtector positions
+ $factory = wfGetLBFactory();
+ $factory->commitMasterChanges();
+ $factory->shutdown();
+
+ wfDebug( __METHOD__ . ' completed; all transactions committed' );
+ }
+
+ /**
+ * This function does work that can be done *after* the
+ * user gets the HTTP response so they don't block on it
+ *
+ * This manages deferred updates, job insertion,
+ * final commit, and the logging of profiling data
+ *
+ * @param string $mode Use 'fast' to always skip job running
+ * @since 1.26
+ */
+ public function doPostOutputShutdown( $mode = 'normal' ) {
+ // Show visible profiling data if enabled (which cannot be post-send)
+ Profiler::instance()->logDataPageOutputOnly();
+
+ $that = $this;
+ $callback = function () use ( $that, $mode ) {
+ try {
+ $that->restInPeace( $mode );
+ } catch ( Exception $e ) {
+ MWExceptionHandler::handleException( $e );
+ }
+ };
+
+ // Defer everything else...
+ if ( function_exists( 'register_postsend_function' ) ) {
+ // https://github.com/facebook/hhvm/issues/1230
+ register_postsend_function( $callback );
+ } else {
if ( function_exists( 'fastcgi_finish_request' ) ) {
fastcgi_finish_request();
+ } else {
+ // Either all DB and deferred updates should happen or none.
+ // The later should not be cancelled due to client disconnect.
+ ignore_user_abort( true );
}
- $this->triggerJobs();
- $this->restInPeace();
- } catch ( Exception $e ) {
- MWExceptionHandler::handleException( $e );
+
+ $callback();
}
}
@@ -440,7 +557,7 @@ class MediaWiki {
list( $host, $lag ) = wfGetLB()->getMaxLag();
if ( $lag > $maxLag ) {
$resp = $this->context->getRequest()->response();
- $resp->header( 'HTTP/1.1 503 Service Unavailable' );
+ $resp->statusHeader( 503 );
$resp->header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) );
$resp->header( 'X-Database-Lag: ' . intval( $lag ) );
$resp->header( 'Content-Type: text/plain' );
@@ -457,7 +574,7 @@ class MediaWiki {
}
private function main() {
- global $wgTitle;
+ global $wgTitle, $wgTrxProfilerLimits;
$request = $this->context->getRequest();
@@ -489,10 +606,9 @@ class MediaWiki {
if ( !$request->wasPosted()
&& in_array( $action, array( 'view', 'edit', 'history' ) )
) {
- $trxProfiler->setExpectation( 'masterConns', 0, __METHOD__ );
- $trxProfiler->setExpectation( 'writes', 0, __METHOD__ );
+ $trxProfiler->setExpectations( $wgTrxProfilerLimits['GET'], __METHOD__ );
} else {
- $trxProfiler->setExpectation( 'maxAffected', 500, __METHOD__ );
+ $trxProfiler->setExpectations( $wgTrxProfilerLimits['POST'], __METHOD__ );
}
// If the user has forceHTTPS set to true, or if the user
@@ -565,22 +681,23 @@ class MediaWiki {
// Actually do the work of the request and build up any output
$this->performRequest();
- // Either all DB and deferred updates should happen or none.
- // The later should not be cancelled due to client disconnect.
- ignore_user_abort( true );
// Now commit any transactions, so that unreported errors after
- // output() don't roll back the whole DB transaction
- wfGetLBFactory()->commitMasterChanges();
+ // 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();
// Output everything!
$this->context->getOutput()->output();
-
}
/**
* Ends this task peacefully
+ * @param string $mode Use 'fast' to always skip job running
*/
- public function restInPeace() {
+ public function restInPeace( $mode = 'fast' ) {
+ // Assure deferred updates are not in the main transaction
+ wfGetLBFactory()->commitMasterChanges();
+
// Ignore things like master queries/connections on GET requests
// as long as they are in deferred updates (which catch errors).
Profiler::instance()->getTransactionProfiler()->resetExpectations();
@@ -588,6 +705,15 @@ class MediaWiki {
// Do any deferred jobs
DeferredUpdates::doUpdates( 'commit' );
+ // Make sure any lazy jobs are pushed
+ JobQueueGroup::pushLazyJobs();
+
+ // Now that everything specific to this request is done,
+ // try to occasionally run jobs (if enabled) from the queues
+ if ( $mode === 'normal' ) {
+ $this->triggerJobs();
+ }
+
// Log profiling data, e.g. in the database or UDP
wfLogProfilingData();
@@ -604,7 +730,7 @@ class MediaWiki {
* to run a specified number of jobs. This registers a callback to cleanup
* the socket once it's done.
*/
- protected function triggerJobs() {
+ public function triggerJobs() {
$jobRunRate = $this->config->get( 'JobRunRate' );
if ( $jobRunRate <= 0 || wfReadOnly() ) {
return;
@@ -647,7 +773,7 @@ class MediaWiki {
$errno = $errstr = null;
$info = wfParseUrl( $this->config->get( 'Server' ) );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$sock = fsockopen(
$info['host'],
isset( $info['port'] ) ? $info['port'] : 80,
@@ -657,7 +783,7 @@ class MediaWiki {
// is a problem elsewhere.
0.1
);
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$sock ) {
$runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
// Fall back to running the job here while the user waits
diff --git a/includes/Message.php b/includes/Message.php
index 134af0ed..54abfd15 100644
--- a/includes/Message.php
+++ b/includes/Message.php
@@ -156,7 +156,7 @@
*
* @since 1.17
*/
-class Message implements MessageSpecifier {
+class Message implements MessageSpecifier, Serializable {
/**
* In which language to get this message. True, which is the default,
@@ -226,8 +226,9 @@ class Message implements MessageSpecifier {
/**
* @since 1.17
*
- * @param string|string[] $key Message key or array of message keys to try and use the first
- * non-empty message for.
+ * @param string|string[]|MessageSpecifier $key Message key, or array of
+ * message keys to try and use the first non-empty message for, or a
+ * MessageSpecifier to copy from.
* @param array $params Message parameters.
* @param Language $language Optional language of the message, defaults to $wgLang.
*
@@ -236,6 +237,16 @@ class Message implements MessageSpecifier {
public function __construct( $key, $params = array(), Language $language = null ) {
global $wgLang;
+ if ( $key instanceof MessageSpecifier ) {
+ if ( $params ) {
+ throw new InvalidArgumentException(
+ '$params must be empty if $key is a MessageSpecifier'
+ );
+ }
+ $params = $key->getParams();
+ $key = $key->getKey();
+ }
+
if ( !is_string( $key ) && !is_array( $key ) ) {
throw new InvalidArgumentException( '$key must be a string or an array' );
}
@@ -253,6 +264,41 @@ class Message implements MessageSpecifier {
}
/**
+ * @see Serializable::serialize()
+ * @since 1.26
+ * @return string
+ */
+ public function serialize() {
+ return serialize( array(
+ 'interface' => $this->interface,
+ 'language' => $this->language->getCode(),
+ 'key' => $this->key,
+ 'keysToTry' => $this->keysToTry,
+ 'parameters' => $this->parameters,
+ 'format' => $this->format,
+ 'useDatabase' => $this->useDatabase,
+ 'title' => $this->title,
+ ) );
+ }
+
+ /**
+ * @see Serializable::unserialize()
+ * @since 1.26
+ * @param string $serialized
+ */
+ public function unserialize( $serialized ) {
+ $data = unserialize( $serialized );
+ $this->interface = $data['interface'];
+ $this->key = $data['key'];
+ $this->keysToTry = $data['keysToTry'];
+ $this->parameters = $data['parameters'];
+ $this->format = $data['format'];
+ $this->useDatabase = $data['useDatabase'];
+ $this->language = Language::factory( $data['language'] );
+ $this->title = $data['title'];
+ }
+
+ /**
* @since 1.24
*
* @return bool True if this is a multi-key message, that is, if the key provided to the
@@ -327,7 +373,7 @@ class Message implements MessageSpecifier {
*
* @since 1.17
*
- * @param string|string[] $key Message key or array of keys.
+ * @param string|string[]|MessageSpecifier $key
* @param mixed $param,... Parameters as strings.
*
* @return Message
@@ -365,6 +411,31 @@ class Message implements MessageSpecifier {
}
/**
+ * Get a title object for a mediawiki message, where it can be found in the mediawiki namespace.
+ * The title will be for the current language, if the message key is in
+ * $wgForceUIMsgAsContentMsg it will be append with the language code (except content
+ * language), because Message::inContentLanguage will also return in user language.
+ *
+ * @see $wgForceUIMsgAsContentMsg
+ * @return Title
+ * @since 1.26
+ */
+ public function getTitle() {
+ global $wgContLang, $wgForceUIMsgAsContentMsg;
+
+ $code = $this->language->getCode();
+ $title = $this->key;
+ if (
+ $wgContLang->getCode() !== $code
+ && in_array( $this->key, (array)$wgForceUIMsgAsContentMsg )
+ ) {
+ $title .= '/' . $code;
+ }
+
+ return Title::makeTitle( NS_MEDIAWIKI, $wgContLang->ucfirst( strtr( $title, ' ', '_' ) ) );
+ }
+
+ /**
* Adds parameters to the parameter list of this message.
*
* @since 1.17
@@ -597,7 +668,7 @@ class Message implements MessageSpecifier {
if ( $lang instanceof Language || $lang instanceof StubUserLang ) {
$this->language = $lang;
} elseif ( is_string( $lang ) ) {
- if ( $this->language->getCode() != $lang ) {
+ if ( !$this->language instanceof Language || $this->language->getCode() != $lang ) {
$this->language = Language::factory( $lang );
}
} else {
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index ebe98a3c..2b240c3b 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -617,16 +617,18 @@ class MimeMagic {
/**
* Guess the MIME type from the file contents.
*
+ * @todo Remove $ext param
+ *
* @param string $file
* @param mixed $ext
* @return bool|string
* @throws MWException
*/
- private function doGuessMimeType( $file, $ext ) { // TODO: remove $ext param
+ private function doGuessMimeType( $file, $ext ) {
// Read a chunk of the file
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$f = fopen( $file, 'rb' );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$f ) {
return 'unknown/unknown';
@@ -693,7 +695,7 @@ class MimeMagic {
}
/* Look for WebP */
- if ( strncmp( $head, "RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 8 ), "WEBPVP8 ", 8 ) == 0 ) {
+ if ( strncmp( $head, "RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 7 ), "WEBPVP8", 7 ) == 0 ) {
wfDebug( __METHOD__ . ": recognized file as image/webp\n" );
return "image/webp";
}
@@ -780,9 +782,9 @@ class MimeMagic {
return $this->detectZipType( $head, $tail, $ext );
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$gis = getimagesize( $file );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $gis && isset( $gis['mime'] ) ) {
$mime = $gis['mime'];
diff --git a/includes/MovePage.php b/includes/MovePage.php
index de7da3f9..2cd9698c 100644
--- a/includes/MovePage.php
+++ b/includes/MovePage.php
@@ -64,21 +64,9 @@ class MovePage {
$status->fatal( 'spamprotectiontext' );
}
- # The move is allowed only if (1) the target doesn't exist, or
- # (2) the target is a redirect to the source, and has no history
- # (so we can undo bad moves right after they're done).
-
- if ( $this->newTitle->getArticleID() ) { # Target exists; check for validity
- if ( !$this->isValidMoveTarget() ) {
- $status->fatal( 'articleexists' );
- }
- } else {
- $tp = $this->newTitle->getTitleProtection();
- if ( $tp !== false ) {
- if ( !$user->isAllowed( $tp['permission'] ) ) {
- $status->fatal( 'cantmove-titleprotected' );
- }
- }
+ $tp = $this->newTitle->getTitleProtection();
+ if ( $tp !== false && !$user->isAllowed( $tp['permission'] ) ) {
+ $status->fatal( 'cantmove-titleprotected' );
}
Hooks::run( 'MovePageCheckPermissions',
@@ -125,6 +113,13 @@ class MovePage {
$status->fatal( 'badarticleerror' );
}
+ # The move is allowed only if (1) the target doesn't exist, or
+ # (2) the target is a redirect to the source, and has no history
+ # (so we can undo bad moves right after they're done).
+ if ( $this->newTitle->getArticleID() && !$this->isValidMoveTarget() ) {
+ $status->fatal( 'articleexists' );
+ }
+
// Content model checks
if ( !$wgContentHandlerUseDB &&
$this->oldTitle->getContentModel() !== $this->newTitle->getContentModel() ) {
@@ -310,8 +305,8 @@ class MovePage {
__METHOD__,
array( 'IGNORE' )
);
- # Update the protection log
- $log = new LogPage( 'protect' );
+
+ // Build comment for log
$comment = wfMessage(
'prot_1movedto2',
$this->oldTitle->getPrefixedText(),
@@ -320,14 +315,6 @@ class MovePage {
if ( $reason ) {
$comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
}
- // @todo FIXME: $params?
- $logId = $log->addEntry(
- 'move_prot',
- $this->newTitle,
- $comment,
- array( $this->oldTitle->getPrefixedText() ),
- $user
- );
// reread inserted pr_ids for log relation
$insertedPrIds = $dbw->select(
@@ -340,7 +327,18 @@ class MovePage {
foreach ( $insertedPrIds as $prid ) {
$logRelationsValues[] = $prid->pr_id;
}
- $log->addRelations( 'pr_id', $logRelationsValues, $logId );
+
+ // Update the protection log
+ $logEntry = new ManualLogEntry( 'protect', 'move_prot' );
+ $logEntry->setTarget( $this->newTitle );
+ $logEntry->setComment( $comment );
+ $logEntry->setPerformer( $user );
+ $logEntry->setParameters( array(
+ '4::oldtitle' => $this->oldTitle->getPrefixedText(),
+ ) );
+ $logEntry->setRelations( array( 'pr_id' => $logRelationsValues ) );
+ $logId = $logEntry->insert();
+ $logEntry->publish( $logId );
}
// Update *_from_namespace fields as needed
@@ -421,6 +419,13 @@ class MovePage {
$redirectContent = null;
}
+ // Figure out whether the content model is no longer the default
+ $oldDefault = ContentHandler::getDefaultModelFor( $this->oldTitle );
+ $contentModel = $this->oldTitle->getContentModel();
+ $newDefault = ContentHandler::getDefaultModelFor( $nt );
+ $defaultContentModelChanging = ( $oldDefault !== $newDefault
+ && $oldDefault === $contentModel );
+
// bug 57084: log_page should be the ID of the *moved* page
$oldid = $this->oldTitle->getArticleID();
$logTitle = clone $this->oldTitle;
@@ -498,6 +503,16 @@ class MovePage {
$newpage->doEditUpdates( $nullRevision, $user,
array( 'changed' => false, 'moved' => true, 'oldcountable' => $oldcountable ) );
+ // If the default content model changes, we need to populate rev_content_model
+ if ( $defaultContentModelChanging ) {
+ $dbw->update(
+ 'revision',
+ array( 'rev_content_model' => $contentModel ),
+ array( 'rev_page' => $nt->getArticleID(), 'rev_content_model IS NULL' ),
+ __METHOD__
+ );
+ }
+
if ( !$moveOverRedirect ) {
WikiPage::onArticleCreate( $nt );
}
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 7e671878..552e1815 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -20,6 +20,9 @@
* @file
*/
+use MediaWiki\Logger\LoggerFactory;
+use WrappedString\WrappedString;
+
/**
* This class should be covered by a general architecture document which does
* not exist as of January 2011. This is one of the Core classes and should
@@ -139,9 +142,6 @@ class OutputPage extends ContextSource {
/** @var string Inline CSS styles. Use addInlineStyle() sparingly */
protected $mInlineStyles = '';
- /** @todo Unused? */
- private $mLinkColours;
-
/**
* @var string Used by skin template.
* Example: $tpl->set( 'displaytitle', $out->mPageLinkTitle );
@@ -162,9 +162,6 @@ class OutputPage extends ContextSource {
/** @var array */
protected $mModuleStyles = array();
- /** @var array */
- protected $mModuleMessages = array();
-
/** @var ResourceLoader */
protected $mResourceLoader;
@@ -306,6 +303,11 @@ class OutputPage extends ContextSource {
private $mEnableSectionEditLinks = true;
/**
+ * @var string|null The URL to send in a <link> element with rel=copyright
+ */
+ private $copyrightUrl;
+
+ /**
* Constructor for OutputPage. This should not be called directly.
* Instead a new RequestContext should be created and it will implicitly create
* a OutputPage tied to that context.
@@ -342,6 +344,18 @@ class OutputPage extends ContextSource {
}
/**
+ * Set the copyright URL to send with the output.
+ * Empty string to omit, null to reset.
+ *
+ * @since 1.26
+ *
+ * @param string|null $url
+ */
+ public function setCopyrightUrl( $url ) {
+ $this->copyrightUrl = $url;
+ }
+
+ /**
* Set the HTTP status code to send with the output.
*
* @param int $statusCode
@@ -594,6 +608,20 @@ class OutputPage extends ContextSource {
* @return array Array of module names
*/
public function getModuleStyles( $filter = false, $position = null ) {
+ // T97420
+ $resourceLoader = $this->getResourceLoader();
+
+ foreach ( $this->mModuleStyles as $val ) {
+ $module = $resourceLoader->getModule( $val );
+
+ if ( $module instanceof ResourceLoaderModule && $module->isPositionDefault() ) {
+ $warning = __METHOD__ . ': style module should define its position explicitly: ' .
+ $val . ' ' . get_class( $module );
+ wfDebugLog( 'resourceloader', $warning );
+ wfLogWarning( $warning );
+ }
+ }
+
return $this->getModules( $filter, $position, 'mModuleStyles' );
}
@@ -613,24 +641,24 @@ class OutputPage extends ContextSource {
/**
* Get the list of module messages to include on this page
*
+ * @deprecated since 1.26 Obsolete
* @param bool $filter
* @param string|null $position
- *
* @return array Array of module names
*/
public function getModuleMessages( $filter = false, $position = null ) {
- return $this->getModules( $filter, $position, 'mModuleMessages' );
+ wfDeprecated( __METHOD__, '1.26' );
+ return array();
}
/**
- * Add only messages of one or more modules recognized by the resource loader.
- * Module messages added through this function will be loaded by the resource
- * loader when the page loads.
+ * Load messages of one or more ResourceLoader modules.
*
+ * @deprecated since 1.26 Use addModules() instead
* @param string|array $modules Module name (string) or array of module names
*/
public function addModuleMessages( $modules ) {
- $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules );
+ wfDeprecated( __METHOD__, '1.26' );
}
/**
@@ -797,9 +825,9 @@ class OutputPage extends ContextSource {
# this breaks strtotime().
$clientHeader = preg_replace( '/;.*$/', '', $clientHeader );
- wfSuppressWarnings(); // E_STRICT system time bitching
+ MediaWiki\suppressWarnings(); // E_STRICT system time bitching
$clientHeaderTime = strtotime( $clientHeader );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$clientHeaderTime ) {
wfDebug( __METHOD__
. ": unable to parse the client's If-Modified-Since header: $clientHeader\n" );
@@ -826,10 +854,10 @@ class OutputPage extends ContextSource {
}
# Not modified
- # Give a 304 response code and disable body output
+ # Give a 304 Not Modified response code and disable body output
wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' );
ini_set( 'zlib.output_compression', 0 );
- $this->getRequest()->response()->header( "HTTP/1.1 304 Not Modified" );
+ $this->getRequest()->response()->statusHeader( 304 );
$this->sendCacheControl();
$this->disable();
@@ -1761,7 +1789,6 @@ class OutputPage extends ContextSource {
$this->addModules( $parserOutput->getModules() );
$this->addModuleScripts( $parserOutput->getModuleScripts() );
$this->addModuleStyles( $parserOutput->getModuleStyles() );
- $this->addModuleMessages( $parserOutput->getModuleMessages() );
$this->addJsConfigVars( $parserOutput->getJsConfigVars() );
$this->mPreventClickjacking = $this->mPreventClickjacking
|| $parserOutput->preventClickjacking();
@@ -1788,6 +1815,11 @@ class OutputPage extends ContextSource {
}
}
+ // enable OOUI if requested via ParserOutput
+ if ( $parserOutput->getEnableOOUI() ) {
+ $this->enableOOUI();
+ }
+
// Link flags are ignored for now, but may in the future be
// used to mark individual language links.
$linkFlags = array();
@@ -1808,7 +1840,6 @@ class OutputPage extends ContextSource {
$this->addModules( $parserOutput->getModules() );
$this->addModuleScripts( $parserOutput->getModuleScripts() );
$this->addModuleStyles( $parserOutput->getModuleStyles() );
- $this->addModuleMessages( $parserOutput->getModuleMessages() );
$this->addJsConfigVars( $parserOutput->getJsConfigVars() );
}
@@ -1978,21 +2009,20 @@ class OutputPage extends ContextSource {
* Add an HTTP header that will influence on the cache
*
* @param string $header Header name
- * @param array|null $option
- * @todo FIXME: Document the $option parameter; it appears to be for
- * X-Vary-Options but what format is acceptable?
+ * @param string[]|null $option Options for X-Vary-Options. Possible options are:
+ * - "string-contains=$XXX" varies on whether the header value as a string
+ * contains $XXX as a substring.
+ * - "list-contains=$XXX" varies on whether the header value as a
+ * comma-separated list contains $XXX as one of the list items.
*/
- public function addVaryHeader( $header, $option = null ) {
+ public function addVaryHeader( $header, array $option = null ) {
if ( !array_key_exists( $header, $this->mVaryHeader ) ) {
- $this->mVaryHeader[$header] = (array)$option;
- } elseif ( is_array( $option ) ) {
- if ( is_array( $this->mVaryHeader[$header] ) ) {
- $this->mVaryHeader[$header] = array_merge( $this->mVaryHeader[$header], $option );
- } else {
- $this->mVaryHeader[$header] = $option;
- }
+ $this->mVaryHeader[$header] = array();
}
- $this->mVaryHeader[$header] = array_unique( (array)$this->mVaryHeader[$header] );
+ if ( !is_array( $option ) ) {
+ $option = array();
+ }
+ $this->mVaryHeader[$header] = array_unique( array_merge( $this->mVaryHeader[$header], $option ) );
}
/**
@@ -2210,8 +2240,7 @@ class OutputPage extends ContextSource {
if ( Hooks::run( "BeforePageRedirect", array( $this, &$redirect, &$code ) ) ) {
if ( $code == '301' || $code == '303' ) {
if ( !$config->get( 'DebugRedirects' ) ) {
- $message = HttpStatus::getMessage( $code );
- $response->header( "HTTP/1.1 $code $message" );
+ $response->statusHeader( $code );
}
$this->mLastModified = wfTimestamp( TS_RFC2822 );
}
@@ -2233,10 +2262,7 @@ class OutputPage extends ContextSource {
return;
} elseif ( $this->mStatusCode ) {
- $message = HttpStatus::getMessage( $this->mStatusCode );
- if ( $message ) {
- $response->header( 'HTTP/1.1 ' . $this->mStatusCode . ' ' . $message );
- }
+ $response->statusHeader( $this->mStatusCode );
}
# Buffer output; final headers may depend on later processing
@@ -2258,14 +2284,14 @@ class OutputPage extends ContextSource {
if ( $this->mArticleBodyOnly ) {
echo $this->mBodytext;
} else {
-
$sk = $this->getSkin();
// add skin specific modules
$modules = $sk->getDefaultModules();
- // enforce various default modules for all skins
+ // Enforce various default modules for all skins
$coreModules = array(
- // keep this list as small as possible
+ // Keep this list as small as possible
+ 'site',
'mediawiki.page.startup',
'mediawiki.user',
);
@@ -2672,16 +2698,14 @@ class OutputPage extends ContextSource {
}
$ret .= Html::element( 'title', null, $this->getHTMLTitle() ) . "\n";
+ $ret .= $this->getInlineHeadScripts() . "\n";
+ $ret .= $this->buildCssLinks() . "\n";
+ $ret .= $this->getExternalHeadScripts() . "\n";
foreach ( $this->getHeadLinksArray() as $item ) {
$ret .= $item . "\n";
}
- // No newline after buildCssLinks since makeResourceLoaderLink did that already
- $ret .= $this->buildCssLinks();
-
- $ret .= $this->getHeadScripts() . "\n";
-
foreach ( $this->mHeadItems as $item ) {
$ret .= $item . "\n";
}
@@ -2729,29 +2753,31 @@ class OutputPage extends ContextSource {
*/
public function getResourceLoader() {
if ( is_null( $this->mResourceLoader ) ) {
- $this->mResourceLoader = new ResourceLoader( $this->getConfig() );
+ $this->mResourceLoader = new ResourceLoader(
+ $this->getConfig(),
+ LoggerFactory::getInstance( 'resourceloader' )
+ );
}
return $this->mResourceLoader;
}
/**
- * @todo Document
+ * Construct neccecary html and loader preset states to load modules on a page.
+ *
+ * Use getHtmlFromLoaderLinks() to convert this array to HTML.
+ *
* @param array|string $modules One or more module names
* @param string $only ResourceLoaderModule TYPE_ class constant
- * @param bool $useESI
- * @param array $extraQuery Array with extra query parameters to add to each
- * request. array( param => value ).
- * @param bool $loadCall If true, output an (asynchronous) mw.loader.load()
- * call rather than a "<script src='...'>" tag.
- * @return string The html "<script>", "<link>" and "<style>" tags
- */
- public function makeResourceLoaderLink( $modules, $only, $useESI = false,
- array $extraQuery = array(), $loadCall = false
- ) {
+ * @param array $extraQuery [optional] Array with extra query parameters for the request
+ * @return array A list of HTML strings and array of client loader preset states
+ */
+ public function makeResourceLoaderLink( $modules, $only, array $extraQuery = array() ) {
$modules = (array)$modules;
$links = array(
- 'html' => '',
+ // List of html strings
+ 'html' => array(),
+ // Associative array of module names and their states
'states' => array(),
);
@@ -2768,8 +2794,8 @@ class OutputPage extends ContextSource {
if ( ResourceLoader::inDebugMode() ) {
// Recursively call us for every item
foreach ( $modules as $name ) {
- $link = $this->makeResourceLoaderLink( $name, $only, $useESI );
- $links['html'] .= $link['html'];
+ $link = $this->makeResourceLoaderLink( $name, $only, $extraQuery );
+ $links['html'] = array_merge( $links['html'], $link['html'] );
$links['states'] += $link['states'];
}
return $links;
@@ -2783,7 +2809,6 @@ class OutputPage extends ContextSource {
// Create keyed-by-source and then keyed-by-group list of module objects from modules list
$sortedModules = array();
$resourceLoader = $this->getResourceLoader();
- $resourceLoaderUseESI = $this->getConfig()->get( 'ResourceLoaderUseESI' );
foreach ( $modules as $name ) {
$module = $resourceLoader->getModule( $name );
# Check that we're allowed to include this module on this page
@@ -2849,21 +2874,18 @@ class OutputPage extends ContextSource {
// Inline private modules. These can't be loaded through load.php for security
// reasons, see bug 34907. Note that these modules should be loaded from
- // getHeadScripts() before the first loader call. Otherwise other modules can't
+ // getExternalHeadScripts() before the first loader call. Otherwise other modules can't
// properly use them as dependencies (bug 30914)
if ( $group === 'private' ) {
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
- $links['html'] .= Html::inlineStyle(
+ $links['html'][] = Html::inlineStyle(
$resourceLoader->makeModuleResponse( $context, $grpModules )
);
} else {
- $links['html'] .= Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- $resourceLoader->makeModuleResponse( $context, $grpModules )
- )
+ $links['html'][] = ResourceLoader::makeInlineScript(
+ $resourceLoader->makeModuleResponse( $context, $grpModules )
);
}
- $links['html'] .= "\n";
continue;
}
@@ -2874,65 +2896,44 @@ class OutputPage extends ContextSource {
// and we shouldn't be putting timestamps in Squid-cached HTML
$version = null;
if ( $group === 'user' ) {
- // Get the maximum timestamp
- $timestamp = 1;
- foreach ( $grpModules as $module ) {
- $timestamp = max( $timestamp, $module->getModifiedTime( $context ) );
- }
- // Add a version parameter so cache will break when things change
- $query['version'] = wfTimestamp( TS_ISO_8601_BASIC, $timestamp );
+ $query['version'] = $resourceLoader->getCombinedVersion( $context, array_keys( $grpModules ) );
}
$query['modules'] = ResourceLoader::makePackedModulesString( array_keys( $grpModules ) );
$moduleContext = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
$url = $resourceLoader->createLoaderURL( $source, $moduleContext, $extraQuery );
- if ( $useESI && $resourceLoaderUseESI ) {
- $esi = Xml::element( 'esi:include', array( 'src' => $url ) );
- if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
- $link = Html::inlineStyle( $esi );
- } else {
- $link = Html::inlineScript( $esi );
- }
+ // Automatically select style/script elements
+ if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
+ $link = Html::linkedStyle( $url );
} else {
- // Automatically select style/script elements
- if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
- $link = Html::linkedStyle( $url );
- } elseif ( $loadCall ) {
- $link = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- Xml::encodeJsCall( 'mw.loader.load', array( $url, 'text/javascript', true ) )
- )
- );
+ if ( $context->getRaw() || $isRaw ) {
+ // Startup module can't load itself, needs to use <script> instead of mw.loader.load
+ $link = Html::element( 'script', array(
+ // In SpecialJavaScriptTest, QUnit must load synchronous
+ 'async' => !isset( $extraQuery['sync'] ),
+ 'src' => $url
+ ) );
} else {
- $link = Html::linkedScript( $url );
- if ( !$context->getRaw() && !$isRaw ) {
- // Wrap only=script / only=combined requests in a conditional as
- // browsers not supported by the startup module would unconditionally
- // execute this module. Otherwise users will get "ReferenceError: mw is
- // undefined" or "jQuery is undefined" from e.g. a "site" module.
- $link = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- Xml::encodeJsCall( 'document.write', array( $link ) )
- )
- );
- }
+ $link = ResourceLoader::makeInlineScript(
+ Xml::encodeJsCall( 'mw.loader.load', array( $url ) )
+ );
+ }
- // For modules requested directly in the html via <link> or <script>,
- // tell mw.loader they are being loading to prevent duplicate requests.
- foreach ( $grpModules as $key => $module ) {
- // Don't output state=loading for the startup module..
- if ( $key !== 'startup' ) {
- $links['states'][$key] = 'loading';
- }
+ // For modules requested directly in the html via <script> or mw.loader.load
+ // tell mw.loader they are being loading to prevent duplicate requests.
+ foreach ( $grpModules as $key => $module ) {
+ // Don't output state=loading for the startup module.
+ if ( $key !== 'startup' ) {
+ $links['states'][$key] = 'loading';
}
}
}
if ( $group == 'noscript' ) {
- $links['html'] .= Html::rawElement( 'noscript', array(), $link ) . "\n";
+ $links['html'][] = Html::rawElement( 'noscript', array(), $link );
} else {
- $links['html'] .= $link . "\n";
+ $links['html'][] = $link;
}
}
}
@@ -2946,26 +2947,26 @@ class OutputPage extends ContextSource {
* @return string HTML
*/
protected static function getHtmlFromLoaderLinks( array $links ) {
- $html = '';
+ $html = array();
$states = array();
foreach ( $links as $link ) {
if ( !is_array( $link ) ) {
- $html .= $link;
+ $html[] = $link;
} else {
- $html .= $link['html'];
+ $html = array_merge( $html, $link['html'] );
$states += $link['states'];
}
}
+ // Filter out empty values
+ $html = array_filter( $html, 'strlen' );
if ( count( $states ) ) {
- $html = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeLoaderStateScript( $states )
- )
- ) . "\n" . $html;
+ array_unshift( $html, ResourceLoader::makeInlineScript(
+ ResourceLoader::makeLoaderStateScript( $states )
+ ) );
}
- return $html;
+ return WrappedString::join( "\n", $html );
}
/**
@@ -2975,127 +2976,149 @@ class OutputPage extends ContextSource {
* @return string HTML fragment
*/
function getHeadScripts() {
- // Startup - this will immediately load jquery and mediawiki modules
+ return $this->getInlineHeadScripts() . "\n" . $this->getExternalHeadScripts();
+ }
+
+ /**
+ * <script src="..."> tags for "<head>". This is the startup module
+ * and other modules marked with position 'top'.
+ *
+ * @return string HTML fragment
+ */
+ function getExternalHeadScripts() {
$links = array();
- $links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
- // Load config before anything else
+ // Startup - this provides the client with the module manifest and loads jquery and mediawiki base modules
+ $links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS );
+
+ return self::getHtmlFromLoaderLinks( $links );
+ }
+
+ /**
+ * <script>...</script> tags to put in "<head>".
+ *
+ * @return string HTML fragment
+ */
+ function getInlineHeadScripts() {
+ $links = array();
+
+ // Client profile classes for <html>. Allows for easy hiding/showing of UI components.
+ // Must be done synchronously on every page to avoid flashes of wrong content.
+ // Note: This class distinguishes MediaWiki-supported JavaScript from the rest.
+ // The "rest" includes browsers that support JavaScript but not supported by our runtime.
+ // For the performance benefit of the majority, this is added unconditionally here and is
+ // then fixed up by the startup module for unsupported browsers.
$links[] = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeConfigSetScript( $this->getJSVars() )
- )
+ 'document.documentElement.className = document.documentElement.className'
+ . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );'
+ );
+
+ // Load config before anything else
+ $links[] = ResourceLoader::makeInlineScript(
+ ResourceLoader::makeConfigSetScript( $this->getJSVars() )
);
// Load embeddable private modules before any loader links
// This needs to be TYPE_COMBINED so these modules are properly wrapped
// in mw.loader.implement() calls and deferred until mw.user is available
- $embedScripts = array( 'user.options', 'user.tokens' );
+ $embedScripts = array( 'user.options' );
$links[] = $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED );
-
- // Scripts and messages "only" requests marked for top inclusion
- // Messages should go first
- $links[] = $this->makeResourceLoaderLink(
- $this->getModuleMessages( true, 'top' ),
- ResourceLoaderModule::TYPE_MESSAGES
- );
- $links[] = $this->makeResourceLoaderLink(
- $this->getModuleScripts( true, 'top' ),
- ResourceLoaderModule::TYPE_SCRIPTS
- );
+ // Separate user.tokens as otherwise caching will be allowed (T84960)
+ $links[] = $this->makeResourceLoaderLink( 'user.tokens', ResourceLoaderModule::TYPE_COMBINED );
// Modules requests - let the client calculate dependencies and batch requests as it likes
// Only load modules that have marked themselves for loading at the top
$modules = $this->getModules( true, 'top' );
if ( $modules ) {
- $links[] = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
- )
+ $links[] = ResourceLoader::makeInlineScript(
+ Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
);
}
- if ( $this->getConfig()->get( 'ResourceLoaderExperimentalAsyncLoading' ) ) {
- $links[] = $this->getScriptsForBottomQueue( true );
- }
+ // "Scripts only" modules marked for top inclusion
+ $links[] = $this->makeResourceLoaderLink(
+ $this->getModuleScripts( true, 'top' ),
+ ResourceLoaderModule::TYPE_SCRIPTS
+ );
return self::getHtmlFromLoaderLinks( $links );
}
/**
- * JS stuff to put at the 'bottom', which can either be the bottom of the
- * "<body>" or the bottom of the "<head>" depending on
- * $wgResourceLoaderExperimentalAsyncLoading: modules marked with position
- * 'bottom', legacy scripts ($this->mScripts), user preferences, site JS
- * and user JS.
+ * JS stuff to put at the 'bottom', which goes at the bottom of the `<body>`.
+ * These are modules marked with position 'bottom', legacy scripts ($this->mScripts),
+ * site JS, and user JS.
*
- * @param bool $inHead If true, this HTML goes into the "<head>",
- * if false it goes into the "<body>".
+ * @param bool $unused Previously used to let this method change its output based
+ * on whether it was called by getExternalHeadScripts() or getBottomScripts().
* @return string
*/
- function getScriptsForBottomQueue( $inHead ) {
- // Scripts and messages "only" requests marked for bottom inclusion
+ function getScriptsForBottomQueue( $unused = null ) {
+ // Scripts "only" requests marked for bottom inclusion
// If we're in the <head>, use load() calls rather than <script src="..."> tags
- // Messages should go first
$links = array();
- $links[] = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'bottom' ),
- ResourceLoaderModule::TYPE_MESSAGES, /* $useESI = */ false, /* $extraQuery = */ array(),
- /* $loadCall = */ $inHead
- );
+
$links[] = $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ),
- ResourceLoaderModule::TYPE_SCRIPTS, /* $useESI = */ false, /* $extraQuery = */ array(),
- /* $loadCall = */ $inHead
+ ResourceLoaderModule::TYPE_SCRIPTS
+ );
+
+ $links[] = $this->makeResourceLoaderLink( $this->getModuleStyles( true, 'bottom' ),
+ ResourceLoaderModule::TYPE_STYLES
);
// Modules requests - let the client calculate dependencies and batch requests as it likes
// Only load modules that have marked themselves for loading at the bottom
$modules = $this->getModules( true, 'bottom' );
if ( $modules ) {
- $links[] = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- Xml::encodeJsCall( 'mw.loader.load', array( $modules, null, true ) )
- )
+ $links[] = ResourceLoader::makeInlineScript(
+ Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
);
}
// Legacy Scripts
- $links[] = "\n" . $this->mScripts;
-
- // Add site JS if enabled
- $links[] = $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS,
- /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
- );
+ $links[] = $this->mScripts;
// Add user JS if enabled
+ // This must use TYPE_COMBINED instead of only=scripts so that its request is handled by
+ // mw.loader.implement() which ensures that execution is scheduled after the "site" module.
if ( $this->getConfig()->get( 'AllowUserJs' )
&& $this->getUser()->isLoggedIn()
&& $this->getTitle()
&& $this->getTitle()->isJsSubpage()
&& $this->userCanPreview()
) {
- # XXX: additional security check/prompt?
- // We're on a preview of a JS subpage
- // Exclude this page from the user module in case it's in there (bug 26283)
- $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false,
- array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead
+ // We're on a preview of a JS subpage. Exclude this page from the user module (T28283)
+ // and include the draft contents as a raw script instead.
+ $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED,
+ array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() )
);
// Load the previewed JS
- $links[] = Html::inlineScript( "\n"
- . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n";
+ $links[] = ResourceLoader::makeInlineScript(
+ Xml::encodeJsCall( 'mw.loader.using', array(
+ array( 'user', 'site' ),
+ new XmlJsCode(
+ 'function () {'
+ . Xml::encodeJsCall( '$.globalEval', array(
+ $this->getRequest()->getText( 'wpTextbox1' )
+ ) )
+ . '}'
+ )
+ ) )
+ );
// FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
// asynchronously and may arrive *after* the inline script here. So the previewed code
- // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js...
+ // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js.
+ // Similarly, when previewing ./common.js and the user module does arrive first, it will
+ // arrive without common.js and the inline script runs after. Thus running common after
+ // the excluded subpage.
} else {
// Include the user module normally, i.e., raw to avoid it being wrapped in a closure.
- $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS,
- /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
- );
+ $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED );
}
// Group JS is only enabled if site JS is enabled.
- $links[] = $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED,
- /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
- );
+ $links[] = $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED );
return self::getHtmlFromLoaderLinks( $links );
}
@@ -3105,17 +3128,10 @@ class OutputPage extends ContextSource {
* @return string
*/
function getBottomScripts() {
- // Optimise jQuery ready event cross-browser.
- // This also enforces $.isReady to be true at </body> which fixes the
- // mw.loader bug in Firefox with using document.write between </body>
- // and the DOMContentReady event (bug 47457).
- $html = Html::inlineScript( 'if(window.jQuery)jQuery.ready();' );
-
- if ( !$this->getConfig()->get( 'ResourceLoaderExperimentalAsyncLoading' ) ) {
- $html .= $this->getScriptsForBottomQueue( false );
- }
+ // In case the skin wants to add bottom CSS
+ $this->getSkin()->setupSkinUserCss( $this );
- return $html;
+ return $this->getScriptsForBottomQueue();
}
/**
@@ -3426,33 +3442,37 @@ class OutputPage extends ContextSource {
$lang = $this->getTitle()->getPageLanguage();
if ( $lang->hasVariants() ) {
$variants = $lang->getVariants();
- foreach ( $variants as $_v ) {
- $tags["variant-$_v"] = Html::element( 'link', array(
+ foreach ( $variants as $variant ) {
+ $tags["variant-$variant"] = Html::element( 'link', array(
'rel' => 'alternate',
- 'hreflang' => wfBCP47( $_v ),
- 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $_v ) ) )
+ 'hreflang' => wfBCP47( $variant ),
+ 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $variant ) ) )
);
}
+ # x-default link per https://support.google.com/webmasters/answer/189077?hl=en
+ $tags["variant-x-default"] = Html::element( 'link', array(
+ 'rel' => 'alternate',
+ 'hreflang' => 'x-default',
+ 'href' => $this->getTitle()->getLocalURL() ) );
}
- # x-default link per https://support.google.com/webmasters/answer/189077?hl=en
- $tags["variant-x-default"] = Html::element( 'link', array(
- 'rel' => 'alternate',
- 'hreflang' => 'x-default',
- 'href' => $this->getTitle()->getLocalURL() ) );
}
# Copyright
- $copyright = '';
- if ( $config->get( 'RightsPage' ) ) {
- $copy = Title::newFromText( $config->get( 'RightsPage' ) );
+ if ( $this->copyrightUrl !== null ) {
+ $copyright = $this->copyrightUrl;
+ } else {
+ $copyright = '';
+ if ( $config->get( 'RightsPage' ) ) {
+ $copy = Title::newFromText( $config->get( 'RightsPage' ) );
- if ( $copy ) {
- $copyright = $copy->getLocalURL();
+ if ( $copy ) {
+ $copyright = $copy->getLocalURL();
+ }
}
- }
- if ( !$copyright && $config->get( 'RightsUrl' ) ) {
- $copyright = $config->get( 'RightsUrl' );
+ if ( !$copyright && $config->get( 'RightsUrl' ) ) {
+ $copyright = $config->get( 'RightsUrl' );
+ }
}
if ( $copyright ) {
@@ -3513,8 +3533,25 @@ class OutputPage extends ContextSource {
if ( $canonicalUrl !== false ) {
$canonicalUrl = wfExpandUrl( $canonicalUrl, PROTO_CANONICAL );
} else {
- $reqUrl = $this->getRequest()->getRequestURL();
- $canonicalUrl = wfExpandUrl( $reqUrl, PROTO_CANONICAL );
+ if ( $this->isArticleRelated() ) {
+ // This affects all requests where "setArticleRelated" is true. This is
+ // typically all requests that show content (query title, curid, oldid, diff),
+ // and all wikipage actions (edit, delete, purge, info, history etc.).
+ // It does not apply to File pages and Special pages.
+ // 'history' and 'info' actions address page metadata rather than the page
+ // content itself, so they may not be canonicalized to the view page url.
+ // TODO: this ought to be better encapsulated in the Action class.
+ $action = Action::getActionName( $this->getContext() );
+ if ( in_array( $action, array( 'history', 'info' ) ) ) {
+ $query = "action={$action}";
+ } else {
+ $query = '';
+ }
+ $canonicalUrl = $this->getTitle()->getCanonicalURL( $query );
+ } else {
+ $reqUrl = $this->getRequest()->getRequestURL();
+ $canonicalUrl = wfExpandUrl( $reqUrl, PROTO_CANONICAL );
+ }
}
}
if ( $canonicalUrl !== false ) {
@@ -3613,10 +3650,10 @@ class OutputPage extends ContextSource {
'noscript' => array()
);
$links = array();
- $otherTags = ''; // Tags to append after the normal <link> tags
+ $otherTags = array(); // Tags to append after the normal <link> tags
$resourceLoader = $this->getResourceLoader();
- $moduleStyles = $this->getModuleStyles();
+ $moduleStyles = $this->getModuleStyles( true, 'top' );
// Per-site custom styles
$moduleStyles[] = 'site';
@@ -3629,10 +3666,10 @@ class OutputPage extends ContextSource {
) {
// We're on a preview of a CSS subpage
// Exclude this page from the user module in case it's in there (bug 26283)
- $link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false,
+ $link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES,
array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() )
);
- $otherTags .= $link['html'];
+ $otherTags = array_merge( $otherTags, $link['html'] );
// Load the previewed CSS
// If needed, Janus it first. This is user-supplied CSS, so it's
@@ -3641,7 +3678,7 @@ class OutputPage extends ContextSource {
if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) {
$previewedCSS = CSSJanus::transform( $previewedCSS, true, false );
}
- $otherTags .= Html::inlineStyle( $previewedCSS ) . "\n";
+ $otherTags[] = Html::inlineStyle( $previewedCSS ) . "\n";
} else {
// Load the user styles normally
$moduleStyles[] = 'user';
@@ -3655,9 +3692,17 @@ class OutputPage extends ContextSource {
if ( !$module ) {
continue;
}
+ if ( $name === 'site' ) {
+ // HACK: The site module shouldn't be fragmented with a cache group and
+ // http request. But in order to ensure its styles are separated and after the
+ // ResourceLoaderDynamicStyles marker, pretend it is in a group called 'site'.
+ // The scripts remain ungrouped and rides the bottom queue.
+ $styles['site'][] = $name;
+ continue;
+ }
$group = $module->getGroup();
- // Modules in groups different than the ones listed on top (see $styles assignment)
- // will be placed in the "other" group
+ // Modules in groups other than the ones needing special treatment (see $styles assignment)
+ // will be placed in the "other" style category.
$styles[isset( $styles[$group] ) ? $group : 'other'][] = $name;
}
@@ -3674,9 +3719,9 @@ class OutputPage extends ContextSource {
$links[] = Html::element(
'meta',
array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' )
- ) . "\n";
+ );
- // Add site, private and user styles
+ // Add site-specific and user-specific styles
// 'private' at present only contains user.options, so put that before 'user'
// Any future private modules will likely have a similar user-specific character
foreach ( array( 'site', 'noscript', 'private', 'user' ) as $group ) {
@@ -3686,7 +3731,7 @@ class OutputPage extends ContextSource {
}
// Add stuff in $otherTags (previewed user CSS if applicable)
- return self::getHtmlFromLoaderLinks( $links ) . $otherTags;
+ return self::getHtmlFromLoaderLinks( $links ) . implode( '', $otherTags );
}
/**
@@ -3918,14 +3963,40 @@ class OutputPage extends ContextSource {
}
/**
+ * Helper function to setup the PHP implementation of OOUI to use in this request.
+ *
+ * @since 1.26
+ * @param String $skinName The Skin name to determine the correct OOUI theme
+ * @param String $dir Language direction
+ */
+ public static function setupOOUI( $skinName = '', $dir = 'ltr' ) {
+ $themes = ExtensionRegistry::getInstance()->getAttribute( 'SkinOOUIThemes' );
+ // Make keys (skin names) lowercase for case-insensitive matching.
+ $themes = array_change_key_case( $themes, CASE_LOWER );
+ $theme = isset( $themes[ $skinName ] ) ? $themes[ $skinName ] : 'MediaWiki';
+ // For example, 'OOUI\MediaWikiTheme'.
+ $themeClass = "OOUI\\{$theme}Theme";
+ OOUI\Theme::setSingleton( new $themeClass() );
+ OOUI\Element::setDefaultDir( $dir );
+ }
+
+ /**
* Add ResourceLoader module styles for OOUI and set up the PHP implementation of it for use with
* MediaWiki and this OutputPage instance.
*
* @since 1.25
*/
public function enableOOUI() {
- OOUI\Theme::setSingleton( new OOUI\MediaWikiTheme() );
- OOUI\Element::setDefaultDir( $this->getLanguage()->getDir() );
- $this->addModuleStyles( 'oojs-ui.styles' );
+ self::setupOOUI(
+ strtolower( $this->getSkin()->getSkinName() ),
+ $this->getLanguage()->getDir()
+ );
+ $this->addModuleStyles( array(
+ 'oojs-ui.styles',
+ 'oojs-ui.styles.icons',
+ 'oojs-ui.styles.indicators',
+ 'oojs-ui.styles.textures',
+ 'mediawiki.widgets.styles',
+ ) );
}
}
diff --git a/includes/PHPVersionCheck.php b/includes/PHPVersionCheck.php
index eee9aa9c..ba3ff1ab 100644
--- a/includes/PHPVersionCheck.php
+++ b/includes/PHPVersionCheck.php
@@ -25,13 +25,23 @@
/**
* Check php version and that external dependencies are installed, and
* display an informative error if either condition is not satisfied.
+ *
+ * @note Since we can't rely on anything, the minimum PHP versions and MW current
+ * version are hardcoded here
*/
function wfEntryPointCheck( $entryPoint ) {
+ $mwVersion = '1.26';
+ $minimumVersionPHP = '5.3.3';
+ $phpVersion = PHP_VERSION;
+
if ( !function_exists( 'version_compare' )
- || version_compare( PHP_VERSION, '5.3.3' ) < 0
- || !file_exists( dirname( __FILE__ ) . '/../vendor/autoload.php' )
+ || version_compare( $phpVersion, $minimumVersionPHP ) < 0
) {
- wfPHPVersionError( $entryPoint );
+ wfPHPVersionError( $entryPoint, $mwVersion, $minimumVersionPHP, $phpVersion );
+ }
+
+ if ( !file_exists( dirname( __FILE__ ) . '/../vendor/autoload.php' ) ) {
+ wfMissingVendorError( $entryPoint, $mwVersion );
}
}
@@ -49,47 +59,39 @@ function wfEntryPointCheck( $entryPoint ) {
* - api.php
* - mw-config/index.php
* - cli
- *
- * @note Since we can't rely on anything, the minimum PHP versions and MW current
- * version are hardcoded here
+ * @param string $mwVersion The number of the MediaWiki version used
+ * @param string $title HTML code to be put within an <h2> tag
+ * @param string $shortText
+ * @param string $longText
+ * @param string $longHtml
*/
-function wfPHPVersionError( $type ) {
- $mwVersion = '1.25';
- $minimumVersionPHP = '5.3.3';
-
- $phpVersion = PHP_VERSION;
+function wfGenericError( $type, $mwVersion, $title, $shortText, $longText, $longHtml ) {
$protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
- $message = "MediaWiki $mwVersion requires at least "
- . "PHP version $minimumVersionPHP, you are using PHP $phpVersion. Installing some "
- . " external dependencies (e.g. via composer) is also required.";
if ( $type == 'cli' ) {
- $finalOutput = "Error: You are missing some external dependencies or are using on older PHP version. \n"
- . "MediaWiki $mwVersion needs PHP $minimumVersionPHP or higher.\n\n"
- . "Check if you have a newer php executable with a different name, such as php5.\n\n"
- . "MediaWiki now also has some external dependencies that need to be installed\n"
- . "via composer or from a separate git repo. Please see\n"
- . "https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries\n"
- . "for help on installing the required components.";
- } elseif ( $type == 'index.php' || $type == 'mw-config/index.php' ) {
- $pathinfo = pathinfo( $_SERVER['SCRIPT_NAME'] );
- if ( $type == 'mw-config/index.php' ) {
- $dirname = dirname( $pathinfo['dirname'] );
- } else {
- $dirname = $pathinfo['dirname'];
- }
- $encLogo = htmlspecialchars(
- str_replace( '//', '/', $dirname . '/' ) .
- 'resources/assets/mediawiki.png'
- );
-
+ $finalOutput = $longText;
+ } else {
header( "$protocol 500 MediaWiki configuration Error" );
- header( 'Content-type: text/html; charset=UTF-8' );
// Don't cache error pages! They cause no end of trouble...
header( 'Cache-control: none' );
header( 'Pragma: no-cache' );
- $finalOutput = <<<HTML
+ if ( $type == 'index.php' || $type == 'mw-config/index.php' ) {
+ $pathinfo = pathinfo( $_SERVER['SCRIPT_NAME'] );
+ if ( $type == 'mw-config/index.php' ) {
+ $dirname = dirname( $pathinfo['dirname'] );
+ } else {
+ $dirname = $pathinfo['dirname'];
+ }
+ $encLogo = htmlspecialchars(
+ str_replace( '//', '/', $dirname . '/' ) .
+ 'resources/assets/mediawiki.png'
+ );
+ $shortHtml = htmlspecialchars( $shortText );
+
+ header( 'Content-type: text/html; charset=UTF-8' );
+
+ $finalOutput = <<<HTML
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
@@ -120,10 +122,43 @@ function wfPHPVersionError( $type ) {
<h1>MediaWiki {$mwVersion} internal error</h1>
<div class='error'>
<p>
- {$message}
+ {$shortHtml}
</p>
- <h2>Supported PHP versions</h2>
+ <h2>{$title}</h2>
<p>
+ {$longHtml}
+ </p>
+ </div>
+ </body>
+</html>
+HTML;
+ // Handle everything that's not index.php
+ } else {
+ // So nothing thinks this is JS or CSS
+ $finalOutput = ( $type == 'load.php' ) ? "/* $shortText */" : $shortText;
+ }
+ }
+ echo "$finalOutput\n";
+ die( 1 );
+}
+
+/**
+ * Display an error for the minimum PHP version requirement not being satisfied.
+ *
+ * @param string $type See wfGenericError
+ * @param string $mwVersion See wfGenericError
+ * @param string $minimumVersionPHP The minimum PHP version supported by MediaWiki
+ * @param string $phpVersion The current PHP version
+ */
+function wfPHPVersionError( $type, $mwVersion, $minimumVersionPHP, $phpVersion ) {
+ $shortText = "MediaWiki $mwVersion requires at least "
+ . "PHP version $minimumVersionPHP, you are using PHP $phpVersion.";
+
+ $longText = "Error: You might be using on older PHP version. \n"
+ . "MediaWiki $mwVersion needs PHP $minimumVersionPHP or higher.\n\n"
+ . "Check if you have a newer php executable with a different name, such as php5.\n\n";
+
+ $longHtml = <<<HTML
Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive
security or bugfix updates.
@@ -134,24 +169,31 @@ function wfPHPVersionError( $type ) {
of MediaWiki from our website. See our
<a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
for details of which versions are compatible with prior versions of PHP.
- </p>
- <h2>External dependencies</h2>
- <p>
+HTML;
+ wfGenericError( $type, $mwVersion, 'Supported PHP versions', $shortText, $longText, $longHtml );
+}
+
+/**
+ * Display an error for the vendor/autoload.php file not being found.
+ *
+ * @param string $type See wfGenericError
+ * @param string $mwVersion See wfGenericError
+ */
+function wfMissingVendorError( $type, $mwVersion ) {
+ $shortText = "Installing some external dependencies (e.g. via composer) is required.";
+
+ $longText = "Error: You are missing some external dependencies. \n"
+ . "MediaWiki now also has some external dependencies that need to be installed\n"
+ . "via composer or from a separate git repo. Please see\n"
+ . "https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries\n"
+ . "for help on installing the required components.";
+
+ $longHtml = <<<HTML
MediaWiki now also has some external dependencies that need to be installed via
composer or from a separate git repo. Please see
<a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a>
for help on installing the required components.
- </p>
- </div>
- </body>
-</html>
HTML;
- // Handle everything that's not index.php
- } else {
- // So nothing thinks this is JS or CSS
- $finalOutput = ( $type == 'load.php' ) ? "/* $message */" : $message;
- header( "$protocol 500 MediaWiki configuration Error" );
- }
- echo "$finalOutput\n";
- die( 1 );
+
+ wfGenericError( $type, $mwVersion, 'External dependencies', $shortText, $longText, $longHtml );
}
diff --git a/includes/Preferences.php b/includes/Preferences.php
index a5239331..d0475c17 100644
--- a/includes/Preferences.php
+++ b/includes/Preferences.php
@@ -124,6 +124,7 @@ class Preferences {
$disable = !$user->isAllowed( 'editmyoptions' );
+ $defaultOptions = User::getDefaultOptions();
## Prod in defaults from the user
foreach ( $defaultPreferences as $name => &$info ) {
$prefFromUser = self::getOptionFromUser( $name, $info, $user );
@@ -131,7 +132,6 @@ class Preferences {
$info['disabled'] = 'disabled';
}
$field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
- $defaultOptions = User::getDefaultOptions();
$globalDefault = isset( $defaultOptions[$name] )
? $defaultOptions[$name]
: null;
@@ -657,8 +657,9 @@ class Preferences {
$now = wfTimestampNow();
$lang = $context->getLanguage();
$nowlocal = Xml::element( 'span', array( 'id' => 'wpLocalTime' ),
- $lang->time( $now, true ) );
- $nowserver = $lang->time( $now, false ) .
+ $lang->userTime( $now, $user ) );
+ $nowserver = $lang->userTime( $now, $user,
+ array( 'format' => false, 'timecorrection' => false ) ) .
Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) );
$defaultPreferences['nowserver'] = array(
@@ -750,7 +751,11 @@ class Preferences {
'type' => 'select',
'section' => 'rendering/advancedrendering',
'options' => $stubThresholdOptions,
- 'label-raw' => $context->msg( 'stub-threshold' )->text(), // Raw HTML message. Yay?
+ // This is not a raw HTML message; label-raw is needed for the manual <a></a>
+ 'label-raw' => $context->msg( 'stub-threshold' )->rawParams(
+ '<a href="#" class="stub">' .
+ $context->msg( 'stub-threshold-sample-link' )->parse() .
+ '</a>' )->parse(),
);
$defaultPreferences['showhiddencats'] = array(
@@ -1293,12 +1298,19 @@ class Preferences {
$opt = array();
$localTZoffset = $context->getConfig()->get( 'LocalTZoffset' );
+ $timeZoneList = self::getTimeZoneList( $context->getLanguage() );
+
$timestamp = MWTimestamp::getLocalInstance();
// Check that the LocalTZoffset is the same as the local time zone offset
if ( $localTZoffset == $timestamp->format( 'Z' ) / 60 ) {
+ $timezoneName = $timestamp->getTimezone()->getName();
+ // Localize timezone
+ if ( isset( $timeZoneList[$timezoneName] ) ) {
+ $timezoneName = $timeZoneList[$timezoneName]['name'];
+ }
$server_tz_msg = $context->msg(
'timezoneuseserverdefault',
- $timestamp->getTimezone()->getName()
+ $timezoneName
)->text();
} else {
$tzstring = sprintf(
@@ -1312,49 +1324,12 @@ class Preferences {
$opt[$context->msg( 'timezoneuseoffset' )->text()] = 'other';
$opt[$context->msg( 'guesstimezone' )->text()] = 'guess';
- if ( function_exists( 'timezone_identifiers_list' ) ) {
- # Read timezone list
- $tzs = timezone_identifiers_list();
- sort( $tzs );
-
- $tzRegions = array();
- $tzRegions['Africa'] = $context->msg( 'timezoneregion-africa' )->text();
- $tzRegions['America'] = $context->msg( 'timezoneregion-america' )->text();
- $tzRegions['Antarctica'] = $context->msg( 'timezoneregion-antarctica' )->text();
- $tzRegions['Arctic'] = $context->msg( 'timezoneregion-arctic' )->text();
- $tzRegions['Asia'] = $context->msg( 'timezoneregion-asia' )->text();
- $tzRegions['Atlantic'] = $context->msg( 'timezoneregion-atlantic' )->text();
- $tzRegions['Australia'] = $context->msg( 'timezoneregion-australia' )->text();
- $tzRegions['Europe'] = $context->msg( 'timezoneregion-europe' )->text();
- $tzRegions['Indian'] = $context->msg( 'timezoneregion-indian' )->text();
- $tzRegions['Pacific'] = $context->msg( 'timezoneregion-pacific' )->text();
- asort( $tzRegions );
-
- $prefill = array_fill_keys( array_values( $tzRegions ), array() );
- $opt = array_merge( $opt, $prefill );
-
- $now = date_create( 'now' );
-
- foreach ( $tzs as $tz ) {
- $z = explode( '/', $tz, 2 );
-
- # timezone_identifiers_list() returns a number of
- # backwards-compatibility entries. This filters them out of the
- # list presented to the user.
- if ( count( $z ) != 2 || !array_key_exists( $z[0], $tzRegions ) ) {
- continue;
- }
-
- # Localize region
- $z[0] = $tzRegions[$z[0]];
-
- $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
-
- $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] );
- $value = "ZoneInfo|$minDiff|$tz";
-
- $opt[$z[0]][$display] = $value;
+ foreach ( $timeZoneList as $timeZoneInfo ) {
+ $region = $timeZoneInfo['region'];
+ if ( !isset( $opt[$region] ) ) {
+ $opt[$region] = array();
}
+ $opt[$region][$timeZoneInfo['name']] = $timeZoneInfo['timecorrection'];
}
return $opt;
}
@@ -1393,7 +1368,7 @@ class Preferences {
}
# Max is +14:00 and min is -12:00, see:
- # http://en.wikipedia.org/wiki/Timezone
+ # https://en.wikipedia.org/wiki/Timezone
$minDiff = min( $minDiff, 840 ); # 14:00
$minDiff = max( $minDiff, - 720 ); # -12:00
return 'Offset|' . $minDiff;
@@ -1458,10 +1433,10 @@ class Preferences {
}
Hooks::run( 'PreferencesFormPreSave', array( $formData, $form, $user, &$result ) );
- $user->saveSettings();
}
$wgAuth->updateExternalDB( $user );
+ $user->saveSettings();
return $result;
}
@@ -1490,6 +1465,68 @@ class Preferences {
return Status::newGood();
}
+
+ /**
+ * Get a list of all time zones
+ * @param Language $language Language used for the localized names
+ * @return array A list of all time zones. The system name of the time zone is used as key and
+ * the value is an array which contains localized name, the timecorrection value used for
+ * preferences and the region
+ * @since 1.26
+ */
+ public static function getTimeZoneList( Language $language ) {
+ $identifiers = DateTimeZone::listIdentifiers();
+ if ( $identifiers === false ) {
+ return array();
+ }
+ sort( $identifiers );
+
+ $tzRegions = array(
+ 'Africa' => wfMessage( 'timezoneregion-africa' )->inLanguage( $language )->text(),
+ 'America' => wfMessage( 'timezoneregion-america' )->inLanguage( $language )->text(),
+ 'Antarctica' => wfMessage( 'timezoneregion-antarctica' )->inLanguage( $language )->text(),
+ 'Arctic' => wfMessage( 'timezoneregion-arctic' )->inLanguage( $language )->text(),
+ 'Asia' => wfMessage( 'timezoneregion-asia' )->inLanguage( $language )->text(),
+ 'Atlantic' => wfMessage( 'timezoneregion-atlantic' )->inLanguage( $language )->text(),
+ 'Australia' => wfMessage( 'timezoneregion-australia' )->inLanguage( $language )->text(),
+ 'Europe' => wfMessage( 'timezoneregion-europe' )->inLanguage( $language )->text(),
+ 'Indian' => wfMessage( 'timezoneregion-indian' )->inLanguage( $language )->text(),
+ 'Pacific' => wfMessage( 'timezoneregion-pacific' )->inLanguage( $language )->text(),
+ );
+ asort( $tzRegions );
+
+ $timeZoneList = array();
+
+ $now = new DateTime();
+
+ foreach ( $identifiers as $identifier ) {
+ $parts = explode( '/', $identifier, 2 );
+
+ // DateTimeZone::listIdentifiers() returns a number of
+ // backwards-compatibility entries. This filters them out of the
+ // list presented to the user.
+ if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
+ continue;
+ }
+
+ // Localize region
+ $parts[0] = $tzRegions[$parts[0]];
+
+ $dateTimeZone = new DateTimeZone( $identifier );
+ $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
+
+ $display = str_replace( '_', ' ', $parts[0] . '/' . $parts[1] );
+ $value = "ZoneInfo|$minDiff|$identifier";
+
+ $timeZoneList[$identifier] = array(
+ 'name' => $display,
+ 'timecorrection' => $value,
+ 'region' => $parts[0],
+ );
+ }
+
+ return $timeZoneList;
+ }
}
/** Some tweaks to allow js prefs to work */
diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php
index 55a4f49b..430b4b89 100644
--- a/includes/PrefixSearch.php
+++ b/includes/PrefixSearch.php
@@ -362,7 +362,11 @@ abstract class PrefixSearch {
$ns = NS_MAIN; // if searching on many always default to main
}
- $t = Title::newFromText( $search, $ns );
+ $t = null;
+ if ( is_string( $search ) ) {
+ $t = Title::newFromText( $search, $ns );
+ }
+
$prefix = $t ? $t->getDBkey() : '';
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php
index 1219da51..4cad7b74 100644
--- a/includes/ProtectionForm.php
+++ b/includes/ProtectionForm.php
@@ -157,7 +157,7 @@ class ProtectionForm {
$value = $this->mExpirySelection[$action];
}
if ( wfIsInfinity( $value ) ) {
- $time = wfGetDB( DB_SLAVE )->getInfinity();
+ $time = 'infinity';
} else {
$unix = strtotime( $value );
@@ -384,7 +384,12 @@ class ProtectionForm {
"mwProtect-$action-expires"
);
- $expiryFormOptions = '';
+ $expiryFormOptions = new XmlSelect( "wpProtectExpirySelection-$action", "mwProtectExpirySelection-$action", $this->mExpirySelection[$action] );
+ $expiryFormOptions->setAttribute( 'tabindex', '2' );
+ if ( $this->disabled ) {
+ $expiryFormOptions->setAttribute( 'disabled', 'disabled' );
+ }
+
if ( $this->mExistingExpiry[$action] ) {
if ( $this->mExistingExpiry[$action] == 'infinity' ) {
$existingExpiryMessage = $context->msg( 'protect-existing-expiry-infinity' );
@@ -394,29 +399,17 @@ class ProtectionForm {
$t = $lang->userTime( $this->mExistingExpiry[$action], $user );
$existingExpiryMessage = $context->msg( 'protect-existing-expiry', $timestamp, $d, $t );
}
- $expiryFormOptions .=
- Xml::option(
- $existingExpiryMessage->text(),
- 'existing',
- $this->mExpirySelection[$action] == 'existing'
- ) . "\n";
+ $expiryFormOptions->addOption( $existingExpiryMessage->text(), 'existing' );
}
- $expiryFormOptions .= Xml::option(
- $context->msg( 'protect-othertime-op' )->text(),
- "othertime"
- ) . "\n";
+ $expiryFormOptions->addOption( $context->msg( 'protect-othertime-op' )->text(), 'othertime' );
foreach ( explode( ',', $scExpiryOptions ) as $option ) {
if ( strpos( $option, ":" ) === false ) {
$show = $value = $option;
} else {
list( $show, $value ) = explode( ":", $option );
}
- $expiryFormOptions .= Xml::option(
- $show,
- htmlspecialchars( $value ),
- $this->mExpirySelection[$action] === $value
- ) . "\n";
+ $expiryFormOptions->addOption( $show, htmlspecialchars( $value ) );
}
# Add expiry dropdown
if ( $showProtectOptions && !$this->disabled ) {
@@ -426,12 +419,7 @@ class ProtectionForm {
{$mProtectexpiry}
</td>
<td class='mw-input'>" .
- Xml::tags( 'select',
- array(
- 'id' => "mwProtectExpirySelection-$action",
- 'name' => "wpProtectExpirySelection-$action",
- 'tabindex' => '2' ) + $this->disabledAttrib,
- $expiryFormOptions ) .
+ $expiryFormOptions->getHTML() .
"</td>
</tr></table>";
}
@@ -541,9 +529,8 @@ class ProtectionForm {
$out .= Xml::closeElement( 'fieldset' );
if ( $user->isAllowed( 'editinterface' ) ) {
- $title = Title::makeTitle( NS_MEDIAWIKI, 'Protect-dropdown' );
- $link = Linker::link(
- $title,
+ $link = Linker::linkKnown(
+ $context->msg( 'protect-dropdown' )->inContentLanguage()->getTitle(),
$context->msg( 'protect-edit-reasonlist' )->escaped(),
array(),
array( 'action' => 'edit' )
@@ -577,18 +564,18 @@ class ProtectionForm {
);
$id = 'mwProtect-level-' . $action;
- $attribs = array(
- 'id' => $id,
- 'name' => $id,
- 'size' => count( $levels ),
- ) + $this->disabledAttrib;
- $out = Xml::openElement( 'select', $attribs );
+ $select = new XmlSelect( $id, $id, $selected );
+ $select->setAttribute( 'size', count( $levels ) );
+ if ( $this->disabled ) {
+ $select->setAttribute( 'disabled', 'disabled' );
+ }
+
foreach ( $levels as $key ) {
- $out .= Xml::option( $this->getOptionLabel( $key ), $key, $key == $selected );
+ $select->addOption( $this->getOptionLabel( $key ), $key );
}
- $out .= Xml::closeElement( 'select' );
- return $out;
+
+ return $select->getHTML();
}
/**
diff --git a/includes/Revision.php b/includes/Revision.php
index 3ba6157c..32ee259f 100644
--- a/includes/Revision.php
+++ b/includes/Revision.php
@@ -194,8 +194,8 @@ class Revision implements IDBAccessObject {
if ( !isset( $attribs['title'] )
&& isset( $row->ar_namespace )
- && isset( $row->ar_title ) ) {
-
+ && isset( $row->ar_title )
+ ) {
$attribs['title'] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
}
@@ -1087,7 +1087,7 @@ class Revision implements IDBAccessObject {
/**
* Returns the content model for this revision.
*
- * If no content model was stored in the database, $this->getTitle()->getContentModel() is
+ * If no content model was stored in the database, the default content model for the title is
* used to determine the content model to use. If no title is know, CONTENT_MODEL_WIKITEXT
* is used as a last resort.
*
@@ -1097,7 +1097,11 @@ class Revision implements IDBAccessObject {
public function getContentModel() {
if ( !$this->mContentModel ) {
$title = $this->getTitle();
- $this->mContentModel = ( $title ? $title->getContentModel() : CONTENT_MODEL_WIKITEXT );
+ if ( $title ) {
+ $this->mContentModel = ContentHandler::getDefaultModelFor( $title );
+ } else {
+ $this->mContentModel = CONTENT_MODEL_WIKITEXT;
+ }
assert( !empty( $this->mContentModel ) );
}
@@ -1284,8 +1288,14 @@ class Revision implements IDBAccessObject {
if ( $wgCompressRevisions ) {
if ( function_exists( 'gzdeflate' ) ) {
- $text = gzdeflate( $text );
- $flags[] = 'gzip';
+ $deflated = gzdeflate( $text );
+
+ if ( $deflated === false ) {
+ wfLogWarning( __METHOD__ . ': gzdeflate() failed' );
+ } else {
+ $text = $deflated;
+ $flags[] = 'gzip';
+ }
} else {
wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
}
@@ -1306,6 +1316,11 @@ class Revision implements IDBAccessObject {
# This can be done periodically via maintenance/compressOld.php, and
# as pages are saved if $wgCompressRevisions is set.
$text = gzinflate( $text );
+
+ if ( $text === false ) {
+ wfLogWarning( __METHOD__ . ': gzinflate() failed' );
+ return false;
+ }
}
if ( in_array( 'object', $flags ) ) {
@@ -1626,8 +1641,9 @@ class Revision implements IDBAccessObject {
$row['content_format'] = $current->rev_content_format;
}
+ $row['title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
+
$revision = new Revision( $row );
- $revision->setTitle( Title::makeTitle( $current->page_namespace, $current->page_title ) );
} else {
$revision = null;
}
diff --git a/includes/RevisionList.php b/includes/RevisionList.php
index 6844dadc..e4174730 100644
--- a/includes/RevisionList.php
+++ b/includes/RevisionList.php
@@ -30,6 +30,7 @@ abstract class RevisionListBase extends ContextSource {
/** @var array */
protected $ids;
+ /** @var ResultWrapper|bool */
protected $res;
/** @var bool|object */
@@ -340,7 +341,8 @@ class RevisionItem extends RevisionItemBase {
* @return string
*/
protected function getRevisionLink() {
- $date = $this->list->getLanguage()->timeanddate( $this->revision->getTimestamp(), true );
+ $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
+ $this->revision->getTimestamp(), $this->list->getUser() ) );
if ( $this->isDeleted() && !$this->canViewContent() ) {
return $date;
diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php
index 96193a74..de63af79 100644
--- a/includes/Sanitizer.php
+++ b/includes/Sanitizer.php
@@ -346,12 +346,9 @@ class Sanitizer {
($space*=$space*
(?:
# The attribute value: quoted or alone
- \"([^<\"]*)\"
- | '([^<']*)'
+ \"([^<\"]*)(?:\"|\$)
+ | '([^<']*)(?:'|\$)
| ([a-zA-Z0-9!#$%&()*,\\-.\\/:;<>?@[\\]^_`{|}~]+)
- | (\#[0-9a-fA-F]+) # Technically wrong, but lots of
- # colors are specified like this.
- # We'll be normalizing it.
)
)?(?=$space|\$)/sx";
}
@@ -359,20 +356,13 @@ class Sanitizer {
}
/**
- * Cleans up HTML, removes dangerous tags and attributes, and
- * removes HTML comments
- * @param string $text
- * @param callable $processCallback Callback to do any variable or parameter
- * replacements in HTML attribute values
- * @param array|bool $args Arguments for the processing callback
+ * Return the various lists of recognized tags
* @param array $extratags For any extra tags to include
* @param array $removetags For any tags (default or extra) to exclude
- * @return string
+ * @return array
*/
- public static function removeHTMLtags( $text, $processCallback = null,
- $args = array(), $extratags = array(), $removetags = array()
- ) {
- global $wgUseTidy, $wgAllowMicrodataAttributes, $wgAllowImageTag;
+ public static function getRecognizedTagData( $extratags = array(), $removetags = array() ) {
+ global $wgAllowMicrodataAttributes, $wgAllowImageTag;
static $htmlpairsStatic, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
$htmllist, $listtags, $htmlsingleallowed, $htmlelementsStatic, $staticInitialised;
@@ -381,7 +371,6 @@ class Sanitizer {
// are changed (like in the screwed up test system) we will re-initialise the settings.
$globalContext = implode( '-', compact( 'wgAllowMicrodataAttributes', 'wgAllowImageTag' ) );
if ( !$staticInitialised || $staticInitialised != $globalContext ) {
-
$htmlpairsStatic = array( # Tags that must be closed
'b', 'bdi', 'del', 'i', 'ins', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1',
'h2', 'h3', 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's',
@@ -431,17 +420,47 @@ class Sanitizer {
}
$staticInitialised = $globalContext;
}
+
# Populate $htmlpairs and $htmlelements with the $extratags and $removetags arrays
$extratags = array_flip( $extratags );
$removetags = array_flip( $removetags );
$htmlpairs = array_merge( $extratags, $htmlpairsStatic );
$htmlelements = array_diff_key( array_merge( $extratags, $htmlelementsStatic ), $removetags );
+ return array(
+ 'htmlpairs' => $htmlpairs,
+ 'htmlsingle' => $htmlsingle,
+ 'htmlsingleonly' => $htmlsingleonly,
+ 'htmlnest' => $htmlnest,
+ 'tabletags' => $tabletags,
+ 'htmllist' => $htmllist,
+ 'listtags' => $listtags,
+ 'htmlsingleallowed' => $htmlsingleallowed,
+ 'htmlelements' => $htmlelements,
+ );
+ }
+
+ /**
+ * Cleans up HTML, removes dangerous tags and attributes, and
+ * removes HTML comments
+ * @param string $text
+ * @param callable $processCallback Callback to do any variable or parameter
+ * replacements in HTML attribute values
+ * @param array|bool $args Arguments for the processing callback
+ * @param array $extratags For any extra tags to include
+ * @param array $removetags For any tags (default or extra) to exclude
+ * @return string
+ */
+ public static function removeHTMLtags( $text, $processCallback = null,
+ $args = array(), $extratags = array(), $removetags = array()
+ ) {
+ extract( self::getRecognizedTagData( $extratags, $removetags ) );
+
# Remove HTML comments
$text = Sanitizer::removeHTMLcomments( $text );
$bits = explode( '<', $text );
$text = str_replace( '>', '&gt;', array_shift( $bits ) );
- if ( !$wgUseTidy ) {
+ if ( !MWTidy::isEnabled() ) {
$tagstack = $tablestack = array();
foreach ( $bits as $x ) {
$regs = array();
@@ -463,9 +482,9 @@ class Sanitizer {
$badtag = true;
} elseif ( $slash ) {
# Closing a tag... is it the one we just opened?
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ot = array_pop( $tagstack );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $ot != $t ) {
if ( isset( $htmlsingleallowed[$ot] ) ) {
@@ -473,32 +492,32 @@ class Sanitizer {
# and see if we find a match below them
$optstack = array();
array_push( $optstack, $ot );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ot = array_pop( $tagstack );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
while ( $ot != $t && isset( $htmlsingleallowed[$ot] ) ) {
array_push( $optstack, $ot );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ot = array_pop( $tagstack );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
if ( $t != $ot ) {
# No match. Push the optional elements back again
$badtag = true;
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ot = array_pop( $optstack );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
while ( $ot ) {
array_push( $tagstack, $ot );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ot = array_pop( $optstack );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
}
} else {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
array_push( $tagstack, $ot );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
# <li> can be nested in <ul> or <ol>, skip those cases:
if ( !isset( $htmllist[$ot] ) || !isset( $listtags[$t] ) ) {
@@ -729,7 +748,7 @@ class Sanitizer {
}
# Allow any attribute beginning with "data-"
- if ( !preg_match( '/^data-/i', $attribute ) && !isset( $whitelist[$attribute] ) ) {
+ if ( !preg_match( '/^data-(?!ooui)/i', $attribute ) && !isset( $whitelist[$attribute] ) ) {
continue;
}
@@ -942,7 +961,8 @@ class Sanitizer {
$value = self::normalizeCss( $value );
// Reject problematic keywords and control characters
- if ( preg_match( '/[\000-\010\013\016-\037\177]/', $value ) ) {
+ if ( preg_match( '/[\000-\010\013\016-\037\177]/', $value ) ||
+ strpos( $value, UtfNormal\Constants::UTF8_REPLACEMENT ) !== false ) {
return '/* invalid control char */';
} elseif ( preg_match(
'! expression
@@ -1239,10 +1259,7 @@ class Sanitizer {
* @return string
*/
private static function getTagAttributeCallback( $set ) {
- if ( isset( $set[6] ) ) {
- # Illegal #XXXXXX color with no quotes.
- return $set[6];
- } elseif ( isset( $set[5] ) ) {
+ if ( isset( $set[5] ) ) {
# No quotes.
return $set[5];
} elseif ( isset( $set[4] ) ) {
@@ -1252,9 +1269,10 @@ class Sanitizer {
# Double-quoted
return $set[3];
} elseif ( !isset( $set[2] ) ) {
- # In XHTML, attributes must have a value.
- # For 'reduced' form, return explicitly the attribute name here.
- return $set[1];
+ # In XHTML, attributes must have a value so return an empty string.
+ # See "Empty attribute syntax",
+ # http://www.w3.org/TR/html5/syntax.html#syntax-attribute-name
+ return "";
} else {
throw new MWException( "Tag conditions not met. This should never happen and is a bug." );
}
@@ -1374,15 +1392,19 @@ class Sanitizer {
}
/**
- * Returns true if a given Unicode codepoint is a valid character in XML.
+ * Returns true if a given Unicode codepoint is a valid character in
+ * both HTML5 and XML.
* @param int $codepoint
* @return bool
*/
private static function validateCodepoint( $codepoint ) {
+ # U+000C is valid in HTML5 but not allowed in XML.
+ # U+000D is valid in XML but not allowed in HTML5.
+ # U+007F - U+009F are disallowed in HTML5 (control characters).
return $codepoint == 0x09
|| $codepoint == 0x0a
- || $codepoint == 0x0d
- || ( $codepoint >= 0x20 && $codepoint <= 0xd7ff )
+ || ( $codepoint >= 0x20 && $codepoint <= 0x7e )
+ || ( $codepoint >= 0xa0 && $codepoint <= 0xd7ff )
|| ( $codepoint >= 0xe000 && $codepoint <= 0xfffd )
|| ( $codepoint >= 0x10000 && $codepoint <= 0x10ffff );
}
@@ -1784,6 +1806,11 @@ class Sanitizer {
$host = preg_replace( $strip, '', $host );
+ // IPv6 host names are bracketed with []. Url-decode these.
+ if ( substr_compare( "//%5B", $host, 0, 5 ) === 0 && preg_match( '!^//%5B([0-9A-Fa-f:.]+)%5D((:\d+)?)$!', $host, $matches ) ) {
+ $host = '//[' . $matches[1] . ']' . $matches[2];
+ }
+
// @todo FIXME: Validate hostnames here
return $protocol . $host . $rest;
diff --git a/includes/Setup.php b/includes/Setup.php
index 1b6d66c0..70e8cde4 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -68,17 +68,18 @@ if ( !empty( $wgActionPaths ) && !isset( $wgActionPaths['view'] ) ) {
$wgActionPaths['view'] = $wgArticlePath;
}
+if ( $wgResourceBasePath === null ) {
+ $wgResourceBasePath = $wgScriptPath;
+}
if ( $wgStylePath === false ) {
- $wgStylePath = "$wgScriptPath/skins";
+ $wgStylePath = "$wgResourceBasePath/skins";
}
if ( $wgLocalStylePath === false ) {
+ // Avoid wgResourceBasePath here since that may point to a different domain (e.g. CDN)
$wgLocalStylePath = "$wgScriptPath/skins";
}
if ( $wgExtensionAssetsPath === false ) {
- $wgExtensionAssetsPath = "$wgScriptPath/extensions";
-}
-if ( $wgResourceBasePath === null ) {
- $wgResourceBasePath = $wgScriptPath;
+ $wgExtensionAssetsPath = "$wgResourceBasePath/extensions";
}
if ( $wgLogo === false ) {
@@ -105,6 +106,10 @@ if ( $wgGitInfoCacheDirectory === false && $wgCacheDirectory !== false ) {
$wgGitInfoCacheDirectory = "{$wgCacheDirectory}/gitinfo";
}
+if ( $wgEnableParserCache === false ) {
+ $wgParserCacheType = CACHE_NONE;
+}
+
// Fix path to icon images after they were moved in 1.24
if ( $wgRightsIcon ) {
$wgRightsIcon = str_replace(
@@ -359,13 +364,13 @@ if ( $wgMetaNamespace === false ) {
// Default value is 2000 or the suhosin limit if it is between 1 and 2000
if ( $wgResourceLoaderMaxQueryLength === false ) {
- $suhosinMaxValueLength = (int) ini_get( 'suhosin.get.max_value_length' );
+ $suhosinMaxValueLength = (int)ini_get( 'suhosin.get.max_value_length' );
if ( $suhosinMaxValueLength > 0 && $suhosinMaxValueLength < 2000 ) {
$wgResourceLoaderMaxQueryLength = $suhosinMaxValueLength;
} else {
$wgResourceLoaderMaxQueryLength = 2000;
}
- unset($suhosinMaxValueLength);
+ unset( $suhosinMaxValueLength );
}
// Ensure the minimum chunk size is less than PHP upload limits or the maximum
@@ -434,12 +439,12 @@ if ( !$wgHtml5Version && $wgAllowRdfaAttributes ) {
}
// Blacklisted file extensions shouldn't appear on the "allowed" list
-$wgFileExtensions = array_values( array_diff ( $wgFileExtensions, $wgFileBlacklist ) );
+$wgFileExtensions = array_values( array_diff( $wgFileExtensions, $wgFileBlacklist ) );
if ( $wgInvalidateCacheOnLocalSettingsChange ) {
- // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged - No GlobalFunction here yet.
- $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', @filemtime( "$IP/LocalSettings.php" ) ) );
- // @codingStandardsIgnoreEnd
+ MediaWiki\suppressWarnings();
+ $wgCacheEpoch = max( $wgCacheEpoch, gmdate( 'YmdHis', filemtime( "$IP/LocalSettings.php" ) ) );
+ MediaWiki\restoreWarnings();
}
if ( $wgNewUserLog ) {
@@ -473,6 +478,21 @@ if ( $wgProfileOnly ) {
$wgDebugLogFile = '';
}
+// Backwards compatibility with old password limits
+if ( $wgMinimalPasswordLength !== false ) {
+ $wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = $wgMinimalPasswordLength;
+}
+
+if ( $wgMaximalPasswordLength !== false ) {
+ $wgPasswordPolicy['policies']['default']['MaximalPasswordLength'] = $wgMaximalPasswordLength;
+}
+
+// Backwards compatibility with deprecated alias
+// Must be before call to wfSetupSession()
+if ( $wgSessionsInMemcached ) {
+ $wgSessionsInObjectCache = true;
+}
+
Profiler::instance()->scopedProfileOut( $ps_default );
// Disable MWDebug for command line mode, this prevents MWDebug from eating up
@@ -488,7 +508,7 @@ if ( !class_exists( 'AutoLoader' ) ) {
MWExceptionHandler::installHandler();
-require_once "$IP/includes/libs/normal/UtfNormalUtil.php";
+require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
$ps_default2 = Profiler::instance()->scopedProfileIn( $fname . '-defaults2' );
@@ -525,11 +545,11 @@ if ( $wgSecureLogin && substr( $wgServer, 0, 2 ) !== '//' ) {
. 'HTTP or HTTPS. Disabling secure login.' );
}
+$wgVirtualRestConfig['global']['domain'] = $wgCanonicalServer;
+
// Now that GlobalFunctions is loaded, set defaults that depend on it.
if ( $wgTmpDirectory === false ) {
- $ps_tmpdir = Profiler::instance()->scopedProfileIn( $fname . '-tempDir' );
$wgTmpDirectory = wfTempDir();
- Profiler::instance()->scopedProfileOut( $ps_tmpdir );
}
// We don't use counters anymore. Left here for extensions still
@@ -538,6 +558,18 @@ if ( !isset( $wgDisableCounters ) ) {
$wgDisableCounters = true;
}
+if ( $wgMainWANCache === false ) {
+ // Setup a WAN cache from $wgMainCacheType with no relayer.
+ // Sites using multiple datacenters can configure a relayer.
+ $wgMainWANCache = 'mediawiki-main-default';
+ $wgWANObjectCaches[$wgMainWANCache] = array(
+ 'class' => 'WANObjectCache',
+ 'cacheId' => $wgMainCacheType,
+ 'pool' => 'mediawiki-main-default',
+ 'relayerConfig' => array( 'class' => 'EventRelayerNull' )
+ );
+}
+
Profiler::instance()->scopedProfileOut( $ps_default2 );
$ps_misc = Profiler::instance()->scopedProfileIn( $fname . '-misc1' );
@@ -551,9 +583,9 @@ wfMemoryLimit();
* explicitly set. Inspired by phpMyAdmin's treatment of the problem.
*/
if ( is_null( $wgLocaltimezone ) ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$wgLocaltimezone = date_default_timezone_get();
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
date_default_timezone_set( $wgLocaltimezone );
@@ -561,6 +593,10 @@ if ( is_null( $wgLocalTZoffset ) ) {
$wgLocalTZoffset = date( 'Z' ) / 60;
}
+if ( !$wgDBerrorLogTZ ) {
+ $wgDBerrorLogTZ = $wgLocaltimezone;
+}
+
// Useful debug output
if ( $wgCommandLineMode ) {
$wgRequest = new FauxRequest( array() );
@@ -654,12 +690,6 @@ if ( !is_object( $wgAuth ) ) {
*/
$wgTitle = null;
-/**
- * @deprecated since 1.24 Use DeferredUpdates::addUpdate instead
- * @var array
- */
-$wgDeferredUpdateList = array();
-
Profiler::instance()->scopedProfileOut( $ps_globals );
$ps_extensions = Profiler::instance()->scopedProfileIn( $fname . '-extensions' );
diff --git a/includes/SiteStats.php b/includes/SiteStats.php
index 15c18f35..81172a14 100644
--- a/includes/SiteStats.php
+++ b/includes/SiteStats.php
@@ -68,6 +68,8 @@ class SiteStats {
* @return bool|ResultWrapper
*/
static function loadAndLazyInit() {
+ global $wgMiserMode;
+
wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
$row = self::doLoad( wfGetDB( DB_SLAVE ) );
@@ -77,7 +79,7 @@ class SiteStats {
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
- if ( !self::isSane( $row ) ) {
+ if ( !$wgMiserMode && !self::isSane( $row ) ) {
// Normally the site_stats table is initialized at install time.
// Some manual construction scenarios may leave the table empty or
// broken, however, for instance when importing from a dump into a
diff --git a/includes/SquidPurgeClient.php b/includes/SquidPurgeClient.php
index 824dd06b..ca8f11ae 100644
--- a/includes/SquidPurgeClient.php
+++ b/includes/SquidPurgeClient.php
@@ -95,9 +95,9 @@ class SquidPurgeClient {
}
$this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
socket_set_nonblock( $this->socket );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ok = socket_connect( $this->socket, $ip, $this->port );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$ok ) {
$error = socket_last_error( $this->socket );
if ( $error !== self::EINPROGRESS ) {
@@ -153,12 +153,12 @@ class SquidPurgeClient {
} elseif ( IP::isIPv6( $this->host ) ) {
throw new MWException( '$wgSquidServers does not support IPv6' );
} else {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$this->ip = gethostbyname( $this->host );
if ( $this->ip === $this->host ) {
$this->ip = false;
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
}
return $this->ip;
@@ -178,11 +178,11 @@ class SquidPurgeClient {
*/
public function close() {
if ( $this->socket ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
socket_set_block( $this->socket );
socket_shutdown( $this->socket );
socket_close( $this->socket );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
$this->socket = null;
$this->readBuffer = '';
@@ -252,9 +252,9 @@ class SquidPurgeClient {
$buf = substr( $this->writeBuffer, 0, self::BUFFER_SIZE );
$flags = 0;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$bytesSent = socket_send( $socket, $buf, strlen( $buf ), $flags );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $bytesSent === false ) {
$error = socket_last_error( $socket );
@@ -278,9 +278,9 @@ class SquidPurgeClient {
}
$buf = '';
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$bytesRead = socket_recv( $socket, $buf, self::BUFFER_SIZE, 0 );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $bytesRead === false ) {
$error = socket_last_error( $socket );
if ( $error != self::EAGAIN && $error != self::EINTR ) {
@@ -442,9 +442,9 @@ class SquidPurgeClientPool {
}
$exceptSockets = null;
$timeout = min( $startTime + $this->timeout - microtime( true ), 1 );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$numReady = socket_select( $readSockets, $writeSockets, $exceptSockets, $timeout );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $numReady === false ) {
wfDebugLog( 'squid', __METHOD__ . ': Error in stream_select: ' .
socket_strerror( socket_last_error() ) . "\n" );
diff --git a/includes/Status.php b/includes/Status.php
index cd10258d..28af7f53 100644
--- a/includes/Status.php
+++ b/includes/Status.php
@@ -69,9 +69,9 @@ class Status {
* Succinct helper method to wrap a StatusValue
*
* This is is useful when formatting StatusValue objects:
- * <code>
+ * @code
* $this->getOutput()->addHtml( Status::wrap( $sv )->getHTML() );
- * </code>
+ * @endcode
*
* @param StatusValue|Status $sv
* @return Status
@@ -281,7 +281,7 @@ class Status {
* Otherwise, if its an array, just use the first value as the
* message and the remaining items as the params.
*
- * @return string
+ * @return Message
*/
protected function getErrorMessage( $error ) {
if ( is_array( $error ) ) {
@@ -316,9 +316,9 @@ class Status {
}
/**
- * Return an array with the wikitext for each item in the array.
+ * Return an array with a Message object for each error.
* @param array $errors
- * @return array
+ * @return Message[]
*/
protected function getErrorMessageArray( $errors ) {
return array_map( array( $this, 'getErrorMessage' ), $errors );
diff --git a/includes/StreamFile.php b/includes/StreamFile.php
index a52b25b0..3f73ae3c 100644
--- a/includes/StreamFile.php
+++ b/includes/StreamFile.php
@@ -44,9 +44,9 @@ class StreamFile {
throw new MWException( __FUNCTION__ . " given storage path '$fname'." );
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$stat = stat( $fname );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
$res = self::prepareForStream( $fname, $stat, $headers, $sendErrors );
if ( $res == self::NOT_MODIFIED ) {
@@ -78,7 +78,7 @@ class StreamFile {
) {
if ( !is_array( $info ) ) {
if ( $sendErrors ) {
- header( 'HTTP/1.0 404 Not Found' );
+ HttpStatus::header( 404 );
header( 'Cache-Control: no-cache' );
header( 'Content-Type: text/html; charset=utf-8' );
$encFile = htmlspecialchars( $path );
@@ -126,7 +126,7 @@ class StreamFile {
$modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
ini_set( 'zlib.output_compression', 0 );
- header( "HTTP/1.0 304 Not Modified" );
+ HttpStatus::header( 304 );
return self::NOT_MODIFIED; // ok
}
}
diff --git a/includes/StubObject.php b/includes/StubObject.php
index 2dfcdc2f..49155d6d 100644
--- a/includes/StubObject.php
+++ b/includes/StubObject.php
@@ -194,7 +194,7 @@ class StubUserLang extends StubObject {
public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
global $wgLang;
$this->_unstub( 'findVariantLink', 3 );
- return $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
+ $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
}
/**
diff --git a/includes/TemplateParser.php b/includes/TemplateParser.php
index 3de70fa2..d6b101b2 100644
--- a/includes/TemplateParser.php
+++ b/includes/TemplateParser.php
@@ -103,7 +103,7 @@ class TemplateParser {
// See if the compiled PHP code is stored in cache.
// CACHE_ACCEL throws an exception if no suitable object cache is present, so fall
// back to CACHE_ANYTHING.
- $cache = ObjectCache::newAccelerator( array(), CACHE_ANYTHING );
+ $cache = ObjectCache::newAccelerator( CACHE_ANYTHING );
$key = wfMemcKey( 'template', $templateName, $fastHash );
$code = $this->forceRecompile ? null : $cache->get( $key );
@@ -130,7 +130,8 @@ class TemplateParser {
if ( !is_callable( $renderer ) ) {
throw new RuntimeException( "Requested template, {$templateName}, is not callable" );
}
- return $this->renderers[$templateName] = $renderer;
+ $this->renderers[$templateName] = $renderer;
+ return $renderer;
}
/**
@@ -172,7 +173,9 @@ class TemplateParser {
array(
// Do not add more flags here without discussion.
// If you do add more flags, be sure to update unit tests as well.
- 'flags' => LightnCandy::FLAG_ERROR_EXCEPTION
+ 'flags' => LightnCandy::FLAG_ERROR_EXCEPTION,
+ 'basedir' => $this->templateDir,
+ 'fileext' => '.mustache',
)
);
}
diff --git a/includes/Title.php b/includes/Title.php
index d8976635..b347edbb 100644
--- a/includes/Title.php
+++ b/includes/Title.php
@@ -96,7 +96,7 @@ class Title {
/** @var array Array of groups allowed to edit this article */
public $mRestrictions = array();
- /** @var bool */
+ /** @var string|bool */
protected $mOldRestrictions = false;
/** @var bool Cascade restrictions on this page to included templates and images? */
@@ -225,9 +225,11 @@ class Title {
public static function newFromDBkey( $key ) {
$t = new Title();
$t->mDbkeyform = $key;
- if ( $t->secureAndSplit() ) {
+
+ try {
+ $t->secureAndSplit();
return $t;
- } else {
+ } catch ( MalformedTitleException $ex ) {
return null;
}
}
@@ -263,7 +265,34 @@ class Title {
if ( is_object( $text ) ) {
throw new InvalidArgumentException( '$text must be a string.' );
} elseif ( !is_string( $text ) ) {
- wfWarn( __METHOD__ . ': $text must be a string. This will throw an InvalidArgumentException in future.' );
+ wfDebugLog( 'T76305', wfGetAllCallers( 5 ) );
+ wfWarn( __METHOD__ . ': $text must be a string. This will throw an InvalidArgumentException in future.', 2 );
+ }
+
+ try {
+ return Title::newFromTextThrow( $text, $defaultNamespace );
+ } catch ( MalformedTitleException $ex ) {
+ return null;
+ }
+ }
+
+ /**
+ * Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,
+ * rather than returning null.
+ *
+ * The exception subclasses encode detailed information about why the title is invalid.
+ *
+ * @see Title::newFromText
+ *
+ * @since 1.25
+ * @param string $text Title text to check
+ * @param int $defaultNamespace
+ * @throws MalformedTitleException If the title is invalid
+ * @return Title
+ */
+ public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
+ if ( is_object( $text ) ) {
+ throw new MWException( 'Title::newFromTextThrow given an object' );
}
$cache = self::getTitleCache();
@@ -284,17 +313,14 @@ class Title {
$filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
$t = new Title();
- $t->mDbkeyform = str_replace( ' ', '_', $filteredText );
+ $t->mDbkeyform = strtr( $filteredText, ' ', '_' );
$t->mDefaultNamespace = intval( $defaultNamespace );
- if ( $t->secureAndSplit() ) {
- if ( $defaultNamespace == NS_MAIN ) {
- $cache->set( $text, $t );
- }
- return $t;
- } else {
- return null;
+ $t->secureAndSplit();
+ if ( $defaultNamespace == NS_MAIN ) {
+ $cache->set( $text, $t );
}
+ return $t;
}
/**
@@ -319,13 +345,15 @@ class Title {
# but some URLs used it as a space replacement and they still come
# from some external search tools.
if ( strpos( self::legalChars(), '+' ) === false ) {
- $url = str_replace( '+', ' ', $url );
+ $url = strtr( $url, '+', ' ' );
}
- $t->mDbkeyform = str_replace( ' ', '_', $url );
- if ( $t->secureAndSplit() ) {
+ $t->mDbkeyform = strtr( $url, ' ', '_' );
+
+ try {
+ $t->secureAndSplit();
return $t;
- } else {
+ } catch ( MalformedTitleException $ex ) {
return null;
}
}
@@ -451,6 +479,9 @@ class Title {
if ( isset( $row->page_lang ) ) {
$this->mDbPageLanguage = (string)$row->page_lang;
}
+ if ( isset( $row->page_restrictions ) ) {
+ $this->mOldRestrictions = $row->page_restrictions;
+ }
} else { // page not found
$this->mArticleID = 0;
$this->mLength = 0;
@@ -478,10 +509,10 @@ class Title {
$t->mInterwiki = $interwiki;
$t->mFragment = $fragment;
$t->mNamespace = $ns = intval( $ns );
- $t->mDbkeyform = str_replace( ' ', '_', $title );
+ $t->mDbkeyform = strtr( $title, ' ', '_' );
$t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
$t->mUrlform = wfUrlencode( $t->mDbkeyform );
- $t->mTextform = str_replace( '_', ' ', $title );
+ $t->mTextform = strtr( $title, '_', ' ' );
$t->mContentModel = false; # initialized lazily in getContentModel()
return $t;
}
@@ -504,9 +535,11 @@ class Title {
$t = new Title();
$t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
- if ( $t->secureAndSplit() ) {
+
+ try {
+ $t->secureAndSplit();
return $t;
- } else {
+ } catch ( MalformedTitleException $ex ) {
return null;
}
}
@@ -937,7 +970,6 @@ class Title {
/**
* Get the page's content model id, see the CONTENT_MODEL_XXX constants.
*
- * @throws MWException
* @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update
* @return string Content model id
*/
@@ -952,10 +984,6 @@ class Title {
$this->mContentModel = ContentHandler::getDefaultModelFor( $this );
}
- if ( !$this->mContentModel ) {
- throw new MWException( 'Failed to determine content model!' );
- }
-
return $this->mContentModel;
}
@@ -1391,7 +1419,7 @@ class Title {
* @param string $fragment Text
*/
public function setFragment( $fragment ) {
- $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) );
+ $this->mFragment = strtr( substr( $fragment, 1 ), '_', ' ' );
}
/**
@@ -1421,7 +1449,7 @@ class Title {
*/
public function getPrefixedDBkey() {
$s = $this->prefix( $this->mDbkeyform );
- $s = str_replace( ' ', '_', $s );
+ $s = strtr( $s, ' ', '_' );
return $s;
}
@@ -1434,7 +1462,7 @@ class Title {
public function getPrefixedText() {
if ( $this->mPrefixedText === null ) {
$s = $this->prefix( $this->mTextform );
- $s = str_replace( '_', ' ', $s );
+ $s = strtr( $s, '_', ' ' );
$this->mPrefixedText = $s;
}
return $this->mPrefixedText;
@@ -1582,7 +1610,7 @@ class Title {
*/
public function getSubpageUrlForm() {
$text = $this->getSubpageText();
- $text = wfUrlencode( str_replace( ' ', '_', $text ) );
+ $text = wfUrlencode( strtr( $text, ' ', '_' ) );
return $text;
}
@@ -1593,7 +1621,7 @@ class Title {
*/
public function getPrefixedURL() {
$s = $this->prefix( $this->mDbkeyform );
- $s = wfUrlencode( str_replace( ' ', '_', $s ) );
+ $s = wfUrlencode( strtr( $s, ' ', '_' ) );
return $s;
}
@@ -1911,7 +1939,6 @@ class Title {
* - quick : does cheap permission checks from slaves (usable for GUI creation)
* - full : does cheap and expensive checks possibly from a slave
* - 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.
* @param array $ignoreErrors Array of Strings Set this to a list of message keys
* whose corresponding errors may be ignored.
* @return array Array of arguments to wfMessage to explain permissions problems.
@@ -2574,6 +2601,7 @@ class Title {
if ( $row['permission'] == 'autoconfirmed' ) {
$row['permission'] = 'editsemiprotected'; // B/C
}
+ $row['expiry'] = $dbr->decodeExpiry( $row['expiry'] );
}
$this->mTitleProtection = $row;
}
@@ -2711,7 +2739,6 @@ class Title {
* false.
*/
public function getCascadeProtectionSources( $getPages = true ) {
- global $wgContLang;
$pagerestrictions = array();
if ( $this->mCascadeSources !== null && $getPages ) {
@@ -2754,7 +2781,7 @@ class Title {
$now = wfTimestampNow();
foreach ( $res as $row ) {
- $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
+ $expiry = $dbr->decodeExpiry( $row->pr_expiry );
if ( $expiry > $now ) {
if ( $getPages ) {
$page_id = $row->pr_page;
@@ -2887,28 +2914,29 @@ class Title {
* restrictions from page table (pre 1.10)
*/
public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
- global $wgContLang;
$dbr = wfGetDB( DB_SLAVE );
$restrictionTypes = $this->getRestrictionTypes();
foreach ( $restrictionTypes as $type ) {
$this->mRestrictions[$type] = array();
- $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW );
+ $this->mRestrictionsExpiry[$type] = 'infinity';
}
$this->mCascadeRestriction = false;
# Backwards-compatibility: also load the restrictions from the page record (old format).
+ if ( $oldFashionedRestrictions !== null ) {
+ $this->mOldRestrictions = $oldFashionedRestrictions;
+ }
- if ( $oldFashionedRestrictions === null ) {
- $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
+ if ( $this->mOldRestrictions === false ) {
+ $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
array( 'page_id' => $this->getArticleID() ), __METHOD__ );
}
- if ( $oldFashionedRestrictions != '' ) {
-
- foreach ( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
+ if ( $this->mOldRestrictions != '' ) {
+ foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
$temp = explode( '=', trim( $restrict ) );
if ( count( $temp ) == 1 ) {
// old old format should be treated as edit/move restriction
@@ -2921,9 +2949,6 @@ class Title {
}
}
}
-
- $this->mOldRestrictions = true;
-
}
if ( count( $rows ) ) {
@@ -2940,7 +2965,7 @@ class Title {
// This code should be refactored, now that it's being used more generally,
// But I don't really see any harm in leaving it in Block for now -werdna
- $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
+ $expiry = $dbr->decodeExpiry( $row->pr_expiry );
// Only apply the restrictions if they haven't expired!
if ( !$expiry || $expiry > $now ) {
@@ -2962,11 +2987,9 @@ class Title {
* restrictions from page table (pre 1.10)
*/
public function loadRestrictions( $oldFashionedRestrictions = null ) {
- global $wgContLang;
if ( !$this->mRestrictionsLoaded ) {
+ $dbr = wfGetDB( DB_SLAVE );
if ( $this->exists() ) {
- $dbr = wfGetDB( DB_SLAVE );
-
$res = $dbr->select(
'page_restrictions',
array( 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ),
@@ -2980,7 +3003,7 @@ class Title {
if ( $title_protection ) {
$now = wfTimestampNow();
- $expiry = $wgContLang->formatExpiry( $title_protection['expiry'], TS_MW );
+ $expiry = $dbr->decodeExpiry( $title_protection['expiry'] );
if ( !$expiry || $expiry > $now ) {
// Apply the restrictions
@@ -2990,7 +3013,7 @@ class Title {
$this->mTitleProtection = false;
}
} else {
- $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW );
+ $this->mRestrictionsExpiry['create'] = 'infinity';
}
$this->mRestrictionsLoaded = true;
}
@@ -3274,6 +3297,7 @@ class Title {
}
$this->mRestrictionsLoaded = false;
$this->mRestrictions = array();
+ $this->mOldRestrictions = false;
$this->mRedirect = null;
$this->mLength = -1;
$this->mLatestID = false;
@@ -3318,6 +3342,7 @@ class Title {
* namespace prefixes, sets the other forms, and canonicalizes
* everything.
*
+ * @throws MalformedTitleException On invalid titles
* @return bool True on success
*/
private function secureAndSplit() {
@@ -3328,15 +3353,12 @@ class Title {
$dbkey = $this->mDbkeyform;
- try {
- // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
- // the parsing code with Title, while avoiding massive refactoring.
- // @todo: get rid of secureAndSplit, refactor parsing code.
- $titleParser = self::getTitleParser();
- $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
- } catch ( MalformedTitleException $ex ) {
- return false;
- }
+ // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
+ // the parsing code with Title, while avoiding massive refactoring.
+ // @todo: get rid of secureAndSplit, refactor parsing code.
+ $titleParser = self::getTitleParser();
+ // MalformedTitleException can be thrown here
+ $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
# Fill fields
$this->setFragment( '#' . $parts['fragment'] );
@@ -3347,7 +3369,7 @@ class Title {
$this->mDbkeyform = $parts['dbkey'];
$this->mUrlform = wfUrlencode( $this->mDbkeyform );
- $this->mTextform = str_replace( '_', ' ', $this->mDbkeyform );
+ $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
# We already know that some pages won't be in the database!
if ( $this->isExternal() || $this->mNamespace == NS_SPECIAL ) {
@@ -3428,8 +3450,6 @@ class Title {
* @return array Array of Title objects linking here
*/
public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
- global $wgContentHandlerUseDB;
-
$id = $this->getArticleID();
# If the page doesn't exist; there can't be any link from this page
@@ -3443,49 +3463,36 @@ class Title {
$db = wfGetDB( DB_SLAVE );
}
- $namespaceFiled = "{$prefix}_namespace";
- $titleField = "{$prefix}_title";
-
- $fields = array(
- $namespaceFiled,
- $titleField,
- 'page_id',
- 'page_len',
- 'page_is_redirect',
- 'page_latest'
- );
-
- if ( $wgContentHandlerUseDB ) {
- $fields[] = 'page_content_model';
- }
+ $blNamespace = "{$prefix}_namespace";
+ $blTitle = "{$prefix}_title";
$res = $db->select(
array( $table, 'page' ),
- $fields,
+ array_merge(
+ array( $blNamespace, $blTitle ),
+ WikiPage::selectFields()
+ ),
array( "{$prefix}_from" => $id ),
__METHOD__,
$options,
array( 'page' => array(
'LEFT JOIN',
- array( "page_namespace=$namespaceFiled", "page_title=$titleField" )
+ array( "page_namespace=$blNamespace", "page_title=$blTitle" )
) )
);
$retVal = array();
- if ( $res->numRows() ) {
- $linkCache = LinkCache::singleton();
- foreach ( $res as $row ) {
- $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
- if ( $titleObj ) {
- if ( $row->page_id ) {
- $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
- } else {
- $linkCache->addBadLinkObj( $titleObj );
- }
- $retVal[] = $titleObj;
- }
+ $linkCache = LinkCache::singleton();
+ foreach ( $res as $row ) {
+ if ( $row->page_id ) {
+ $titleObj = Title::newFromRow( $row );
+ } else {
+ $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+ $linkCache->addBadLinkObj( $titleObj );
}
+ $retVal[] = $titleObj;
}
+
return $retVal;
}
@@ -3624,7 +3631,7 @@ class Title {
);
}
- return $errors ? : true;
+ return $errors ?: true;
}
/**
@@ -4236,10 +4243,12 @@ class Title {
* If you want to know if a title can be meaningfully viewed, you should
* probably call the isKnown() method instead.
*
+ * @param int $flags An optional bit field; may be Title::GAID_FOR_UPDATE to check
+ * from master/for update
* @return bool
*/
- public function exists() {
- $exists = $this->getArticleID() != 0;
+ public function exists( $flags = 0 ) {
+ $exists = $this->getArticleID( $flags ) != 0;
Hooks::run( 'TitleExists', array( $this, &$exists ) );
return $exists;
}
@@ -4370,9 +4379,10 @@ class Title {
/**
* Updates page_touched for this page; called from LinksUpdate.php
*
+ * @param integer $purgeTime TS_MW timestamp [optional]
* @return bool True if the update succeeded
*/
- public function invalidateCache() {
+ public function invalidateCache( $purgeTime = null ) {
if ( wfReadOnly() ) {
return false;
}
@@ -4384,11 +4394,13 @@ class Title {
$method = __METHOD__;
$dbw = wfGetDB( DB_MASTER );
$conds = $this->pageCond();
- $dbw->onTransactionIdle( function () use ( $dbw, $conds, $method ) {
+ $dbw->onTransactionIdle( function () use ( $dbw, $conds, $method, $purgeTime ) {
+ $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
+
$dbw->update(
'page',
- array( 'page_touched' => $dbw->timestamp() ),
- $conds,
+ array( 'page_touched' => $dbTimestamp ),
+ $conds + array( 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ),
$method
);
} );
@@ -4432,35 +4444,29 @@ class Title {
* @return string|null
*/
public function getNotificationTimestamp( $user = null ) {
- global $wgUser, $wgShowUpdatedMarker;
+ global $wgUser;
+
// Assume current user if none given
if ( !$user ) {
$user = $wgUser;
}
// Check cache first
$uid = $user->getId();
+ if ( !$uid ) {
+ return false;
+ }
// avoid isset here, as it'll return false for null entries
if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
return $this->mNotificationTimestamp[$uid];
}
- if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
- $this->mNotificationTimestamp[$uid] = false;
- return $this->mNotificationTimestamp[$uid];
- }
// Don't cache too much!
if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
$this->mNotificationTimestamp = array();
}
- $dbr = wfGetDB( DB_SLAVE );
- $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
- 'wl_notificationtimestamp',
- array(
- 'wl_user' => $user->getId(),
- 'wl_namespace' => $this->getNamespace(),
- 'wl_title' => $this->getDBkey(),
- ),
- __METHOD__
- );
+
+ $watchedItem = WatchedItem::fromUserTitle( $user, $this );
+ $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
+
return $this->mNotificationTimestamp[$uid];
}
@@ -4540,15 +4546,17 @@ class Title {
public function isValidRedirectTarget() {
global $wgInvalidRedirectTargets;
- // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
- if ( $this->isSpecial( 'Userlogout' ) ) {
- return false;
- }
-
- foreach ( $wgInvalidRedirectTargets as $target ) {
- if ( $this->isSpecial( $target ) ) {
+ if ( $this->isSpecialPage() ) {
+ // invalid redirect targets are stored in a global array, but explicitly disallow Userlogout here
+ if ( $this->isSpecial( 'Userlogout' ) ) {
return false;
}
+
+ foreach ( $wgInvalidRedirectTargets as $target ) {
+ if ( $this->isSpecial( $target ) ) {
+ return false;
+ }
+ }
}
return true;
@@ -4731,7 +4739,7 @@ class Title {
}
} else {
// Even if there are no subpages in namespace, we still don't want "/" in MediaWiki message keys
- $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() );
+ $editnoticeText = $editnotice_ns . '-' . strtr( $this->getDBkey(), '/', '-' );
$msg = wfMessage( $editnoticeText );
if ( $msg->exists() ) {
$html = $msg->parseAsBlock();
@@ -4752,4 +4760,26 @@ class Title {
Hooks::run( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) );
return $notices;
}
+
+ /**
+ * @return array
+ */
+ public function __sleep() {
+ return array(
+ 'mNamespace',
+ 'mDbkeyform',
+ 'mFragment',
+ 'mInterwiki',
+ 'mLocalInterwiki',
+ 'mUserCaseDBKey',
+ 'mDefaultNamespace',
+ );
+ }
+
+ public function __wakeup() {
+ $this->mArticleID = ( $this->mNamespace >= 0 ) ? -1 : 0;
+ $this->mUrlform = wfUrlencode( $this->mDbkeyform );
+ $this->mTextform = strtr( $this->mDbkeyform, '_', ' ' );
+ }
+
}
diff --git a/includes/User.php b/includes/User.php
index 663a80b7..22c90cdd 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -183,50 +183,50 @@ class User implements IDBAccessObject {
*/
protected static $mAllRights = false;
- /** @name Cache variables */
+ /** Cache variables */
//@{
public $mId;
-
+ /** @var string */
public $mName;
-
+ /** @var string */
public $mRealName;
-
/**
* @todo Make this actually private
* @private
+ * @var Password
*/
public $mPassword;
-
/**
* @todo Make this actually private
* @private
+ * @var Password
*/
public $mNewpassword;
-
+ /** @var string */
public $mNewpassTime;
-
+ /** @var string */
public $mEmail;
/** @var string TS_MW timestamp from the DB */
public $mTouched;
/** @var string TS_MW timestamp from cache */
protected $mQuickTouched;
-
+ /** @var string */
protected $mToken;
-
+ /** @var string */
public $mEmailAuthenticated;
-
+ /** @var string */
protected $mEmailToken;
-
+ /** @var string */
protected $mEmailTokenExpires;
-
+ /** @var string */
protected $mRegistration;
-
+ /** @var int */
protected $mEditCount;
-
+ /** @var array */
public $mGroups;
-
+ /** @var array */
protected $mOptionOverrides;
-
+ /** @var string */
protected $mPasswordExpires;
//@}
@@ -257,29 +257,29 @@ class User implements IDBAccessObject {
* Lazy-initialized variables, invalidated with clearInstanceCache
*/
protected $mNewtalk;
-
+ /** @var string */
protected $mDatePreference;
-
+ /** @var string */
public $mBlockedby;
-
+ /** @var string */
protected $mHash;
-
+ /** @var array */
public $mRights;
-
+ /** @var string */
protected $mBlockreason;
-
+ /** @var array */
protected $mEffectiveGroups;
-
+ /** @var array */
protected $mImplicitGroups;
-
+ /** @var array */
protected $mFormerGroups;
-
+ /** @var bool */
protected $mBlockedGlobally;
-
+ /** @var bool */
protected $mLocked;
-
+ /** @var bool */
public $mHideName;
-
+ /** @var array */
public $mOptions;
/**
@@ -330,7 +330,7 @@ class User implements IDBAccessObject {
*
* @param integer $flags User::READ_* constant bitfield
*/
- public function load( $flags = self::READ_LATEST ) {
+ public function load( $flags = self::READ_NORMAL ) {
if ( $this->mLoadedItems === true ) {
return;
}
@@ -344,9 +344,13 @@ class User implements IDBAccessObject {
$this->loadDefaults();
break;
case 'name':
- // @TODO: this gets the ID from a slave, assuming renames
- // are rare. This should be controllable and more consistent.
- $this->mId = self::idFromName( $this->mName );
+ // Make sure this thread sees its own changes
+ if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) {
+ $flags |= self::READ_LATEST;
+ $this->queryFlagsUsed = $flags;
+ }
+
+ $this->mId = self::idFromName( $this->mName, $flags );
if ( !$this->mId ) {
// Nonexistent user placeholder object
$this->loadDefaults( $this->mName );
@@ -365,7 +369,8 @@ class User implements IDBAccessObject {
Hooks::run( 'UserLoadAfterLoadFromSession', array( $this ) );
break;
default:
- throw new MWException( "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
+ throw new UnexpectedValueException(
+ "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
}
}
@@ -374,27 +379,26 @@ class User implements IDBAccessObject {
* @param integer $flags User::READ_* constant bitfield
* @return bool False if the ID does not exist, true otherwise
*/
- public function loadFromId( $flags = self::READ_LATEST ) {
+ public function loadFromId( $flags = self::READ_NORMAL ) {
if ( $this->mId == 0 ) {
$this->loadDefaults();
return false;
}
- // Try cache
- $cache = $this->loadFromCache();
- if ( !$cache ) {
+ // Try cache (unless this needs to lock the DB).
+ // NOTE: if this thread called saveSettings(), the cache was cleared.
+ $locking = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING );
+ if ( $locking || !$this->loadFromCache() ) {
wfDebug( "User: cache miss for user {$this->mId}\n" );
- // Load from DB
+ // Load from DB (make sure this thread sees its own changes)
+ if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) {
+ $flags |= self::READ_LATEST;
+ }
if ( !$this->loadFromDatabase( $flags ) ) {
// Can't load from ID, user is anonymous
return false;
}
- if ( $flags & self::READ_LATEST ) {
- // Only save master data back to the cache to keep it consistent.
- // @TODO: save it anyway and have callers specifiy $flags and have
- // load() called as needed. That requires updating MANY callers...
- $this->saveToCache();
- }
+ $this->saveToCache();
}
$this->mLoadedItems = true;
@@ -410,15 +414,13 @@ class User implements IDBAccessObject {
* @since 1.25
*/
protected function loadFromCache() {
- global $wgMemc;
-
if ( $this->mId == 0 ) {
$this->loadDefaults();
return false;
}
$key = wfMemcKey( 'user', 'id', $this->mId );
- $data = $wgMemc->get( $key );
+ $data = ObjectCache::getMainWANInstance()->get( $key );
if ( !is_array( $data ) || $data['mVersion'] < self::VERSION ) {
// Object is expired
return false;
@@ -440,8 +442,6 @@ class User implements IDBAccessObject {
* This method should not be called outside the User class
*/
public function saveToCache() {
- global $wgMemc;
-
$this->load();
$this->loadGroups();
$this->loadOptions();
@@ -451,13 +451,6 @@ class User implements IDBAccessObject {
return;
}
- // The cache needs good consistency due to its high TTL, so the user
- // should have been loaded from the master to avoid lag amplification.
- if ( !( $this->queryFlagsUsed & self::READ_LATEST ) ) {
- wfWarn( "Cannot cache slave-loaded User object with ID '{$this->mId}'." );
- return;
- }
-
$data = array();
foreach ( self::$mCacheVars as $name ) {
$data[$name] = $this->$name;
@@ -465,7 +458,7 @@ class User implements IDBAccessObject {
$data['mVersion'] = self::VERSION;
$key = wfMemcKey( 'user', 'id', $this->mId );
- $wgMemc->set( $key, $data );
+ ObjectCache::getMainWANInstance()->set( $key, $data, 3600 );
}
/** @name newFrom*() static factory methods */
@@ -604,9 +597,10 @@ class User implements IDBAccessObject {
/**
* Get database id given a user name
* @param string $name Username
+ * @param integer $flags User::READ_* constant bitfield
* @return int|null The corresponding user's ID, or null if user is nonexistent
*/
- public static function idFromName( $name ) {
+ public static function idFromName( $name, $flags = self::READ_NORMAL ) {
$nt = Title::makeTitleSafe( NS_USER, $name );
if ( is_null( $nt ) ) {
// Illegal name
@@ -617,8 +611,11 @@ class User implements IDBAccessObject {
return self::$idCacheByName[$name];
}
- $dbr = wfGetDB( DB_SLAVE );
- $s = $dbr->selectRow(
+ $db = ( $flags & self::READ_LATEST )
+ ? wfGetDB( DB_MASTER )
+ : wfGetDB( DB_SLAVE );
+
+ $s = $db->selectRow(
'user',
array( 'user_id' ),
array( 'user_name' => $nt->getText() ),
@@ -846,19 +843,19 @@ class User implements IDBAccessObject {
* able to set their password to this.
*
* @param string $password Desired password
+ * @param string $purpose one of 'login', 'create', 'reset'
* @return Status
* @since 1.23
*/
- public function checkPasswordValidity( $password ) {
- global $wgMinimalPasswordLength, $wgMaximalPasswordLength, $wgContLang;
+ public function checkPasswordValidity( $password, $purpose = 'login' ) {
+ global $wgPasswordPolicy;
- static $blockedLogins = array(
- 'Useruser' => 'Passpass', 'Useruser1' => 'Passpass1', # r75589
- 'Apitestsysop' => 'testpass', 'Apitestuser' => 'testpass' # r75605
+ $upp = new UserPasswordPolicy(
+ $wgPasswordPolicy['policies'],
+ $wgPasswordPolicy['checks']
);
$status = Status::newGood();
-
$result = false; //init $result to false for the internal checks
if ( !Hooks::run( 'isValidPassword', array( $password, &$result, $this ) ) ) {
@@ -867,28 +864,8 @@ class User implements IDBAccessObject {
}
if ( $result === false ) {
- if ( strlen( $password ) < $wgMinimalPasswordLength ) {
- $status->error( 'passwordtooshort', $wgMinimalPasswordLength );
- return $status;
- } elseif ( strlen( $password ) > $wgMaximalPasswordLength ) {
- // T64685: Password too long, might cause DoS attack
- $status->fatal( 'passwordtoolong', $wgMaximalPasswordLength );
- return $status;
- } elseif ( $wgContLang->lc( $password ) == $wgContLang->lc( $this->mName ) ) {
- $status->error( 'password-name-match' );
- return $status;
- } elseif ( isset( $blockedLogins[$this->getName()] )
- && $password == $blockedLogins[$this->getName()]
- ) {
- $status->error( 'password-login-forbidden' );
- return $status;
- } else {
- //it seems weird returning a Good status here, but this is because of the
- //initialization of $result to false above. If the hook is never run or it
- //doesn't modify $result, then we will likely get down into this if with
- //a valid password.
- return $status;
- }
+ $status->merge( $upp->checkUserPassword( $this, $password, $purpose ) );
+ return $status;
} elseif ( $result === true ) {
return $status;
} else {
@@ -974,7 +951,7 @@ class User implements IDBAccessObject {
* - 'usable' Valid for batch processes and login
* - 'creatable' Valid for batch processes, login and account creation
*
- * @throws MWException
+ * @throws InvalidArgumentException
* @return bool|string
*/
public static function getCanonicalName( $name, $validate = 'valid' ) {
@@ -1021,7 +998,8 @@ class User implements IDBAccessObject {
}
break;
default:
- throw new MWException( 'Invalid parameter value for $validate in ' . __METHOD__ );
+ throw new InvalidArgumentException(
+ 'Invalid parameter value for $validate in ' . __METHOD__ );
}
return $name;
}
@@ -1168,7 +1146,6 @@ class User implements IDBAccessObject {
}
$proposedUser = User::newFromId( $sId );
- $proposedUser->load( self::READ_LATEST );
if ( !$proposedUser->isLoggedIn() ) {
// Not a valid ID
return false;
@@ -1235,7 +1212,7 @@ class User implements IDBAccessObject {
self::selectFields(),
array( 'user_id' => $this->mId ),
__METHOD__,
- ( $flags & self::READ_LOCKING == self::READ_LOCKING )
+ ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
? array( 'LOCK IN SHARE MODE' )
: array()
);
@@ -1436,38 +1413,86 @@ class User implements IDBAccessObject {
public function addAutopromoteOnceGroups( $event ) {
global $wgAutopromoteOnceLogInRC, $wgAuth;
- $toPromote = array();
- if ( !wfReadOnly() && $this->getId() ) {
- $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
- if ( count( $toPromote ) ) {
- $oldGroups = $this->getGroups(); // previous groups
+ if ( wfReadOnly() || !$this->getId() ) {
+ return array();
+ }
- foreach ( $toPromote as $group ) {
- $this->addGroup( $group );
- }
- // update groups in external authentication database
- $wgAuth->updateExternalDBGroups( $this, $toPromote );
+ $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
+ if ( !count( $toPromote ) ) {
+ return array();
+ }
- $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
+ if ( !$this->checkAndSetTouched() ) {
+ return array(); // raced out (bug T48834)
+ }
- $logEntry = new ManualLogEntry( 'rights', 'autopromote' );
- $logEntry->setPerformer( $this );
- $logEntry->setTarget( $this->getUserPage() );
- $logEntry->setParameters( array(
- '4::oldgroups' => $oldGroups,
- '5::newgroups' => $newGroups,
- ) );
- $logid = $logEntry->insert();
- if ( $wgAutopromoteOnceLogInRC ) {
- $logEntry->publish( $logid );
- }
- }
+ $oldGroups = $this->getGroups(); // previous groups
+ foreach ( $toPromote as $group ) {
+ $this->addGroup( $group );
+ }
+ // update groups in external authentication database
+ Hooks::run( 'UserGroupsChanged', array( $this, $toPromote, array(), false ) );
+ $wgAuth->updateExternalDBGroups( $this, $toPromote );
+
+ $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
+
+ $logEntry = new ManualLogEntry( 'rights', 'autopromote' );
+ $logEntry->setPerformer( $this );
+ $logEntry->setTarget( $this->getUserPage() );
+ $logEntry->setParameters( array(
+ '4::oldgroups' => $oldGroups,
+ '5::newgroups' => $newGroups,
+ ) );
+ $logid = $logEntry->insert();
+ if ( $wgAutopromoteOnceLogInRC ) {
+ $logEntry->publish( $logid );
}
return $toPromote;
}
/**
+ * Bump user_touched if it didn't change since this object was loaded
+ *
+ * On success, the mTouched field is updated.
+ * The user serialization cache is always cleared.
+ *
+ * @return bool Whether user_touched was actually updated
+ * @since 1.26
+ */
+ protected function checkAndSetTouched() {
+ $this->load();
+
+ if ( !$this->mId ) {
+ return false; // anon
+ }
+
+ // Get a new user_touched that is higher than the old one
+ $oldTouched = $this->mTouched;
+ $newTouched = $this->newTouchedTimestamp();
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->update( 'user',
+ array( 'user_touched' => $dbw->timestamp( $newTouched ) ),
+ array(
+ 'user_id' => $this->mId,
+ 'user_touched' => $dbw->timestamp( $oldTouched ) // CAS check
+ ),
+ __METHOD__
+ );
+ $success = ( $dbw->affectedRows() > 0 );
+
+ if ( $success ) {
+ $this->mTouched = $newTouched;
+ }
+
+ // Clears on failure too since that is desired if the cache is stale
+ $this->clearSharedCache();
+
+ return $success;
+ }
+
+ /**
* Clear various cached data stored in this object. The cache of the user table
* data (i.e. self::$mCacheVars) is not cleared unless $reloadFrom is given.
*
@@ -1566,7 +1591,7 @@ class User implements IDBAccessObject {
# We only need to worry about passing the IP address to the Block generator if the
# user is not immune to autoblocks/hardblocks, and they are the current user so we
# know which IP address they're actually coming from
- if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->getID() == $wgUser->getID() ) {
+ if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->equals( $wgUser ) ) {
$ip = $this->getRequest()->getIP();
} else {
$ip = null;
@@ -1968,6 +1993,7 @@ class User implements IDBAccessObject {
global $wgAuth;
$authUser = $wgAuth->getUserInstance( $this );
$this->mLocked = (bool)$authUser->isLocked();
+ Hooks::run( 'UserIsLocked', array( $this, &$this->mLocked ) );
return $this->mLocked;
}
@@ -1985,6 +2011,7 @@ class User implements IDBAccessObject {
global $wgAuth;
$authUser = $wgAuth->getUserInstance( $this );
$this->mHideName = (bool)$authUser->isHidden();
+ Hooks::run( 'UserIsHidden', array( $this, &$this->mHideName ) );
}
return $this->mHideName;
}
@@ -2133,6 +2160,7 @@ class User implements IDBAccessObject {
&& $newMessageLinks[0]['wiki'] === wfWikiID()
&& $newMessageLinks[0]['rev']
) {
+ /** @var Revision $newMessageRevision */
$newMessageRevision = $newMessageLinks[0]['rev'];
$newMessageRevisionId = $newMessageRevision->getId();
}
@@ -2209,8 +2237,6 @@ class User implements IDBAccessObject {
* page. Ignored if null or !$val.
*/
public function setNewtalk( $val, $curRev = null ) {
- global $wgMemc;
-
if ( wfReadOnly() ) {
return;
}
@@ -2232,12 +2258,6 @@ class User implements IDBAccessObject {
$changed = $this->deleteNewtalk( $field, $id );
}
- if ( $this->isAnon() ) {
- // Anons have a separate memcached space, since
- // user records aren't kept for them.
- $key = wfMemcKey( 'newtalk', 'ip', $id );
- $wgMemc->set( $key, $val ? 1 : 0, 1800 );
- }
if ( $changed ) {
$this->invalidateCache();
}
@@ -2267,11 +2287,10 @@ class User implements IDBAccessObject {
* Called implicitly from invalidateCache() and saveSettings().
*/
public function clearSharedCache() {
- global $wgMemc;
-
- $this->load();
- if ( $this->mId ) {
- $wgMemc->delete( wfMemcKey( 'user', 'id', $this->mId ) );
+ $id = $this->getId();
+ if ( $id ) {
+ $key = wfMemcKey( 'user', 'id', $id );
+ ObjectCache::getMainWANInstance()->delete( $key );
}
}
@@ -2298,15 +2317,11 @@ class User implements IDBAccessObject {
* @since 1.25
*/
public function touch() {
- global $wgMemc;
-
- $this->load();
-
- if ( $this->mId ) {
- $key = wfMemcKey( 'user-quicktouched', 'id', $this->mId );
- $timestamp = $this->newTouchedTimestamp();
- $wgMemc->set( $key, $timestamp );
- $this->mQuickTouched = $timestamp;
+ $id = $this->getId();
+ if ( $id ) {
+ $key = wfMemcKey( 'user-quicktouched', 'id', $id );
+ ObjectCache::getMainWANInstance()->touchCheckKey( $key );
+ $this->mQuickTouched = null;
}
}
@@ -2321,23 +2336,21 @@ class User implements IDBAccessObject {
/**
* Get the user touched timestamp
+ *
+ * Use this value only to validate caches via inequalities
+ * such as in the case of HTTP If-Modified-Since response logic
+ *
* @return string TS_MW Timestamp
*/
public function getTouched() {
- global $wgMemc;
-
$this->load();
if ( $this->mId ) {
if ( $this->mQuickTouched === null ) {
$key = wfMemcKey( 'user-quicktouched', 'id', $this->mId );
- $timestamp = $wgMemc->get( $key );
- if ( $timestamp ) {
- $this->mQuickTouched = $timestamp;
- } else {
- # Set the timestamp to get HTTP 304 cache hits
- $this->touch();
- }
+ $cache = ObjectCache::getMainWANInstance();
+
+ $this->mQuickTouched = wfTimestamp( TS_MW, $cache->getCheckKeyTime( $key ) );
}
return max( $this->mTouched, $this->mQuickTouched );
@@ -2347,6 +2360,17 @@ class User implements IDBAccessObject {
}
/**
+ * Get the user_touched timestamp field (time of last DB updates)
+ * @return string TS_MW Timestamp
+ * @since 1.26
+ */
+ public function getDBTouched() {
+ $this->load();
+
+ return $this->mTouched;
+ }
+
+ /**
* @return Password
* @since 1.24
*/
@@ -2416,6 +2440,7 @@ class User implements IDBAccessObject {
*/
public function setInternalPassword( $str ) {
$this->setToken();
+ $this->setOption( 'watchlisttoken', false );
$passwordFactory = self::getPasswordFactory();
$this->mPassword = $passwordFactory->newFromPlaintext( $str );
@@ -2693,20 +2718,24 @@ class User implements IDBAccessObject {
* @return string|bool User's current value for the option, or false if this option is disabled.
* @see resetTokenFromOption()
* @see getOption()
+ * @deprecated 1.26 Applications should use the OAuth extension
*/
public function getTokenFromOption( $oname ) {
global $wgHiddenPrefs;
- if ( in_array( $oname, $wgHiddenPrefs ) ) {
+
+ $id = $this->getId();
+ if ( !$id || in_array( $oname, $wgHiddenPrefs ) ) {
return false;
}
$token = $this->getOption( $oname );
if ( !$token ) {
- $token = $this->resetTokenFromOption( $oname );
- if ( !wfReadOnly() ) {
- $this->saveSettings();
- }
+ // Default to a value based on the user token to avoid space
+ // wasted on storing tokens for all users. When this option
+ // is set manually by the user, only then is it stored.
+ $token = hash_hmac( 'sha1', "$oname:$id", $this->getToken() );
}
+
return $token;
}
@@ -3186,10 +3215,10 @@ class User implements IDBAccessObject {
/**
* Check if user is allowed to access a feature / make an action
*
- * @param string $permissions,... Permissions to test
+ * @param string ... Permissions to test
* @return bool True if user is allowed to perform *any* of the given actions
*/
- public function isAllowedAny( /*...*/ ) {
+ public function isAllowedAny() {
$permissions = func_get_args();
foreach ( $permissions as $permission ) {
if ( $this->isAllowed( $permission ) ) {
@@ -3201,10 +3230,10 @@ class User implements IDBAccessObject {
/**
*
- * @param string $permissions,... Permissions to test
+ * @param string ... Permissions to test
* @return bool True if the user is allowed to perform *all* of the given actions
*/
- public function isAllowedAll( /*...*/ ) {
+ public function isAllowedAll() {
$permissions = func_get_args();
foreach ( $permissions as $permission ) {
if ( !$this->isAllowed( $permission ) ) {
@@ -3368,19 +3397,24 @@ class User implements IDBAccessObject {
return;
}
- $nextid = $oldid ? $title->getNextRevisionID( $oldid ) : null;
+ $that = $this;
+ // Try to update the DB post-send and only if needed...
+ DeferredUpdates::addCallableUpdate( function() use ( $that, $title, $oldid ) {
+ if ( !$that->getNewtalk() ) {
+ return; // no notifications to clear
+ }
- if ( !$oldid || !$nextid ) {
- // If we're looking at the latest revision, we should definitely clear it
- $this->setNewtalk( false );
- } else {
- // Otherwise we should update its revision, if it's present
- if ( $this->getNewtalk() ) {
- // Naturally the other one won't clear by itself
- $this->setNewtalk( false );
- $this->setNewtalk( true, Revision::newFromId( $nextid ) );
+ // Delete the last notifications (they stack up)
+ $that->setNewtalk( false );
+
+ // If there is a new, unseen, revision, use its timestamp
+ $nextid = $oldid
+ ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
+ : null;
+ if ( $nextid ) {
+ $that->setNewtalk( true, Revision::newFromId( $nextid ) );
}
- }
+ } );
}
if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
@@ -3401,7 +3435,9 @@ class User implements IDBAccessObject {
$force = 'force';
}
- $this->getWatchedItem( $title )->resetNotificationTimestamp( $force, $oldid );
+ $this->getWatchedItem( $title )->resetNotificationTimestamp(
+ $force, $oldid, WatchedItem::DEFERRED
+ );
}
/**
@@ -3430,7 +3466,7 @@ class User implements IDBAccessObject {
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'watchlist',
array( /* SET */ 'wl_notificationtimestamp' => null ),
- array( /* WHERE */ 'wl_user' => $id ),
+ array( /* 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;
@@ -3477,6 +3513,31 @@ class User implements IDBAccessObject {
}
/**
+ * Set an extended login cookie on the user's client. The expiry of the cookie
+ * is controlled by the $wgExtendedLoginCookieExpiration configuration
+ * variable.
+ *
+ * @see User::setCookie
+ *
+ * @param string $name Name of the cookie to set
+ * @param string $value Value to set
+ * @param bool $secure
+ * true: Force setting the secure attribute when setting the cookie
+ * false: Force NOT setting the secure attribute when setting the cookie
+ * null (default): Use the default ($wgCookieSecure) to set the secure attribute
+ */
+ protected function setExtendedLoginCookie( $name, $value, $secure ) {
+ global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
+
+ $exp = time();
+ $exp += $wgExtendedLoginCookieExpiration !== null
+ ? $wgExtendedLoginCookieExpiration
+ : $wgCookieExpiration;
+
+ $this->setCookie( $name, $value, $exp, $secure );
+ }
+
+ /**
* Set the default cookies for this session on the user's client.
*
* @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
@@ -3485,6 +3546,8 @@ class User implements IDBAccessObject {
* @param bool $rememberMe Whether to add a Token cookie for elongated sessions
*/
public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
+ global $wgExtendedLoginCookies;
+
if ( $request === null ) {
$request = $this->getRequest();
}
@@ -3526,6 +3589,8 @@ class User implements IDBAccessObject {
foreach ( $cookies as $name => $value ) {
if ( $value === false ) {
$this->clearCookie( $name );
+ } elseif ( $rememberMe && in_array( $name, $wgExtendedLoginCookies ) ) {
+ $this->setExtendedLoginCookie( $name, $value, $secure );
} else {
$this->setCookie( $name, $value, 0, $secure, array(), $request );
}
@@ -3598,17 +3663,11 @@ class User implements IDBAccessObject {
return; // anon
}
- // This method is for updating existing users, so the user should
- // have been loaded from the master to begin with to avoid problems.
- if ( !( $this->queryFlagsUsed & self::READ_LATEST ) ) {
- wfWarn( "Attempting to save slave-loaded User object with ID '{$this->mId}'." );
- }
-
// 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.
$oldTouched = $this->mTouched;
- $this->mTouched = $this->newTouchedTimestamp();
+ $newTouched = $this->newTouchedTimestamp();
if ( !$wgAuth->allowSetLocalPassword() ) {
$this->mPassword = self::getPasswordFactory()->newFromCiphertext( null );
@@ -3624,7 +3683,7 @@ class User implements IDBAccessObject {
'user_real_name' => $this->mRealName,
'user_email' => $this->mEmail,
'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
- 'user_touched' => $dbw->timestamp( $this->mTouched ),
+ 'user_touched' => $dbw->timestamp( $newTouched ),
'user_token' => strval( $this->mToken ),
'user_email_token' => $this->mEmailToken,
'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
@@ -3636,16 +3695,17 @@ class User implements IDBAccessObject {
);
if ( !$dbw->affectedRows() ) {
- // User was changed in the meantime or loaded with stale data
- MWExceptionHandler::logException( new MWException(
- "CAS update failed on user_touched for user ID '{$this->mId}'."
- ) );
// Maybe the problem was a missed cache update; clear it to be safe
$this->clearSharedCache();
-
- return;
+ // User was changed in the meantime or loaded with stale data
+ $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'slave';
+ 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."
+ );
}
+ $this->mTouched = $newTouched;
$this->saveOptions();
Hooks::run( 'UserSaveSettings', array( $this ) );
@@ -3655,20 +3715,28 @@ class User implements IDBAccessObject {
/**
* If only this user's username is known, and it exists, return the user ID.
+ *
+ * @param int $flags Bitfield of User:READ_* constants; useful for existence checks
* @return int
*/
- public function idForName() {
+ public function idForName( $flags = 0 ) {
$s = trim( $this->getName() );
if ( $s === '' ) {
return 0;
}
- $dbr = wfGetDB( DB_SLAVE );
- $id = $dbr->selectField( 'user', 'user_id', array( 'user_name' => $s ), __METHOD__ );
- if ( $id === false ) {
- $id = 0;
- }
- return $id;
+ $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
+ ? wfGetDB( DB_MASTER )
+ : wfGetDB( DB_SLAVE );
+
+ $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
+ ? array( 'LOCK IN SHARE MODE' )
+ : array();
+
+ $id = $db->selectField( 'user',
+ 'user_id', array( 'user_name' => $s ), __METHOD__, $options );
+
+ return (int)$id;
}
/**
@@ -4172,22 +4240,25 @@ class User implements IDBAccessObject {
*
* @param string $subject Message subject
* @param string $body Message body
- * @param string $from Optional From address; if unspecified, default
+ * @param User|null $from Optional sending user; if unspecified, default
* $wgPasswordSender will be used.
* @param string $replyto Reply-To address
* @return Status
*/
public function sendMail( $subject, $body, $from = null, $replyto = null ) {
- if ( is_null( $from ) ) {
- global $wgPasswordSender;
+ global $wgPasswordSender;
+
+ if ( $from instanceof User ) {
+ $sender = MailAddress::newFromUser( $from );
+ } else {
$sender = new MailAddress( $wgPasswordSender,
wfMessage( 'emailsender' )->inContentLanguage()->text() );
- } else {
- $sender = MailAddress::newFromUser( $from );
}
-
$to = MailAddress::newFromUser( $this );
- return UserMailer::send( $to, $sender, $subject, $body, $replyto );
+
+ return UserMailer::send( $to, $sender, $subject, $body, array(
+ 'replyTo' => $replyto,
+ ) );
}
/**
@@ -4745,37 +4816,50 @@ class User implements IDBAccessObject {
}
/**
+ * Deferred version of incEditCountImmediate()
+ */
+ public function incEditCount() {
+ $that = $this;
+ wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() use ( $that ) {
+ $that->incEditCountImmediate();
+ } );
+ }
+
+ /**
* Increment the user's edit-count field.
* Will have no effect for anonymous users.
+ * @since 1.26
*/
- public function incEditCount() {
- if ( !$this->isAnon() ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update(
- 'user',
- array( 'user_editcount=user_editcount+1' ),
- array( 'user_id' => $this->getId() ),
- __METHOD__
- );
+ public function incEditCountImmediate() {
+ if ( $this->isAnon() ) {
+ return;
+ }
- // Lazy initialization check...
- if ( $dbw->affectedRows() == 0 ) {
- // Now here's a goddamn hack...
- $dbr = wfGetDB( DB_SLAVE );
- if ( $dbr !== $dbw ) {
- // If we actually have a slave server, the count is
- // at least one behind because the current transaction
- // has not been committed and replicated.
- $this->initEditCount( 1 );
- } else {
- // But if DB_SLAVE is selecting the master, then the
- // count we just read includes the revision that was
- // just added in the working transaction.
- $this->initEditCount();
- }
+ $dbw = wfGetDB( DB_MASTER );
+ // No rows will be "affected" if user_editcount is NULL
+ $dbw->update(
+ 'user',
+ array( 'user_editcount=user_editcount+1' ),
+ array( 'user_id' => $this->getId() ),
+ __METHOD__
+ );
+ // Lazy initialization check...
+ if ( $dbw->affectedRows() == 0 ) {
+ // Now here's a goddamn hack...
+ $dbr = wfGetDB( DB_SLAVE );
+ if ( $dbr !== $dbw ) {
+ // If we actually have a slave server, the count is
+ // at least one behind because the current transaction
+ // has not been committed and replicated.
+ $this->initEditCount( 1 );
+ } else {
+ // But if DB_SLAVE is selecting the master, then the
+ // count we just read includes the revision that was
+ // just added in the working transaction.
+ $this->initEditCount();
}
}
- // edit count in user cache too
+ // Edit count in user cache too
$this->invalidateCache();
}
diff --git a/includes/UserRightsProxy.php b/includes/UserRightsProxy.php
index 1b9e4b69..a19f6984 100644
--- a/includes/UserRightsProxy.php
+++ b/includes/UserRightsProxy.php
@@ -278,8 +278,8 @@ class UserRightsProxy {
array( 'user_id' => $this->id ),
__METHOD__ );
- global $wgMemc;
+ $cache = ObjectCache::getMainWANInstance();
$key = wfForeignMemcKey( $this->database, false, 'user', 'id', $this->id );
- $wgMemc->delete( $key );
+ $cache->delete( $key );
}
}
diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php
index 4d226924..adee1264 100644
--- a/includes/WatchedItem.php
+++ b/includes/WatchedItem.php
@@ -27,20 +27,6 @@
* @ingroup Watchlist
*/
class WatchedItem {
- /**
- * Constant to specify that user rights 'editmywatchlist' and
- * 'viewmywatchlist' should not be checked.
- * @since 1.22
- */
- const IGNORE_USER_RIGHTS = 0;
-
- /**
- * Constant to specify that user rights 'editmywatchlist' and
- * 'viewmywatchlist' should be checked.
- * @since 1.22
- */
- const CHECK_USER_RIGHTS = 1;
-
/** @var Title */
public $mTitle;
@@ -60,6 +46,31 @@ class WatchedItem {
private $timestamp;
/**
+ * Constant to specify that user rights 'editmywatchlist' and
+ * 'viewmywatchlist' should not be checked.
+ * @since 1.22
+ */
+ const IGNORE_USER_RIGHTS = 0;
+
+ /**
+ * Constant to specify that user rights 'editmywatchlist' and
+ * 'viewmywatchlist' should be checked.
+ * @since 1.22
+ */
+ const CHECK_USER_RIGHTS = 1;
+
+ /**
+ * Do DB master updates right now
+ * @since 1.26
+ */
+ const IMMEDIATE = 0;
+ /**
+ * Do DB master updates via the job queue
+ * @since 1.26
+ */
+ const DEFERRED = 1;
+
+ /**
* Create a WatchedItem object with the given user and title
* @since 1.22 $checkRights parameter added
* @param User $user The user to use for (un)watching
@@ -208,8 +219,11 @@ class WatchedItem {
* @param bool $force Whether to force the write query to be executed even if the
* page is not watched or the notification timestamp is already NULL.
* @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed.
+ * @mode int $mode WatchedItem::DEFERRED/IMMEDIATE
*/
- public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
+ public function resetNotificationTimestamp(
+ $force = '', $oldid = 0, $mode = self::IMMEDIATE
+ ) {
// Only loggedin user can have a watchlist
if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
return;
@@ -240,11 +254,7 @@ class WatchedItem {
} else {
// Oldid given and isn't the latest; update the timestamp.
// This will result in no further notification emails being sent!
- $dbr = wfGetDB( DB_SLAVE );
- $notificationTimestamp = $dbr->selectField(
- 'revision', 'rev_timestamp',
- array( 'rev_page' => $title->getArticleID(), 'rev_id' => $oldid )
- );
+ $notificationTimestamp = Revision::getTimestampFromId( $title, $oldid );
// We need to go one second to the future because of various strict comparisons
// throughout the codebase
$ts = new MWTimestamp( $notificationTimestamp );
@@ -262,11 +272,30 @@ class WatchedItem {
}
}
- // If the page is watched by the user (or may be watched), update the timestamp on any
- // any matching rows
- $dbw = wfGetDB( DB_MASTER );
- $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $notificationTimestamp ),
- $this->dbCond(), __METHOD__ );
+ // If the page is watched by the user (or may be watched), update the timestamp
+ if ( $mode === self::DEFERRED ) {
+ $job = new ActivityUpdateJob(
+ $title,
+ array(
+ 'type' => 'updateWatchlistNotification',
+ 'userid' => $this->getUserId(),
+ 'notifTime' => $notificationTimestamp,
+ 'curTime' => time()
+ )
+ );
+ // Try to run this post-send
+ DeferredUpdates::addCallableUpdate( function() use ( $job ) {
+ $job->run();
+ } );
+ } else {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->update( 'watchlist',
+ array( 'wl_notificationtimestamp' => $notificationTimestamp ),
+ $this->dbCond(),
+ __METHOD__
+ );
+ }
+
$this->timestamp = null;
}
diff --git a/includes/WebRequest.php b/includes/WebRequest.php
index 054eceb9..b4b8be9b 100644
--- a/includes/WebRequest.php
+++ b/includes/WebRequest.php
@@ -39,6 +39,12 @@ class WebRequest {
protected $data, $headers = array();
/**
+ * Flag to make WebRequest::getHeader return an array of values.
+ * @since 1.26
+ */
+ const GETHEADER_LIST = 1;
+
+ /**
* Lazy-init response object
* @var WebResponse
*/
@@ -98,9 +104,9 @@ class WebRequest {
if ( !preg_match( '!^https?://!', $url ) ) {
$url = 'http://unused' . $url;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$a = parse_url( $url );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $a ) {
$path = isset( $a['path'] ) ? $a['path'] : '';
@@ -170,6 +176,8 @@ class WebRequest {
* @return string
*/
public static function detectServer() {
+ global $wgAssumeProxiesUseDefaultProtocolPorts;
+
$proto = self::detectProtocol();
$stdPort = $proto === 'https' ? 443 : 80;
@@ -180,13 +188,15 @@ class WebRequest {
if ( !isset( $_SERVER[$varName] ) ) {
continue;
}
+
$parts = IP::splitHostAndPort( $_SERVER[$varName] );
if ( !$parts ) {
// Invalid, do not use
continue;
}
+
$host = $parts[0];
- if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
+ if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
// Bug 70021: Assume that upstream proxy is running on the default
// port based on the protocol. We have no reliable way to determine
// the actual port in use upstream.
@@ -685,7 +695,7 @@ class WebRequest {
// This shouldn't happen!
throw new MWException( "Web server doesn't provide either " .
"REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
- "of your web server configuration to http://bugzilla.wikimedia.org/" );
+ "of your web server configuration to https://phabricator.wikimedia.org/" );
}
// User-agents should not send a fragment with the URI, but
// if they do, and the web server passes it on to us, we
@@ -768,7 +778,7 @@ class WebRequest {
*
* @param int $deflimit Limit to use if no input and the user hasn't set the option.
* @param string $optionname To specify an option other than rclimit to pull from.
- * @return array First element is limit, second is offset
+ * @return int[] First element is limit, second is offset
*/
public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
global $wgUser;
@@ -861,7 +871,7 @@ class WebRequest {
/**
* Initialise the header list
*/
- private function initHeaders() {
+ protected function initHeaders() {
if ( count( $this->headers ) ) {
return;
}
@@ -894,19 +904,28 @@ class WebRequest {
}
/**
- * Get a request header, or false if it isn't set
- * @param string $name Case-insensitive header name
+ * Get a request header, or false if it isn't set.
*
- * @return string|bool False on failure
- */
- public function getHeader( $name ) {
+ * @param string $name Case-insensitive header name
+ * @param int $flags Bitwise combination of:
+ * WebRequest::GETHEADER_LIST Treat the header as a comma-separated list
+ * of values, as described in RFC 2616 § 4.2.
+ * (since 1.26).
+ * @return string|array|bool False if header is unset; otherwise the
+ * header value(s) as either a string (the default) or an array, if
+ * WebRequest::GETHEADER_LIST flag was set.
+ */
+ public function getHeader( $name, $flags = 0 ) {
$this->initHeaders();
$name = strtoupper( $name );
- if ( isset( $this->headers[$name] ) ) {
- return $this->headers[$name];
- } else {
+ if ( !isset( $this->headers[$name] ) ) {
return false;
}
+ $value = $this->headers[$name];
+ if ( $flags & self::GETHEADER_LIST ) {
+ $value = array_map( 'trim', explode( ',', $value ) );
+ }
+ return $value;
}
/**
@@ -1278,6 +1297,7 @@ class FauxRequest extends WebRequest {
private $wasPosted = false;
private $session = array();
private $requestUrl;
+ protected $cookies = array();
/**
* @param array $data Array of *non*-urlencoded key => value pairs, the
@@ -1305,11 +1325,10 @@ class FauxRequest extends WebRequest {
}
/**
- * @param string $method
- * @throws MWException
+ * Initialise the header list
*/
- private function notImplemented( $method ) {
- throw new MWException( "{$method}() not implemented" );
+ protected function initHeaders() {
+ // Nothing to init
}
/**
@@ -1352,7 +1371,38 @@ class FauxRequest extends WebRequest {
}
public function getCookie( $key, $prefix = null, $default = null ) {
- return $default;
+ if ( $prefix === null ) {
+ global $wgCookiePrefix;
+ $prefix = $wgCookiePrefix;
+ }
+ $name = $prefix . $key;
+ return isset( $this->cookies[$name] ) ? $this->cookies[$name] : $default;
+ }
+
+ /**
+ * @since 1.26
+ * @param string $name Unprefixed name of the cookie to set
+ * @param string|null $value Value of the cookie to set
+ * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
+ */
+ public function setCookie( $key, $value, $prefix = null ) {
+ $this->setCookies( array( $key => $value ), $prefix );
+ }
+
+ /**
+ * @since 1.26
+ * @param array $cookies
+ * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
+ */
+ public function setCookies( $cookies, $prefix = null ) {
+ if ( $prefix === null ) {
+ global $wgCookiePrefix;
+ $prefix = $wgCookiePrefix;
+ }
+ foreach ( $cookies as $key => $value ) {
+ $name = $prefix . $key;
+ $this->cookies[$name] = $value;
+ }
}
public function checkSessionCookie() {
@@ -1375,21 +1425,22 @@ class FauxRequest extends WebRequest {
}
/**
- * @param string $name The name of the header to get (case insensitive).
- * @return bool|string
+ * @param string $name
+ * @param string $val
*/
- public function getHeader( $name ) {
- $name = strtoupper( $name );
- return isset( $this->headers[$name] ) ? $this->headers[$name] : false;
+ public function setHeader( $name, $val ) {
+ $this->setHeaders( array( $name => $val ) );
}
/**
- * @param string $name
- * @param string $val
+ * @since 1.26
+ * @param array $headers
*/
- public function setHeader( $name, $val ) {
- $name = strtoupper( $name );
- $this->headers[$name] = $val;
+ public function setHeaders( $headers ) {
+ foreach ( $headers as $name => $val ) {
+ $name = strtoupper( $name );
+ $this->headers[$name] = $val;
+ }
}
/**
@@ -1488,8 +1539,8 @@ class DerivativeRequest extends FauxRequest {
return $this->base->checkSessionCookie();
}
- public function getHeader( $name ) {
- return $this->base->getHeader( $name );
+ public function getHeader( $name, $flags = 0 ) {
+ return $this->base->getHeader( $name, $flags );
}
public function getAllHeaders() {
diff --git a/includes/WebResponse.php b/includes/WebResponse.php
index ab34931c..1b6947cd 100644
--- a/includes/WebResponse.php
+++ b/includes/WebResponse.php
@@ -28,7 +28,7 @@
class WebResponse {
/**
- * Output a HTTP header, wrapper for PHP's header()
+ * Output an HTTP header, wrapper for PHP's header()
* @param string $string Header to output
* @param bool $replace Replace current similar header
* @param null|int $http_response_code Forces the HTTP response code to the specified value.
@@ -54,6 +54,15 @@ class WebResponse {
}
/**
+ * Output an HTTP status code header
+ * @since 1.26
+ * @param int $code Status code
+ */
+ public function statusHeader( $code ) {
+ HttpStatus::header( $code );
+ }
+
+ /**
* Set the browser cookie
* @param string $name The name of the cookie.
* @param string $value The value to be stored in the cookie.
@@ -163,6 +172,14 @@ class FauxResponse extends WebResponse {
}
/**
+ * @since 1.26
+ * @param int $code Status code
+ */
+ public function statusHeader( $code ) {
+ $this->code = intval( $code );
+ }
+
+ /**
* @param string $key The name of the header to get (case insensitive).
* @return string|null The header value (if set); null otherwise.
*/
diff --git a/includes/WebStart.php b/includes/WebStart.php
index 9c71f3e1..f5a4f93b 100644
--- a/includes/WebStart.php
+++ b/includes/WebStart.php
@@ -78,7 +78,6 @@ if ( $IP === false ) {
# Grab profiling functions
require_once "$IP/includes/profiler/ProfilerFunctions.php";
-$wgRUstart = wfGetRusage() ?: array();
# Start the autoloader, so that extensions can derive classes from core files
require_once "$IP/includes/AutoLoader.php";
@@ -137,3 +136,8 @@ if ( ob_get_level() == 0 ) {
if ( !defined( 'MW_NO_SETUP' ) ) {
require_once "$IP/includes/Setup.php";
}
+
+# Multiple DBs or commits might be used; keep the request as transactional as possible
+if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' ) {
+ ignore_user_abort( true );
+}
diff --git a/includes/WikiMap.php b/includes/WikiMap.php
index f16f5aa7..027ff72f 100644
--- a/includes/WikiMap.php
+++ b/includes/WikiMap.php
@@ -108,13 +108,15 @@ class WikiMap {
*
* @param string $wikiID Wiki'd id (generally database name)
* @param string $page Page name (must be normalised before calling this function!)
+ * @param string|null $fragmentId
+ *
* @return string|bool URL or false if the wiki was not found
*/
- public static function getForeignURL( $wikiID, $page ) {
+ public static function getForeignURL( $wikiID, $page, $fragmentId = null ) {
$wiki = WikiMap::getWiki( $wikiID );
if ( $wiki ) {
- return $wiki->getFullUrl( $page );
+ return $wiki->getFullUrl( $page, $fragmentId );
}
return false;
@@ -147,33 +149,18 @@ class WikiReference {
}
/**
- * @return string
- * @throws MWException
- */
- public function getHostname() {
- $prefixes = array( 'http://', 'https://' );
- foreach ( $prefixes as $prefix ) {
- if ( substr( $this->mCanonicalServer, 0, strlen( $prefix ) ) ) {
- return substr( $this->mCanonicalServer, strlen( $prefix ) );
- }
- }
- throw new MWException( "Invalid hostname for wiki {$this->mMinor}.{$this->mMajor}" );
- }
-
- /**
* Get the URL in a way to be displayed to the user
* More or less Wikimedia specific
*
* @return string
*/
public function getDisplayName() {
- $url = $this->getUrl( '' );
- $parsed = wfParseUrl( $url );
+ $parsed = wfParseUrl( $this->mCanonicalServer );
if ( $parsed ) {
return $parsed['host'];
} else {
- // Invalid URL. There's no sane thing to do here, so just return it
- return $url;
+ // Invalid server spec. There's no sane thing to do here, so just return the canonical server name in full
+ return $this->mCanonicalServer;
}
}
@@ -181,21 +168,32 @@ class WikiReference {
* Helper function for getUrl()
*
* @todo FIXME: This may be generalized...
- * @param string $page Page name (must be normalised before calling this function!)
- * @return string Url fragment
+ *
+ * @param string $page Page name (must be normalised before calling this function! May contain a section part.)
+ * @param string|null $fragmentId
+ *
+ * @return string relative URL, without the server part.
*/
- private function getLocalUrl( $page ) {
- return str_replace( '$1', wfUrlEncode( str_replace( ' ', '_', $page ) ), $this->mPath );
+ private function getLocalUrl( $page, $fragmentId = null ) {
+ $page = wfUrlEncode( str_replace( ' ', '_', $page ) );
+
+ if ( is_string( $fragmentId ) && $fragmentId !== '' ) {
+ $page .= '#' . wfUrlEncode( $fragmentId );
+ }
+
+ return str_replace( '$1', $page, $this->mPath );
}
/**
* Get a canonical (i.e. based on $wgCanonicalServer) URL to a page on this foreign wiki
*
* @param string $page Page name (must be normalised before calling this function!)
+ * @param string|null $fragmentId
+ *
* @return string Url
*/
- public function getCanonicalUrl( $page ) {
- return $this->mCanonicalServer . $this->getLocalUrl( $page );
+ public function getCanonicalUrl( $page, $fragmentId = null ) {
+ return $this->mCanonicalServer . $this->getLocalUrl( $page, $fragmentId );
}
/**
@@ -209,10 +207,12 @@ class WikiReference {
/**
* Alias for getCanonicalUrl(), for backwards compatibility.
* @param string $page
+ * @param string|null $fragmentId
+ *
* @return string
*/
- public function getUrl( $page ) {
- return $this->getCanonicalUrl( $page );
+ public function getUrl( $page, $fragmentId = null ) {
+ return $this->getCanonicalUrl( $page, $fragmentId );
}
/**
@@ -220,10 +220,12 @@ class WikiReference {
* when called locally on the wiki.
*
* @param string $page Page name (must be normalized before calling this function!)
+ * @param string|null $fragmentId
+ *
* @return string URL
*/
- public function getFullUrl( $page ) {
+ public function getFullUrl( $page, $fragmentId = null ) {
return $this->mServer .
- $this->getLocalUrl( $page );
+ $this->getLocalUrl( $page, $fragmentId );
}
}
diff --git a/includes/Xml.php b/includes/Xml.php
index f0bd70b2..37cffdef 100644
--- a/includes/Xml.php
+++ b/includes/Xml.php
@@ -144,26 +144,19 @@ class Xml {
public static function monthSelector( $selected = '', $allmonths = null, $id = 'month' ) {
global $wgLang;
$options = array();
+ $data = new XmlSelect( 'month', $id, $selected );
if ( is_null( $selected ) ) {
$selected = '';
}
if ( !is_null( $allmonths ) ) {
- $options[] = self::option(
- wfMessage( 'monthsall' )->text(),
- $allmonths,
- $selected === $allmonths
- );
+ $options[wfMessage( 'monthsall' )->text()] = $allmonths;
}
for ( $i = 1; $i < 13; $i++ ) {
- $options[] = self::option( $wgLang->getMonthName( $i ), $i, $selected === $i );
+ $options[$wgLang->getMonthName( $i )] = $i;
}
- return self::openElement( 'select', array(
- 'id' => $id,
- 'name' => 'month',
- 'class' => 'mw-month-selector'
- ) )
- . implode( "\n", $options )
- . self::closeElement( 'select' );
+ $data->addOptions( $options );
+ $data->setAttribute( 'class', 'mw-month-selector' );
+ return $data->getHTML();
}
/**
@@ -871,112 +864,6 @@ class Xml {
}
}
-class XmlSelect {
- protected $options = array();
- protected $default = false;
- protected $attributes = array();
-
- public function __construct( $name = false, $id = false, $default = false ) {
- if ( $name ) {
- $this->setAttribute( 'name', $name );
- }
-
- if ( $id ) {
- $this->setAttribute( 'id', $id );
- }
-
- if ( $default !== false ) {
- $this->default = $default;
- }
- }
-
- /**
- * @param string $default
- */
- public function setDefault( $default ) {
- $this->default = $default;
- }
-
- /**
- * @param string $name
- * @param array $value
- */
- public function setAttribute( $name, $value ) {
- $this->attributes[$name] = $value;
- }
-
- /**
- * @param string $name
- * @return array|null
- */
- public function getAttribute( $name ) {
- if ( isset( $this->attributes[$name] ) ) {
- return $this->attributes[$name];
- } else {
- return null;
- }
- }
-
- /**
- * @param string $name
- * @param bool $value
- */
- public function addOption( $name, $value = false ) {
- // Stab stab stab
- $value = $value !== false ? $value : $name;
-
- $this->options[] = array( $name => $value );
- }
-
- /**
- * This accepts an array of form
- * label => value
- * label => ( label => value, label => value )
- *
- * @param array $options
- */
- public function addOptions( $options ) {
- $this->options[] = $options;
- }
-
- /**
- * This accepts an array of form
- * label => value
- * label => ( label => value, label => value )
- *
- * @param array $options
- * @param bool $default
- * @return string
- */
- static function formatOptions( $options, $default = false ) {
- $data = '';
-
- foreach ( $options as $label => $value ) {
- if ( is_array( $value ) ) {
- $contents = self::formatOptions( $value, $default );
- $data .= Html::rawElement( 'optgroup', array( 'label' => $label ), $contents ) . "\n";
- } else {
- $data .= Xml::option( $label, $value, $value === $default ) . "\n";
- }
- }
-
- return $data;
- }
-
- /**
- * @return string
- */
- public function getHTML() {
- $contents = '';
-
- foreach ( $this->options as $options ) {
- $contents .= self::formatOptions( $options, $this->default );
- }
-
- return Html::rawElement( 'select', $this->attributes, rtrim( $contents ) );
- }
-}
-
/**
* A wrapper class which causes Xml::encodeJsVar() and Xml::encodeJsCall() to
* interpret a given string as being a JavaScript expression, instead of string
diff --git a/includes/XmlSelect.php b/includes/XmlSelect.php
new file mode 100644
index 00000000..78f47645
--- /dev/null
+++ b/includes/XmlSelect.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Class for generating HTML <select> elements.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Class for generating HTML <select> elements.
+ */
+class XmlSelect {
+ protected $options = array();
+ protected $default = false;
+ protected $attributes = array();
+
+ public function __construct( $name = false, $id = false, $default = false ) {
+ if ( $name ) {
+ $this->setAttribute( 'name', $name );
+ }
+
+ if ( $id ) {
+ $this->setAttribute( 'id', $id );
+ }
+
+ if ( $default !== false ) {
+ $this->default = $default;
+ }
+ }
+
+ /**
+ * @param string|array $default
+ */
+ public function setDefault( $default ) {
+ $this->default = $default;
+ }
+
+ /**
+ * @param string $name
+ * @param string $value
+ */
+ public function setAttribute( $name, $value ) {
+ $this->attributes[$name] = $value;
+ }
+
+ /**
+ * @param string $name
+ * @return string|null
+ */
+ public function getAttribute( $name ) {
+ if ( isset( $this->attributes[$name] ) ) {
+ return $this->attributes[$name];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param string $label
+ * @param string $value If not given, assumed equal to $label
+ */
+ public function addOption( $label, $value = false ) {
+ $value = $value !== false ? $value : $label;
+ $this->options[] = array( $label => $value );
+ }
+
+ /**
+ * This accepts an array of form
+ * label => value
+ * label => ( label => value, label => value )
+ *
+ * @param array $options
+ */
+ public function addOptions( $options ) {
+ $this->options[] = $options;
+ }
+
+ /**
+ * This accepts an array of form:
+ * label => value
+ * label => ( label => value, label => value )
+ *
+ * @param array $options
+ * @param string|array $default
+ * @return string
+ */
+ static function formatOptions( $options, $default = false ) {
+ $data = '';
+
+ foreach ( $options as $label => $value ) {
+ if ( is_array( $value ) ) {
+ $contents = self::formatOptions( $value, $default );
+ $data .= Html::rawElement( 'optgroup', array( 'label' => $label ), $contents ) . "\n";
+ } else {
+ // If $default is an array, then the <select> probably has the multiple attribute,
+ // so we should check if each $value is in $default, rather than checking if
+ // $value is equal to $default.
+ $selected = is_array( $default ) ? in_array( $value, $default ) : $value === $default;
+ $data .= Xml::option( $label, $value, $selected ) . "\n";
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @return string
+ */
+ public function getHTML() {
+ $contents = '';
+
+ foreach ( $this->options as $options ) {
+ $contents .= self::formatOptions( $options, $this->default );
+ }
+
+ return Html::rawElement( 'select', $this->attributes, rtrim( $contents ) );
+ }
+}
diff --git a/includes/ZhConversion.php b/includes/ZhConversion.php
index 4be27513..893ae040 100644
--- a/includes/ZhConversion.php
+++ b/includes/ZhConversion.php
@@ -46,7 +46,6 @@ $zh2Hant = array(
'㱮' => '殨',
'㲿' => '瀇',
'㳔' => '濧',
-'㳕' => '灡',
'㳠' => '澾',
'㳡' => '濄',
'㳢' => '𣾷',
@@ -79,7 +78,6 @@ $zh2Hant = array(
'䍁' => '繸',
'䎬' => '䎱',
'䏝' => '膞',
-'䓕' => '薳',
'䓖' => '藭',
'䗖' => '螮',
'䘛' => '𧝞',
@@ -375,6 +373,7 @@ $zh2Hant = array(
'啰' => '囉',
'啴' => '嘽',
'啸' => '嘯',
+'喂' => '餵',
'喷' => '噴',
'喽' => '嘍',
'喾' => '嚳',
@@ -415,7 +414,6 @@ $zh2Hant = array(
'垭' => '埡',
'垱' => '壋',
'垲' => '塏',
-'垴' => '堖',
'埘' => '塒',
'埙' => '塤',
'埚' => '堝',
@@ -504,7 +502,6 @@ $zh2Hant = array(
'岖' => '嶇',
'岗' => '崗',
'岘' => '峴',
-'岙' => '嶴',
'岚' => '嵐',
'岛' => '島',
'岭' => '嶺',
@@ -1373,6 +1370,7 @@ $zh2Hant = array(
'脶' => '腡',
'脸' => '臉',
'腊' => '臘',
+'腌' => '醃',
'腘' => '膕',
'腭' => '齶',
'腻' => '膩',
@@ -1477,7 +1475,6 @@ $zh2Hant = array(
'虑' => '慮',
'虚' => '虛',
'虫' => '蟲',
-'虬' => '虯',
'虮' => '蟣',
'虽' => '雖',
'虾' => '蝦',
@@ -2889,7 +2886,6 @@ $zh2Hant = array(
'𩧿' => '䮠',
'𩨀' => '騔',
'𩨁' => '䮞',
-'𩨂' => '驄',
'𩨃' => '騝',
'𩨄' => '騪',
'𩨅' => '𩤸',
@@ -3002,8 +2998,8 @@ $zh2Hant = array(
'𫛢' => '鸋',
'𫛶' => '鶒',
'𫛸' => '鶗',
-'0出現' => '0出現',
'0出现' => '0出現',
+'0出現' => '0出現',
'0出線' => '0出線',
'0出线' => '0出線',
'0只支持' => '0只支持',
@@ -3079,6 +3075,8 @@ $zh2Hant = array(
'9余' => '9餘',
'·范' => '·范',
'’s' => '’s',
+'、面点' => '、麵點',
+'。个中' => '。箇中',
'〇周后' => '〇周後',
'〇年' => '〇年',
'〇只' => '〇隻',
@@ -3116,6 +3114,7 @@ $zh2Hant = array(
'一争两丑' => '一爭兩醜',
'一物克一物' => '一物剋一物',
'一目了然' => '一目了然',
+'一碗面' => '一碗麵',
'一扎' => '一紮',
'一冲' => '一衝',
'一厘一毫' => '一釐一毫',
@@ -3188,6 +3187,7 @@ $zh2Hant = array(
'下签' => '下籤',
'下课钟' => '下課鐘',
'不干不净' => '不乾不淨',
+'不干胶' => '不乾膠',
'不克自制' => '不克自制',
'不加自制' => '不加自制',
'不占凶吉' => '不占凶吉',
@@ -3201,26 +3201,11 @@ $zh2Hant = array(
'不好干预' => '不好干預',
'不嫌母丑' => '不嫌母醜',
'不寒而栗' => '不寒而慄',
-'不干事' => '不干事',
-'不干他' => '不干他',
-'不干休' => '不干休',
-'不干你' => '不干你',
-'不干她' => '不干她',
-'不干它' => '不干它',
-'不干我' => '不干我',
-'不干擾' => '不干擾',
-'不干扰' => '不干擾',
-'不干涉' => '不干涉',
-'不干牠' => '不干牠',
-'不干犯' => '不干犯',
-'不干預' => '不干預',
-'不干预' => '不干預',
-'不干' => '不幹',
'不吊' => '不弔',
'不卷' => '不捲',
'不采' => '不採',
-'不斗膽' => '不斗膽',
'不斗胆' => '不斗膽',
+'不斗膽' => '不斗膽',
'不断发' => '不斷發',
'不每只' => '不每只',
'不谷' => '不穀',
@@ -3228,8 +3213,8 @@ $zh2Hant = array(
'不负所托' => '不負所托',
'不通吊庆' => '不通弔慶',
'不丑' => '不醜',
-'不采聲' => '不采聲',
'不采声' => '不采聲',
+'不采聲' => '不采聲',
'不锈钢' => '不鏽鋼',
'不食干腊' => '不食乾腊',
'不斗' => '不鬥',
@@ -3260,6 +3245,7 @@ $zh2Hant = array(
'中型钟表面' => '中型鐘表面',
'中型钟表' => '中型鐘錶',
'中型钟面' => '中型鐘面',
+'中境里' => '中境里',
'中岳' => '中嶽',
'中庄子' => '中庄子',
'中文里' => '中文裡',
@@ -3275,31 +3261,36 @@ $zh2Hant = array(
'中签订' => '中簽訂',
'中签' => '中籤',
'中风后' => '中風後',
-'丰儀' => '丰儀',
'丰仪' => '丰儀',
+'丰儀' => '丰儀',
'丰南' => '丰南',
'丰姿' => '丰姿',
'丰容' => '丰容',
-'丰度' => '丰度',
'丰情' => '丰情',
'丰标' => '丰標',
-'丰標不凡' => '丰標不凡',
'丰标不凡' => '丰標不凡',
+'丰標不凡' => '丰標不凡',
'丰神' => '丰神',
'丰茸' => '丰茸',
'丰采' => '丰采',
-'丰韻' => '丰韻',
'丰韵' => '丰韻',
+'丰韻' => '丰韻',
+'丹棱' => '丹稜',
'主仆' => '主僕',
'主干' => '主幹',
'主钟差' => '主鐘差',
'主钟曲线' => '主鐘曲線',
'乃系' => '乃係',
'么么唱唱' => '么么唱唱',
+'么九' => '么九',
'么儿' => '么兒',
+'么半' => '么半',
'么喝' => '么喝',
+'么女' => '么女',
'么妹' => '么妹',
+'么子' => '么子',
'么弟' => '么弟',
+'么正' => '么正',
'么爷' => '么爺',
'么雞' => '么雞',
'么么小丑' => '么麼小丑',
@@ -3321,12 +3312,8 @@ $zh2Hant = array(
'九扎' => '九紮',
'九只' => '九隻',
'九余' => '九餘',
-'也斗了胆' => '也斗了膽',
-'干上' => '乾上',
'干干' => '乾乾',
-'干干儿的' => '乾乾兒的',
'干干净净' => '乾乾淨淨',
-'干了' => '乾了',
'干井' => '乾井',
'干个够' => '乾個夠',
'干儿' => '乾兒',
@@ -3349,12 +3336,12 @@ $zh2Hant = array(
'干回付' => '乾回付',
'干圆洁净' => '乾圓潔淨',
'干地' => '乾地',
-'干坤' => '乾坤',
'干坞' => '乾塢',
'干女' => '乾女',
'干奴才' => '乾奴才',
'干妹' => '乾妹',
'干姊' => '乾姊',
+'干姐' => '乾姐',
'干娘' => '乾娘',
'干妈' => '乾媽',
'干子' => '乾子',
@@ -3364,7 +3351,6 @@ $zh2Hant = array(
'干巴' => '乾巴',
'干式' => '乾式',
'干弟' => '乾弟',
-'干得' => '乾得',
'干急' => '乾急',
'干性' => '乾性',
'干打雷' => '乾打雷',
@@ -3374,7 +3360,6 @@ $zh2Hant = array(
'干擦' => '乾擦',
'干支剌' => '乾支剌',
'干支支' => '乾支支',
-'干敲梆子不卖油' => '乾敲梆子不賣油',
'干料' => '乾料',
'干旱' => '乾旱',
'干暖' => '乾暖',
@@ -3418,6 +3403,7 @@ $zh2Hant = array(
'干癣' => '乾癬',
'干瘾' => '乾癮',
'干白儿' => '乾白兒',
+'干白葡萄酒' => '乾白葡萄酒',
'干的' => '乾的',
'干眼' => '乾眼',
'干瞪眼' => '乾瞪眼',
@@ -3428,6 +3414,7 @@ $zh2Hant = array(
'干篾片' => '乾篾片',
'干粉' => '乾粉',
'干粮' => '乾糧',
+'干红葡萄酒' => '乾紅葡萄酒',
'干结' => '乾結',
'干丝' => '乾絲',
'干纲' => '乾綱',
@@ -3464,13 +3451,14 @@ $zh2Hant = array(
'干醋' => '乾醋',
'干重' => '乾重',
'干量' => '乾量',
+'干锅' => '乾鍋',
'干阿奶' => '乾阿奶',
-'干隆' => '乾隆',
'干雷' => '乾雷',
'干电' => '乾電',
'干霍乱' => '乾霍亂',
'干颡' => '乾顙',
'干台' => '乾颱',
+'干食' => '乾食',
'干饭' => '乾飯',
'干馆' => '乾館',
'干糇' => '乾餱',
@@ -3481,13 +3469,10 @@ $zh2Hant = array(
'乱发生' => '亂發生',
'乱发脾气' => '亂發脾氣',
'乱发' => '亂髮',
-'乱哄' => '亂鬨',
-'乱哄不过来' => '亂鬨不過來',
+'乱哄哄' => '亂鬨鬨',
'了然后' => '了然後',
-'事情干脆' => '事情干脆',
'事有斗巧' => '事有鬥巧',
'事里' => '事裡',
-'事都干脆' => '事都干脆',
'二不棱登' => '二不稜登',
'二个' => '二個',
'二只得' => '二只得',
@@ -3507,11 +3492,11 @@ $zh2Hant = array(
'于仲文' => '于仲文',
'于佳卉' => '于佳卉',
'于来山' => '于來山',
-'于偉國' => '于偉國',
'于伟国' => '于偉國',
+'于偉國' => '于偉國',
'于光新' => '于光新',
-'于光遠' => '于光遠',
'于光远' => '于光遠',
+'于光遠' => '于光遠',
'于克-兰多县' => '于克-蘭多縣',
'于克-蘭多縣' => '于克-蘭多縣',
'于克勒' => '于克勒',
@@ -3530,12 +3515,12 @@ $zh2Hant = array(
'于吉' => '于吉',
'于和伟' => '于和偉',
'于品海' => '于品海',
-'于國楨' => '于國楨',
'于国桢' => '于國楨',
+'于國楨' => '于國楨',
'于国治' => '于國治',
'于國治' => '于國治',
-'于堅' => '于堅',
'于坚' => '于堅',
+'于堅' => '于堅',
'于大宝' => '于大寶',
'于大寶' => '于大寶',
'于天仁' => '于天仁',
@@ -3558,12 +3543,12 @@ $zh2Hant = array(
'于小惠' => '于小惠',
'于少保' => '于少保',
'于山' => '于山',
-'于山國' => '于山國',
'于山国' => '于山國',
+'于山國' => '于山國',
'于帅' => '于帥',
'于帥' => '于帥',
-'于幼軍' => '于幼軍',
'于幼军' => '于幼軍',
+'于幼軍' => '于幼軍',
'于康震' => '于康震',
'于广洲' => '于廣洲',
'于廣洲' => '于廣洲',
@@ -3571,14 +3556,14 @@ $zh2Hant = array(
'于从濂' => '于從濂',
'于從濂' => '于從濂',
'于德海' => '于德海',
-'于志寧' => '于志寧',
'于志宁' => '于志寧',
+'于志寧' => '于志寧',
'于忠肃集' => '于忠肅集',
'于思' => '于思',
'于慎行' => '于慎行',
'于慧' => '于慧',
-'于成龙' => '于成龍',
'于成龍' => '于成龍',
+'于成龙' => '于成龍',
'于振' => '于振',
'于振武' => '于振武',
'于敏' => '于敏',
@@ -3587,24 +3572,24 @@ $zh2Hant = array(
'于斯塔德' => '于斯塔德',
'于斯納爾斯貝里' => '于斯納爾斯貝里',
'于斯纳尔斯贝里' => '于斯納爾斯貝里',
-'于斯達爾' => '于斯達爾',
'于斯达尔' => '于斯達爾',
-'于明濤' => '于明濤',
+'于斯達爾' => '于斯達爾',
'于明涛' => '于明濤',
+'于明濤' => '于明濤',
'于是之' => '于是之',
'于晨楠' => '于晨楠',
'于晴' => '于晴',
'于会泳' => '于會泳',
'于會泳' => '于會泳',
-'于根偉' => '于根偉',
'于根伟' => '于根偉',
+'于根偉' => '于根偉',
'于格' => '于格',
'于枫' => '于楓',
'于楓' => '于楓',
'于荣光' => '于榮光',
'于樂' => '于樂',
-'于樹潔' => '于樹潔',
'于树洁' => '于樹潔',
+'于樹潔' => '于樹潔',
'于欣' => '于欣',
'于欣源' => '于欣源',
'于正昇' => '于正昇',
@@ -3615,18 +3600,18 @@ $zh2Hant = array(
'于江震' => '于江震',
'于波' => '于波',
'于洋' => '于洋',
-'于洪區' => '于洪區',
'于洪区' => '于洪區',
+'于洪區' => '于洪區',
'于浩威' => '于浩威',
'于海' => '于海',
'于海洋' => '于海洋',
-'于湘蘭' => '于湘蘭',
'于湘兰' => '于湘蘭',
-'于漢超' => '于漢超',
+'于湘蘭' => '于湘蘭',
'于汉超' => '于漢超',
+'于漢超' => '于漢超',
'于澄' => '于澄',
-'于澤爾' => '于澤爾',
'于泽尔' => '于澤爾',
+'于澤爾' => '于澤爾',
'于涛' => '于濤',
'于濤' => '于濤',
'于熙珍' => '于熙珍',
@@ -3655,10 +3640,9 @@ $zh2Hant = array(
'于谨' => '于謹',
'于貝爾' => '于貝爾',
'于贝尔' => '于貝爾',
-'于赠' => '于贈',
'于贈' => '于贈',
+'于赠' => '于贈',
'于越' => '于越',
-'于军' => '于軍',
'于軍' => '于軍',
'于道泉' => '于道泉',
'于远伟' => '于遠偉',
@@ -3679,23 +3663,23 @@ $zh2Hant = array(
'于非闇' => '于非闇',
'于韋斯屈萊' => '于韋斯屈萊',
'于韦斯屈莱' => '于韋斯屈萊',
-'于风政' => '于風政',
'于風政' => '于風政',
+'于风政' => '于風政',
+'于飛' => '于飛',
'于飞' => '于飛',
-'于飛島' => '于飛島',
-'于飞岛' => '于飛島',
'于余曲折' => '于餘曲折',
'于鬯' => '于鬯',
'于魁智' => '于魁智',
-'于鳳桐' => '于鳳桐',
'于凤桐' => '于鳳桐',
-'于鳳至' => '于鳳至',
+'于鳳桐' => '于鳳桐',
'于凤至' => '于鳳至',
-'于默奧' => '于默奧',
+'于鳳至' => '于鳳至',
'于默奥' => '于默奧',
+'于默奧' => '于默奧',
'云乎' => '云乎',
'云云' => '云云',
'云何' => '云何',
+'云敞' => '云敞',
'云为' => '云為',
'云為' => '云為',
'云然' => '云然',
@@ -3704,6 +3688,7 @@ $zh2Hant = array(
'五个' => '五個',
'五周后' => '五周後',
'五天后' => '五天後',
+'五峰县' => '五峯縣',
'五岳' => '五嶽',
'五年' => '五年',
'五谷' => '五穀',
@@ -3715,7 +3700,6 @@ $zh2Hant = array(
'五只' => '五隻',
'五余' => '五餘',
'井干' => '井幹',
-'井干摧败' => '井榦摧敗',
'井里' => '井裡',
'亚于' => '亞於',
'亚美尼亚历' => '亞美尼亞曆',
@@ -3723,27 +3707,31 @@ $zh2Hant = array(
'交游' => '交遊',
'交哄' => '交鬨',
'亦云' => '亦云',
+'京沈' => '京瀋',
'亮丑' => '亮醜',
'亮钟' => '亮鐘',
'人云' => '人云',
'人如风后入江云' => '人如風後入江雲',
+'人干的' => '人幹的',
'人欲' => '人慾',
'人数只' => '人數只',
'人数里' => '人數裡',
'人物志' => '人物誌',
'人生天里' => '人生天里',
+'人发指' => '人髮指',
'什锦面' => '什錦麵',
'仇仇' => '仇讎',
'介胄' => '介冑',
+'他干的' => '他幹的',
'他钟' => '他鐘',
'付托' => '付託',
'仙后' => '仙后',
'仙后座' => '仙后座',
+'仙游' => '仙遊',
'代数里' => '代數裡',
'代理发行' => '代理發行',
'代码表' => '代碼表',
'代表' => '代表',
-'令人发指' => '令人髮指',
'以自制' => '以自制',
'仲裁制' => '仲裁制',
'件钟' => '件鐘',
@@ -3767,6 +3755,7 @@ $zh2Hant = array(
'伊郁' => '伊鬱',
'伏几' => '伏几',
'伐罪吊民' => '伐罪弔民',
+'休克期' => '休克期',
'休征' => '休徵',
'伙头' => '伙頭',
'伴游' => '伴遊',
@@ -3791,15 +3780,13 @@ $zh2Hant = array(
'佛罗棱萨' => '佛羅稜薩',
'佛钟' => '佛鐘',
'作品里' => '作品裡',
-'作奸犯科' => '作姦犯科',
'作准' => '作準',
-'你斗了胆' => '你斗了膽',
'你夸' => '你誇',
'佣金' => '佣金',
'佣鈿' => '佣鈿',
'佣钿' => '佣鈿',
-'佣钱' => '佣錢',
'佣錢' => '佣錢',
+'佣钱' => '佣錢',
'佳肴' => '佳肴',
'佳里鎮' => '佳里鎮',
'并一不二' => '併一不二',
@@ -3808,7 +3795,7 @@ $zh2Hant = array(
'并到' => '併到',
'并合' => '併合',
'并名' => '併名',
-'并吞' => '併吞',
+'并吞下' => '併吞下',
'并拢' => '併攏',
'并案' => '併案',
'并流' => '併流',
@@ -3834,6 +3821,7 @@ $zh2Hant = array(
'依托' => '依託',
'侵并' => '侵併',
'局促' => '侷促',
+'便于' => '便於',
'系数' => '係數',
'系为' => '係為',
'保险柜' => '保險柜',
@@ -3843,17 +3831,17 @@ $zh2Hant = array(
'修杰麟' => '修杰麟',
'修胡刀' => '修鬍刀',
'俯冲' => '俯衝',
+'个月里' => '個月裡',
'个里' => '個裡',
'个钟' => '個鐘',
'个钟表' => '個鐘錶',
-'们斗了胆' => '們斗了膽',
+'们干的' => '們幹的',
'幸免' => '倖免',
'幸存' => '倖存',
'幸幸' => '倖幸',
'候复' => '候覆',
'倚闲' => '倚閑',
'倛丑' => '倛醜',
-'借听于聋' => '借聽於聾',
'借鉴' => '借鑑',
'倦游' => '倦遊',
'假里' => '假裡',
@@ -3870,11 +3858,12 @@ $zh2Hant = array(
'佣仆' => '傭僕',
'傲游' => '傲遊',
'傲霜斗雪' => '傲霜鬥雪',
-'傳位于四太子' => '傳位于四太子',
'传位于四太子' => '傳位于四太子',
+'傳位于四太子' => '傳位于四太子',
'传于' => '傳於',
'债累累' => '債纍纍',
'傻里傻气' => '傻裡傻氣',
+'仅余' => '僅餘',
'仆人' => '僕人',
'仆使' => '僕使',
'仆仆' => '僕僕',
@@ -3956,14 +3945,15 @@ $zh2Hant = array(
'凶险' => '兇險',
'先采' => '先採',
'光致致' => '光緻緻',
+'克期间' => '克期間',
'免征' => '免徵',
'党太尉' => '党太尉',
'党姓' => '党姓',
'党家' => '党家',
'党怀英' => '党懷英',
'党进' => '党進',
-'党项' => '党項',
'党項' => '党項',
+'党项' => '党項',
'内脏' => '內臟',
'内制' => '內製',
'内面包' => '內面包',
@@ -3971,8 +3961,6 @@ $zh2Hant = array(
'内斗' => '內鬥',
'内哄' => '內鬨',
'全干' => '全乾',
-'全面包围' => '全面包圍',
-'全面包裹' => '全面包裹',
'两个' => '兩個',
'两周后' => '兩周後',
'两天后' => '兩天後',
@@ -4032,10 +4020,10 @@ $zh2Hant = array(
'冷面相' => '冷面相',
'冷面' => '冷麵',
'准三后' => '准三后',
-'准保護' => '准保護',
'准保护' => '准保護',
-'准保釋' => '准保釋',
+'准保護' => '准保護',
'准保释' => '准保釋',
+'准保釋' => '准保釋',
'凌蒙初' => '凌濛初',
'凝炼' => '凝鍊',
'几上' => '几上',
@@ -4065,8 +4053,8 @@ $zh2Hant = array(
'分子钟' => '分子鐘',
'分子云' => '分子雲',
'分布于' => '分布於',
-'分散于' => '分散於',
'分钟' => '分鐘',
+'分钟里' => '分鐘裡',
'刑余' => '刑餘',
'划一桨' => '划一槳',
'划上' => '划上',
@@ -4074,8 +4062,8 @@ $zh2Hant = array(
'划不來' => '划不來',
'划不来' => '划不來',
'划了一会' => '划了一會',
-'划来划去' => '划來划去',
'划來划去' => '划來划去',
+'划来划去' => '划來划去',
'划具' => '划具',
'划到岸' => '划到岸',
'划到江心' => '划到江心',
@@ -4086,29 +4074,30 @@ $zh2Hant = array(
'划得來' => '划得來',
'划得来' => '划得來',
'划拳' => '划拳',
-'划槳' => '划槳',
'划桨' => '划槳',
+'划槳' => '划槳',
'划水' => '划水',
+'划着独木舟' => '划着獨木舟',
+'划着竹筏' => '划着竹筏',
+'划着船' => '划着船',
'划算' => '划算',
'划船' => '划船',
'划艇' => '划艇',
-'划著' => '划著',
'划行' => '划行',
'划走' => '划走',
'划起' => '划起',
-'划進' => '划進',
'划进' => '划進',
+'划進' => '划進',
'划过' => '划過',
'划過' => '划過',
-'划龙舟' => '划龍舟',
'划龍舟' => '划龍舟',
+'划龙舟' => '划龍舟',
'判断发' => '判斷發',
'别辟' => '別闢',
'利欲' => '利慾',
'利于' => '利於',
'刮来刮去' => '刮來刮去',
'刮起来' => '刮起來',
-'刮风下雪倒便宜' => '刮風下雪倒便宜',
'刮胡' => '刮鬍',
'到山里' => '到山裡',
'制冷机' => '制冷機',
@@ -4136,7 +4125,6 @@ $zh2Hant = array(
'剥制' => '剝製',
'剩余' => '剩餘',
'剪其发' => '剪其髮',
-'剪牡丹喂牛' => '剪牡丹喂牛',
'剪发' => '剪髮',
'割舍' => '割捨',
'创获' => '創穫',
@@ -4148,8 +4136,9 @@ $zh2Hant = array(
'铲头' => '剷頭',
'划入' => '劃入',
'划为' => '劃為',
-'劉佳怜' => '劉佳怜',
+'划著' => '劃著名',
'刘佳怜' => '劉佳怜',
+'劉佳怜' => '劉佳怜',
'刘芸后' => '劉芸后',
'力拼' => '力拚',
'力拼众敌' => '力拼眾敵',
@@ -4157,7 +4146,6 @@ $zh2Hant = array(
'功勋' => '功勳',
'功致' => '功緻',
'加氢精制' => '加氫精制',
-'加注' => '加註',
'劣于' => '劣於',
'助于' => '助於',
'劫余' => '劫餘',
@@ -4179,6 +4167,7 @@ $zh2Hant = array(
'包扎' => '包紮',
'匏系' => '匏繫',
'北山索面' => '北山索麵',
+'北仑河' => '北崙河',
'北岳' => '北嶽',
'北回线' => '北迴線',
'北回铁路' => '北迴鐵路',
@@ -4214,6 +4203,7 @@ $zh2Hant = array(
'千钧一发' => '千鈞一髮',
'千只' => '千隻',
'千余' => '千餘',
+'升高后' => '升高後',
'半制品' => '半制品',
'半只可' => '半只可',
'半只够' => '半只夠',
@@ -4230,21 +4220,17 @@ $zh2Hant = array(
'南回线' => '南迴線',
'南回铁路' => '南迴鐵路',
'南游' => '南遊',
-'博汇' => '博彙',
'博采' => '博採',
'博尔术' => '博爾朮',
'卜云吉' => '卜云吉',
'占了卜' => '占了卜',
-'占便宜的是呆' => '占便宜的是獃',
'印累绶若' => '印纍綬若',
'印制' => '印製',
'印鉴' => '印鑑',
'危于' => '危於',
'卵与石斗' => '卵與石鬥',
-'卷发' => '卷髮',
'卷须' => '卷鬚',
'厂部' => '厂部',
-'厝薪于火' => '厝薪於火',
'原子钟' => '原子鐘',
'原钟' => '原鐘',
'历物之意' => '厤物之意',
@@ -4254,9 +4240,10 @@ $zh2Hant = array(
'反朴' => '反樸',
'反冲' => '反衝',
'反复制' => '反複製',
-'反覆' => '反覆',
'反复' => '反覆',
+'反覆' => '反覆',
'取舍' => '取捨',
+'取决于' => '取決於',
'受雇' => '受僱',
'受托' => '受託',
'丛林里' => '叢林裡',
@@ -4270,12 +4257,13 @@ $zh2Hant = array(
'口腹之欲' => '口腹之慾',
'口里' => '口裡',
'口钟' => '口鐘',
-'古書云' => '古書云',
+'古人有云' => '古人有云',
'古书云' => '古書云',
+'古書云' => '古書云',
'古柯咸' => '古柯鹹',
'古朴' => '古樸',
-'古语云' => '古語云',
'古語云' => '古語云',
+'古语云' => '古語云',
'古迹' => '古蹟',
'古钟' => '古鐘',
'古钟表' => '古鐘錶',
@@ -4294,8 +4282,8 @@ $zh2Hant = array(
'只身上有' => '只身上有',
'只身上沒' => '只身上沒',
'只身上没' => '只身上沒',
-'只身上無' => '只身上無',
'只身上无' => '只身上無',
+'只身上無' => '只身上無',
'只身上的' => '只身上的',
'只身世' => '只身世',
'只身份' => '只身份',
@@ -4304,19 +4292,19 @@ $zh2Hant = array(
'只身子' => '只身子',
'只身形' => '只身形',
'只身影' => '只身影',
-'只身後' => '只身後',
'只身后' => '只身後',
+'只身後' => '只身後',
'只身心' => '只身心',
'只身旁' => '只身旁',
'只身材' => '只身材',
'只身段' => '只身段',
'只身为' => '只身為',
'只身為' => '只身為',
-'只身邊' => '只身邊',
'只身边' => '只身邊',
+'只身邊' => '只身邊',
'只身首' => '只身首',
-'只身體' => '只身體',
'只身体' => '只身體',
+'只身體' => '只身體',
'只身高' => '只身高',
'只采声' => '只采聲',
'叮叮当当' => '叮叮噹噹',
@@ -4339,9 +4327,11 @@ $zh2Hant = array(
'叶音' => '叶音',
'叶韵' => '叶韻',
'吃板刀面' => '吃板刀麵',
+'吃碗面' => '吃碗麵',
'吃姜' => '吃薑',
'吃里扒外' => '吃裡扒外',
'吃里爬外' => '吃裡爬外',
+'吃面' => '吃麵',
'各辟' => '各闢',
'各类钟' => '各類鐘',
'合伙人' => '合伙人',
@@ -4359,6 +4349,7 @@ $zh2Hant = array(
'同伙' => '同夥',
'同于' => '同於',
'同余' => '同餘',
+'名单于' => '名單於',
'后冠' => '后冠',
'后北街' => '后北街',
'后土' => '后土',
@@ -4369,19 +4360,19 @@ $zh2Hant = array(
'后庄' => '后庄',
'后座' => '后座',
'后母戊' => '后母戊',
-'后海灣' => '后海灣',
'后海湾' => '后海灣',
+'后海灣' => '后海灣',
'后瑞站' => '后瑞站',
'后稷' => '后稷',
'后綜' => '后綜',
'后羿' => '后羿',
'后街' => '后街',
'后角' => '后角',
-'后豐' => '后豐',
'后丰' => '后豐',
+'后豐' => '后豐',
'后里' => '后里',
-'后髮FK型星' => '后髮FK型星',
'后发FK型星' => '后髮FK型星',
+'后髮FK型星' => '后髮FK型星',
'后发座' => '后髮座',
'后髮座' => '后髮座',
'后发星系团' => '后髮星系團',
@@ -4402,19 +4393,21 @@ $zh2Hant = array(
'吾为之范我驰驱' => '吾爲之範我馳驅',
'吕后' => '呂后',
'呂后' => '呂后',
-'呆呆兽' => '呆呆獸',
'呆致致' => '呆緻緻',
'呆里呆气' => '呆裡呆氣',
'告札' => '告劄',
+'呦喂' => '呦喂',
'周后' => '周后',
+'周惠后' => '周惠后',
'周历' => '周曆',
-'周杰倫' => '周杰倫',
-'周杰伦' => '周杰倫',
+'周杰' => '周杰',
'周历史' => '周歷史',
-'周游' => '周遊',
+'周游列国' => '周遊列國',
+'呵喂' => '呵喂',
'呼吁' => '呼籲',
'命中注定' => '命中注定',
'和奸' => '和姦',
+'和制汉' => '和製漢',
'咎征' => '咎徵',
'咕咕钟' => '咕咕鐘',
'咪表' => '咪錶',
@@ -4422,7 +4415,6 @@ $zh2Hant = array(
'咯当' => '咯噹',
'哀吊' => '哀弔',
'哀挽' => '哀輓',
-'品汇' => '品彙',
'品鉴' => '品鑑',
'哄堂大笑' => '哄堂大笑',
'員山庄' => '員山庄',
@@ -4437,9 +4429,14 @@ $zh2Hant = array(
'商历' => '商曆',
'商标准许' => '商標准許',
'商历史' => '商歷史',
+'啊喂' => '啊喂',
'启发式' => '啟發式',
'啷当' => '啷噹',
'喂了一声' => '喂了一聲',
+'喂喂' => '喂喂',
+'喂哟' => '喂喲',
+'喂!' => '喂!',
+'喂,' => '喂,',
'善于' => '善於',
'喜向往' => '喜向往',
'喜欢表' => '喜歡錶',
@@ -4450,11 +4447,13 @@ $zh2Hant = array(
'喧哄' => '喧鬨',
'丧钟' => '喪鐘',
'乔岳' => '喬嶽',
-'單于' => '單于',
'单于' => '單于',
+'單于' => '單于',
'单单于' => '單單於',
'单干' => '單幹',
'单打独斗' => '單打獨鬥',
+'哟喂' => '喲喂',
+'喲喂' => '喲喂',
'嘉谷' => '嘉穀',
'嘉肴' => '嘉肴',
'嘴里' => '嘴裡',
@@ -4507,6 +4506,7 @@ $zh2Hant = array(
'回游' => '回遊',
'因于' => '因於',
'困倦起来' => '困倦起來',
+'困于' => '困於',
'困兽之斗' => '困獸之鬥',
'困兽犹斗' => '困獸猶鬥',
'困斗' => '困鬥',
@@ -4558,6 +4558,7 @@ $zh2Hant = array(
'埃及历史' => '埃及歷史',
'埃及艳后' => '埃及豔后',
'埃荣冲' => '埃榮衝',
+'城市里' => '城市裡',
'城里' => '城裡',
'埔子里' => '埔子里',
'埔里社' => '埔裏社',
@@ -4570,13 +4571,18 @@ $zh2Hant = array(
'堡子里' => '堡子里',
'场里' => '場裡',
'塞耳盗钟' => '塞耳盜鐘',
+'境里' => '境裡',
+'境里程' => '境里程',
'墓志铭' => '墓志銘',
'墓志' => '墓誌',
'增辟' => '增闢',
'墨子里' => '墨子里',
-'墨沈' => '墨沈',
-'墨沈未干' => '墨瀋未乾',
+'墨斗' => '墨斗',
+'墨沈沈' => '墨沈沈',
+'墨沈' => '墨瀋',
'垦辟' => '墾闢',
+'压制出' => '壓製出',
+'压制机' => '壓製機',
'壮游' => '壯遊',
'壮面' => '壯麵',
'壹郁' => '壹鬱',
@@ -4597,16 +4603,16 @@ $zh2Hant = array(
'多只含' => '多只含',
'多只在' => '多只在',
'多只是' => '多只是',
-'多只會' => '多只會',
'多只会' => '多只會',
+'多只會' => '多只會',
'多只有' => '多只有',
'多只比' => '多只比',
'多只用' => '多只用',
'多只能' => '多只能',
'多只限' => '多只限',
'多只需' => '多只需',
-'多只须' => '多只須',
'多只須' => '多只須',
+'多只须' => '多只須',
'多周后' => '多周後',
'多天后' => '多天後',
'多于' => '多於',
@@ -4665,7 +4671,6 @@ $zh2Hant = array(
'大钟' => '大鐘',
'大只' => '大隻',
'大风后' => '大風後',
-'大曲酒' => '大麴酒',
'天克地冲' => '天克地衝',
'天后' => '天后',
'天后宫' => '天后宮',
@@ -4691,15 +4696,10 @@ $zh2Hant = array(
'太后' => '太后',
'太丑' => '太醜',
'太阁' => '太閤',
-'夯干' => '夯幹',
-'夸人' => '夸人',
'夸克' => '夸克',
-'夸姣' => '夸姣',
-'夸容' => '夸容',
'夸父' => '夸父',
'夸特' => '夸特',
'夸脱' => '夸脫',
-'夸丽' => '夸麗',
'奇勋' => '奇勳',
'奇迹' => '奇蹟',
'奇丑' => '奇醜',
@@ -4710,7 +4710,6 @@ $zh2Hant = array(
'女仆' => '女僕',
'奴仆' => '奴僕',
'奸淫掳掠' => '奸淫擄掠',
-'好干' => '好乾',
'好家伙' => '好傢夥',
'好凶' => '好兇',
'好勇斗狠' => '好勇鬥狠',
@@ -4734,7 +4733,6 @@ $zh2Hant = array(
'始于' => '始於',
'委托' => '委託',
'委托书' => '委託書',
-'姜文杰' => '姜文杰',
'奸夫' => '姦夫',
'奸妇' => '姦婦',
'奸宄' => '姦宄',
@@ -4761,11 +4759,12 @@ $zh2Hant = array(
'字汇' => '字彙',
'字码表' => '字碼表',
'字里行间' => '字裡行間',
-'存十一于千百' => '存十一於千百',
'存折' => '存摺',
'存于' => '存於',
'孛里海' => '孛里海',
-'孤寡不谷' => '孤寡不穀',
+'孝惠后' => '孝惠后',
+'孙杰' => '孫杰',
+'孫杰' => '孫杰',
'学家' => '學家',
'学里' => '學裡',
'宇宙志' => '宇宙誌',
@@ -4805,10 +4804,10 @@ $zh2Hant = array(
'实干' => '實幹',
'实累累' => '實纍纍',
'写字台' => '寫字檯',
-'宽宽松松' => '寬寬鬆鬆',
'宽于' => '寬於',
'宽余' => '寬餘',
'宽松' => '寬鬆',
+'宽松松' => '寬鬆鬆',
'寮采' => '寮寀',
'寶山庄' => '寶山庄',
'宝历' => '寶曆',
@@ -4832,6 +4831,7 @@ $zh2Hant = array(
'对准表' => '對準錶',
'对准钟' => '對準鐘',
'对准钟表' => '對準鐘錶',
+'对着干' => '對着幹',
'对华发' => '對華發',
'对表中' => '對表中',
'对表扬' => '對表揚',
@@ -4841,6 +4841,7 @@ $zh2Hant = array(
'对表达' => '對表達',
'导游' => '導遊',
'小丑' => '小丑',
+'小井里' => '小井里',
'小价' => '小价',
'小仆' => '小僕',
'小几' => '小几',
@@ -4856,13 +4857,16 @@ $zh2Hant = array(
'小型钟表面' => '小型鐘表面',
'小型钟表' => '小型鐘錶',
'小型钟面' => '小型鐘面',
+'小时里' => '小時裡',
'小米面' => '小米麵',
'小只' => '小隻',
'少采' => '少採',
'就范' => '就範',
'就里' => '就裡',
'尸位素餐' => '尸位素餐',
+'尸佼' => '尸佼',
'尸利' => '尸利',
+'尸子' => '尸子',
'尸居余气' => '尸居餘氣',
'尸弃佛' => '尸棄佛',
'尸祝' => '尸祝',
@@ -4892,26 +4896,29 @@ $zh2Hant = array(
'山羊胡' => '山羊鬍',
'山里有' => '山裡有',
'山里的' => '山裡的',
-'山谷道' => '山谷道',
+'山谷' => '山谷',
'山重水复' => '山重水複',
+'岫岩' => '岫巖',
'岱岳' => '岱嶽',
'峇里海' => '峇里海',
'峰回' => '峰迴',
'峻岭' => '峻岭',
-'昆剧' => '崑劇',
'崑剧' => '崑劇',
+'昆剧' => '崑劇',
'崑山' => '崑山',
'昆山' => '崑山',
'昆冈' => '崑岡',
'昆仑' => '崑崙',
+'昆嵛' => '崑嵛',
+'昆承湖' => '崑承湖',
'崑曲' => '崑曲',
'昆曲' => '崑曲',
-'昆腔' => '崑腔',
'崑腔' => '崑腔',
-'昆苏' => '崑蘇',
+'昆腔' => '崑腔',
'崑苏' => '崑蘇',
-'昆调' => '崑調',
+'昆苏' => '崑蘇',
'崑调' => '崑調',
+'昆调' => '崑調',
'崖广' => '崖广',
'嶒棱' => '嶒稜',
'岳岳' => '嶽嶽',
@@ -4923,7 +4930,6 @@ $zh2Hant = array(
'工作台' => '工作檯',
'工致' => '工緻',
'左冲右突' => '左衝右突',
-'巧妇做不得无面馎饦' => '巧婦做不得無麵餺飥',
'巧干' => '巧幹',
'巧历' => '巧曆',
'巧历史' => '巧歷史',
@@ -4935,7 +4941,7 @@ $zh2Hant = array(
'已占算' => '已占算',
'巴尔干' => '巴爾幹',
'巷里' => '巷裡',
-'市里' => '市裡',
+'市里的' => '市裡的',
'布谷' => '布穀',
'布谷鸟' => '布穀鳥',
'布谷鸟钟' => '布穀鳥鐘',
@@ -4948,6 +4954,7 @@ $zh2Hant = array(
'师范' => '師範',
'席卷' => '席捲',
'带征' => '帶徵',
+'带余' => '帶餘',
'带发修行' => '帶髮修行',
'幅图里' => '幅圖裡',
'干系' => '干係',
@@ -4960,19 +4967,22 @@ $zh2Hant = array(
'年里' => '年裡',
'年鉴' => '年鑑',
'并力' => '并力',
+'并吞' => '并吞',
'并州' => '并州',
'并日而食' => '并日而食',
'并迭' => '并迭',
'幸免于难' => '幸免於難',
'幸于' => '幸於',
'幸运胡' => '幸運鬍',
+'干上' => '幹上',
'干下去' => '幹下去',
'干不了' => '幹不了',
'干不成' => '幹不成',
+'干了' => '幹了',
'干事' => '幹事',
'干些' => '幹些',
-'干人' => '幹人',
'干什么' => '幹什麼',
+'干仗' => '幹仗',
'干个' => '幹個',
'干劲' => '幹勁',
'干吏' => '幹吏',
@@ -4981,10 +4991,10 @@ $zh2Hant = array(
'干吗' => '幹嗎',
'干嘛' => '幹嘛',
'干坏事' => '幹壞事',
+'干大事' => '幹大事',
'干完' => '幹完',
'干家' => '幹家',
-'干将' => '幹將',
-'干得了' => '幹得了',
+'干得' => '幹得',
'干性油' => '幹性油',
'干才' => '幹才',
'干掉' => '幹掉',
@@ -4999,7 +5009,8 @@ $zh2Hant = array(
'干甚么' => '幹甚麼',
'干略' => '幹略',
'干当' => '幹當',
-'干的停当' => '幹的停當',
+'干的事' => '幹的事',
+'干的好事' => '幹的好事',
'干细胞' => '幹細胞',
'干线' => '幹線',
'干练' => '幹練',
@@ -5010,8 +5021,7 @@ $zh2Hant = array(
'干起来' => '幹起來',
'干路' => '幹路',
'干办' => '幹辦',
-'干这一行' => '幹這一行',
-'干这种事' => '幹這種事',
+'干这' => '幹這',
'干道' => '幹道',
'干部' => '幹部',
'干革命' => '幹革命',
@@ -5027,8 +5037,8 @@ $zh2Hant = array(
'床席' => '床蓆',
'店里' => '店裡',
'府干卿' => '府干卿',
-'府干擾' => '府干擾',
'府干扰' => '府干擾',
+'府干擾' => '府干擾',
'府干政' => '府干政',
'府干涉' => '府干涉',
'府干犯' => '府干犯',
@@ -5041,8 +5051,8 @@ $zh2Hant = array(
'厨余' => '廚餘',
'厮斗' => '廝鬥',
'庙里' => '廟裡',
-'廢后' => '廢后',
'废后' => '廢后',
+'廢后' => '廢后',
'广征' => '廣徵',
'广舍' => '廣捨',
'延历' => '延曆',
@@ -5093,9 +5103,11 @@ $zh2Hant = array(
'弘历史' => '弘歷史',
'弱于' => '弱於',
'弱水三千只取一瓢' => '弱水三千只取一瓢',
-'張三丰' => '張三丰',
'张三丰' => '張三丰',
+'張三丰' => '張三丰',
'张勋' => '張勳',
+'张杰' => '張杰',
+'張杰' => '張杰',
'张乐于张徐' => '張樂于張徐',
'强制作用' => '強制作用',
'强奸' => '強姦',
@@ -5109,13 +5121,9 @@ $zh2Hant = array(
'弹子台' => '彈子檯',
'弹珠台' => '彈珠檯',
'汇刊' => '彙刊',
-'汇报' => '彙報',
-'汇整' => '彙整',
'汇算' => '彙算',
-'汇编' => '彙編',
'汇纂' => '彙纂',
'汇辑' => '彙輯',
-'汇集' => '彙集',
'形单影只' => '形單影隻',
'形于' => '形於',
'彭于晏' => '彭于晏',
@@ -5129,15 +5137,18 @@ $zh2Hant = array(
'很凶' => '很兇',
'很准' => '很準',
'很丑' => '很醜',
+'很松' => '很鬆',
'律历志' => '律曆志',
'后印' => '後印',
'后台老板' => '後台老板',
'后天' => '後天',
+'後庄' => '後庄',
'后面店' => '後面店',
'徐干' => '徐幹',
'徒杠' => '徒杠',
'徒托空言' => '徒託空言',
'得到回复' => '得到回覆',
+'得力干将' => '得力幹將',
'从仆' => '從僕',
'从图里' => '從圖裡',
'从山里' => '從山裡',
@@ -5326,9 +5337,9 @@ $zh2Hant = array(
'忠人之托' => '忠人之托',
'忠仆' => '忠僕',
'忠于' => '忠於',
-'快干' => '快乾',
'快快当当' => '快快當當',
'快冲' => '快衝',
+'怎么干' => '怎麼幹',
'怒于' => '怒於',
'怒气冲天' => '怒氣衝天',
'怒火冲天' => '怒火衝天',
@@ -5343,7 +5354,9 @@ $zh2Hant = array(
'怪里怪气' => '怪裡怪氣',
'怫郁' => '怫鬱',
'恂栗' => '恂慄',
+'恒基' => '恒基',
'恒生' => '恒生',
+'恒隆' => '恒隆',
'恕乏价催' => '恕乏价催',
'息交绝游' => '息交絕遊',
'息谷' => '息穀',
@@ -5381,6 +5394,7 @@ $zh2Hant = array(
'愿而恭' => '愿而恭',
'栗冽' => '慄冽',
'栗栗' => '慄慄',
+'慈溪' => '慈谿',
'慌里慌张' => '慌裡慌張',
'惨淡' => '慘澹',
'庆吊' => '慶弔',
@@ -5406,9 +5420,8 @@ $zh2Hant = array(
'应征' => '應徵',
'应钟' => '應鐘',
'懔栗' => '懍慄',
-'蒙懂' => '懞懂',
-'蒙蒙懂懂' => '懞懞懂懂',
-'蒙直' => '懞直',
+'懞懞懂懂' => '懞懞懂懂',
+'懞直' => '懞直',
'惩忿窒欲' => '懲忿窒欲',
'怀里' => '懷裡',
'怀钟' => '懷鐘',
@@ -5424,6 +5437,7 @@ $zh2Hant = array(
'截发' => '截髮',
'战天斗地' => '戰天鬥地',
'战栗' => '戰慄',
+'战于' => '戰於',
'战斗' => '戰鬥',
'戏里' => '戲裡',
'戲院里' => '戲院里',
@@ -5437,20 +5451,19 @@ $zh2Hant = array(
'所占算' => '所占算',
'所托' => '所託',
'扁拟谷盗虫' => '扁擬穀盜蟲',
-'手冢治虫' => '手塚治虫',
'手塚治虫' => '手塚治虫',
'手折' => '手摺',
-'手表態' => '手表態',
'手表态' => '手表態',
+'手表態' => '手表態',
'手表明' => '手表明',
-'手表決' => '手表決',
'手表决' => '手表決',
+'手表決' => '手表決',
'手表演' => '手表演',
-'手表現' => '手表現',
'手表现' => '手表現',
+'手表現' => '手表現',
'手表示' => '手表示',
-'手表達' => '手表達',
'手表达' => '手表達',
+'手表達' => '手表達',
'手表露' => '手表露',
'手表面' => '手表面',
'手里剑' => '手裏劍',
@@ -5484,7 +5497,6 @@ $zh2Hant = array(
'打斗' => '打鬥',
'托管国' => '托管國',
'扛大梁' => '扛大樑',
-'捍御' => '扞禦',
'扯面' => '扯麵',
'扶余' => '扶餘',
'批准的' => '批准的',
@@ -5492,7 +5504,6 @@ $zh2Hant = array(
'批复' => '批覆',
'批注' => '批註',
'批斗' => '批鬥',
-'承制' => '承製',
'抑制作用' => '抑制作用',
'抑制剂' => '抑制劑',
'抑郁' => '抑鬱',
@@ -5573,6 +5584,7 @@ $zh2Hant = array(
'捉奸党' => '捉奸黨',
'捉奸' => '捉姦',
'捉发' => '捉髮',
+'捍御' => '捍禦',
'捏面人' => '捏麵人',
'舍不得' => '捨不得',
'舍入' => '捨入',
@@ -5620,7 +5632,6 @@ $zh2Hant = array(
'卷纸' => '捲紙',
'卷缩' => '捲縮',
'卷舌' => '捲舌',
-'卷铺盖' => '捲舖蓋',
'卷烟' => '捲菸',
'卷叶蛾' => '捲葉蛾',
'卷袖' => '捲袖',
@@ -5628,9 +5639,10 @@ $zh2Hant = array(
'卷起' => '捲起',
'卷轴' => '捲軸',
'卷逃' => '捲逃',
+'卷铺盖' => '捲鋪蓋',
'卷云' => '捲雲',
'卷风' => '捲風',
-'卷发器' => '捲髮器',
+'卷发' => '捲髮',
'捵面' => '捵麵',
'捶炼' => '捶鍊',
'扫荡' => '掃蕩',
@@ -5686,8 +5698,8 @@ $zh2Hant = array(
'采种' => '採種',
'采空区' => '採空區',
'采空采穗' => '採空採穗',
-'采纳' => '採納',
'采納' => '採納',
+'采纳' => '採納',
'采给' => '採給',
'采花' => '採花',
'采芹人' => '採芹人',
@@ -5697,6 +5709,7 @@ $zh2Hant = array(
'采薇' => '採薇',
'采薪' => '採薪',
'采药' => '採藥',
+'采血' => '採血',
'采行' => '採行',
'采补' => '採補',
'采访' => '採訪',
@@ -5719,11 +5732,11 @@ $zh2Hant = array(
'控制' => '控制',
'推情准理' => '推情準理',
'推托之词' => '推托之詞',
-'推舟于陆' => '推舟於陸',
'推托' => '推託',
'提子干' => '提子乾',
'提心吊胆' => '提心弔膽',
'提摩太后书' => '提摩太後書',
+'提高后' => '提高後',
'插于' => '插於',
'换签' => '換籤',
'换只' => '換隻',
@@ -5734,8 +5747,8 @@ $zh2Hant = array(
'揪发' => '揪髮',
'揪须' => '揪鬚',
'揭丑' => '揭醜',
-'揮手表' => '揮手表',
'挥手表' => '揮手表',
+'揮手表' => '揮手表',
'搋面' => '搋麵',
'损于' => '損於',
'搏斗' => '搏鬥',
@@ -5773,7 +5786,8 @@ $zh2Hant = array(
'撩斗' => '撩鬥',
'播于' => '播於',
'扑冬' => '撲鼕',
-'扑冬冬' => '撲鼕鼕',
+'扑咚' => '撲鼕',
+'扑咚咚' => '撲鼕鼕',
'擀面' => '擀麵',
'击扑' => '擊扑',
'击钟' => '擊鐘',
@@ -5781,7 +5795,6 @@ $zh2Hant = array(
'担仔面' => '擔仔麵',
'担担面' => '擔擔麵',
'据云' => '據云',
-'据干而窥井底' => '據榦而窺井底',
'擢发' => '擢髮',
'擦干' => '擦乾',
'擦干净' => '擦乾淨',
@@ -5791,9 +5804,10 @@ $zh2Hant = array(
'支干' => '支幹',
'支配欲' => '支配慾',
'收获' => '收穫',
+'改制成' => '改制成',
'改征' => '改徵',
'改采' => '改採',
-'放蒙挣' => '放懞掙',
+'放懞挣' => '放懞掙',
'放荡' => '放蕩',
'放松' => '放鬆',
'政斗' => '政鬥',
@@ -5840,14 +5854,15 @@ $zh2Hant = array(
'数与虏确' => '數與虜确',
'数只' => '數隻',
'文丑' => '文丑',
-'文汇报' => '文匯報',
'文学志' => '文學誌',
'文征明' => '文徵明',
'文思泉涌' => '文思泉湧',
+'文杰' => '文杰',
'文采郁郁' => '文采郁郁',
'斗牛星' => '斗牛星',
'斫雕为朴' => '斫雕為樸',
'新井里美' => '新井里美',
+'新干县' => '新幹縣',
'新历' => '新曆',
'新历史' => '新歷史',
'新扎' => '新紮',
@@ -5855,123 +5870,23 @@ $zh2Hant = array(
'断发' => '斷髮',
'断发文身' => '斷髮文身',
'方便面' => '方便麵',
-'方几' => '方几',
'方向往' => '方向往',
'方志恒' => '方志恒',
'方法里' => '方法裡',
'方志' => '方誌',
-'方面' => '方面',
-'于0' => '於0',
-'于1' => '於1',
-'于2' => '於2',
-'于3' => '於3',
-'于4' => '於4',
-'于5' => '於5',
-'于6' => '於6',
-'于7' => '於7',
-'于8' => '於8',
-'于9' => '於9',
-'于一' => '於一',
-'于一役' => '於一役',
-'于七' => '於七',
-'于三' => '於三',
-'于世' => '於世',
-'于之' => '於之',
-'于乎' => '於乎',
-'于九' => '於九',
-'于事' => '於事',
-'于二' => '於二',
-'于五' => '於五',
-'于人' => '於人',
-'于今' => '於今',
-'于他' => '於他',
-'于伏' => '於伏',
-'于何' => '於何',
-'于你' => '於你',
-'于八' => '於八',
-'于六' => '於六',
-'于前' => '於前',
-'于劣' => '於劣',
-'于勤' => '於勤',
-'于十' => '於十',
-'于半' => '於半',
-'于呼哀哉' => '於呼哀哉',
-'于四' => '於四',
-'于国' => '於國',
-'于坏' => '於坏',
-'于垂' => '於垂',
-'于夫罗' => '於夫羅',
-'於夫罗' => '於夫羅',
-'於夫羅' => '於夫羅',
-'于她' => '於她',
-'于好' => '於好',
-'于始' => '於始',
-'於姓' => '於姓',
-'于它' => '於它',
-'于家' => '於家',
-'于密' => '於密',
-'于差' => '於差',
-'于己' => '於己',
-'于市' => '於市',
-'于幕' => '於幕',
-'于弱' => '於弱',
-'于强' => '於強',
'于后' => '於後',
'于征' => '於徵',
-'于心' => '於心',
-'于怀' => '於懷',
-'于我' => '於我',
-'于戏' => '於戲',
-'于敝' => '於敝',
-'于斯' => '於斯',
-'于是' => '於是',
-'于是乎' => '於是乎',
-'于时' => '於時',
-'于梨华' => '於梨華',
-'於梨華' => '於梨華',
-'于乐' => '於樂',
-'于此' => '於此',
-'於氏' => '於氏',
-'于民' => '於民',
-'于水' => '於水',
-'于法' => '於法',
'于海上' => '於海上',
'于海边' => '於海邊',
-'于潜县' => '於潛縣',
-'于火' => '於火',
-'于焉' => '於焉',
-'于墙' => '於牆',
-'于物' => '於物',
-'于毕' => '於畢',
-'于尽' => '於盡',
-'于盲' => '於盲',
-'于祂' => '於祂',
-'于穆' => '於穆',
-'于终' => '於終',
-'于美' => '於美',
-'于色' => '於色',
-'于菟' => '於菟',
-'于蓝' => '於藍',
-'于行' => '於行',
-'于衷' => '於衷',
-'于该' => '於該',
-'于农' => '於農',
-'于途' => '於途',
-'于过' => '於過',
-'于邑' => '於邑',
-'于丑' => '於醜',
-'于野' => '於野',
-'于陆' => '於陸',
'于震中' => '於震中',
'于震前' => '於震前',
-'于震后' => '於震后',
+'于震后' => '於震後',
'施舍' => '施捨',
'施于' => '施於',
'施舍之道' => '施舍之道',
'旁征博引' => '旁徵博引',
'旁注' => '旁註',
'旅游' => '旅遊',
-'旋干转坤' => '旋乾轉坤',
'旋回' => '旋迴',
'族里' => '族裡',
'日心历表' => '日心曆表',
@@ -5991,11 +5906,13 @@ $zh2Hant = array(
'明范' => '明範',
'明鉴' => '明鑑',
'易于' => '易於',
+'昔人有云' => '昔人有云',
'星历' => '星曆',
'星期后' => '星期後',
'星历史' => '星歷史',
'春游' => '春遊',
'春香斗学' => '春香鬥學',
+'昭惠后' => '昭惠后',
'是发小' => '是髮小',
'时钟' => '時鐘',
'时间不准' => '時間不準',
@@ -6004,7 +5921,7 @@ $zh2Hant = array(
'晚钟' => '晚鐘',
'晞发' => '晞髮',
'晨钟' => '晨鐘',
-'普冬冬' => '普鼕鼕',
+'普咚咚' => '普鼕鼕',
'晾干' => '晾乾',
'暗地里' => '暗地裡',
'暗沟里' => '暗溝裡',
@@ -6018,7 +5935,7 @@ $zh2Hant = array(
'历始' => '曆始',
'历室' => '曆室',
'历尾' => '曆尾',
-'历数' => '曆數',
+'历数书' => '曆數書',
'历日' => '曆日',
'历书' => '曆書',
'历本' => '曆本',
@@ -6030,34 +5947,32 @@ $zh2Hant = array(
'晒谷' => '曬穀',
'曰云' => '曰云',
'更仆难数' => '更僕難數',
-'更加注' => '更加注',
'更签' => '更籤',
'更钟' => '更鐘',
'书签' => '書籤',
'书面' => '書面',
'曹子里' => '曹子里',
-'曼谷人' => '曼谷人',
+'曼谷' => '曼谷',
'曾朴' => '曾樸',
'最多' => '最多',
'最多只' => '最多只',
-'會干擾' => '會干擾',
'会干扰' => '會干擾',
+'會干擾' => '會干擾',
'会干' => '會幹',
'会吊' => '會弔',
'会里' => '會裡',
'月历' => '月曆',
'月历史' => '月歷史',
'月球历表' => '月球曆表',
-'月离于毕' => '月離於畢',
+'月里来' => '月裡來',
'月面' => '月面',
-'月丽于箕' => '月麗於箕',
'有事之无范' => '有事之無範',
'有仆' => '有僕',
'有只不' => '有只不',
'有只允' => '有只允',
'有只容' => '有只容',
-'有只采' => '有只採',
'有只採' => '有只採',
+'有只采' => '有只採',
'有只是' => '有只是',
'有只用' => '有只用',
'有回复' => '有回覆',
@@ -6066,8 +5981,8 @@ $zh2Hant = array(
'有征战' => '有征戰',
'有征戰' => '有征戰',
'有征服' => '有征服',
-'有征讨' => '有征討',
'有征討' => '有征討',
+'有征讨' => '有征討',
'有征' => '有徵',
'有恒街' => '有恒街',
'有栖川' => '有栖川',
@@ -6081,6 +5996,7 @@ $zh2Hant = array(
'望后石' => '望后石',
'朝乾夕惕' => '朝乾夕惕',
'朝钟' => '朝鐘',
+'朝鲜于' => '朝鮮於',
'朦胧' => '朦朧',
'蒙胧' => '朦朧',
'木偶戏扎' => '木偶戲紮',
@@ -6093,6 +6009,7 @@ $zh2Hant = array(
'未干涉' => '未干涉',
'未干預' => '未干預',
'未干预' => '未干預',
+'本庄' => '本庄',
'本征' => '本徵',
'本出戏' => '本齣戲',
'术赤' => '朮赤',
@@ -6101,6 +6018,7 @@ $zh2Hant = array(
'朱理安历史' => '朱理安歷史',
'朴子里' => '朴子里',
'李志喜' => '李志喜',
+'李适' => '李适',
'李连杰' => '李連杰',
'李連杰' => '李連杰',
'材干' => '材幹',
@@ -6113,20 +6031,22 @@ $zh2Hant = array(
'束发' => '束髮',
'杠人' => '杠人',
'杠梁' => '杠梁',
-'杠轂' => '杠轂',
'杠毂' => '杠轂',
+'杠轂' => '杠轂',
'杯干' => '杯乾',
'杯面' => '杯麵',
'杰伦' => '杰倫',
-'杰威爾音樂' => '杰威爾音樂',
-'杰威尔音乐' => '杰威爾音樂',
-'杰特' => '杰特',
+'杰倫' => '杰倫',
+'杰威尔' => '杰威爾',
+'杰威爾' => '杰威爾',
'东周钟' => '東周鐘',
'东岳' => '東嶽',
'東湖里' => '東湖里',
'东冲西突' => '東衝西突',
'东游' => '東遊',
+'松口镇' => '松口鎮',
'松山庄' => '松山庄',
+'松溪县' => '松谿縣',
'板荡' => '板蕩',
'林宏岳' => '林宏嶽',
'林杰樑' => '林杰樑',
@@ -6146,7 +6066,9 @@ $zh2Hant = array(
'柜上' => '柜上',
'柜子' => '柜子',
'柜柳' => '柜柳',
+'查封后' => '查封後',
'柱梁' => '柱樑',
+'柳斌杰' => '柳斌杰',
'柳诒征' => '柳詒徵',
'栖栖皇皇' => '栖栖皇皇',
'栗栖溪' => '栗栖溪',
@@ -6160,6 +6082,7 @@ $zh2Hant = array(
'格里高利历' => '格里高利曆',
'格斗' => '格鬥',
'桂圆干' => '桂圓乾',
+'框里' => '框裡',
'桌几' => '桌几',
'桌历' => '桌曆',
'桌历史' => '桌歷史',
@@ -6169,6 +6092,7 @@ $zh2Hant = array(
'杆秤' => '桿秤',
'杆菌' => '桿菌',
'梁上君子' => '梁上君子',
+'梁启超' => '梁啓超',
'条干' => '條幹',
'梨干' => '梨乾',
'梯冲' => '梯衝',
@@ -6185,14 +6109,15 @@ $zh2Hant = array(
'植发' => '植髮',
'椒面' => '椒麵',
'椰枣干' => '椰棗乾',
-'楊雅筑' => '楊雅筑',
'杨雅筑' => '楊雅筑',
+'楊雅筑' => '楊雅筑',
'桢干' => '楨幹',
'业余' => '業餘',
'榨干' => '榨乾',
'枪杆' => '槍桿',
'杠杆' => '槓桿',
'乐器钟' => '樂器鐘',
+'乐游原' => '樂遊原',
'樊于期' => '樊於期',
'梁上' => '樑上',
'梁柱' => '樑柱',
@@ -6208,8 +6133,8 @@ $zh2Hant = array(
'模范14棒' => '模范14棒',
'模范21棒' => '模范21棒',
'模范七棒' => '模范七棒',
-'模范三軍' => '模范三軍',
'模范三军' => '模范三軍',
+'模范三軍' => '模范三軍',
'模范棒棒堂' => '模范棒棒堂',
'模制' => '模製',
'样范' => '樣範',
@@ -6242,6 +6167,7 @@ $zh2Hant = array(
'机械表' => '機械錶',
'机械钟' => '機械鐘',
'机械钟表' => '機械鐘錶',
+'横峰县' => '橫峯縣',
'横征暴敛' => '橫徵暴斂',
'横梁' => '橫樑',
'横冲' => '橫衝',
@@ -6250,6 +6176,7 @@ $zh2Hant = array(
'台灯' => '檯燈',
'台球' => '檯球',
'台面上' => '檯面上',
+'台面化' => '檯面化',
'柜台' => '櫃檯',
'栉发工' => '櫛髮工',
'欲海难填' => '欲海難填',
@@ -6259,6 +6186,7 @@ $zh2Hant = array(
'欧游' => '歐遊',
'止于' => '止於',
'正官庄' => '正官庄',
+'正杰' => '正杰',
'武丑' => '武丑',
'武后' => '武后',
'武斗' => '武鬥',
@@ -6299,6 +6227,7 @@ $zh2Hant = array(
'水来汤里去' => '水來湯裡去',
'水准' => '水準',
'水无怜奈' => '水無怜奈',
+'水表面' => '水表面',
'水里' => '水裡',
'水里商工' => '水里商工',
'水里溪' => '水里溪',
@@ -6312,13 +6241,11 @@ $zh2Hant = array(
'永志不忘' => '永誌不忘',
'求知欲' => '求知慾',
'求签' => '求籤',
-'求道于盲' => '求道於盲',
-'污蔑' => '汙衊',
'池里' => '池裡',
+'污蔑' => '污衊',
'汤卤' => '汤滷',
'汲于' => '汲於',
'决斗' => '決鬥',
-'沈海蓉' => '沈海蓉',
'沈淀' => '沈澱',
'沈郁' => '沈鬱',
'沉淀' => '沉澱',
@@ -6327,15 +6254,14 @@ $zh2Hant = array(
'没事干' => '沒事幹',
'没干' => '沒幹',
'没折至' => '沒摺至',
-'没梢干' => '沒梢幹',
'没样范' => '沒樣範',
'没准' => '沒準',
'冲冠发怒' => '沖冠髮怒',
'冲天' => '沖天',
+'沙琅' => '沙瑯',
'沙羡' => '沙羡',
'沙里淘金' => '沙裡淘金',
'河岳' => '河嶽',
-'河流汇集' => '河流匯集',
'河里' => '河裡',
'油泼面' => '油潑麵',
'油斗' => '油鬥',
@@ -6356,7 +6282,7 @@ $zh2Hant = array(
'泱郁' => '泱鬱',
'泳气钟' => '泳氣鐘',
'洄游' => '洄遊',
-'洋河大曲' => '洋河大麴',
+'洋河大曲' => '洋河大麯',
'洒家' => '洒家',
'洒扫' => '洒掃',
'洒水' => '洒水',
@@ -6371,6 +6297,8 @@ $zh2Hant = array(
'洗发' => '洗髮',
'洛钟东应' => '洛鐘東應',
'洞里' => '洞裡',
+'洞里萨' => '洞里薩',
+'洞里薩' => '洞里薩',
'泄欲' => '洩慾',
'洪范' => '洪範',
'洪谷子' => '洪谷子',
@@ -6391,8 +6319,8 @@ $zh2Hant = array(
'浮夸' => '浮誇',
'浮松' => '浮鬆',
'海干' => '海乾',
-'海淀山後' => '海淀山後',
'海淀山后' => '海淀山後',
+'海淀山後' => '海淀山後',
'浸卤' => '浸滷',
'涂善妮' => '涂善妮',
'涂坤' => '涂坤',
@@ -6413,8 +6341,9 @@ $zh2Hant = array(
'涂醒哲' => '涂醒哲',
'涂長望' => '涂長望',
'涂长望' => '涂長望',
-'涂鸿钦' => '涂鴻欽',
'涂鴻欽' => '涂鴻欽',
+'涂鸿钦' => '涂鴻欽',
+'涌水塘' => '涌水塘',
'涳蒙' => '涳濛',
'涸干' => '涸乾',
'凉席' => '涼蓆',
@@ -6425,7 +6354,6 @@ $zh2Hant = array(
'泪如泉涌' => '淚如泉湧',
'淡于' => '淡於',
'淡蒙蒙' => '淡濛濛',
-'淡朱' => '淡硃',
'净余' => '淨餘',
'净发' => '淨髮',
'淫欲' => '淫慾',
@@ -6442,7 +6370,6 @@ $zh2Hant = array(
'渠冲' => '渠衝',
'测不准' => '測不準',
'港制' => '港製',
-'游牧民族' => '游牧民族',
'游离' => '游離',
'浑朴' => '渾樸',
'浑个' => '渾箇',
@@ -6453,6 +6380,7 @@ $zh2Hant = array(
'涌入' => '湧入',
'涌出' => '湧出',
'涌向' => '湧向',
+'涌水' => '湧水',
'涌泉' => '湧泉',
'涌现' => '湧現',
'涌起' => '湧起',
@@ -6495,13 +6423,17 @@ $zh2Hant = array(
'准军事' => '準軍事',
'准头' => '準頭',
'准点' => '準點',
+'沟大曲' => '溝大麯',
+'沟谷' => '溝谷',
'溟蒙' => '溟濛',
'溢于' => '溢於',
+'温洛克期' => '溫洛克期',
'溲面' => '溲麵',
'溺于' => '溺於',
'滃郁' => '滃鬱',
'滑借' => '滑藉',
'汇丰' => '滙豐',
+'渗漓' => '滲灕',
'卤了' => '滷了',
'卤五花' => '滷五花',
'卤味' => '滷味',
@@ -6542,9 +6474,9 @@ $zh2Hant = array(
'潮涌' => '潮湧',
'溃于' => '潰於',
'涩谷区' => '澀谷區',
+'澄江县' => '澂江縣',
'澄澹精致' => '澄澹精致',
'澒蒙' => '澒濛',
-'泽渗漓而下降' => '澤滲灕而下降',
'淀乃不耕之地' => '澱乃不耕之地',
'淀北片' => '澱北片',
'淀山' => '澱山',
@@ -6560,19 +6492,29 @@ $zh2Hant = array(
'蒙汜' => '濛汜',
'蒙蒙细雨' => '濛濛細雨',
'蒙雾' => '濛霧',
-'蒙松雨' => '濛鬆雨',
'蒙鸿' => '濛鴻',
+'浚州' => '濬州',
+'浚县' => '濬縣',
'滨田里佳子' => '濱田里佳子',
-'沈吉线' => '瀋吉線',
+'沈丹客运' => '瀋丹客運',
+'沈丹线' => '瀋丹線',
+'沈丹铁路' => '瀋丹鐵路',
+'沈北' => '瀋北',
+'沈吉' => '瀋吉',
+'沈大线' => '瀋大線',
+'沈大铁路' => '瀋大鐵路',
+'沈大高速' => '瀋大高速',
'沈山线' => '瀋山線',
+'沈山铁路' => '瀋山鐵路',
'沈州' => '瀋州',
'沈抚' => '瀋撫',
'沈水' => '瀋水',
'沈河' => '瀋河',
-'沈海' => '瀋海',
'沈海铁路' => '瀋海鐵路',
+'沈海高速' => '瀋海高速',
'沈阳' => '瀋陽',
'泸州大曲' => '瀘州大麯',
+'沥干' => '瀝乾',
'潇洒' => '瀟洒',
'弥山遍野' => '瀰山遍野',
'弥漫' => '瀰漫',
@@ -6604,6 +6546,7 @@ $zh2Hant = array(
'烘制' => '烘製',
'烤干' => '烤乾',
'烤卤' => '烤滷',
+'烹制' => '烹製',
'焙干' => '焙乾',
'无征不信' => '無徵不信',
'无业游民' => '無業游民',
@@ -6613,6 +6556,7 @@ $zh2Hant = array(
'炼制' => '煉製',
'煎面' => '煎麵',
'烟卷' => '煙捲',
+'烟台' => '煙臺',
'照入签' => '照入籤',
'照相干片' => '照相乾片',
'煨干' => '煨乾',
@@ -6622,8 +6566,6 @@ $zh2Hant = array(
'燎发' => '燎髮',
'烧干' => '燒乾',
'燕几' => '燕几',
-'燕巢于幕' => '燕巢於幕',
-'燕燕于飞' => '燕燕于飛',
'燕游' => '燕遊',
'烫一个发' => '燙一個髮',
'烫一次发' => '燙一次髮',
@@ -6674,7 +6616,6 @@ $zh2Hant = array(
'犹如表' => '猶如錶',
'犹如钟' => '猶如鐘',
'犹如钟表' => '猶如鐘錶',
-'呆串了皮' => '獃串了皮',
'狱里' => '獄裡',
'奖杯' => '獎盃',
'独裁制' => '獨裁制',
@@ -6687,6 +6628,7 @@ $zh2Hant = array(
'玉米面' => '玉米面',
'王侯后' => '王侯后',
'王后' => '王后',
+'王添灯' => '王添灯',
'王田里' => '王田里',
'王鉴' => '王鑑',
'王余鱼' => '王餘魚',
@@ -6701,6 +6643,7 @@ $zh2Hant = array(
'理次发' => '理次髮',
'理发' => '理髮',
'琴钟' => '琴鐘',
+'珐琅' => '琺瑯',
'瑞城里' => '瑞城里',
'瑞征' => '瑞徵',
'瑶签' => '瑤籤',
@@ -6722,6 +6665,7 @@ $zh2Hant = array(
'生发' => '生髮',
'产卵洄游' => '產卵洄游',
'苏醒' => '甦醒',
+'用于' => '用於',
'用法里' => '用法裡',
'甩发' => '甩髮',
'田子里' => '田子里',
@@ -6732,10 +6676,10 @@ $zh2Hant = array(
'由于' => '由於',
'甲胄' => '甲冑',
'甲后路' => '甲后路',
-'电影后' => '电影後',
'男仆' => '男僕',
'界里' => '界裡',
'畏于' => '畏於',
+'留长发' => '留長髮',
'留发' => '留髮',
'毕于' => '畢於',
'毕业于' => '畢業於',
@@ -6765,13 +6709,11 @@ $zh2Hant = array(
'癸丑' => '癸丑',
'发干' => '發乾',
'发呆' => '發獃',
-'发蒙' => '發矇',
'发签' => '發籤',
'发松' => '發鬆',
'发面' => '發麵',
-'白干' => '白乾',
+'白干儿' => '白乾兒',
'白子里' => '白子里',
-'白干儿' => '白干兒',
'白术' => '白朮',
'白朴' => '白樸',
'白净面皮' => '白淨面皮',
@@ -6785,11 +6727,11 @@ $zh2Hant = array(
'白霉' => '白黴',
'百个' => '百個',
'百只可' => '百只可',
-'百只夠' => '百只夠',
'百只够' => '百只夠',
+'百只夠' => '百只夠',
'百只怕' => '百只怕',
-'百只足夠' => '百只足夠',
'百只足够' => '百只足夠',
+'百只足夠' => '百只足夠',
'百周后' => '百周後',
'百天后' => '百天後',
'百年' => '百年',
@@ -6805,6 +6747,8 @@ $zh2Hant = array(
'的回复' => '的回覆',
'的图里' => '的圖裡',
'的山里' => '的山裡',
+'的干将' => '的幹將',
+'的个中' => '的箇中',
'的钟' => '的鐘',
'的长发' => '的長髮',
'的发小' => '的髮小',
@@ -6857,11 +6801,13 @@ $zh2Hant = array(
'看钟' => '看鐘',
'真凶' => '真兇',
'真个' => '真箇',
+'真丑' => '真醜',
'眼干' => '眼乾',
'眼帘' => '眼帘',
'眼眶里' => '眼眶裡',
'眼睛里' => '眼睛裡',
'眼里' => '眼裡',
+'着眼于' => '着眼於',
'困乏' => '睏乏',
'困了' => '睏了',
'困倦' => '睏倦',
@@ -6877,6 +6823,7 @@ $zh2Hant = array(
'瞳蒙' => '瞳矇',
'蒙事' => '矇事',
'蒙昧无知' => '矇昧無知',
+'蒙松雨' => '矇松雨',
'蒙混' => '矇混',
'蒙瞍' => '矇瞍',
'蒙眬' => '矇矓',
@@ -6887,6 +6834,7 @@ $zh2Hant = array(
'矜夸' => '矜誇',
'短几' => '短几',
'短于' => '短於',
+'短发生' => '短發生',
'短发' => '短髮',
'矮几' => '矮几',
'石几' => '石几',
@@ -6895,20 +6843,14 @@ $zh2Hant = array(
'石英钟' => '石英鐘',
'石英钟表' => '石英鐘錶',
'石钟' => '石鐘',
-'石钟山' => '石鐘山',
'研制' => '研製',
'砰当' => '砰噹',
'破鉴' => '破鑑',
-'朱唇皓齿' => '硃唇皓齒',
-'朱批' => '硃批',
'朱砂' => '硃砂',
-'朱笔' => '硃筆',
-'朱红色' => '硃紅色',
-'朱色' => '硃色',
-'朱谕' => '硃諭',
'硬干' => '硬幹',
'确瘠' => '确瘠',
'碑志' => '碑誌',
+'碗里' => '碗裡',
'碰钟' => '碰鐘',
'确系' => '確係',
'码表' => '碼錶',
@@ -6952,7 +6894,6 @@ $zh2Hant = array(
'私欲' => '私慾',
'私斗' => '私鬥',
'秋游' => '秋遊',
-'秋阴入井干' => '秋陰入井幹',
'秋发' => '秋髮',
'种丹妮' => '种丹妮',
'种师中' => '种師中',
@@ -6965,6 +6906,7 @@ $zh2Hant = array(
'秒表示' => '秒表示',
'秒钟' => '秒鐘',
'秤杆' => '秤桿',
+'秦沈客运' => '秦瀋客運',
'移祸于' => '移禍於',
'稀松' => '稀鬆',
'棱台' => '稜台',
@@ -7006,10 +6948,10 @@ $zh2Hant = array(
'谷草' => '穀草',
'谷贵饿农' => '穀貴餓農',
'谷贱伤农' => '穀賤傷農',
-'谷道' => '穀道',
'谷雨' => '穀雨',
'谷类' => '穀類',
'谷食' => '穀食',
+'穆棱' => '穆稜',
'穆罕默德历' => '穆罕默德曆',
'穆罕默德历史' => '穆罕默德歷史',
'积淀' => '積澱',
@@ -7021,7 +6963,6 @@ $zh2Hant = array(
'空蒙' => '空濛',
'空荡' => '空蕩',
'空荡荡' => '空蕩蕩',
-'空谷回音' => '空谷回音',
'空钟' => '空鐘',
'空余' => '空餘',
'窒欲' => '窒慾',
@@ -7035,7 +6976,6 @@ $zh2Hant = array(
'窃钟掩耳' => '竊鐘掩耳',
'立于' => '立於',
'立范' => '立範',
-'站干岸儿' => '站乾岸兒',
'童仆' => '童僕',
'竞斗' => '競鬥',
'竹几' => '竹几',
@@ -7043,6 +6983,7 @@ $zh2Hant = array(
'竹签' => '竹籤',
'竹席' => '竹蓆',
'竹制' => '竹製',
+'竹溪县' => '竹谿縣',
'笑里藏刀' => '笑裡藏刀',
'第一出现' => '第一出現',
'第一出現' => '第一出現',
@@ -7053,8 +6994,8 @@ $zh2Hant = array(
'第三出局' => '第三出局',
'第三出' => '第三齣',
'第九出' => '第九齣',
-'第二出线' => '第二出線',
'第二出線' => '第二出線',
+'第二出线' => '第二出線',
'第二出' => '第二齣',
'第五出局' => '第五出局',
'第五出' => '第五齣',
@@ -7076,21 +7017,19 @@ $zh2Hant = array(
'筑肥' => '筑肥',
'筑西' => '筑西',
'筑邦' => '筑邦',
-'筑陽' => '筑陽',
'筑阳' => '筑陽',
+'筑陽' => '筑陽',
'答复' => '答覆',
'筵几' => '筵几',
'个中原因' => '箇中原因',
-'个中奥妙' => '箇中奧妙',
-'个中奥秘' => '箇中奧秘',
+'个中奥' => '箇中奧',
'个中好手' => '箇中好手',
'个中强手' => '箇中強手',
-'个中消息' => '箇中消息',
'个中滋味' => '箇中滋味',
'个中玄机' => '箇中玄機',
'个中理由' => '箇中理由',
-'个中讯息' => '箇中訊息',
-'个中资讯' => '箇中資訊',
+'个中翘楚' => '箇中翹楚',
+'个中道理' => '箇中道理',
'个中高手' => '箇中高手',
'个旧' => '箇舊',
'算历' => '算曆',
@@ -7103,6 +7042,7 @@ $zh2Hant = array(
'节欲' => '節慾',
'节目里' => '節目裡',
'节余' => '節餘',
+'范亭' => '範亭',
'范例' => '範例',
'范围' => '範圍',
'范字' => '範字',
@@ -7115,8 +7055,9 @@ $zh2Hant = array(
'范金' => '範金',
'简并' => '簡併',
'简朴' => '簡樸',
-'簡筑翎' => '簡筑翎',
+'简短发' => '簡短發',
'简筑翎' => '簡筑翎',
+'簡筑翎' => '簡筑翎',
'簸荡' => '簸蕩',
'签幐' => '籤幐',
'签押' => '籤押',
@@ -7198,13 +7139,12 @@ $zh2Hant = array(
'绝于' => '絕於',
'绞干' => '絞乾',
'络腮胡' => '絡腮鬍',
-'給我干脆' => '給我干脆',
-'给我干脆' => '給我干脆',
'给于' => '給於',
'丝恩发怨' => '絲恩髮怨',
'丝制' => '絲製',
'丝发' => '絲髮',
'绑扎' => '綁紮',
+'绥棱' => '綏稜',
'捆扎' => '綑紮',
'經有云' => '經有云',
'经有云' => '經有云',
@@ -7213,6 +7153,9 @@ $zh2Hant = array(
'维系' => '維繫',
'绾发' => '綰髮',
'纲鉴' => '綱鑑',
+'網球台' => '網球台',
+'网球台' => '網球台',
+'网站里' => '網站裡',
'网里' => '網裡',
'网志' => '網誌',
'网游' => '網遊',
@@ -7237,6 +7180,7 @@ $zh2Hant = array(
'缝里' => '縫裡',
'缝制' => '縫製',
'缩栗' => '縮慄',
+'缩短发' => '縮短發',
'纵欲' => '縱慾',
'纤夫' => '縴夫',
'纤手' => '縴手',
@@ -7247,7 +7191,6 @@ $zh2Hant = array(
'繁复' => '繁複',
'繁钟' => '繁鐘',
'绷扒吊拷' => '繃扒弔拷',
-'穗帏飘井干' => '繐幃飄井幹',
'绕梁' => '繞樑',
'绘制' => '繪製',
'系上。' => '繫上。',
@@ -7273,11 +7216,11 @@ $zh2Hant = array(
'系紧' => '繫緊',
'系绳' => '繫繩',
'系累' => '繫纍',
+'系舟' => '繫舟',
'系船' => '繫船',
'系辞' => '繫辭',
'系鞋带' => '繫鞋帶',
'系风捕影' => '繫風捕影',
-'继承制' => '繼承制',
'累囚' => '纍囚',
'累堆' => '纍堆',
'累瓦结绳' => '纍瓦結繩',
@@ -7296,7 +7239,6 @@ $zh2Hant = array(
'羁系' => '羈繫',
'美容美发' => '美容美髮',
'美于' => '美於',
-'美制' => '美製',
'美丑' => '美醜',
'美发学' => '美髮學',
'美发师' => '美髮師',
@@ -7317,9 +7259,10 @@ $zh2Hant = array(
'老干' => '老乾',
'老仆' => '老僕',
'老干部' => '老幹部',
-'老蒙' => '老懞',
+'老懞' => '老懞',
'老于' => '老於',
'老爷钟' => '老爺鐘',
+'老白干' => '老白乾',
'老姜' => '老薑',
'老板' => '老闆',
'老面皮' => '老面皮',
@@ -7331,6 +7274,8 @@ $zh2Hant = array(
'聊斋志异' => '聊齋志異',
'圣人历' => '聖人曆',
'圣后' => '聖后',
+'圣马尔谷日' => '聖馬爾谷日',
+'聖馬爾谷日' => '聖馬爾谷日',
'聘雇' => '聘僱',
'聚药雄蕊' => '聚葯雄蕊',
'闻风后' => '聞風後',
@@ -7356,7 +7301,9 @@ $zh2Hant = array(
'胜肽' => '胜肽',
'胜键' => '胜鍵',
'胡云' => '胡云',
+'胡子婴' => '胡子嬰',
'胡子昂' => '胡子昂',
+'胡杰' => '胡杰',
'胡朴安' => '胡樸安',
'胡里胡涂' => '胡裡胡塗',
'胰脏' => '胰臟',
@@ -7380,6 +7327,7 @@ $zh2Hant = array(
'腊味' => '腊味',
'腊毒' => '腊毒',
'腊笔' => '腊筆',
+'腌臜' => '腌臢',
'肾脏' => '腎臟',
'腐干' => '腐乾',
'腐余' => '腐餘',
@@ -7401,6 +7349,7 @@ $zh2Hant = array(
'卧游' => '臥遊',
'臧谷亡羊' => '臧穀亡羊',
'临潼斗宝' => '臨潼鬥寶',
+'自干五' => '自乾五',
'自制一下' => '自制一下',
'自制下来' => '自制下來',
'自制不' => '自制不',
@@ -7419,6 +7368,7 @@ $zh2Hant = array(
'自制能力' => '自制能力',
'自于' => '自於',
'自然数里' => '自然數裡',
+'自由钟' => '自由鐘',
'自制' => '自製',
'自觉自愿' => '自覺自愿',
'自夸' => '自誇',
@@ -7431,8 +7381,8 @@ $zh2Hant = array(
'台静农' => '臺靜農',
'臻于' => '臻於',
'舂谷' => '舂穀',
-'舉手表' => '舉手表',
'举手表' => '舉手表',
+'舉手表' => '舉手表',
'舊庄' => '舊庄',
'旧历' => '舊曆',
'旧历史' => '舊歷史',
@@ -7468,14 +7418,15 @@ $zh2Hant = array(
'苑里' => '苑裡',
'若干' => '若干',
'苦干' => '苦幹',
+'苦于' => '苦於',
'苦里' => '苦裡',
'苦斗' => '苦鬥',
'苎麻' => '苧麻',
'茂都淀' => '茂都澱',
'范文同' => '范文同',
'范文正公' => '范文正公',
-'范文瀾' => '范文瀾',
'范文澜' => '范文瀾',
+'范文瀾' => '范文瀾',
'范文照' => '范文照',
'范文程' => '范文程',
'范文芳' => '范文芳',
@@ -7484,8 +7435,8 @@ $zh2Hant = array(
'范登堡' => '范登堡',
'范賢惠' => '范賢惠',
'范贤惠' => '范賢惠',
-'茅于轼' => '茅于軾',
'茅于軾' => '茅于軾',
+'茅于轼' => '茅于軾',
'茶几' => '茶几',
'茶余' => '茶餘',
'茶面' => '茶麵',
@@ -7502,12 +7453,12 @@ $zh2Hant = array(
'莽荡' => '莽蕩',
'菜干' => '菜乾',
'菜坛' => '菜罈',
-'菜肴' => '菜肴',
+'菜肴' => '菜餚',
'菠棱菜' => '菠稜菜',
'菠萝干' => '菠蘿乾',
'华严钟' => '華嚴鐘',
-'萬一只' => '萬一只',
'万一只' => '萬一只',
+'萬一只' => '萬一只',
'万个' => '萬個',
'万周后' => '萬周後',
'万天后' => '萬天後',
@@ -7521,6 +7472,7 @@ $zh2Hant = array(
'万象' => '萬象',
'万只' => '萬隻',
'万余' => '萬餘',
+'落于' => '落於',
'落腮胡' => '落腮鬍',
'落发' => '落髮',
'叶叶琴' => '葉叶琴',
@@ -7528,6 +7480,7 @@ $zh2Hant = array(
'葡萄干' => '葡萄乾',
'董氏封发' => '董氏封髮',
'葫芦里卖甚么药' => '葫蘆裡賣甚麼藥',
+'葬于' => '葬於',
'蒙雾露' => '蒙霧露',
'蒜发' => '蒜髮',
'蒲席' => '蒲蓆',
@@ -7541,9 +7494,9 @@ $zh2Hant = array(
'蓄须' => '蓄鬚',
'席子' => '蓆子',
'蓊郁' => '蓊鬱',
-'蓬蓬松松' => '蓬蓬鬆鬆',
'蓬发' => '蓬髮',
'蓬松' => '蓬鬆',
+'蓬松松' => '蓬鬆鬆',
'参绥' => '蔘綏',
'葱郁' => '蔥鬱',
'荞麦面' => '蕎麥麵',
@@ -7589,8 +7542,8 @@ $zh2Hant = array(
'熏风' => '薰風',
'熏香' => '薰香',
'苧悴' => '薴悴',
-'薴烯' => '薴烯',
'苧烯' => '薴烯',
+'薴烯' => '薴烯',
'借以' => '藉以',
'借助' => '藉助',
'借口' => '藉口',
@@ -7612,14 +7565,16 @@ $zh2Hant = array(
'藤制' => '藤製',
'药签' => '藥籤',
'药面儿' => '藥麵兒',
-'苏昆' => '蘇崑',
'苏崑' => '蘇崑',
+'苏昆' => '蘇崑',
'苹果' => '蘋果',
'苹果干' => '蘋果乾',
+'兰溪市' => '蘭谿市',
'萝卜' => '蘿蔔',
'萝卜干' => '蘿蔔乾',
'虎须' => '虎鬚',
'虎斗' => '虎鬥',
+'处于' => '處於',
'虚夸' => '虛誇',
'号志' => '號誌',
'虫部' => '虫部',
@@ -7634,6 +7589,7 @@ $zh2Hant = array(
'蝎虎' => '蝎虎',
'蝎蝎螫螫' => '蝎蝎螫螫',
'蝎谮' => '蝎譖',
+'虾面' => '蝦麵',
'虮虱相吊' => '蟣蝨相弔',
'蛏干' => '蟶乾',
'蚁后' => '蟻后',
@@ -7645,8 +7601,8 @@ $zh2Hant = array(
'行事历' => '行事曆',
'行事历史' => '行事歷史',
'行凶' => '行兇',
+'行家里手' => '行家裡手',
'行于' => '行於',
-'行百里者半于九十' => '行百里者半於九十',
'卫后庄公' => '衛後莊公',
'卫星钟' => '衛星鐘',
'冲上' => '衝上',
@@ -7698,6 +7654,7 @@ $zh2Hant = array(
'冲头阵' => '衝頭陣',
'冲风' => '衝風',
'衡鉴' => '衡鑑',
+'表面包' => '表面包',
'衷于' => '衷於',
'袋杆' => '袋桿',
'袋里' => '袋裡',
@@ -7716,9 +7673,9 @@ $zh2Hant = array(
'夹裙' => '袷裙',
'裁并' => '裁併',
'裁制' => '裁製',
-'里手' => '裏手',
'里水镇' => '裏水鎮',
'里海' => '裏海',
+'里运河' => '裏運河',
'补于' => '補於',
'补注' => '補註',
'装折' => '裝摺',
@@ -7771,6 +7728,7 @@ $zh2Hant = array(
'复利' => '複利',
'复印' => '複印',
'复句' => '複句',
+'复合' => '複合',
'复壁' => '複壁',
'复姓' => '複姓',
'复字键' => '複字鍵',
@@ -7780,6 +7738,7 @@ $zh2Hant = array(
'复平面' => '複平面',
'复式' => '複式',
'复数' => '複數',
+'复方' => '複方',
'复本' => '複本',
'复查' => '複查',
'复次' => '複次',
@@ -7813,6 +7772,7 @@ $zh2Hant = array(
'复韵' => '複韻',
'褒赞' => '褒讚',
'衬里' => '襯裡',
+'西井里' => '西井里',
'西周钟' => '西周鐘',
'西昆' => '西崑',
'西岳' => '西嶽',
@@ -7840,12 +7800,14 @@ $zh2Hant = array(
'角落里' => '角落裡',
'觚棱' => '觚稜',
'解雇' => '解僱',
+'解封后' => '解封後',
'解铃仍须系铃人' => '解鈴仍須繫鈴人',
'解铃还须系铃人' => '解鈴還須繫鈴人',
'解发佯狂' => '解髮佯狂',
'触须' => '觸鬚',
'言云' => '言云',
'言大而夸' => '言大而夸',
+'言里' => '言裡',
'言辩而确' => '言辯而确',
'订制' => '訂製',
'计划' => '計劃',
@@ -7855,6 +7817,7 @@ $zh2Hant = array(
'托交' => '託交',
'托人' => '託人',
'托付' => '託付',
+'托克逊' => '託克遜',
'托儿' => '託兒',
'托古讽今' => '託古諷今',
'托名' => '託名',
@@ -7876,9 +7839,10 @@ $zh2Hant = array(
'托辞' => '託辭',
'托运' => '託運',
'托过' => '託過',
+'托里县' => '託里縣',
'托附' => '託附',
'许愿起经' => '許愿起經',
-'许虬' => '許虬',
+'許聖杰' => '許聖杰',
'注上' => '註上',
'注册' => '註冊',
'注失' => '註失',
@@ -7901,7 +7865,6 @@ $zh2Hant = array(
'词汇' => '詞彙',
'词余' => '詞餘',
'询于' => '詢於',
-'询于刍荛' => '詢於芻蕘',
'试制' => '試製',
'詩云' => '詩云',
'诗云' => '詩云',
@@ -7915,6 +7878,7 @@ $zh2Hant = array(
'诔赞' => '誄讚',
'夸下海口' => '誇下海口',
'夸了' => '誇了',
+'夸人' => '誇人',
'夸他' => '誇他',
'夸你' => '誇你',
'夸来夸去' => '誇來誇去',
@@ -7926,7 +7890,9 @@ $zh2Hant = array(
'夸多斗靡' => '誇多鬥靡',
'夸大' => '誇大',
'夸她' => '誇她',
+'夸姣' => '誇姣',
'夸官' => '誇官',
+'夸容' => '誇容',
'夸张' => '誇張',
'夸强说会' => '誇強說會',
'夸得' => '誇得',
@@ -7951,6 +7917,7 @@ $zh2Hant = array(
'夸辩' => '誇辯',
'夸过' => '誇過',
'夸饰' => '誇飾',
+'夸丽' => '誇麗',
'志哀' => '誌哀',
'志喜' => '誌喜',
'志庆' => '誌慶',
@@ -7980,13 +7947,14 @@ $zh2Hant = array(
'咨询' => '諮詢',
'诸余' => '諸餘',
'谋干' => '謀幹',
+'謝杰' => '謝杰',
+'谢杰' => '謝杰',
'谢华后' => '謝華后',
'谬采虚声' => '謬採虛聲',
'谬赞' => '謬讚',
'謷丑' => '謷醜',
'謹愿' => '謹愿',
'谨愿' => '謹愿',
-'谨于心' => '謹於心',
'哗噪' => '譁噪',
'哗嚣' => '譁囂',
'哗然' => '譁然',
@@ -8023,6 +7991,7 @@ $zh2Hant = array(
'豆干' => '豆乾',
'豆腐干' => '豆腐乾',
'竖起脊梁' => '豎起脊梁',
+'丰度' => '豐度',
'丰滨' => '豐濱',
'丰滨乡' => '豐濱鄉',
'丰台' => '豐臺',
@@ -8043,8 +8012,8 @@ $zh2Hant = array(
'賢后' => '賢后',
'贤后' => '賢后',
'卖断发' => '賣斷發',
-'赋范' => '賦范',
'賦范' => '賦范',
+'赋范' => '賦范',
'质数里' => '質數裡',
'质朴' => '質樸',
'赌后' => '賭后',
@@ -8065,6 +8034,7 @@ $zh2Hant = array(
'赵治勋' => '趙治勳',
'趱干' => '趲幹',
'足于' => '足於',
+'足球台' => '足球台',
'跌扑' => '跌扑',
'路图里' => '路圖裡',
'路签' => '路籤',
@@ -8098,8 +8068,8 @@ $zh2Hant = array(
'挽输' => '輓輸',
'挽辞' => '輓辭',
'轻于' => '輕於',
-'轻轻松松' => '輕輕鬆鬆',
'轻松' => '輕鬆',
+'轻松松' => '輕鬆鬆',
'轮奸' => '輪姦',
'轮回' => '輪迴',
'转向往' => '轉向往',
@@ -8112,6 +8082,7 @@ $zh2Hant = array(
'辞汇' => '辭彙',
'辫发' => '辮髮',
'辩斗' => '辯鬥',
+'辰溪县' => '辰谿縣',
'农历' => '農曆',
'农历史' => '農歷史',
'农民历' => '農民曆',
@@ -8122,11 +8093,16 @@ $zh2Hant = array(
'迥然回异' => '迥然迴異',
'迫于' => '迫於',
'回光返照' => '迴光返照',
-'回向' => '迴向',
'回圈' => '迴圈',
'回廊' => '迴廊',
'回形夹' => '迴形夾',
-'回文' => '迴文',
+'回文序列' => '迴文序列',
+'回文数' => '迴文數',
+'回文构词' => '迴文構詞',
+'回文结构' => '迴文結構',
+'回文联' => '迴文聯',
+'回文诗' => '迴文詩',
+'回文锦' => '迴文錦',
'回旋' => '迴旋',
'回环' => '迴環',
'回纹针' => '迴紋針',
@@ -8141,14 +8117,12 @@ $zh2Hant = array(
'回递性' => '迴遞性',
'回避' => '迴避',
'回銮' => '迴鑾',
-'回音' => '迴音',
'回响' => '迴響',
'回风' => '迴風',
'迷于' => '迷於',
'迷蒙' => '迷濛',
'追凶' => '追兇',
'退伙' => '退夥',
-'退藏于密' => '退藏於密',
'逆钟' => '逆鐘',
'逆钟向' => '逆鐘向',
'逆风后' => '逆風後',
@@ -8185,6 +8159,7 @@ $zh2Hant = array(
'这里' => '這裡',
'这钟' => '這鐘',
'这只' => '這隻',
+'这么干' => '這麼幹',
'这出' => '這齣',
'通奸' => '通姦',
'通心面' => '通心麵',
@@ -8196,11 +8171,12 @@ $zh2Hant = array(
'造钟' => '造鐘',
'连三并四' => '連三併四',
'连采' => '連採',
+'连发式' => '連發式',
'连系' => '連繫',
-'周游世界' => '週遊世界',
+'周游' => '週遊',
'进两出' => '進兩出',
-'進制' => '進制',
'进制' => '進制',
+'進制' => '進制',
'逼并' => '逼併',
'遇风后' => '遇風後',
'游了' => '遊了',
@@ -8234,6 +8210,7 @@ $zh2Hant = array(
'游目骋怀' => '遊目騁懷',
'游程' => '遊程',
'游丝' => '遊絲',
+'游美学务' => '遊美學務',
'游兴' => '遊興',
'游船' => '遊船',
'游艇' => '遊艇',
@@ -8259,6 +8236,7 @@ $zh2Hant = array(
'递回' => '遞迴',
'远游' => '遠遊',
'遨游' => '遨遊',
+'适于' => '適於',
'遮丑' => '遮醜',
'迁于' => '遷於',
'选手表明' => '選手表明',
@@ -8301,6 +8279,7 @@ $zh2Hant = array(
'部子里' => '部子里',
'部落发' => '部落發',
'郭后' => '郭后',
+'都市里' => '都市裡',
'都于' => '都於',
'乡愿' => '鄉愿',
'鄉愿' => '鄉愿',
@@ -8308,16 +8287,16 @@ $zh2Hant = array(
'鄭凱云' => '鄭凱云',
'配制饲料' => '配制飼料',
'配图里' => '配圖裡',
-'配水干管' => '配水幹管',
'配制' => '配製',
'酒帘' => '酒帘',
'酒气冲天' => '酒氣衝天',
'酒坛' => '酒罈',
'酒肴' => '酒肴',
-'酒麹' => '酒麴',
'酒曲' => '酒麴',
+'酒麹' => '酒麴',
'酥松' => '酥鬆',
'酸姜' => '酸薑',
+'腌制' => '醃製',
'醇朴' => '醇樸',
'醉于' => '醉於',
'醋坛' => '醋罈',
@@ -8337,7 +8316,6 @@ $zh2Hant = array(
'丑女' => '醜女',
'丑女效颦' => '醜女效顰',
'丑奴儿' => '醜奴兒',
-'丑婆子' => '醜婆子',
'丑妇' => '醜婦',
'丑媳' => '醜媳',
'丑媳妇' => '醜媳婦',
@@ -8417,6 +8395,7 @@ $zh2Hant = array(
'金表露' => '金表露',
'金表面' => '金表面',
'金装玉里' => '金裝玉裡',
+'金溪县' => '金谿縣',
'金链' => '金鍊',
'金钟' => '金鐘',
'金发' => '金髮',
@@ -8511,6 +8490,7 @@ $zh2Hant = array(
'钟罩' => '鐘罩',
'钟声' => '鐘聲',
'钟腰' => '鐘腰',
+'钟花' => '鐘花',
'钟螺' => '鐘螺',
'钟行' => '鐘行',
'钟表面' => '鐘表面',
@@ -8620,8 +8600,10 @@ $zh2Hant = array(
'陳冲' => '陳冲',
'陳士杰' => '陳士杰',
'陈升' => '陳昇',
-'陳有后' => '陳有后',
'陈有后' => '陳有后',
+'陳有后' => '陳有后',
+'陈杰' => '陳杰',
+'陳杰' => '陳杰',
'陈炼' => '陳鍊',
'陆游' => '陸遊',
'阳春面' => '陽春麵',
@@ -8649,7 +8631,6 @@ $zh2Hant = array(
'雕梁画栋' => '雕樑畫棟',
'双折射' => '雙折射',
'双折' => '雙摺',
-'双沟大曲' => '雙溝大麯',
'双胜类' => '雙胜類',
'双雕' => '雙鵰',
'杂合面儿' => '雜合麵兒',
@@ -8671,8 +8652,9 @@ $zh2Hant = array(
'雪里' => '雪裡',
'雪里红' => '雪裡紅',
'雪里蕻' => '雪裡蕻',
-'云吞面' => '雲吞麵',
+'云吞' => '雲吞',
'云笈七签' => '雲笈七籤',
+'云里雾里' => '雲裡霧裡',
'云游' => '雲遊',
'云须' => '雲鬚',
'零个' => '零個',
@@ -8684,16 +8666,22 @@ $zh2Hant = array(
'电子表格' => '電子表格',
'电子钟' => '電子鐘',
'电子钟表' => '電子鐘錶',
+'电影后' => '電影後',
+'电梯里' => '電梯裡',
'电波钟' => '電波鐘',
'电码表' => '電碼表',
'电冲' => '電衝',
+'电视台风' => '電視台風',
'电表' => '電錶',
'电钟' => '電鐘',
'震栗' => '震慄',
'霉气冲天' => '霉氣衝天',
+'沾化' => '霑化',
+'沾益' => '霑益',
'雾里' => '霧裡',
'露丑' => '露醜',
'霁范' => '霽範',
+'灵昆' => '靈崑',
'青山一发' => '青山一髮',
'青霉' => '青黴',
'非常准' => '非常準',
@@ -8710,18 +8698,18 @@ $zh2Hant = array(
'面包管' => '面包管',
'面包扎' => '面包紮',
'面包罗' => '面包羅',
+'面包着' => '面包著',
'面包藏' => '面包藏',
'面包装' => '面包裝',
'面包裹' => '面包裹',
'面包起' => '面包起',
'面包办' => '面包辦',
'面店铺' => '面店鋪',
-'面條目' => '面條目',
'面条目' => '面條目',
+'面條目' => '面條目',
'面粉碎' => '面粉碎',
'面粉红' => '面粉紅',
'面食饭' => '面食飯',
-'面食面' => '面食麵',
'鞋里' => '鞋裡',
'鞣制' => '鞣製',
'秋千' => '鞦韆',
@@ -8737,8 +8725,8 @@ $zh2Hant = array(
'頁面' => '頁面',
'页面' => '頁面',
'顶凶' => '頂兇',
-'顶多' => '頂多',
'頂多' => '頂多',
+'顶多' => '頂多',
'项链' => '項鍊',
'顺于' => '順於',
'顺钟向' => '順鐘向',
@@ -8749,7 +8737,6 @@ $zh2Hant = array(
'预报不准' => '預報不準',
'预制' => '預製',
'领袖欲' => '領袖慾',
-'头儿干' => '頭兒幹',
'头里' => '頭裡',
'头长发' => '頭長髮',
'头发' => '頭髮',
@@ -8783,11 +8770,13 @@ $zh2Hant = array(
'风起云涌' => '風起雲湧',
'風采' => '風采',
'风采' => '風采',
+'风刮' => '風颳',
'台风' => '颱風',
'台风后' => '颱風後',
'刮了' => '颳了',
'刮倒' => '颳倒',
'刮去' => '颳去',
+'刮大风' => '颳大風',
'刮得' => '颳得',
'刮走' => '颳走',
'刮起' => '颳起',
@@ -8797,6 +8786,7 @@ $zh2Hant = array(
'飘荡' => '飄蕩',
'飘游' => '飄遊',
'飘飘荡荡' => '飄飄蕩蕩',
+'飘发自由女神' => '飄髮自由女神',
'飞扎' => '飛紮',
'飞刍挽粟' => '飛芻輓粟',
'飞行钟' => '飛行鐘',
@@ -8854,6 +8844,7 @@ $zh2Hant = array(
'余子' => '餘子',
'余存' => '餘存',
'余孽' => '餘孽',
+'余干' => '餘干',
'余年' => '餘年',
'余式' => '餘式',
'余弦' => '餘弦',
@@ -8876,6 +8867,7 @@ $zh2Hant = array(
'余殃' => '餘殃',
'余毒' => '餘毒',
'余气' => '餘氣',
+'余江' => '餘江',
'余波' => '餘波',
'余温' => '餘溫',
'余泽' => '餘澤',
@@ -8924,7 +8916,6 @@ $zh2Hant = array(
'馄饨面' => '餛飩麵',
'馆谷' => '館穀',
'馆里' => '館裡',
-'餵驴' => '餵驢',
'饥寒' => '饑寒',
'饥民' => '饑民',
'饥渴' => '饑渴',
@@ -8940,13 +8931,14 @@ $zh2Hant = array(
'香山庄' => '香山庄',
'马干' => '馬乾',
'馬占山' => '馬占山',
+'马德钟' => '馬德鐘',
'马斯垂克期' => '馬斯垂克期',
'馬格里布' => '馬格里布',
'马格里布' => '馬格里布',
'驻扎' => '駐紮',
'骀荡' => '駘蕩',
-'騰格里' => '騰格里',
'腾格里' => '騰格里',
+'騰格里' => '騰格里',
'腾涌' => '騰湧',
'腾冲' => '騰衝',
'惊栗' => '驚慄',
@@ -8959,6 +8951,7 @@ $zh2Hant = array(
'体范' => '體範',
'体系' => '體系',
'高几' => '高几',
+'高后' => '高后',
'高干扰' => '高干擾',
'高干预' => '高干預',
'高干' => '高幹',
@@ -8973,6 +8966,7 @@ $zh2Hant = array(
'发乳' => '髮乳',
'发光可鉴' => '髮光可鑑',
'发匪' => '髮匪',
+'发及腰' => '髮及腰',
'发型' => '髮型',
'发夹' => '髮夾',
'发妻' => '髮妻',
@@ -8983,7 +8977,7 @@ $zh2Hant = array(
'发廊' => '髮廊',
'发式' => '髮式',
'发引千钧' => '髮引千鈞',
-'发指' => '髮指',
+'发披肩' => '髮披肩',
'发卷' => '髮捲',
'发根' => '髮根',
'发油' => '髮油',
@@ -9048,7 +9042,7 @@ $zh2Hant = array(
'松通' => '鬆通',
'松开' => '鬆開',
'松饼' => '鬆餅',
-'松松' => '鬆鬆',
+'松松地' => '鬆鬆地',
'鬈发' => '鬈髮',
'胡子' => '鬍子',
'胡梢' => '鬍梢',
@@ -9075,11 +9069,13 @@ $zh2Hant = array(
'斗剑' => '鬥劍',
'斗力' => '鬥力',
'斗劲' => '鬥勁',
+'斗勇' => '鬥勇',
'斗胜' => '鬥勝',
'斗口' => '鬥口',
'斗合' => '鬥合',
'斗嘴' => '鬥嘴',
'斗地主' => '鬥地主',
+'斗垮' => '鬥垮',
'斗士' => '鬥士',
'斗富' => '鬥富',
'斗巧' => '鬥巧',
@@ -9092,9 +9088,11 @@ $zh2Hant = array(
'斗志' => '鬥志',
'斗闷' => '鬥悶',
'斗成' => '鬥成',
+'斗战' => '鬥戰',
'斗打' => '鬥打',
'斗批改' => '鬥批改',
'斗技' => '鬥技',
+'斗败' => '鬥敗',
'斗文' => '鬥文',
'斗智' => '鬥智',
'斗暴' => '鬥暴',
@@ -9110,7 +9108,9 @@ $zh2Hant = array(
'斗牛' => '鬥牛',
'斗犀台' => '鬥犀臺',
'斗犬' => '鬥犬',
+'斗狗' => '鬥狗',
'斗狠' => '鬥狠',
+'斗兽' => '鬥獸',
'斗叠' => '鬥疊',
'斗百草' => '鬥百草',
'斗眼' => '鬥眼',
@@ -9123,6 +9123,7 @@ $zh2Hant = array(
'斗草' => '鬥草',
'斗叶儿' => '鬥葉兒',
'斗叶子' => '鬥葉子',
+'斗蛐' => '鬥蛐',
'斗蟋蟀' => '鬥蟋蟀',
'斗话' => '鬥話',
'斗艳' => '鬥豔',
@@ -9140,6 +9141,7 @@ $zh2Hant = array(
'斗鸭' => '鬥鴨',
'斗鹌鹑' => '鬥鵪鶉',
'斗丽' => '鬥麗',
+'斗龙' => '鬥龍',
'闹表' => '鬧錶',
'闹钟' => '鬧鐘',
'哄动' => '鬨動',
@@ -9189,14 +9191,14 @@ $zh2Hant = array(
'鬼谷子' => '鬼谷子',
'魂牵梦系' => '魂牽夢繫',
'魏征' => '魏徵',
-'魔杰座' => '魔杰座',
'魔表' => '魔錶',
'鱼干' => '魚乾',
'鱼松' => '魚鬆',
-'鲜于枢' => '鮮于樞',
-'鮮于樞' => '鮮于樞',
+'鮮于' => '鮮于',
+'鲜于' => '鮮于',
'鲸须' => '鯨鬚',
-'鳳凰于飛' => '鳳凰于飛',
+'鳥栖' => '鳥栖',
+'鸟栖市' => '鳥栖市',
'凤梨干' => '鳳梨乾',
'鸣钟' => '鳴鐘',
'鸿范' => '鴻範',
@@ -9206,6 +9208,7 @@ $zh2Hant = array(
'雕悍' => '鵰悍',
'雕翎' => '鵰翎',
'雕鹗' => '鵰鶚',
+'鹤峰县' => '鶴峯縣',
'鹤吊' => '鶴弔',
'鹤发' => '鶴髮',
'鸾鉴' => '鸞鑑',
@@ -9230,7 +9233,7 @@ $zh2Hant = array(
'咸菜' => '鹹菜',
'咸菜干' => '鹹菜乾',
'咸蛋' => '鹹蛋',
-'咸猪肉' => '鹹豬肉',
+'咸猪' => '鹹豬',
'咸类' => '鹹類',
'咸食' => '鹹食',
'咸鱼' => '鹹魚',
@@ -9242,16 +9245,17 @@ $zh2Hant = array(
'盐余' => '鹽餘',
'鹿場里' => '鹿場里',
'丽于' => '麗於',
+'麟游' => '麟遊',
+'曲酒' => '麯酒',
'曲尘' => '麴塵',
'曲櫱' => '麴櫱',
'曲秀才' => '麴秀才',
'曲车' => '麴車',
'曲道士' => '麴道士',
'曲钱' => '麴錢',
-'麹霉' => '麴黴',
'曲霉' => '麴黴',
+'麹霉' => '麴黴',
'面人儿' => '麵人兒',
-'面价' => '麵價',
'面包' => '麵包',
'面坊' => '麵坊',
'面坯儿' => '麵坯兒',
@@ -9263,7 +9267,6 @@ $zh2Hant = array(
'面条' => '麵條',
'面汤' => '麵湯',
'面浆' => '麵漿',
-'面灰' => '麵灰',
'面疙瘩' => '麵疙瘩',
'面皮' => '麵皮',
'面码儿' => '麵碼兒',
@@ -9273,15 +9276,23 @@ $zh2Hant = array(
'面团' => '麵糰',
'面缸' => '麵缸',
'面茶' => '麵茶',
+'面制品' => '麵製品',
'面食' => '麵食',
'面饺' => '麵餃',
'面饼' => '麵餅',
'面馆' => '麵館',
+'面点、' => '麵點、',
+'面点师' => '麵點師',
'麻将席' => '麻將蓆',
'麻酱面' => '麻醬麵',
'黄干黑瘦' => '黃乾黑瘦',
+'黄岩区' => '黃巖區',
+'黄岩县' => '黃巖縣',
'黄历' => '黃曆',
+'黃杰' => '黃杰',
+'黄杰' => '黃杰',
'黄历史' => '黃歷史',
+'黄白术' => '黃白術',
'黃詩杰' => '黃詩杰',
'黄诗杰' => '黃詩杰',
'黄金表' => '黃金表',
@@ -9291,14 +9302,18 @@ $zh2Hant = array(
'黄发' => '黃髮',
'黄曲毒素' => '黃麴毒素',
'黎克特制' => '黎克特制',
-'黎吉雲' => '黎吉雲',
'黎吉云' => '黎吉雲',
+'黎吉雲' => '黎吉雲',
'黑奴吁天录' => '黑奴籲天錄',
+'黑干将' => '黑幹將',
+'黑长发' => '黑長髮',
'黑发' => '黑髮',
+'点个赞' => '點個讚',
'点札' => '點劄',
'点半钟' => '點半鐘',
'点多钟' => '點多鐘',
'点里' => '點裡',
+'点赞' => '點讚',
'点里程' => '點里程',
'点钟' => '點鐘',
'霉毒' => '黴毒',
@@ -9309,6 +9324,7 @@ $zh2Hant = array(
'鼓里' => '鼓裡',
'鼓噪' => '鼓譟',
'冬冬鼓' => '鼕鼕鼓',
+'咚咚鼓' => '鼕鼕鼓',
'鼠曲草' => '鼠麴草',
'鼻梁儿' => '鼻梁兒',
'鼻梁' => '鼻樑',
@@ -9318,6 +9334,7 @@ $zh2Hant = array(
'齿落发白' => '齒落髮白',
'齿发' => '齒髮',
'出儿' => '齣兒',
+'龙岩' => '龍巖',
'龙卷' => '龍捲',
'龙眼干' => '龍眼乾',
'龙须' => '龍鬚',
@@ -9325,13 +9342,13 @@ $zh2Hant = array(
'龙斗虎伤' => '龍鬥虎傷',
'龜山庄' => '龜山庄',
'龟鉴' => '龜鑑',
+',个中' => ',箇中',
);
$zh2Hans = array(
'㑯' => '㑔',
'㑳' => '㑇',
'㑶' => '㐹',
-'㑺' => '俊',
'㒓' => '𠉂',
'㒺' => '罔',
'㓂' => '寇',
@@ -9359,10 +9376,8 @@ $zh2Hans = array(
'㢝' => '𢋈',
'㤙' => '恩',
'㥦' => '惬',
-'㥫' => '惇',
'㥮' => '㤘',
'㦎' => '𢛯',
-'㧱' => '拿',
'㨗' => '捷',
'㨪' => '晃',
'㨿' => '据',
@@ -9489,7 +9504,6 @@ $zh2Hans = array(
'來' => '来',
'侖' => '仑',
'侶' => '侣',
-'侷' => '局',
'俁' => '俣',
'係' => '系',
'俔' => '伣',
@@ -9766,7 +9780,6 @@ $zh2Hans = array(
'執' => '执',
'堅' => '坚',
'堊' => '垩',
-'堖' => '垴',
'堝' => '埚',
'堯' => '尧',
'報' => '报',
@@ -9776,7 +9789,6 @@ $zh2Hans = array(
'塏' => '垲',
'塒' => '埘',
'塗' => '涂',
-'塚' => '冢',
'塟' => '葬',
'塢' => '坞',
'塤' => '埙',
@@ -9933,7 +9945,6 @@ $zh2Hans = array(
'嶧' => '峄',
'嶨' => '峃',
'嶮' => '崄',
-'嶴' => '岙',
'嶸' => '嵘',
'嶺' => '岭',
'嶼' => '屿',
@@ -10075,7 +10086,6 @@ $zh2Hans = array(
'應' => '应',
'懌' => '怿',
'懍' => '懔',
-'懞' => '蒙',
'懟' => '怼',
'懣' => '懑',
'懨' => '恹',
@@ -10281,8 +10291,6 @@ $zh2Hans = array(
'棧' => '栈',
'棲' => '栖',
'棶' => '梾',
-'椀' => '碗',
-'椉' => '乘',
'椏' => '桠',
'椗' => '碇',
'椲' => '㭏',
@@ -10446,7 +10454,6 @@ $zh2Hans = array(
'湞' => '浈',
'湧' => '涌',
'湯' => '汤',
-'湻' => '淳',
'湼' => '涅',
'溈' => '沩',
'準' => '准',
@@ -10550,7 +10557,6 @@ $zh2Hans = array(
'灙' => '𣺼',
'灝' => '灏',
'灠' => '漤',
-'灡' => '㳕',
'灣' => '湾',
'灤' => '滦',
'灧' => '滟',
@@ -10794,7 +10800,6 @@ $zh2Hans = array(
'矚' => '瞩',
'矯' => '矫',
'砲' => '炮',
-'硃' => '朱',
'硜' => '硁',
'硤' => '硖',
'硨' => '砗',
@@ -11334,7 +11339,6 @@ $zh2Hans = array(
'薟' => '莶',
'薦' => '荐',
'薩' => '萨',
-'薳' => '䓕',
'薴' => '苧',
'薺' => '荠',
'藍' => '蓝',
@@ -11426,7 +11430,6 @@ $zh2Hans = array(
'衊' => '蔑',
'術' => '术',
'衕' => '同',
-'衖' => '弄',
'衚' => '胡',
'衛' => '卫',
'衝' => '冲',
@@ -11489,7 +11492,6 @@ $zh2Hans = array(
'覽' => '览',
'覿' => '觌',
'觀' => '观',
-'觔' => '斤',
'觝' => '抵',
'觴' => '觞',
'觶' => '觯',
@@ -11920,7 +11922,6 @@ $zh2Hans = array(
'適' => '适',
'遯' => '遁',
'遲' => '迟',
-'遶' => '绕',
'遷' => '迁',
'選' => '选',
'遺' => '遗',
@@ -12023,6 +12024,7 @@ $zh2Hans = array(
'鈿' => '钿',
'鉀' => '钾',
'鉁' => '𨱅',
+'鉄' => '铁',
'鉅' => '钜',
'鉆' => '钻',
'鉈' => '铊',
@@ -12380,7 +12382,6 @@ $zh2Hans = array(
'靭' => '韧',
'靱' => '韧',
'鞀' => '鼗',
-'鞌' => '鞍',
'鞏' => '巩',
'鞝' => '绱',
'鞦' => '秋',
@@ -12528,6 +12529,7 @@ $zh2Hans = array(
'餭' => '𫗮',
'餱' => '糇',
'餳' => '饧',
+'餵' => '喂',
'餶' => '馉',
'餷' => '馇',
'餸' => '𩠌',
@@ -12582,7 +12584,6 @@ $zh2Hans = array(
'駧' => '𩧲',
'駩' => '𩧴',
'駭' => '骇',
-'駮' => '驳',
'駰' => '骃',
'駱' => '骆',
'駶' => '𩧺',
@@ -12621,7 +12622,7 @@ $zh2Hans = array(
'驁' => '骜',
'驂' => '骖',
'驃' => '骠',
-'驄' => '𩨂',
+'驄' => '骢',
'驅' => '驱',
'驊' => '骅',
'驋' => '𩧯',
@@ -12649,10 +12650,8 @@ $zh2Hans = array(
'體' => '体',
'髕' => '髌',
'髖' => '髋',
-'髣' => '仿',
'髥' => '髯',
'髮' => '发',
-'髴' => '佛',
'鬀' => '剃',
'鬆' => '松',
'鬉' => '鬃',
@@ -12665,7 +12664,6 @@ $zh2Hans = array(
'鬨' => '哄',
'鬩' => '阋',
'鬪' => '斗',
-'鬭' => '斗',
'鬮' => '阄',
'鬰' => '郁',
'鬱' => '郁',
@@ -12944,7 +12942,7 @@ $zh2Hans = array(
'鼇' => '鳌',
'鼈' => '鳖',
'鼉' => '鼍',
-'鼕' => '冬',
+'鼕' => '咚',
'鼴' => '鼹',
'齊' => '齐',
'齋' => '斋',
@@ -12977,43 +12975,30 @@ $zh2Hans = array(
'龜' => '龟',
'龭' => '𩨎',
'龯' => '𨱆',
-'𠇮' => '命',
-'𠌂' => '伞',
'𠌥' => '𠆿',
'𠏢' => '𠉗',
'𠕂' => '再',
'𠕅' => '再',
-'𠖇' => '冥',
'𠞆' => '𠛆',
'𠞰' => '剿',
'𠠎' => '𠚳',
-'𠪾' => '历',
-'𠴟' => '咩',
-'𠻳' => '嗽',
'𡄔' => '𠴢',
'𡄣' => '𠵸',
'𡅏' => '𠲥',
-'𡐨' => '野',
'𡑭' => '𡋗',
'𡓾' => '𡋀',
'𡚁' => '弊',
'𡞵' => '㛟',
'𡠹' => '㛿',
'𡢃' => '㛠',
-'𡨘' => '冤',
'𡨥' => '寇',
-'𡬶' => '寻',
'𡮉' => '𡭜',
'𡮣' => '𡭬',
'𡻕' => '岁',
'𡾱' => '㟜',
'𢣚' => '𢘝',
'𢣭' => '𢘞',
-'𢬸' => '括',
-'𢭏' => '捣',
-'𢮥' => '操',
'𢶫' => '𢫞',
-'𢷬' => '捣',
'𢷮' => '𢫊',
'𢹿' => '𢬦',
'𣙎' => '㭣',
@@ -13030,20 +13015,15 @@ $zh2Hans = array(
'𤨏' => '琐',
'𤪺' => '㻘',
'𤫩' => '㻏',
-'𤰜' => '亩',
'𤱈' => '亩',
-'𤱊' => '留',
'𤳸' => '𤳄',
'𤸫' => '𤶧',
'𤺥' => '瘩',
-'𥄨' => '瞅',
'𥌃' => '𥅘',
'𥕥' => '𥐰',
'𥖅' => '𥐯',
'𥢢' => '䅪',
-'𥦗' => '窗',
'𥨐' => '𥧂',
-'𥲻' => '纂',
'𥵃' => '𥱔',
'𥵊' => '𥭉',
'𥸠' => '𥮋',
@@ -13051,16 +13031,12 @@ $zh2Hans = array(
'𥽖' => '𥺇',
'𥿊' => '𦈈',
'𦂅' => '𦈒',
-'𦂳' => '紧',
-'𦃂' => '紧',
'𦃄' => '𦈗',
-'𦉆' => '碴',
'𦊱' => '挂',
'𦍑' => '羌',
'𦕈' => '眇',
'𦢈' => '𣍨',
'𦣎' => '𦟗',
-'𦪙' => '䑽',
'𦪽' => '𦨩',
'𦵏' => '葬',
'𧔥' => '𧒭',
@@ -13182,22 +13158,21 @@ $zh2Hans = array(
'』' => '’',
'「' => '“',
'「' => '“',
-'」' => '”',
'」' => '”',
+'」' => '”',
'。陞' => '。升',
'《易乾' => '《易乾',
'一釐' => '一厘',
-'一口鍾' => '一口钟',
-'一鍾' => '一钟',
'上昇' => '上升',
+'不穀' => '不穀',
'專著' => '专著',
-'世界鍾' => '世界钟',
-'喪鍾' => '丧钟',
'乾一坛' => '乾一坛',
'乾一壇' => '乾一坛',
-'乾一组' => '乾一组',
'乾一組' => '乾一组',
+'乾一组' => '乾一组',
'乾上乾下' => '乾上乾下',
+'乾东' => '乾东',
+'乾東' => '乾东',
'乾為天' => '乾为天',
'乾為陽' => '乾为阳',
'乾九' => '乾九',
@@ -13212,27 +13187,27 @@ $zh2Hans = array(
'乾光' => '乾光',
'乾兴' => '乾兴',
'乾興' => '乾兴',
-'乾岡' => '乾冈',
'乾冈' => '乾冈',
+'乾岡' => '乾冈',
'乾刘' => '乾刘',
'乾劉' => '乾刘',
'乾刚' => '乾刚',
'乾剛' => '乾刚',
-'乾務' => '乾务',
'乾务' => '乾务',
+'乾務' => '乾务',
'乾化' => '乾化',
'乾卦' => '乾卦',
'乾县' => '乾县',
'乾縣' => '乾县',
'乾台' => '乾台',
'乾吉' => '乾吉',
-'乾啟' => '乾启',
'乾启' => '乾启',
+'乾啟' => '乾启',
'乾命' => '乾命',
'乾和' => '乾和',
'乾嘉' => '乾嘉',
-'乾圖' => '乾图',
'乾图' => '乾图',
+'乾圖' => '乾图',
'乾坤' => '乾坤',
'乾城' => '乾城',
'乾基' => '乾基',
@@ -13251,9 +13226,8 @@ $zh2Hans = array(
'乾崗' => '乾岗',
'乾巛' => '乾巛',
'乾州' => '乾州',
-'乾式' => '乾式',
-'乾錄' => '乾录',
'乾录' => '乾录',
+'乾錄' => '乾录',
'乾律' => '乾律',
'乾德' => '乾德',
'乾心' => '乾心',
@@ -13266,17 +13240,17 @@ $zh2Hans = array(
'乾旦' => '乾旦',
'乾明' => '乾明',
'乾昧' => '乾昧',
-'乾暉' => '乾晖',
'乾晖' => '乾晖',
+'乾暉' => '乾晖',
'乾景' => '乾景',
'乾晷' => '乾晷',
'乾曜' => '乾曜',
-'乾構' => '乾构',
'乾构' => '乾构',
+'乾構' => '乾构',
'乾枢' => '乾枢',
'乾樞' => '乾枢',
-'乾棟' => '乾栋',
'乾栋' => '乾栋',
+'乾棟' => '乾栋',
'乾步' => '乾步',
'乾氏' => '乾氏',
'乾沓和' => '乾沓和',
@@ -13288,6 +13262,7 @@ $zh2Hans = array(
'乾潭' => '乾潭',
'乾灵' => '乾灵',
'乾靈' => '乾灵',
+'乾生元' => '乾生元',
'乾男' => '乾男',
'乾皋' => '乾皋',
'乾盛世' => '乾盛世',
@@ -13295,8 +13270,8 @@ $zh2Hans = array(
'乾祐' => '乾祐',
'乾神' => '乾神',
'乾穹' => '乾穹',
-'乾竇' => '乾窦',
'乾窦' => '乾窦',
+'乾竇' => '乾窦',
'乾竺' => '乾竺',
'乾笃' => '乾笃',
'乾篤' => '乾笃',
@@ -13307,14 +13282,14 @@ $zh2Hans = array(
'乾红' => '乾红',
'乾綱' => '乾纲',
'乾纲' => '乾纲',
-'乾纽' => '乾纽',
'乾紐' => '乾纽',
-'乾络' => '乾络',
+'乾纽' => '乾纽',
'乾絡' => '乾络',
+'乾络' => '乾络',
'乾統' => '乾统',
'乾统' => '乾统',
-'乾维' => '乾维',
'乾維' => '乾维',
+'乾维' => '乾维',
'乾罗' => '乾罗',
'乾羅' => '乾罗',
'乾花' => '乾花',
@@ -13325,18 +13300,18 @@ $zh2Hans = array(
'乾西' => '乾西',
'乾覆' => '乾覆',
'乾象' => '乾象',
-'乾象歷' => '乾象历',
'乾象历' => '乾象历',
+'乾象歷' => '乾象历',
'乾貞' => '乾贞',
'乾贞' => '乾贞',
-'乾贵士' => '乾贵士',
'乾貴士' => '乾贵士',
+'乾贵士' => '乾贵士',
'乾貺' => '乾贶',
'乾贶' => '乾贶',
'乾車' => '乾车',
'乾车' => '乾车',
-'乾轴' => '乾轴',
'乾軸' => '乾轴',
+'乾轴' => '乾轴',
'乾通' => '乾通',
'乾造' => '乾造',
'乾道' => '乾道',
@@ -13355,25 +13330,27 @@ $zh2Hans = array(
'乾風' => '乾风',
'乾风' => '乾风',
'乾首' => '乾首',
-'乾马' => '乾马',
'乾馬' => '乾马',
+'乾马' => '乾马',
'乾鵠' => '乾鹄',
'乾鹄' => '乾鹄',
'乾鵲' => '乾鹊',
'乾鹊' => '乾鹊',
-'乾龙' => '乾龙',
'乾龍' => '乾龙',
+'乾龙' => '乾龙',
'乾,健也' => '乾,健也',
'乾,天也' => '乾,天也',
'五箇山' => '五箇山',
+'什么' => '什么',
'仇讎' => '仇雠',
'以微知著' => '以微知著',
-'以莛叩鍾' => '以莛叩钟',
'仰屋著書' => '仰屋著书',
'彷彿' => '仿佛',
'夥計' => '伙计',
'佛頭著糞' => '佛头著粪',
'偵蒐' => '侦搜',
+'倖一郎' => '倖一郎',
+'倖田' => '倖田',
'候覆' => '候复',
'藉助' => '借助',
'藉口' => '借口',
@@ -13384,19 +13361,18 @@ $zh2Hans = array(
'藉由' => '借由',
'藉端' => '借端',
'藉詞' => '借词',
+'傒倖' => '傒倖',
'先名後姓' => '先名后姓',
+'兒寬' => '兒宽',
'六么' => '六幺',
'蘭質薰心' => '兰质薰心',
'內聯陞' => '内联升',
'憑藉' => '凭借',
-'分鍾' => '分钟',
'初昇' => '初升',
'利欲薰心' => '利欲薰心',
-'刻鍾' => '刻钟',
'剋了' => '剋了',
'剋架' => '剋架',
'剖釐' => '剖厘',
-'千鍾' => '千钟',
'陞為' => '升为',
'陞了' => '升了',
'昇仙' => '升仙',
@@ -13412,7 +13388,6 @@ $zh2Hans = array(
'昇降' => '升降',
'卓著' => '卓著',
'博和託' => '博和讬',
-'卷舌' => '卷舌',
'歷陞' => '历升',
'釐改' => '厘改',
'釐整' => '厘整',
@@ -13421,14 +13396,11 @@ $zh2Hans = array(
'釐清' => '厘清',
'釐訂' => '厘订',
'釐革' => '厘革',
-'原子鍾' => '原子钟',
'原著' => '原著',
'又陞' => '又升',
'反反覆覆' => '反反复复',
'反覆' => '反复',
-'古鍾' => '古钟',
'可穿著' => '可穿著',
-'台鍾' => '台钟',
'吃衣著飯' => '吃衣著饭',
'合著' => '合著',
'同陞和' => '同升和',
@@ -13439,41 +13411,40 @@ $zh2Hans = array(
'回覆' => '回复',
'土著' => '土著',
'坤乾' => '坤乾',
-'塔鍾' => '塔钟',
'墨瀋' => '墨渖',
-'壁鍾' => '壁钟',
'覆查' => '复查',
'覆核' => '复核',
'覆检' => '复检',
'復甦' => '复苏',
-'多鍾' => '多钟',
+'多么' => '多么',
'大麴' => '大曲',
-'大鍾' => '大钟',
-'天道為乾' => '天道为乾',
'天道为乾' => '天道为乾',
+'天道為乾' => '天道为乾',
'奧區' => '奧区',
'如瀋' => '如渖',
'姓么' => '姓幺',
'子餘' => '子馀',
'字乾生' => '字乾生',
-'孫乾' => '孙乾',
'孙乾' => '孙乾',
-'宋鍾國' => '宋钟国',
+'孫乾' => '孙乾',
'宏碁' => '宏碁',
'官陞' => '官升',
'將軍抽俥' => '将军抽俥',
'將軍抽車' => '将军抽車',
'爾冬陞' => '尔冬升',
'尼乾陀' => '尼乾陀',
+'侷促' => '局促',
'跼促' => '局促',
+'侷限' => '局限',
'跼限' => '局限',
-'山崩鍾應' => '山崩钟应',
-'崔秀鍾' => '崔秀钟',
+'山崎闇齋' => '山崎闇斋',
+'岳託' => '岳讬',
'巨著' => '巨著',
'乾乾淨淨' => '干干净净',
'乾乾脆脆' => '干干脆脆',
'乾泉水' => '干泉水',
'年陞' => '年升',
+'么九' => '幺九',
'么二三' => '幺二三',
'么元' => '幺元',
'么鳳' => '幺凤',
@@ -13482,15 +13453,18 @@ $zh2Hans = array(
'么廝' => '幺厮',
'幺厮' => '幺厮',
'么叔' => '幺叔',
+'么女' => '幺女',
'么媽' => '幺妈',
'么妹' => '幺妹',
'么姓' => '幺姓',
'么姨' => '幺姨',
'么娘' => '幺娘',
-'幺孃' => '幺娘',
'么孃' => '幺娘',
+'幺孃' => '幺娘',
+'么子' => '幺子',
'么小' => '幺小',
'么弟' => '幺弟',
+'么正' => '幺正',
'么氏' => '幺氏',
'么爸' => '幺爸',
'么爹' => '幺爹',
@@ -13498,22 +13472,20 @@ $zh2Hans = array(
'么舅' => '幺舅',
'么蛾子' => '幺蛾子',
'么謙' => '幺谦',
-'么麽' => '幺麽',
'么麼' => '幺麽',
+'么麽' => '幺麽',
'么麽小丑' => '幺麽小丑',
'慶餘' => '庆馀',
-'座鍾' => '座钟',
'康乾' => '康乾',
-'張法乾' => '张法乾',
'张法乾' => '张法乾',
-'張鍾英' => '张钟英',
+'張法乾' => '张法乾',
'彰明較著' => '彰明较著',
'待覆' => '待复',
'後姓' => '後姓',
'慫慂' => '怂恿',
+'怎么' => '怎么',
'恩威並著' => '恩威并著',
'噁心' => '恶心',
-'懸鍾' => '悬钟',
'情蒐' => '情搜',
'情鍾' => '情钟',
'惏悷' => '惏悷',
@@ -13526,6 +13498,7 @@ $zh2Hans = array(
'扞格' => '扞格',
'執著' => '执著',
'批覆' => '批复',
+'承乾' => '承乾',
'拉鍊' => '拉链',
'拙著' => '拙著',
'拚命' => '拚命',
@@ -13533,9 +13506,7 @@ $zh2Hans = array(
'拚死' => '拚死',
'拾瀋' => '拾渖',
'拿破崙' => '拿破仑',
-'掛鍾' => '挂钟',
'挨剋' => '挨剋',
-'掩耳盜鍾' => '掩耳盗钟',
'提昇' => '提升',
'蒐錄' => '搜录',
'蒐索' => '搜索',
@@ -13544,17 +13515,15 @@ $zh2Hans = array(
'蒐證' => '搜证',
'蒐購' => '搜购',
'蒐輯' => '搜辑',
-'蒐采' => '搜采',
'蒐採' => '搜采',
+'蒐采' => '搜采',
'蒐集' => '搜集',
'搥打' => '搥打',
'搥胸頓足' => '搥胸顿足',
-'擺鍾' => '摆钟',
-'撞鍾' => '撞钟',
'撰著' => '撰著',
'效果顯著' => '效果显著',
-'敲鍾' => '敲钟',
'文徵明' => '文徵明',
+'觔斗' => '斤斗',
'新著' => '新著',
'於世成' => '於世成',
'於之瑩' => '於之莹',
@@ -13572,10 +13541,10 @@ $zh2Hans = array(
'於志賀' => '於志贺',
'於志贺' => '於志贺',
'於戲' => '於戏',
-'於梨華' => '於梨华',
'於梨华' => '於梨华',
+'於梨華' => '於梨华',
'於氏' => '於氏',
-'於潜县' => '於潜县',
+'於潜' => '於潜',
'於潛縣' => '於潜县',
'於祥玉' => '於祥玉',
'於菟' => '於菟',
@@ -13583,7 +13552,6 @@ $zh2Hans = array(
'於除鞬' => '於除鞬',
'旋乾轉坤' => '旋乾转坤',
'旋乾转坤' => '旋乾转坤',
-'時鍾' => '时钟',
'曠若發矇' => '旷若发矇',
'崑崙' => '昆仑',
'崑劇' => '昆剧',
@@ -13593,10 +13561,10 @@ $zh2Hans = array(
'崑蘇' => '昆苏',
'崑調' => '昆调',
'易·乾' => '易·乾',
-'易经·乾' => '易经·乾',
'易經·乾' => '易经·乾',
-'易经乾' => '易经乾',
+'易经·乾' => '易经·乾',
'易經乾' => '易经乾',
+'易经乾' => '易经乾',
'昭著' => '昭著',
'顯著' => '显著',
'顯著地' => '显著地',
@@ -13606,17 +13574,17 @@ $zh2Hans = array(
'顯著效果' => '显著效果',
'顯著特點' => '显著特点',
'晉陞' => '晋升',
-'晚鍾' => '晚钟',
-'晨鍾' => '晨钟',
'暗闇' => '暗闇',
'麴黴' => '曲霉',
-'曾運乾' => '曾运乾',
'曾运乾' => '曾运乾',
+'曾運乾' => '曾运乾',
'月陞' => '月升',
'朝乾夕惕' => '朝乾夕惕',
-'朝鍾暮鼓' => '朝钟暮鼓',
'朱有燉' => '朱有燉',
'朱淛' => '朱淛',
+'硃砂' => '朱砂',
+'硃紅' => '朱红',
+'硃色' => '朱色',
'朴於宇同' => '朴於宇同',
'李乾德' => '李乾德',
'李乾順' => '李乾顺',
@@ -13624,18 +13592,14 @@ $zh2Hans = array(
'李澤鉅' => '李泽钜',
'李祕' => '李祕',
'李譔' => '李譔',
-'李鍾原' => '李钟原',
-'林鍾' => '林钟',
-'柳诒徵' => '柳诒徵',
'柳詒徵' => '柳诒徵',
+'柳诒徵' => '柳诒徵',
'校讎' => '校雠',
'楈枒' => '楈枒',
'樊於期' => '樊於期',
-'橡椀' => '橡椀',
-'此鍾' => '此钟',
'殘瀋' => '残渖',
-'慇懃' => '殷勤',
'慇勤' => '殷勤',
+'慇懃' => '殷勤',
'比較顯著' => '比较显著',
'毫釐' => '毫厘',
'氆氌' => '氆氌',
@@ -13647,9 +13611,7 @@ $zh2Hans = array(
'沈默' => '沉默',
'氾濫' => '泛滥',
'洗鍊' => '洗练',
-'洪鍾' => '洪钟',
'瀋液' => '渖液',
-'點鍾' => '点钟',
'薰習' => '熏习',
'薰心' => '熏心',
'薰沐' => '熏沐',
@@ -13660,36 +13622,34 @@ $zh2Hans = array(
'王道乾' => '王道乾',
'王餘魚' => '王馀鱼',
'甚夥' => '甚夥',
-'生物鍾' => '生物钟',
-'電鍾' => '电钟',
-'男為乾' => '男为乾',
'男为乾' => '男为乾',
-'男爲乾' => '男为乾',
-'男性爲乾' => '男性为乾',
-'男性為乾' => '男性为乾',
+'男為乾' => '男为乾',
'男性为乾' => '男性为乾',
+'男性為乾' => '男性为乾',
'療效顯著' => '疗效显著',
'白瀋' => '白渖',
'皁保' => '皁保',
'目劄' => '目劄',
'直昇' => '直升',
'睹微知著' => '睹微知著',
-'瞭臺' => '瞭台',
'瞭台' => '瞭台',
+'瞭臺' => '瞭台',
'瞭望' => '瞭望',
'矇眬' => '矇眬',
'矇矓' => '矇眬',
'石碁' => '石碁',
'石碁鎮' => '石碁镇',
-'石英鍾' => '石英钟',
-'石鍾乳' => '石钟乳',
+'碩託' => '硕讬',
'鹼菜' => '硷菜',
-'碁聖' => '碁圣',
'碁圣' => '碁圣',
+'碁聖' => '碁圣',
'碁所' => '碁所',
'祕宜' => '祕宜',
-'秒鍾' => '秒钟',
+'穀旦' => '穀旦',
'穀梁' => '穀梁',
+'穀水' => '穀水',
+'穀阳' => '穀阳',
+'穀陽' => '穀阳',
'穿著者' => '穿着者',
'竹昇' => '竹升',
'答覆' => '答复',
@@ -13697,10 +13657,11 @@ $zh2Hans = array(
'米瀋' => '米渖',
'餬口' => '糊口',
'繙㠾' => '繙㠾',
+'遶境' => '绕境',
'線國安' => '缐国安',
'線姓' => '缐姓',
'編著' => '编著',
-'編鍾' => '编钟',
+'老么' => '老幺',
'肉乾乾' => '肉干干',
'肘手鍊足' => '肘手链足',
'甦醒' => '苏醒',
@@ -13723,14 +13684,19 @@ $zh2Hans = array(
'著者' => '著者',
'著身' => '著身',
'著述' => '著述',
-'覆蓋' => '覆蓋',
+'蔡孝乾' => '蔡孝乾',
+'蔡絛' => '蔡絛',
+'行餘' => '行馀',
+'覆蓋' => '覆盖',
'見微知著' => '见微知著',
'見著' => '见著',
'視微知著' => '视微知著',
'言幾析理' => '言幾析理',
'諲譔' => '諲譔',
-'警鍾' => '警钟',
'譩譆' => '譩譆',
+'託庸' => '讬庸',
+'託恩多' => '讬恩多',
+'託麻' => '讬麻',
'論著' => '论著',
'譯著' => '译著',
'謝肇淛' => '谢肇淛',
@@ -13739,12 +13705,11 @@ $zh2Hans = array(
'較著' => '较著',
'近角聪信' => '近角聪信',
'这么' => '这么',
-'進化鍾' => '进化钟',
'造麴' => '造曲',
'遺著' => '遗著',
+'那么' => '那么',
'那麽' => '那麽',
'郭子乾' => '郭子乾',
-'郭行餘' => '郭行馀',
'酒麴' => '酒曲',
'醉瀋' => '醉渖',
'醯壶' => '醯壶',
@@ -13753,48 +13718,17 @@ $zh2Hans = array(
'醯醬' => '醯酱',
'醯醋' => '醯醋',
'醯醢' => '醯醢',
-'醯鸡' => '醯鸡',
'醯雞' => '醯鸡',
+'醯鸡' => '醯鸡',
'重覆' => '重复',
-'金尚鍾' => '金尚钟',
-'金民鍾' => '金民钟',
-'金鍾' => '金钟',
'金鍊' => '金链',
-'鍾麗緹' => '钟丽缇',
-'鍾乳石' => '钟乳石',
-'鍾儀奏楚' => '钟仪奏楚',
-'鍾關' => '钟关',
-'鍾聲' => '钟声',
-'鍾頭' => '钟头',
-'鍾山' => '钟山',
-'鍾差' => '钟差',
-'鍾座' => '钟座',
'鍾情' => '钟情',
'鍾意' => '钟意',
-'鍾慧冰' => '钟慧冰',
-'鍾擺' => '钟摆',
-'鍾架' => '钟架',
-'鍾楚紅' => '钟楚红',
-'鍾樓' => '钟楼',
-'鍾漢良' => '钟汉良',
-'鍾汶' => '钟汶',
-'鍾淑慧' => '钟淑慧',
'鍾靈' => '钟灵',
-'鍾點' =&g