Merge "Add tests for WikiMap and WikiReference"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 14 Sep 2015 10:12:37 +0000 (10:12 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 14 Sep 2015 10:12:37 +0000 (10:12 +0000)
814 files changed:
.jscsrc
.travis.yml
Gruntfile.js
RELEASE-NOTES-1.26
autoload.php
composer.json
docs/extension.schema.json
includes/DefaultSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/Hooks.php
includes/HtmlFormatter.php
includes/HttpFunctions.php
includes/MagicWord.php
includes/OutputPage.php
includes/Preferences.php
includes/PrefixSearch.php
includes/Sanitizer.php
includes/Setup.php
includes/StubObject.php
includes/Title.php
includes/User.php
includes/ZhConversion.php
includes/api/ApiBase.php
includes/api/ApiFormatRaw.php
includes/api/ApiLogin.php
includes/api/ApiMain.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryRandom.php
includes/api/ApiQueryUserInfo.php
includes/api/ApiResult.php
includes/api/i18n/ar.json
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/fi.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/ksh.json
includes/api/i18n/lb.json
includes/api/i18n/mr.json [new file with mode: 0644]
includes/api/i18n/olo.json [new file with mode: 0644]
includes/api/i18n/pl.json
includes/api/i18n/ps.json
includes/api/i18n/pt-br.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sq.json [new file with mode: 0644]
includes/api/i18n/zh-hans.json
includes/cache/MessageCache.php
includes/changes/EnhancedChangesList.php
includes/clientpool/RedisConnectionPool.php
includes/debug/logger/LegacyLogger.php
includes/debug/logger/monolog/LineFormatter.php
includes/exception/MWExceptionHandler.php
includes/filebackend/FileBackend.php
includes/filerepo/file/File.php
includes/filerepo/file/LocalFile.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/HTMLTextField.php
includes/installer/LocalSettingsGenerator.php
includes/installer/i18n/ar.json
includes/installer/i18n/it.json
includes/installer/i18n/nap.json
includes/installer/i18n/olo.json [new file with mode: 0644]
includes/installer/i18n/pt-br.json
includes/installer/i18n/pt.json
includes/installer/i18n/qqq.json
includes/installer/i18n/sq.json
includes/installer/i18n/sr-ec.json
includes/installer/i18n/tokipona.json [new file with mode: 0644]
includes/installer/i18n/yi.json
includes/libs/ReplacementArray.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/virtualrest/ParsoidVirtualRESTService.php
includes/media/Bitmap.php
includes/media/DjVuImage.php
includes/media/SVGMetadataExtractor.php
includes/media/XMP.php
includes/media/XMPInfo.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/MemcachedPhpBagOStuff.php
includes/page/ImagePage.php
includes/parser/MWTidy.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/Preprocessor_Hash.php
includes/poolcounter/PoolCounterRedis.php
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/skins/SkinTemplate.php
includes/specials/SpecialLinkSearch.php
includes/specials/SpecialListusers.php
includes/specials/SpecialPagesWithProp.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUserlogin.php
includes/templates/Usercreate.php
includes/tidy.conf [deleted file]
includes/tidy/Html5Depurate.php [new file with mode: 0644]
includes/tidy/RaggettBase.php [new file with mode: 0644]
includes/tidy/RaggettExternal.php [new file with mode: 0644]
includes/tidy/RaggettInternalHHVM.php [new file with mode: 0644]
includes/tidy/RaggettInternalPHP.php [new file with mode: 0644]
includes/tidy/RaggettWrapper.php [new file with mode: 0644]
includes/tidy/TidyDriverBase.php [new file with mode: 0644]
includes/tidy/tidy.conf [new file with mode: 0644]
includes/upload/UploadBase.php
includes/utils/iterators/IteratorDecorator.php
languages/Language.php
languages/i18n/ady-cyrl.json
languages/i18n/af.json
languages/i18n/aln.json
languages/i18n/an.json
languages/i18n/ar.json
languages/i18n/arz.json
languages/i18n/as.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/bcl.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/cv.json
languages/i18n/de-formal.json
languages/i18n/de.json
languages/i18n/dty.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/gom-deva.json
languages/i18n/gom-latn.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/kk-cyrl.json
languages/i18n/kn.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/lad.json
languages/i18n/lb.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mr.json
languages/i18n/mzn.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/olo.json [new file with mode: 0644]
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/scn.json
languages/i18n/sl.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/uz.json
languages/i18n/vec.json
languages/i18n/vi.json
languages/i18n/war.json
languages/i18n/wo.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/i18n/zh-tw.json
languages/messages/MessagesAce.php
languages/messages/MessagesAf.php
languages/messages/MessagesAln.php
languages/messages/MessagesAn.php
languages/messages/MessagesAr.php
languages/messages/MessagesArc.php
languages/messages/MessagesArz.php
languages/messages/MessagesAs.php
languages/messages/MessagesAv.php
languages/messages/MessagesAvk.php
languages/messages/MessagesBa.php
languages/messages/MessagesBcc.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesBg.php
languages/messages/MessagesBgn.php
languages/messages/MessagesBjn.php
languages/messages/MessagesBr.php
languages/messages/MessagesBs.php
languages/messages/MessagesBxr.php
languages/messages/MessagesCa.php
languages/messages/MessagesCdo.php
languages/messages/MessagesCe.php
languages/messages/MessagesCkb.php
languages/messages/MessagesCs.php
languages/messages/MessagesDa.php
languages/messages/MessagesDe.php
languages/messages/MessagesDiq.php
languages/messages/MessagesDsb.php
languages/messages/MessagesEl.php
languages/messages/MessagesEn.php
languages/messages/MessagesEo.php
languages/messages/MessagesEs.php
languages/messages/MessagesEt.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFr.php
languages/messages/MessagesFrp.php
languages/messages/MessagesFur.php
languages/messages/MessagesFy.php
languages/messages/MessagesGag.php
languages/messages/MessagesGl.php
languages/messages/MessagesGsw.php
languages/messages/MessagesGu.php
languages/messages/MessagesHe.php
languages/messages/MessagesHr.php
languages/messages/MessagesHsb.php
languages/messages/MessagesHt.php
languages/messages/MessagesHu.php
languages/messages/MessagesHy.php
languages/messages/MessagesIa.php
languages/messages/MessagesId.php
languages/messages/MessagesIe.php
languages/messages/MessagesIs.php
languages/messages/MessagesIt.php
languages/messages/MessagesJa.php
languages/messages/MessagesKa.php
languages/messages/MessagesKaa.php
languages/messages/MessagesKk_arab.php
languages/messages/MessagesKk_cyrl.php
languages/messages/MessagesKk_latn.php
languages/messages/MessagesKm.php
languages/messages/MessagesKo.php
languages/messages/MessagesKrc.php
languages/messages/MessagesKsh.php
languages/messages/MessagesKw.php
languages/messages/MessagesLa.php
languages/messages/MessagesLad.php
languages/messages/MessagesLb.php
languages/messages/MessagesLi.php
languages/messages/MessagesLij.php
languages/messages/MessagesLrc.php
languages/messages/MessagesLt.php
languages/messages/MessagesLv.php
languages/messages/MessagesMdf.php
languages/messages/MessagesMg.php
languages/messages/MessagesMin.php
languages/messages/MessagesMk.php
languages/messages/MessagesMl.php
languages/messages/MessagesMr.php
languages/messages/MessagesMs.php
languages/messages/MessagesMt.php
languages/messages/MessagesMyv.php
languages/messages/MessagesNb.php
languages/messages/MessagesNds.php
languages/messages/MessagesNds_nl.php
languages/messages/MessagesNl.php
languages/messages/MessagesNn.php
languages/messages/MessagesOc.php
languages/messages/MessagesOr.php
languages/messages/MessagesOs.php
languages/messages/MessagesPa.php
languages/messages/MessagesPl.php
languages/messages/MessagesPs.php
languages/messages/MessagesPt.php
languages/messages/MessagesPt_br.php
languages/messages/MessagesQu.php
languages/messages/MessagesRo.php
languages/messages/MessagesRu.php
languages/messages/MessagesRue.php
languages/messages/MessagesSa.php
languages/messages/MessagesScn.php
languages/messages/MessagesSd.php
languages/messages/MessagesSdc.php
languages/messages/MessagesSe.php
languages/messages/MessagesSh.php
languages/messages/MessagesSi.php
languages/messages/MessagesSk.php
languages/messages/MessagesSl.php
languages/messages/MessagesSq.php
languages/messages/MessagesSr_ec.php
languages/messages/MessagesSu.php
languages/messages/MessagesSv.php
languages/messages/MessagesSw.php
languages/messages/MessagesTe.php
languages/messages/MessagesTh.php
languages/messages/MessagesTl.php
languages/messages/MessagesTr.php
languages/messages/MessagesTt_cyrl.php
languages/messages/MessagesUk.php
languages/messages/MessagesUr.php
languages/messages/MessagesVec.php
languages/messages/MessagesVi.php
languages/messages/MessagesYi.php
languages/messages/MessagesYue.php
languages/messages/MessagesZh.php
languages/messages/MessagesZh_hans.php
languages/messages/MessagesZh_hant.php
maintenance/getConfiguration.php
maintenance/interwiki.list
maintenance/interwiki.sql
maintenance/jsduck/categories.json
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/language/zhtable/tradphrases_exclude.manual
maintenance/namespaceDupes.php
maintenance/refreshLinks.php
maintenance/tables.sql
mw-config/config.js
mw-config/images/help-question-hover.gif [new file with mode: 0644]
mw-config/images/help-question.gif [new file with mode: 0644]
package.json
phpcs.xml
resources/Resources.php
resources/lib/oojs-ui/i18n/ar.json
resources/lib/oojs-ui/i18n/ast.json
resources/lib/oojs-ui/i18n/bn.json
resources/lib/oojs-ui/i18n/de.json
resources/lib/oojs-ui/i18n/en-ca.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/en.json
resources/lib/oojs-ui/i18n/es.json
resources/lib/oojs-ui/i18n/et.json
resources/lib/oojs-ui/i18n/eu.json
resources/lib/oojs-ui/i18n/fa.json
resources/lib/oojs-ui/i18n/fi.json
resources/lib/oojs-ui/i18n/fr.json
resources/lib/oojs-ui/i18n/gl.json
resources/lib/oojs-ui/i18n/glk.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/he.json
resources/lib/oojs-ui/i18n/ja.json
resources/lib/oojs-ui/i18n/km.json
resources/lib/oojs-ui/i18n/ksh.json
resources/lib/oojs-ui/i18n/lb.json
resources/lib/oojs-ui/i18n/lt.json
resources/lib/oojs-ui/i18n/mk.json
resources/lib/oojs-ui/i18n/nap.json
resources/lib/oojs-ui/i18n/pl.json
resources/lib/oojs-ui/i18n/ps.json
resources/lib/oojs-ui/i18n/pt.json
resources/lib/oojs-ui/i18n/qqq.json
resources/lib/oojs-ui/i18n/ro.json
resources/lib/oojs-ui/i18n/roa-tara.json
resources/lib/oojs-ui/i18n/ru.json
resources/lib/oojs-ui/i18n/sv.json
resources/lib/oojs-ui/i18n/uk.json
resources/lib/oojs-ui/i18n/vec.json
resources/lib/oojs-ui/i18n/vi.json
resources/lib/oojs-ui/i18n/zh-hans.json
resources/lib/oojs-ui/i18n/zh-hant.json
resources/lib/oojs-ui/oojs-ui-apex-noimages.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/lib/oojs-ui/themes/apex/images/icons/advanced.png
resources/lib/oojs-ui/themes/apex/images/icons/advanced.svg
resources/lib/oojs-ui/themes/apex/images/icons/arched-arrow-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-arab-ain.png
resources/lib/oojs-ui/themes/apex/images/icons/bold-arab-ain.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-arab-dad.png
resources/lib/oojs-ui/themes/apex/images/icons/bold-arab-dad.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-armn-to.png
resources/lib/oojs-ui/themes/apex/images/icons/bold-armn-to.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-cyrl-be.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-cyrl-zhe.svg
resources/lib/oojs-ui/themes/apex/images/icons/bold-g.png
resources/lib/oojs-ui/themes/apex/images/icons/bold-g.svg
resources/lib/oojs-ui/themes/apex/images/icons/case-sensitive.png
resources/lib/oojs-ui/themes/apex/images/icons/case-sensitive.svg
resources/lib/oojs-ui/themes/apex/images/icons/close.png
resources/lib/oojs-ui/themes/apex/images/icons/close.svg
resources/lib/oojs-ui/themes/apex/images/icons/code.png
resources/lib/oojs-ui/themes/apex/images/icons/code.svg
resources/lib/oojs-ui/themes/apex/images/icons/external-link-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/external-link-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/external-link-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/external-link-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/find-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/find-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/find-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/find-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/help-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/help-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/history.svg
resources/lib/oojs-ui/themes/apex/images/icons/info.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-arab-keheh-jeem.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-arab-keheh-jeem.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-arab-meem.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-arab-meem.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-armn-sha.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-armn-sha.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-c.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-c.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-d.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-geor-kan.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-geor-kan.svg
resources/lib/oojs-ui/themes/apex/images/icons/italic-s.png
resources/lib/oojs-ui/themes/apex/images/icons/italic-s.svg
resources/lib/oojs-ui/themes/apex/images/icons/language.png
resources/lib/oojs-ui/themes/apex/images/icons/language.svg
resources/lib/oojs-ui/themes/apex/images/icons/link.svg
resources/lib/oojs-ui/themes/apex/images/icons/lock.svg
resources/lib/oojs-ui/themes/apex/images/icons/move-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/notice.svg
resources/lib/oojs-ui/themes/apex/images/icons/regular-expression.png
resources/lib/oojs-ui/themes/apex/images/icons/regular-expression.svg
resources/lib/oojs-ui/themes/apex/images/icons/search.svg
resources/lib/oojs-ui/themes/apex/images/icons/secure-link.svg
resources/lib/oojs-ui/themes/apex/images/icons/settings.svg
resources/lib/oojs-ui/themes/apex/images/icons/specialCharacter.png
resources/lib/oojs-ui/themes/apex/images/icons/specialCharacter.svg
resources/lib/oojs-ui/themes/apex/images/icons/strikethrough-s.svg
resources/lib/oojs-ui/themes/apex/images/icons/tag.svg
resources/lib/oojs-ui/themes/apex/images/icons/text-style.svg
resources/lib/oojs-ui/themes/apex/images/icons/unLock-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/unLock-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/underline-u.svg
resources/lib/oojs-ui/themes/apex/images/indicators/alert.svg
resources/lib/oojs-ui/themes/apex/images/indicators/arrow-ltr.svg
resources/lib/oojs-ui/themes/apex/images/indicators/arrow-rtl.svg
resources/lib/oojs-ui/themes/apex/images/indicators/clear.svg
resources/lib/oojs-ui/themes/apex/images/indicators/required.svg
resources/lib/oojs-ui/themes/apex/images/indicators/search-ltr.png
resources/lib/oojs-ui/themes/apex/images/indicators/search-ltr.svg
resources/lib/oojs-ui/themes/apex/images/indicators/search-rtl.png
resources/lib/oojs-ui/themes/apex/images/indicators/search-rtl.svg
resources/lib/oojs-ui/themes/apex/images/textures/transparency.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bright.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/code.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/code.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/halfBright.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/history-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/history.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/largerText-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/largerText-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/moon.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notBright.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smallerText-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smallerText-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-warning.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/textures/transparency.svg
resources/src/jquery/jquery.accessKeyLabel.js
resources/src/jquery/jquery.autoEllipsis.js
resources/src/jquery/jquery.byteLimit.js
resources/src/jquery/jquery.color.js
resources/src/jquery/jquery.colorUtil.js
resources/src/jquery/jquery.expandableField.js
resources/src/jquery/jquery.farbtastic.js
resources/src/jquery/jquery.getAttrs.js
resources/src/jquery/jquery.hidpi.js
resources/src/jquery/jquery.highlightText.js
resources/src/jquery/jquery.localize.js
resources/src/jquery/jquery.mwExtension.js
resources/src/jquery/jquery.placeholder.js
resources/src/jquery/jquery.qunit.completenessTest.js
resources/src/jquery/jquery.spinner.js
resources/src/jquery/jquery.suggestions.js
resources/src/jquery/jquery.tablesorter.js
resources/src/jquery/jquery.textSelection.js
resources/src/mediawiki.action/images/checker.png [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.history.js
resources/src/mediawiki.action/mediawiki.action.view.filepage.css [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.view.filepage.print.css [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.view.metadata.css
resources/src/mediawiki.action/mediawiki.action.view.postEdit.js
resources/src/mediawiki.api/mediawiki.api.edit.js
resources/src/mediawiki.api/mediawiki.api.js
resources/src/mediawiki.api/mediawiki.api.login.js
resources/src/mediawiki.api/mediawiki.api.options.js
resources/src/mediawiki.api/mediawiki.api.parse.js
resources/src/mediawiki.api/mediawiki.api.upload.js
resources/src/mediawiki.api/mediawiki.api.watch.js
resources/src/mediawiki.language/languages/bs.js
resources/src/mediawiki.language/languages/dsb.js
resources/src/mediawiki.language/languages/fi.js
resources/src/mediawiki.language/languages/ga.js
resources/src/mediawiki.language/languages/he.js
resources/src/mediawiki.language/languages/hsb.js
resources/src/mediawiki.language/languages/hu.js
resources/src/mediawiki.language/languages/hy.js
resources/src/mediawiki.language/languages/la.js
resources/src/mediawiki.language/languages/os.js
resources/src/mediawiki.language/languages/ru.js
resources/src/mediawiki.language/languages/sl.js
resources/src/mediawiki.language/languages/uk.js
resources/src/mediawiki.language/mediawiki.cldr.js
resources/src/mediawiki.language/mediawiki.language.init.js
resources/src/mediawiki.language/mediawiki.language.js
resources/src/mediawiki.language/mediawiki.language.numbers.js
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.legacy/images/checker.png [deleted file]
resources/src/mediawiki.legacy/images/question.png [deleted file]
resources/src/mediawiki.legacy/images/question.svg [deleted file]
resources/src/mediawiki.legacy/oldshared.css
resources/src/mediawiki.legacy/protect.js
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.legacy/wikibits.js
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js
resources/src/mediawiki.page/mediawiki.page.gallery.css
resources/src/mediawiki.page/mediawiki.page.gallery.js
resources/src/mediawiki.page/mediawiki.page.image.pagination.js
resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
resources/src/mediawiki.special/mediawiki.special.changeemail.js
resources/src/mediawiki.special/mediawiki.special.changeslist.enhanced.css
resources/src/mediawiki.special/mediawiki.special.preferences.css
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.search.js
resources/src/mediawiki.special/mediawiki.special.upload.js
resources/src/mediawiki.special/mediawiki.special.userlogin.common.js [deleted file]
resources/src/mediawiki.special/mediawiki.special.userlogin.signup.js
resources/src/mediawiki.special/templates/thumbnail.html
resources/src/mediawiki.toolbar/toolbar.js
resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less
resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
resources/src/mediawiki/images/question.png [new file with mode: 0644]
resources/src/mediawiki/images/question.svg [new file with mode: 0644]
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.ForeignUpload.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.Upload.Dialog.js
resources/src/mediawiki/mediawiki.Upload.js
resources/src/mediawiki/mediawiki.Uri.js
resources/src/mediawiki/mediawiki.Uri.loose.regexp [new file with mode: 0644]
resources/src/mediawiki/mediawiki.Uri.strict.regexp [new file with mode: 0644]
resources/src/mediawiki/mediawiki.confirmCloseWindow.js
resources/src/mediawiki/mediawiki.debug.js
resources/src/mediawiki/mediawiki.errorLogger.js
resources/src/mediawiki/mediawiki.experiments.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.htmlform.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.htmlform.js
resources/src/mediawiki/mediawiki.inspect.js
resources/src/mediawiki/mediawiki.jqueryMsg.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.notification.js
resources/src/mediawiki/mediawiki.searchSuggest.js
resources/src/mediawiki/mediawiki.startUp.js [deleted file]
resources/src/mediawiki/mediawiki.template.js
resources/src/mediawiki/mediawiki.template.regexp.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.user.js
resources/src/mediawiki/mediawiki.userSuggest.js
resources/src/mediawiki/mediawiki.util.js
resources/src/startup.js
tests/parser/parserTest.inc
tests/parser/parserTests.txt
tests/phpunit/Makefile
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/helpers/WellProtectedClass.php
tests/phpunit/data/less/common/test.common.mixins.less
tests/phpunit/data/less/module/styles.css
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/GlobalFunctions/wfArrayPlus2dTest.php [new file with mode: 0644]
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/TestingAccessWrapper.php
tests/phpunit/includes/TestingAccessWrapperTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/api/ApiResultTest.php
tests/phpunit/includes/content/TextContentHandlerTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/debug/logger/monolog/LineFormatterTest.php
tests/phpunit/includes/libs/IEUrlExtensionTest.php
tests/phpunit/includes/objectcache/BagOStuffTest.php
tests/phpunit/includes/parser/MediaWikiParserTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/includes/parser/TagHooksTest.php
tests/phpunit/includes/parser/TidyTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/upload/UploadStashTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js
tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js
tests/qunit/suites/resources/jquery/jquery.color.test.js
tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js
tests/qunit/suites/resources/jquery/jquery.hidpi.test.js
tests/qunit/suites/resources/jquery/jquery.localize.test.js
tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js
tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/jquery/jquery.textSelection.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.ForeignStructuredUpload.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.ForeignUpload.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
thumb.php

diff --git a/.jscsrc b/.jscsrc
index 6a3c564..aaa876c 100644 (file)
--- a/.jscsrc
+++ b/.jscsrc
@@ -1,9 +1,16 @@
 {
        "preset": "wikimedia",
+       "es3": true,
 
-       "disallowQuotedKeysInObjects": null,
-       "requireSpacesInsideParentheses": null,
-       "requireSpacesInsideArrayBrackets": null,
+       "requireVarDeclFirst": null,
+
+       "disallowQuotedKeysInObjects": "allButReserved",
+       "requireDotNotation": { "allExcept": [ "keywords" ] },
+       "jsDoc": {
+               "requireNewlineAfterDescription": true,
+               "requireParamTypes": true,
+               "requireReturnTypes": true
+       },
 
        "excludeFiles": [
                "docs/**",
index 512d735..8ba46b5 100644 (file)
@@ -8,19 +8,17 @@
 #
 language: php
 
-php:
-  - hhvm-nightly
-  - 5.3
-
-env:
-  - dbtype=mysql
-  - dbtype=postgres
-
-# TODO: Travis CI's hhvm does not support PostgreSQL at the moment.
 matrix:
-  exclude:
-    - php: hhvm-nightly
-      env: dbtype=postgres
+  fast_finish: true
+  include:
+    - env: dbtype=mysql
+      php: 5.3
+    - env: dbtype=postgres
+      php: 5.3
+    - env: dbtype=mysql
+      php: hhvm
+    - env: dbtype=mysql
+      php: 7
 
 services:
   - mysql
index e1e5e4a..035a208 100644 (file)
@@ -12,7 +12,7 @@ module.exports = function ( grunt ) {
                wgScriptPath = process.env.MW_SCRIPT_PATH,
                karmaProxy = {};
 
-       karmaProxy[wgScriptPath] = wgServer + wgScriptPath;
+       karmaProxy[ wgScriptPath ] = wgServer + wgScriptPath;
 
        grunt.initConfig( {
                jshint: {
@@ -87,14 +87,14 @@ module.exports = function ( grunt ) {
                }
                if ( !process.env.MW_SCRIPT_PATH ) {
                        grunt.log.error( 'Environment variable MW_SCRIPT_PATH must be set.\n' +
-                               'Set this like $wgScriptPath, e.g. "/w"');
+                               'Set this like $wgScriptPath, e.g. "/w"' );
                }
                return !!( process.env.MW_SERVER && process.env.MW_SCRIPT_PATH );
        } );
 
-       grunt.registerTask( 'lint', ['jshint', 'jscs', 'jsonlint', 'banana'] );
+       grunt.registerTask( 'lint', [ 'jshint', 'jscs', 'jsonlint', 'banana' ] );
        grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:main' ] );
 
-       grunt.registerTask( 'test', ['lint'] );
+       grunt.registerTask( 'test', [ 'lint' ] );
        grunt.registerTask( 'default', 'test' );
 };
index 5b85bbc..8f5db0e 100644 (file)
@@ -27,6 +27,9 @@ production.
   MediaWiki 1.26, in where ResourceLoader became fully asynchronous.
 * $wgMasterWaitTimeout was removed (deprecated in 1.24).
 * Fields in ParserOptions are now private. Use the accessors instead.
+* Custom LESS functions (defined via $wgResourceLoaderLESSFunctions)
+  have been removed, after being deprecated in 1.24.
+* $wgAlwaysUseTidy has been removed.
 
 === New features in 1.26 ===
 * (T51506) Now action=info gives estimates of actual watchers for a page.
@@ -66,6 +69,10 @@ production.
   for potentially slow POST requests that need to be as atomic as possible.
 * ResourceLoader now loads all scripts asynchronously. The top-queue and
   startup modules are no longer synchronously loaded.
+* 'mediawiki.ui.button' styles are no longer unconditionally loaded on every
+  page. During the deprecation period, the styles will only be loaded on pages
+  which contain 'mw-ui-button' in their HTML. Starting in 1.28, the styles will
+  only be loaded if explicitly required.
 
 ==== External libraries ====
 * Update es5-shim from v4.0.0 to v4.1.5.
@@ -79,6 +86,8 @@ production.
 * (T53283) load.php sometimes sends 304 response without full headers
 * (T65198) Talk page tabs now have a "rel=discussion" attribute
 * (T98841) {{msgnw:}} now preserves comments even when subst: is not used.
+* (T104142) $wgEmergencyContact and $wgPasswordSender now use their default
+  value if set to an empty string.
 
 === Action API changes in 1.26 ===
 * New-style continuation is now the default for action=continue. Clients may
@@ -97,10 +106,22 @@ production.
   sometimes being numerically-indexed objects with formatversion=2.
 * When errors about users being blocked are returned, they now include
   information about the relevant block.
+* (T99926) list=random has higher limits, in line with other API modules.
+* list=random's rnredirect parameter is deprecated in favor of a new
+  rnfilterredir parameter that also allows for listing both redirects and
+  non-redirects.
+* list=random now supports continuation.
+* API responses to GET requests may now include ETag and Last-Modified headers,
+  and will honor corresponding If-None-Match and If-Modified-Since on such
+  requests.
 
 === Action API internal changes in 1.26 ===
 * New metadata item ApiResult::META_KVP_MERGE to allow for merging the KVP key
   into the value when the value is an assoc.
+* API action modules may now provide values for the RFC 7232 ETag and
+  Last-Modified headers. The API will check these against If-None-Match and
+  If-Modified-Since request headers on GET requests and avoid executing the
+  module when appropriate.
 
 === Languages updated in 1.26 ===
 
@@ -162,6 +183,9 @@ changes to languages because of Phabricator reports.
   a lengthy deprecation period.
 * The ScopedPHPTimeout class was removed.
 * Removed maintenance script fixSlaveDesync.php.
+* Watchlist tokens, SpecialResetTokens, and User::getTokenFromOption()
+  are deprecated. Applications using those can work via the OAuth
+  extension instead. New tokens types should not be added.
 
 == Compatibility ==
 
index 82a45b4..4db3ec7 100644 (file)
@@ -724,7 +724,6 @@ $wgAutoloadLocalClasses = array(
        'MWOldPassword' => __DIR__ . '/includes/password/MWOldPassword.php',
        'MWSaltedPassword' => __DIR__ . '/includes/password/MWSaltedPassword.php',
        'MWTidy' => __DIR__ . '/includes/parser/MWTidy.php',
-       'MWTidyWrapper' => __DIR__ . '/includes/parser/MWTidy.php',
        'MWTimestamp' => __DIR__ . '/includes/MWTimestamp.php',
        'MachineReadableRCFeedFormatter' => __DIR__ . '/includes/rcfeed/MachineReadableRCFeedFormatter.php',
        'MagicWord' => __DIR__ . '/includes/MagicWord.php',
@@ -761,6 +760,13 @@ $wgAutoloadLocalClasses = array(
        'MediaWiki\\Logger\\Monolog\\WikiProcessor' => __DIR__ . '/includes/debug/logger/monolog/WikiProcessor.php',
        'MediaWiki\\Logger\\NullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
        'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php',
+       'MediaWiki\\Tidy\\Html5Depurate' => __DIR__ . '/includes/tidy/Html5Depurate.php',
+       'MediaWiki\\Tidy\\RaggettBase' => __DIR__ . '/includes/tidy/RaggettBase.php',
+       'MediaWiki\\Tidy\\RaggettExternal' => __DIR__ . '/includes/tidy/RaggettExternal.php',
+       'MediaWiki\\Tidy\\RaggettInternalHHVM' => __DIR__ . '/includes/tidy/RaggettInternalHHVM.php',
+       'MediaWiki\\Tidy\\RaggettInternalPHP' => __DIR__ . '/includes/tidy/RaggettInternalPHP.php',
+       'MediaWiki\\Tidy\\RaggettWrapper' => __DIR__ . '/includes/tidy/RaggettWrapper.php',
+       'MediaWiki\\Tidy\\TidyDriverBase' => __DIR__ . '/includes/tidy/TidyDriverBase.php',
        'MediaWiki\\Widget\\ComplexNamespaceInputWidget' => __DIR__ . '/includes/widget/ComplexNamespaceInputWidget.php',
        'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php',
        'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php',
index 852f2d2..1fe1e50 100644 (file)
                "leafo/lessphp": "0.5.0",
                "liuggio/statsd-php-client": "1.0.16",
                "mediawiki/at-ease": "1.0.0",
-               "oojs/oojs-ui": "0.12.6",
+               "oojs/oojs-ui": "0.12.8",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
                "wikimedia/assert": "0.2.2",
-               "wikimedia/cdb": "1.0.1",
+               "wikimedia/cdb": "1.3.0",
                "wikimedia/composer-merge-plugin": "1.2.1",
                "wikimedia/ip-set": "1.0.1",
-               "wikimedia/utfnormal": "1.0.2",
+               "wikimedia/utfnormal": "1.0.3",
                "wikimedia/wrappedstring": "2.0.0",
                "zordius/lightncandy": "0.21"
        },
index 1d78ecc..d11635d 100644 (file)
                        "type": "object",
                        "description": "ResourceLoader LESS variables"
                },
-               "ResourceLoaderLESSFunctions": {
-                       "type": "object",
-                       "description": "ResourceLoader LESS functions"
-               },
                "ResourceLoaderLESSImportPaths": {
                        "type": "object",
                        "description": "ResourceLoader import paths"
                "config": {
                        "type": "object",
                        "description": "Configuration options for this extension",
+                       "properties": {
+                               "_prefix": {
+                                       "type": "string",
+                                       "default": "wg",
+                                       "description": "Prefix to put in front of configuration settings when exporting them to $GLOBALS"
+                               }
+                       },
                        "patternProperties": {
                                "^[a-zA-Z_\u007f-\u00ff][a-zA-Z0-9_\u007f-\u00ff]*$": {
                                        "type": ["object", "array", "string", "integer", "null", "boolean"],
index c0aad5d..37429b9 100644 (file)
@@ -211,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;
@@ -226,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;
@@ -3473,8 +3473,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;
 
@@ -3636,18 +3636,6 @@ $wgResourceLoaderValidateStaticJS = 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
@@ -4116,44 +4104,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' );
 
@@ -7237,12 +7236,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
index 85a3014..05e0ac0 100644 (file)
@@ -655,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" );
 
@@ -668,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 );
                }
index 9d89633..2852561 100644 (file)
@@ -2179,14 +2179,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.
@@ -4273,3 +4283,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;
+}
index 036d65c..a414562 100644 (file)
@@ -231,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()
+               );
        }
 }
index b2926d1..221cefb 100644 (file)
@@ -63,7 +63,15 @@ class HtmlFormatter {
         */
        public function getDoc() {
                if ( !$this->doc ) {
-                       $html = mb_convert_encoding( $this->html, 'HTML-ENTITIES', 'UTF-8' );
+                       // DOMDocument::loadHTML apparently isn't very good with encodings, so
+                       // convert input to ASCII by encoding everything above 128 as entities.
+                       if ( function_exists( 'mb_convert_encoding' ) ) {
+                               $html = mb_convert_encoding( $this->html, 'HTML-ENTITIES', 'UTF-8' );
+                       } else {
+                               $html = preg_replace_callback( '/[\x{80}-\x{10ffff}]/u', function ( $m ) {
+                                       return '&#' . UtfNormal\Utils::utf8ToCodepoint( $m[0] ) . ';';
+                               }, $this->html );
+                       }
 
                        // Workaround for bug that caused spaces before references
                        // to disappear during processing:
@@ -244,7 +252,14 @@ class HtmlFormatter {
                        ) );
                }
                $html = $replacements->replace( $html );
-               $html = mb_convert_encoding( $html, 'UTF-8', 'HTML-ENTITIES' );
+
+               if ( function_exists( 'mb_convert_encoding' ) ) {
+                       // Just in case the conversion in getDoc() above used named
+                       // entities that aren't known to html_entity_decode().
+                       $html = mb_convert_encoding( $html, 'UTF-8', 'HTML-ENTITIES' );
+               } else {
+                       $html = html_entity_decode( $html, ENT_COMPAT, 'utf-8' );
+               }
                return $html;
        }
 
index 1c79485..bc5a957 100644 (file)
@@ -855,6 +855,8 @@ class CurlHttpRequest extends MWHttpRequest {
 
 class PhpHttpRequest extends MWHttpRequest {
 
+       private $fopenErrors = array();
+
        /**
         * @param string $url
         * @return string
@@ -865,6 +867,60 @@ class PhpHttpRequest extends MWHttpRequest {
                return 'tcp://' . $parsedUrl['host'] . ':' . $parsedUrl['port'];
        }
 
+       /**
+        * Returns an array with a 'capath' or 'cafile' key that is suitable to be merged into the 'ssl' sub-array of a
+        * stream context options array. Uses the 'caInfo' option of the class if it is provided, otherwise uses the system
+        * default CA bundle if PHP supports that, or searches a few standard locations.
+        * @return array
+        * @throws DomainException
+        */
+       protected function getCertOptions() {
+               $certOptions = array();
+               $certLocations = array();
+               if ( $this->caInfo ) {
+                       $certLocations = array( 'manual' => $this->caInfo );
+               } elseif ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
+                       // Default locations, based on
+                       // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
+                       // PHP 5.5 and older doesn't have any defaults, so we try to guess ourselves. PHP 5.6+ gets the CA location
+                       // from OpenSSL as long as it is not set manually, so we should leave capath/cafile empty there.
+                       $certLocations = array_filter( array(
+                               getenv( 'SSL_CERT_DIR' ),
+                               getenv( 'SSL_CERT_PATH' ),
+                               '/etc/pki/tls/certs/ca-bundle.crt', # Fedora et al
+                               '/etc/ssl/certs',  # Debian et al
+                               '/etc/pki/tls/certs/ca-bundle.trust.crt',
+                               '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem',
+                               '/System/Library/OpenSSL', # OSX
+                       ) );
+               }
+
+               foreach( $certLocations as $key => $cert ) {
+                       if ( is_dir( $cert ) ) {
+                               $certOptions['capath'] = $cert;
+                               break;
+                       } elseif ( is_file( $cert ) ) {
+                               $certOptions['cafile'] = $cert;
+                               break;
+                       } elseif ( $key === 'manual' ) {
+                               // fail more loudly if a cert path was manually configured and it is not valid
+                               throw new DomainException( "Invalid CA info passed: $cert" );
+                       }
+               }
+
+               return $certOptions;
+       }
+
+       /**
+        * Custom error handler for dealing with fopen() errors. fopen() tends to fire multiple errors in succession, and the last one
+        * is completely useless (something like "fopen: failed to open stream") so normal methods of handling errors programmatically
+        * like get_last_error() don't work.
+        */
+       public function errorHandler( $errno, $errstr ) {
+               $n = count( $this->fopenErrors ) + 1;
+               $this->fopenErrors += array( "errno$n" => $errno, "errstr$n" => $errstr );
+       }
+
        public function execute() {
 
                parent::execute();
@@ -926,13 +982,7 @@ class PhpHttpRequest extends MWHttpRequest {
                        }
                }
 
-               if ( is_dir( $this->caInfo ) ) {
-                       $options['ssl']['capath'] = $this->caInfo;
-               } elseif ( is_file( $this->caInfo ) ) {
-                       $options['ssl']['cafile'] = $this->caInfo;
-               } elseif ( $this->caInfo ) {
-                       throw new MWException( "Invalid CA info passed: {$this->caInfo}" );
-               }
+               $options['ssl'] += $this->getCertOptions();
 
                $context = stream_context_create( $options );
 
@@ -949,9 +999,10 @@ class PhpHttpRequest extends MWHttpRequest {
                }
                do {
                        $reqCount++;
-                       MediaWiki\suppressWarnings();
+                       $this->fopenErrors = array();
+                       set_error_handler( array( $this, 'errorHandler' ) );
                        $fh = fopen( $url, "r", false, $context );
-                       MediaWiki\restoreWarnings();
+                       restore_error_handler();
 
                        if ( !$fh ) {
                                // HACK for instant commons.
@@ -997,6 +1048,10 @@ class PhpHttpRequest extends MWHttpRequest {
                $this->setStatus();
 
                if ( $fh === false ) {
+                       if ( $this->fopenErrors ) {
+                               LoggerFactory::getInstance( 'http' )->warning( __CLASS__
+                                       . ': error opening connection: {errstr1}', $this->fopenErrors );
+                       }
                        $this->status->fatal( 'http-request-error' );
                        return $this->status;
                }
index 186821d..833f852 100644 (file)
@@ -718,9 +718,6 @@ class MagicWordArray {
 
        private $regex;
 
-       /** @todo Unused? */
-       private $matches;
-
        /**
         * @param array $names
         */
index 073762a..552e181 100644 (file)
@@ -142,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 );
@@ -2012,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();
+               }
+               if ( !is_array( $option ) ) {
+                       $option = array();
                }
-               $this->mVaryHeader[$header] = array_unique( (array)$this->mVaryHeader[$header] );
+               $this->mVaryHeader[$header] = array_unique( array_merge( $this->mVaryHeader[$header], $option ) );
        }
 
        /**
@@ -2909,8 +2905,7 @@ class OutputPage extends ContextSource {
 
                                // Automatically select style/script elements
                                if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
-                                       $media = $group === 'print' ? 'print' : 'all';
-                                       $link = Html::linkedStyle( $url, $media );
+                                       $link = Html::linkedStyle( $url );
                                } else {
                                        if ( $context->getRaw() || $isRaw ) {
                                                // Startup module can't load itself, needs to use <script> instead of mw.loader.load
index cd675c6..248b308 100644 (file)
@@ -751,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(
index 55a4f49..430b4b8 100644 (file)
@@ -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',
index c02bdc9..de63af7 100644 (file)
@@ -346,8 +346,8 @@ class Sanitizer {
                                  ($space*=$space*
                                        (?:
                                         # The attribute value: quoted or alone
-                                         \"([^<\"]*)\"
-                                        | '([^<']*)'
+                                         \"([^<\"]*)(?:\"|\$)
+                                        | '([^<']*)(?:'|\$)
                                         |  ([a-zA-Z0-9!#$%&()*,\\-.\\/:;<>?@[\\]^_`{|}~]+)
                                        )
                                )?(?=$space|\$)/sx";
@@ -454,15 +454,13 @@ class Sanitizer {
        public static function removeHTMLtags( $text, $processCallback = null,
                $args = array(), $extratags = array(), $removetags = array()
        ) {
-               global $wgUseTidy;
-
                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();
index 86df4b8..479ce8c 100644 (file)
@@ -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 ) {
@@ -521,11 +522,11 @@ unset( $serverParts );
 
 // Set defaults for configuration variables
 // that are derived from the server name by default
-if ( $wgEmergencyContact === false ) {
+// Note: $wgEmergencyContact and $wgPasswordSender may be false or empty string (T104142)
+if ( !$wgEmergencyContact ) {
        $wgEmergencyContact = 'wikiadmin@' . $wgServerName;
 }
-
-if ( $wgPasswordSender === false ) {
+if ( !$wgPasswordSender ) {
        $wgPasswordSender = 'apache@' . $wgServerName;
 }
 
index 2dfcdc2..49155d6 100644 (file)
@@ -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 );
        }
 
        /**
index 8a15b54..b347edb 100644 (file)
@@ -1939,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.
index 9b958f4..605dab6 100644 (file)
@@ -2438,6 +2438,7 @@ class User implements IDBAccessObject {
         */
        public function setInternalPassword( $str ) {
                $this->setToken();
+               $this->setOption( 'watchlisttoken', false );
 
                $passwordFactory = self::getPasswordFactory();
                $this->mPassword = $passwordFactory->newFromPlaintext( $str );
@@ -2715,20 +2716,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;
        }
 
@@ -3208,10 +3213,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 ) ) {
@@ -3223,10 +3228,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 ) ) {
index 31806b7..893ae04 100644 (file)
@@ -3074,7 +3074,7 @@ $zh2Hant = array(
 '9只' => '9隻',
 '9余' => '9餘',
 '·范' => '·范',
-'’s ' => '’s',
+'’s' => '’s',
 '、面点' => '、麵點',
 '。个中' => '。箇中',
 '〇周后' => '〇周後',
@@ -3201,21 +3201,6 @@ $zh2Hant = array(
 '不好干预' => '不好干預',
 '不嫌母丑' => '不嫌母醜',
 '不寒而栗' => '不寒而慄',
-'不干事' => '不干事',
-'不干他' => '不干他',
-'不干休' => '不干休',
-'不干你' => '不干你',
-'不干她' => '不干她',
-'不干它' => '不干它',
-'不干我' => '不干我',
-'不干扰' => '不干擾',
-'不干擾' => '不干擾',
-'不干涉' => '不干涉',
-'不干牠' => '不干牠',
-'不干犯' => '不干犯',
-'不干預' => '不干預',
-'不干预' => '不干預',
-'不干' => '不幹',
 '不吊' => '不弔',
 '不卷' => '不捲',
 '不采' => '不採',
@@ -3795,7 +3780,6 @@ $zh2Hant = array(
 '佛罗棱萨' => '佛羅稜薩',
 '佛钟' => '佛鐘',
 '作品里' => '作品裡',
-'作奸犯科' => '作姦犯科',
 '作准' => '作準',
 '你夸' => '你誇',
 '佣金' => '佣金',
@@ -4219,6 +4203,7 @@ $zh2Hant = array(
 '千钧一发' => '千鈞一髮',
 '千只' => '千隻',
 '千余' => '千餘',
+'升高后' => '升高後',
 '半制品' => '半制品',
 '半只可' => '半只可',
 '半只够' => '半只夠',
@@ -4422,6 +4407,7 @@ $zh2Hant = array(
 '呼吁' => '呼籲',
 '命中注定' => '命中注定',
 '和奸' => '和姦',
+'和制汉' => '和製漢',
 '咎征' => '咎徵',
 '咕咕钟' => '咕咕鐘',
 '咪表' => '咪錶',
@@ -4591,9 +4577,12 @@ $zh2Hant = array(
 '墓志' => '墓誌',
 '增辟' => '增闢',
 '墨子里' => '墨子里',
+'墨斗' => '墨斗',
 '墨沈沈' => '墨沈沈',
 '墨沈' => '墨瀋',
 '垦辟' => '墾闢',
+'压制出' => '壓製出',
+'压制机' => '壓製機',
 '壮游' => '壯遊',
 '壮面' => '壯麵',
 '壹郁' => '壹鬱',
@@ -5720,6 +5709,7 @@ $zh2Hant = array(
 '采薇' => '採薇',
 '采薪' => '採薪',
 '采药' => '採藥',
+'采血' => '採血',
 '采行' => '採行',
 '采补' => '採補',
 '采访' => '採訪',
@@ -5746,6 +5736,7 @@ $zh2Hant = array(
 '提子干' => '提子乾',
 '提心吊胆' => '提心弔膽',
 '提摩太后书' => '提摩太後書',
+'提高后' => '提高後',
 '插于' => '插於',
 '换签' => '換籤',
 '换只' => '換隻',
@@ -5944,7 +5935,7 @@ $zh2Hant = array(
 '历始' => '曆始',
 '历室' => '曆室',
 '历尾' => '曆尾',
-'历数' => 'æ\9b\86æ\95¸',
+'历数书' => 'æ\9b\86æ\95¸æ\9b¸',
 '历日' => '曆日',
 '历书' => '曆書',
 '历本' => '曆本',
@@ -6005,6 +5996,7 @@ $zh2Hant = array(
 '望后石' => '望后石',
 '朝乾夕惕' => '朝乾夕惕',
 '朝钟' => '朝鐘',
+'朝鲜于' => '朝鮮於',
 '朦胧' => '朦朧',
 '蒙胧' => '朦朧',
 '木偶戏扎' => '木偶戲紮',
@@ -6305,6 +6297,8 @@ $zh2Hant = array(
 '洗发' => '洗髮',
 '洛钟东应' => '洛鐘東應',
 '洞里' => '洞裡',
+'洞里萨' => '洞里薩',
+'洞里薩' => '洞里薩',
 '泄欲' => '洩慾',
 '洪范' => '洪範',
 '洪谷子' => '洪谷子',
@@ -6376,7 +6370,6 @@ $zh2Hant = array(
 '渠冲' => '渠衝',
 '测不准' => '測不準',
 '港制' => '港製',
-'游牧民族' => '游牧民族',
 '游离' => '游離',
 '浑朴' => '渾樸',
 '浑个' => '渾箇',
@@ -6716,7 +6709,6 @@ $zh2Hant = array(
 '癸丑' => '癸丑',
 '发干' => '發乾',
 '发呆' => '發獃',
-'发蒙' => '發矇',
 '发签' => '發籤',
 '发松' => '發鬆',
 '发面' => '發麵',
@@ -6956,7 +6948,6 @@ $zh2Hant = array(
 '谷草' => '穀草',
 '谷贵饿农' => '穀貴餓農',
 '谷贱伤农' => '穀賤傷農',
-'谷道' => '穀道',
 '谷雨' => '穀雨',
 '谷类' => '穀類',
 '谷食' => '穀食',
@@ -7310,6 +7301,7 @@ $zh2Hant = array(
 '胜肽' => '胜肽',
 '胜键' => '胜鍵',
 '胡云' => '胡云',
+'胡子婴' => '胡子嬰',
 '胡子昂' => '胡子昂',
 '胡杰' => '胡杰',
 '胡朴安' => '胡樸安',
@@ -8959,6 +8951,7 @@ $zh2Hant = array(
 '体范' => '體範',
 '体系' => '體系',
 '高几' => '高几',
+'高后' => '高后',
 '高干扰' => '高干擾',
 '高干预' => '高干預',
 '高干' => '高幹',
@@ -9201,8 +9194,8 @@ $zh2Hant = array(
 '魔表' => '魔錶',
 '鱼干' => '魚乾',
 '鱼松' => '魚鬆',
-'鮮于樞' => '鮮于樞',
-'鲜于枢' => '鮮于樞',
+'鮮于' => '鮮于',
+'鲜于' => '鮮于',
 '鲸须' => '鯨鬚',
 '鳥栖' => '鳥栖',
 '鸟栖市' => '鳥栖市',
@@ -9299,6 +9292,7 @@ $zh2Hant = array(
 '黃杰' => '黃杰',
 '黄杰' => '黃杰',
 '黄历史' => '黃歷史',
+'黄白术' => '黃白術',
 '黃詩杰' => '黃詩杰',
 '黄诗杰' => '黃詩杰',
 '黄金表' => '黃金表',
@@ -13846,8 +13840,6 @@ $zh2TW = array(
 '掌上壓' => '伏地挺身',
 '伯明翰' => '伯明罕',
 '服务器' => '伺服器',
-'字節' => '位元組',
-'字节' => '位元組',
 '佛罗伦萨' => '佛羅倫斯',
 '操作系统' => '作業系統',
 '系数' => '係數',
@@ -14062,6 +14054,7 @@ $zh2TW = array(
 '戒烟' => '戒菸',
 '戒煙' => '戒菸',
 '戴克里先' => '戴克里先',
+'打印度' => '打印度',
 '抽烟' => '抽菸',
 '抽煙' => '抽菸',
 '拉普兰' => '拉布蘭',
@@ -14078,7 +14071,7 @@ $zh2TW = array(
 '搜索引擎' => '搜尋引擎',
 '摩根士丹利' => '摩根史坦利',
 '台球' => '撞球',
-'攻打印' => '攻打印',
+'攻打' => '攻打',
 '数字技术' => '數位技術',
 '數碼技術' => '數位技術',
 '数字照相机' => '數位照相機',
@@ -14129,6 +14122,7 @@ $zh2TW = array(
 '撒切尔' => '柴契爾',
 '格林納達' => '格瑞那達',
 '格林纳达' => '格瑞那達',
+'台式电脑' => '桌上型電腦',
 '乒乓' => '桌球',
 '乒乓球' => '桌球',
 '杆弟' => '桿弟',
@@ -14264,6 +14258,8 @@ $zh2TW = array(
 '弗吉尼亚' => '維吉尼亞',
 '佛得角' => '維德角',
 '维特根斯坦' => '維根斯坦',
+'網絡遊戲' => '網路遊戲',
+'网络游戏' => '網路遊戲',
 '互联网' => '網際網路',
 '互联网络' => '網際網路',
 '互聯網' => '網際網路',
@@ -14455,7 +14451,6 @@ $zh2TW = array(
 '链接' => '連結',
 '連結他' => '連結他',
 '进制' => '進位',
-'算子' => '運算元',
 '达·芬奇' => '達·文西',
 '达芬奇' => '達文西',
 '溫納圖萬' => '那杜',
@@ -15651,6 +15646,7 @@ $zh2HK = array(
 '扛著錄' => '扛著錄',
 '找不著' => '找不着',
 '找得著' => '找得着',
+'承宣布政' => '承宣布政',
 '抓著' => '抓着',
 '抓著作' => '抓著作',
 '抓著名' => '抓著名',
@@ -16001,6 +15997,7 @@ $zh2HK = array(
 '葛萊美獎' => '格林美獎',
 '格鲁吉亚' => '格魯吉亞',
 '框里' => '框裏',
+'台式电脑' => '桌上型電腦',
 '台球' => '桌球',
 '撞球' => '桌球',
 '梅鐸' => '梅鐸',
@@ -17174,6 +17171,8 @@ $zh2HK = array(
 '遇著述' => '遇著述',
 '遇著錄' => '遇著錄',
 '遍布' => '遍佈',
+'遍佈著' => '遍佈着',
+'遍布著' => '遍佈着',
 '過著' => '過着',
 '达·芬奇' => '達·文西',
 '达芬奇' => '達文西',
@@ -18023,6 +18022,7 @@ $zh2CN = array(
 '叫著稱' => '叫著称',
 '叫著者' => '叫著者',
 '叫著述' => '叫著述',
+'桌上型電腦' => '台式电脑',
 '撞球' => '台球',
 '台帳' => '台账',
 '叱吒' => '叱咤',
@@ -19937,6 +19937,8 @@ $zh2CN = array(
 '遇著稱' => '遇著称',
 '遇著者' => '遇著者',
 '遇著述' => '遇著述',
+'遍佈著' => '遍布着',
+'遍布著' => '遍布着',
 '部份' => '部分',
 '配合著' => '配合着',
 '配合著名' => '配合著名',
@@ -19982,6 +19984,7 @@ $zh2CN = array(
 '鋪著稱' => '铺著称',
 '鋪著者' => '铺著者',
 '鋪著述' => '铺著述',
+'鏈結' => '链接',
 '銷帳' => '销账',
 '鉲' => '锎',
 '鎝' => '锝',
index 7b71e6c..d53797b 100644 (file)
@@ -345,6 +345,22 @@ abstract class ApiBase extends ContextSource {
                return null;
        }
 
+       /**
+        * Returns data for HTTP conditional request mechanisms.
+        *
+        * @since 1.26
+        * @param string $condition Condition being queried:
+        *  - last-modified: Return a timestamp representing the maximum of the
+        *    last-modified dates for all resources involved in the request. See
+        *    RFC 7232 § 2.2 for semantics.
+        *  - etag: Return an entity-tag representing the state of all resources involved
+        *    in the request. Quotes must be included. See RFC 7232 § 2.3 for semantics.
+        * @return string|boolean|null As described above, or null if no value is available.
+        */
+       public function getConditionalRequestData( $condition ) {
+               return null;
+       }
+
        /**@}*/
 
        /************************************************************************//**
index 7bb2453..9dbd4a5 100644 (file)
 class ApiFormatRaw extends ApiFormatBase {
 
        private $errorFallback;
+       private $mFailWithHTTPError = false;
+
 
        /**
         * @param ApiMain $main
-        * @param ApiFormatBase $errorFallback Object to fall back on for errors
+        * @param ApiFormatBase |null $errorFallback Object to fall back on for errors
         */
-       public function __construct( ApiMain $main, ApiFormatBase $errorFallback ) {
+       public function __construct( ApiMain $main, ApiFormatBase $errorFallback = null ) {
                parent::__construct( $main, 'raw' );
-               $this->errorFallback = $errorFallback;
+               if ( $errorFallback === null ) {
+                       $this->errorFallback = $main->createPrinterByName( $main->getParameter( 'format' ) );
+               } else {
+                       $this->errorFallback = $errorFallback;
+               }
        }
 
        public function getMimeType() {
@@ -59,6 +65,9 @@ class ApiFormatRaw extends ApiFormatBase {
                $data = $this->getResult()->getResultData();
                if ( isset( $data['error'] ) ) {
                        $this->errorFallback->initPrinter( $unused );
+                       if ( $this->mFailWithHTTPError ) {
+                               $this->getMain()->getRequest()->response()->statusHeader( 400 );
+                       }
                } else {
                        parent::initPrinter( $unused );
                }
@@ -85,4 +94,17 @@ class ApiFormatRaw extends ApiFormatBase {
                }
                $this->printText( $data['text'] );
        }
+
+       /**
+        * Output HTTP error code 400 when if an error is encountered
+        *
+        * The purpose is for output formats where the user-agent will
+        * not be able to interpret the validity of the content in any
+        * other way. For example subtitle files read by browser video players.
+        *
+        * @param bool $fail
+        */
+       public function setFailWithHTTPError( $fail ) {
+               $this->mFailWithHTTPError = $fail;
+       }
 }
index 8c65310..c66e215 100644 (file)
@@ -145,10 +145,10 @@ class ApiLogin extends ApiBase {
                        case LoginForm::CREATE_BLOCKED:
                                $result['result'] = 'CreateBlocked';
                                $result['details'] = 'Your IP address is blocked from account creation';
-                               $result = array_merge(
-                                       $result,
-                                       ApiQueryUserInfo::getBlockInfo( $context->getUser()->getBlock() )
-                               );
+                               $block = $context->getUser()->getBlock();
+                               if ( $block ) {
+                                       $result = array_merge( $result, ApiQueryUserInfo::getBlockInfo( $block ) );
+                               }
                                break;
 
                        case LoginForm::THROTTLED:
@@ -159,10 +159,10 @@ class ApiLogin extends ApiBase {
 
                        case LoginForm::USER_BLOCKED:
                                $result['result'] = 'Blocked';
-                               $result = array_merge(
-                                       $result,
-                                       ApiQueryUserInfo::getBlockInfo( User::newFromName( $params['name'] )->getBlock() )
-                               );
+                               $block = User::newFromName( $params['name'] )->getBlock();
+                               if ( $block ) {
+                                       $result = array_merge( $result, ApiQueryUserInfo::getBlockInfo( $block ) );
+                               }
                                break;
 
                        case LoginForm::ABORTED:
index f2059d7..c558c2b 100644 (file)
@@ -425,13 +425,16 @@ class ApiMain extends ApiBase {
 
                // In case an error occurs during data output,
                // clear the output buffer and print just the error information
+               $obLevel = ob_get_level();
                ob_start();
 
                $t = microtime( true );
                try {
                        $this->executeAction();
+                       $isError = false;
                } catch ( Exception $e ) {
                        $this->handleException( $e );
+                       $isError = true;
                }
 
                // Log the request whether or not there was an error
@@ -439,9 +442,13 @@ class ApiMain extends ApiBase {
 
                // Send cache headers after any code which might generate an error, to
                // avoid sending public cache headers for errors.
-               $this->sendCacheHeaders();
+               $this->sendCacheHeaders( $isError );
 
-               ob_end_flush();
+               // Executing the action might have already messed with the output
+               // buffers.
+               while ( ob_get_level() > $obLevel ) {
+                       ob_end_flush();
+               }
        }
 
        /**
@@ -532,7 +539,7 @@ class ApiMain extends ApiBase {
 
                // Log the request and reset cache headers
                $main->logRequest( 0 );
-               $main->sendCacheHeaders();
+               $main->sendCacheHeaders( true );
 
                ob_end_flush();
        }
@@ -701,7 +708,12 @@ class ApiMain extends ApiBase {
                return "/^https?:\/\/$wildcard$/";
        }
 
-       protected function sendCacheHeaders() {
+       /**
+        * Send caching headers
+        * @param boolean $isError Whether an error response is being output
+        * @since 1.26 added $isError parameter
+        */
+       protected function sendCacheHeaders( $isError ) {
                $response = $this->getRequest()->response();
                $out = $this->getOutput();
 
@@ -711,6 +723,19 @@ class ApiMain extends ApiBase {
                        $out->addVaryHeader( 'X-Forwarded-Proto' );
                }
 
+               if ( !$isError && $this->mModule &&
+                       ( $this->getRequest()->getMethod() === 'GET' || $this->getRequest()->getMethod() === 'HEAD' )
+               ) {
+                       $etag = $this->mModule->getConditionalRequestData( 'etag' );
+                       if ( $etag !== null ) {
+                               $response->header( "ETag: $etag" );
+                       }
+                       $lastMod = $this->mModule->getConditionalRequestData( 'last-modified' );
+                       if ( $lastMod !== null ) {
+                               $response->header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $lastMod ) );
+                       }
+               }
+
                // The logic should be:
                // $this->mCacheControl['max-age'] is set?
                //    Use it, the module knows better than our guess.
@@ -993,6 +1018,121 @@ class ApiMain extends ApiBase {
                return true;
        }
 
+       /**
+        * Check selected RFC 7232 precondition headers
+        *
+        * RFC 7232 envisions a particular model where you send your request to "a
+        * resource", and for write requests that you can read "the resource" by
+        * changing the method to GET. When the API receives a GET request, it
+        * works out even though "the resource" from RFC 7232's perspective might
+        * be many resources from MediaWiki's perspective. But it totally fails for
+        * a POST, since what HTTP sees as "the resource" is probably just
+        * "/api.php" with all the interesting bits in the body.
+        *
+        * Therefore, we only support RFC 7232 precondition headers for GET (and
+        * HEAD). That means we don't need to bother with If-Match and
+        * If-Unmodified-Since since they only apply to modification requests.
+        *
+        * And since we don't support Range, If-Range is ignored too.
+        *
+        * @since 1.26
+        * @param ApiBase $module Api module being used
+        * @return bool True on success, false should exit immediately
+        */
+       protected function checkConditionalRequestHeaders( $module ) {
+               if ( $this->mInternalMode ) {
+                       // No headers to check in internal mode
+                       return true;
+               }
+
+               if ( $this->getRequest()->getMethod() !== 'GET' && $this->getRequest()->getMethod() !== 'HEAD' ) {
+                       // Don't check POSTs
+                       return true;
+               }
+
+               $return304 = false;
+
+               $ifNoneMatch = array_diff(
+                       $this->getRequest()->getHeader( 'If-None-Match', WebRequest::GETHEADER_LIST ) ?: array(),
+                       array( '' )
+               );
+               if ( $ifNoneMatch ) {
+                       if ( $ifNoneMatch === array( '*' ) ) {
+                               // API responses always "exist"
+                               $etag = '*';
+                       } else {
+                               $etag = $module->getConditionalRequestData( 'etag' );
+                       }
+               }
+               if ( $ifNoneMatch && $etag !== null ) {
+                       $test = substr( $etag, 0, 2 ) === 'W/' ? substr( $etag, 2 ) : $etag;
+                       $match = array_map( function ( $s ) {
+                               return substr( $s, 0, 2 ) === 'W/' ? substr( $s, 2 ) : $s;
+                       }, $ifNoneMatch );
+                       $return304 = in_array( $test, $match, true );
+               } else {
+                       $value = trim( $this->getRequest()->getHeader( 'If-Modified-Since' ) );
+
+                       // Some old browsers sends sizes after the date, like this:
+                       //  Wed, 20 Aug 2003 06:51:19 GMT; length=5202
+                       // Ignore that.
+                       $i = strpos( $value, ';' );
+                       if ( $i !== false ) {
+                               $value = trim( substr( $value, 0, $i ) );
+                       }
+
+                       if ( $value !== '' ) {
+                               try {
+                                       $ts = new MWTimestamp( $value );
+                                       if (
+                                               // RFC 7231 IMF-fixdate
+                                               $ts->getTimestamp( TS_RFC2822 ) === $value ||
+                                               // RFC 850
+                                               $ts->format( 'l, d-M-y H:i:s' ) . ' GMT' === $value ||
+                                               // asctime (with and without space-padded day)
+                                               $ts->format( 'D M j H:i:s Y' ) === $value ||
+                                               $ts->format( 'D M  j H:i:s Y' ) === $value
+                                       ) {
+                                               $lastMod = $module->getConditionalRequestData( 'last-modified' );
+                                               if ( $lastMod !== null ) {
+                                                       // Mix in some MediaWiki modification times
+                                                       $modifiedTimes = array(
+                                                               'page' => $lastMod,
+                                                               'user' => $this->getUser()->getTouched(),
+                                                               'epoch' => $this->getConfig()->get( 'CacheEpoch' ),
+                                                       );
+                                                       if ( $this->getConfig()->get( 'UseSquid' ) ) {
+                                                               // T46570: the core page itself may not change, but resources might
+                                                               $modifiedTimes['sepoch'] = wfTimestamp(
+                                                                       TS_MW, time() - $this->getConfig()->get( 'SquidMaxage' )
+                                                               );
+                                                       }
+                                                       Hooks::run( 'OutputPageCheckLastModified', array( &$modifiedTimes ) );
+                                                       $lastMod = max( $modifiedTimes );
+                                                       $return304 = wfTimestamp( TS_MW, $lastMod ) <= $ts->getTimestamp( TS_MW );
+                                               }
+                                       }
+                               } catch ( TimestampException $e ) {
+                                       // Invalid timestamp, ignore it
+                               }
+                       }
+               }
+
+               if ( $return304 ) {
+                       $this->getRequest()->response()->statusHeader( 304 );
+
+                       // Avoid outputting the compressed representation of a zero-length body
+                       MediaWiki\suppressWarnings();
+                       ini_set( 'zlib.output_compression', 0 );
+                       MediaWiki\restoreWarnings();
+                       wfClearOutputBuffers();
+
+                       return false;
+               }
+
+               return true;
+       }
+
        /**
         * Check for sufficient permissions to execute
         * @param ApiBase $module An Api module
@@ -1083,6 +1223,10 @@ class ApiMain extends ApiBase {
                        return;
                }
 
+               if ( !$this->checkConditionalRequestHeaders( $module ) ) {
+                       return;
+               }
+
                if ( !$this->mInternalMode ) {
                        $this->setupExternalResponse( $module, $params );
                }
index 4e4d2af..172ee99 100644 (file)
@@ -55,7 +55,6 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                $result = $this->getResult();
                $pageSet = $this->getPageSet();
-               $titles = $pageSet->getTitles();
 
                // This module operates in two modes:
                // 'user': List deleted revs by a certain user
@@ -83,6 +82,21 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                        }
                }
 
+               // If we're generating titles only, we can use DISTINCT for a better
+               // query. But we can't do that in 'user' mode (wrong index), and we can
+               // only do it when sorting ASC (because MySQL apparently can't use an
+               // index backwards for grouping even though it can for ORDER BY, WTF?)
+               $dir = $params['dir'];
+               $optimizeGenerateTitles = false;
+               if ( $mode === 'all' && $params['generatetitles'] && $resultPageSet !== null ) {
+                       if ( $dir === 'newer' ) {
+                               $optimizeGenerateTitles = true;
+                       } else {
+                               $p = $this->getModulePrefix();
+                               $this->setWarning( "For better performance when generating titles, set {$p}dir=newer" );
+                       }
+               }
+
                $this->addTables( 'archive' );
                if ( $resultPageSet === null ) {
                        $this->parseParameters( $params );
@@ -90,7 +104,12 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                        $this->addFields( array( 'ar_title', 'ar_namespace' ) );
                } else {
                        $this->limit = $this->getParameter( 'limit' ) ?: 10;
-                       $this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+                       $this->addFields( array( 'ar_title', 'ar_namespace' ) );
+                       if ( $optimizeGenerateTitles ) {
+                               $this->addOption( 'DISTINCT' );
+                       } else {
+                               $this->addFields( array( 'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+                       }
                }
 
                if ( $this->fld_tags ) {
@@ -130,7 +149,6 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                        }
                }
 
-               $dir = $params['dir'];
                $miser_ns = null;
 
                if ( $mode == 'all' ) {
@@ -229,7 +247,14 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                if ( !is_null( $params['continue'] ) ) {
                        $cont = explode( '|', $params['continue'] );
                        $op = ( $dir == 'newer' ? '>' : '<' );
-                       if ( $mode == 'all' ) {
+                       if ( $optimizeGenerateTitles ) {
+                               $this->dieContinueUsageIf( count( $cont ) != 2 );
+                               $ns = intval( $cont[0] );
+                               $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] );
+                               $title = $db->addQuotes( $cont[1] );
+                               $this->addWhere( "ar_namespace $op $ns OR " .
+                                       "(ar_namespace = $ns AND ar_title $op= $title)" );
+                       } elseif ( $mode == 'all' ) {
                                $this->dieContinueUsageIf( count( $cont ) != 4 );
                                $ns = intval( $cont[0] );
                                $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] );
@@ -259,7 +284,13 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                $sort = ( $dir == 'newer' ? '' : ' DESC' );
                $orderby = array();
-               if ( $mode == 'all' ) {
+               if ( $optimizeGenerateTitles ) {
+                       // Targeting index name_title_timestamp
+                       if ( $params['namespace'] === null || count( array_unique( $params['namespace'] ) ) > 1 ) {
+                               $orderby[] = "ar_namespace $sort";
+                       }
+                       $orderby[] = "ar_title $sort";
+               } elseif ( $mode == 'all' ) {
                        // Targeting index name_title_timestamp
                        if ( $params['namespace'] === null || count( array_unique( $params['namespace'] ) ) > 1 ) {
                                $orderby[] = "ar_namespace $sort";
@@ -283,7 +314,9 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                foreach ( $res as $row ) {
                        if ( ++$count > $this->limit ) {
                                // We've had enough
-                               if ( $mode == 'all' ) {
+                               if ( $optimizeGenerateTitles ) {
+                                       $this->setContinueEnumParameter( 'continue', "$row->ar_namespace|$row->ar_title" );
+                               } elseif ( $mode == 'all' ) {
                                        $this->setContinueEnumParameter( 'continue',
                                                "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
                                        );
index a2c2844..8e7031c 100644 (file)
@@ -31,8 +31,6 @@
  * @ingroup API
  */
 class ApiQueryRandom extends ApiQueryGeneratorBase {
-       private $pageIDs;
-
        public function __construct( ApiQuery $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'rn' );
        }
@@ -46,102 +44,131 @@ class ApiQueryRandom extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param string $randstr
-        * @param int $limit
-        * @param int $namespace
-        * @param ApiPageSet $resultPageSet
-        * @param bool $redirect
-        * @return void
+        * Actually perform the query and add pages to the result.
+        * @param ApiPageSet|null $resultPageSet
+        * @param int $limit Number of pages to fetch
+        * @param string|null $start Starting page_random
+        * @param int|null $startId Starting page_id
+        * @param string|null $end Ending page_random
+        * @return array (int, string|null) Number of pages left to query and continuation string
         */
-       protected function prepareQuery( $randstr, $limit, $namespace, &$resultPageSet, $redirect ) {
+       protected function runQuery( $resultPageSet, $limit, $start, $startId, $end ) {
+               $params = $this->extractRequestParams();
+
                $this->resetQueryParams();
                $this->addTables( 'page' );
-               $this->addOption( 'LIMIT', $limit );
-               $this->addWhereFld( 'page_namespace', $namespace );
-               $this->addWhereRange( 'page_random', 'newer', $randstr, null );
-               $this->addWhereFld( 'page_is_redirect', $redirect );
+               $this->addFields( array( 'page_id', 'page_random' ) );
                if ( is_null( $resultPageSet ) ) {
-                       $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) );
+                       $this->addFields( array( 'page_title', 'page_namespace' ) );
                } else {
                        $this->addFields( $resultPageSet->getPageTableFields() );
                }
-       }
+               $this->addWhereFld( 'page_namespace', $params['namespace'] );
+               if ( $params['redirect'] || $params['filterredir'] === 'redirects' ) {
+                       $this->addWhereFld( 'page_is_redirect', 1 );
+               } elseif ( $params['filterredir'] === 'nonredirects' ) {
+                       $this->addWhereFld( 'page_is_redirect', 0 );
+               } elseif ( is_null( $resultPageSet ) ) {
+                       $this->addFields( array( 'page_is_redirect' ) );
+               }
+               $this->addOption( 'LIMIT', $limit + 1 );
+
+               if ( $start !== null ) {
+                       $start = $this->getDB()->addQuotes( $start );
+                       if ( $startId !== null ) {
+                               $startId = (int)$startId;
+                               $this->addWhere( "page_random = $start AND page_id >= $startId OR page_random > $start" );
+                       } else {
+                               $this->addWhere( "page_random >= $start" );
+                       }
+               }
+               if ( $end !== null ) {
+                       $this->addWhere( 'page_random < ' . $this->getDB()->addQuotes( $end ) );
+               }
+               $this->addOption( 'ORDER BY', array( 'page_random', 'page_id' ) );
+
+               $result = $this->getResult();
+               $path = array( 'query', $this->getModuleName() );
 
-       /**
-        * @param ApiPageSet $resultPageSet
-        * @return int
-        */
-       protected function runQuery( $resultPageSet = null ) {
                $res = $this->select( __METHOD__ );
                $count = 0;
                foreach ( $res as $row ) {
-                       $count++;
+                       if ( $count++ >= $limit ) {
+                               return array( 0, "{$row->page_random}|{$row->page_id}" );
+                       }
                        if ( is_null( $resultPageSet ) ) {
-                               // Prevent duplicates
-                               if ( !in_array( $row->page_id, $this->pageIDs ) ) {
-                                       $fit = $this->getResult()->addValue(
-                                               array( 'query', $this->getModuleName() ),
-                                               null, $this->extractRowInfo( $row ) );
-                                       if ( !$fit ) {
-                                               // We can't really query-continue a random list.
-                                               // Return an insanely high value so
-                                               // $count < $limit is false
-                                               return 1E9;
-                                       }
-                                       $this->pageIDs[] = $row->page_id;
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $page = array(
+                                       'id' => (int)$row->page_id,
+                               );
+                               ApiQueryBase::addTitleInfo( $page, $title );
+                               if ( isset( $row->page_is_redirect ) ) {
+                                       $page['redirect'] = (bool)$row->page_is_redirect;
+                               }
+                               $fit = $result->addValue( $path, null, $page );
+                               if ( !$fit ) {
+                                       return array( 0, "{$row->page_random}|{$row->page_id}" );
                                }
                        } else {
                                $resultPageSet->processDbRow( $row );
                        }
                }
 
-               return $count;
+               return array( $limit - $count, null );
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
-        * @return void
+        * @param ApiPageSet|null $resultPageSet
         */
        public function run( $resultPageSet = null ) {
                $params = $this->extractRequestParams();
-               $result = $this->getResult();
-               $this->pageIDs = array();
-
-               $this->prepareQuery(
-                       wfRandom(),
-                       $params['limit'],
-                       $params['namespace'],
-                       $resultPageSet,
-                       $params['redirect']
-               );
-               $count = $this->runQuery( $resultPageSet );
-               if ( $count < $params['limit'] ) {
-                       /* We got too few pages, we probably picked a high value
-                        * for page_random. We'll just take the lowest ones, see
-                        * also the comment in Title::getRandomTitle()
-                        */
-                       $this->prepareQuery(
-                               0,
-                               $params['limit'] - $count,
-                               $params['namespace'],
-                               $resultPageSet,
-                               $params['redirect']
-                       );
-                       $this->runQuery( $resultPageSet );
+
+               // Since 'filterredir" will always be set in $params, we have to dig
+               // into the WebRequest to see if it was actually passed.
+               $request = $this->getMain()->getRequest();
+               if ( $request->getCheck( $this->encodeParamName( 'filterredir' ) ) ) {
+                       $this->requireMaxOneParameter( $params, 'filterredir', 'redirect' );
                }
 
-               if ( is_null( $resultPageSet ) ) {
-                       $result->addIndexedTagName( array( 'query', $this->getModuleName() ), 'page' );
+               if ( $params['redirect'] ) {
+                       $this->logFeatureUsage( "list=random&rnredirect=" );
+               }
+
+               if ( isset( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       $this->dieContinueUsageIf( count( $cont ) != 4 );
+                       $rand = $cont[0];
+                       $start = $cont[1];
+                       $startId = (int)$cont[2];
+                       $end = $cont[3] ? $rand : null;
+                       $this->dieContinueUsageIf( !preg_match( '/^0\.\d+$/', $rand ) );
+                       $this->dieContinueUsageIf( !preg_match( '/^0\.\d+$/', $start ) );
+                       $this->dieContinueUsageIf( $cont[2] !== (string)$startId );
+                       $this->dieContinueUsageIf( $cont[3] !== '0' && $cont[3] !== '1' );
+               } else {
+                       $rand = wfRandom();
+                       $start = $rand;
+                       $startId = null;
+                       $end = null;
                }
-       }
 
-       private function extractRowInfo( $row ) {
-               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-               $vals = array();
-               $vals['id'] = intval( $row->page_id );
-               ApiQueryBase::addTitleInfo( $vals, $title );
+               list( $left, $continue ) = $this->runQuery( $resultPageSet, $params['limit'], $start, $startId, $end );
+               if ( $end === null && $continue === null ) {
+                       // Wrap around. We do this even if $left === 0 for continuation
+                       // (saving a DB query in this rare case probably isn't worth the
+                       // added code complexity it would require).
+                       $end = $rand;
+                       list( $left, $continue ) = $this->runQuery( $resultPageSet, $left, null, null, $end );
+               }
+
+               if ( $continue !== null ) {
+                       $endFlag = $end === null ? 0 : 1;
+                       $this->setContinueEnumParameter( 'continue', "$rand|$continue|$endFlag" );
+               }
 
-               return $vals;
+               if ( is_null( $resultPageSet ) ) {
+                       $this->getResult()->addIndexedTagName( array( 'query', $this->getModuleName() ), 'page' );
+               }
        }
 
        public function getCacheMode( $params ) {
@@ -154,14 +181,24 @@ class ApiQueryRandom extends ApiQueryGeneratorBase {
                                ApiBase::PARAM_TYPE => 'namespace',
                                ApiBase::PARAM_ISMULTI => true
                        ),
+                       'filterredir' => array(
+                               ApiBase::PARAM_TYPE => array( 'all', 'redirects', 'nonredirects' ),
+                               ApiBase::PARAM_DFLT => 'nonredirects', // for BC
+                       ),
+                       'redirect' => array(
+                               ApiBase::PARAM_DEPRECATED => true,
+                               ApiBase::PARAM_DFLT => false,
+                       ),
                        'limit' => array(
                                ApiBase::PARAM_TYPE => 'limit',
                                ApiBase::PARAM_DFLT => 1,
                                ApiBase::PARAM_MIN => 1,
-                               ApiBase::PARAM_MAX => 10,
-                               ApiBase::PARAM_MAX2 => 20
+                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
+                       ),
+                       'continue' => array(
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue'
                        ),
-                       'redirect' => false,
                );
        }
 
index 75a0adb..251c42b 100644 (file)
@@ -78,7 +78,6 @@ class ApiQueryUserInfo extends ApiQueryBase {
 
        protected function getCurrentUserInfo() {
                $user = $this->getUser();
-               $result = $this->getResult();
                $vals = array();
                $vals['id'] = intval( $user->getId() );
                $vals['name'] = $user->getName();
index 2675185..142a780 100644 (file)
@@ -108,7 +108,7 @@ class ApiResult implements ApiSerializable {
        const META_TYPE = '_type';
 
        /**
-        * Key for the metatata item whose value specifies the name used for the
+        * Key for the metadata item whose value specifies the name used for the
         * kvp key in the alternative output format with META_TYPE 'kvp' or
         * 'BCkvp', i.e. the "name" in <container><item name="key">value</item></container>.
         * Value is string.
@@ -288,7 +288,7 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
-               if ( !( $flags & ApiResult::NO_VALIDATE ) ) {
+               if ( ( $flags & ApiResult::NO_VALIDATE ) !== ApiResult::NO_VALIDATE ) {
                        $value = self::validateValue( $value );
                }
 
@@ -402,6 +402,11 @@ class ApiResult implements ApiSerializable {
                $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
 
                if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+                       // self::valueSize needs the validated value. Then flag
+                       // to not re-validate later.
+                       $value = self::validateValue( $value );
+                       $flags |= ApiResult::NO_VALIDATE;
+
                        $newsize = $this->size + self::valueSize( $value );
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
                                /// @todo Add i18n message when replacing calls to ->setWarning()
@@ -1079,14 +1084,12 @@ class ApiResult implements ApiSerializable {
         * or the sum of the strlen()s of the elements if the item is an array.
         * @note Once the deprecated public self::size is removed, we can rename
         *       this back to a less awkward name.
-        * @param mixed $value
+        * @param mixed $value Validated value (see self::validateValue())
         * @return int
         */
        private static function valueSize( $value ) {
                $s = 0;
-               if ( is_array( $value ) ||
-                       is_object( $value ) && !is_callable( array( $value, '__toString' ) )
-               ) {
+               if ( is_array( $value ) ) {
                        foreach ( $value as $k => $v ) {
                                if ( !self::isMetadataKey( $s ) ) {
                                        $s += self::valueSize( $v );
@@ -1274,7 +1277,7 @@ class ApiResult implements ApiSerializable {
         */
        public static function setElement( &$arr, $name, $value, $flags = 0 ) {
                wfDeprecated( __METHOD__, '1.25' );
-               return self::setValue( $arr, $name, $value, $flags );
+               self::setValue( $arr, $name, $value, $flags );
        }
 
        /**
@@ -1488,7 +1491,7 @@ class ApiResult implements ApiSerializable {
         */
        public static function size( $value ) {
                wfDeprecated( __METHOD__, '1.25' );
-               return self::valueSize( $value );
+               return self::valueSize( self::validateValue( $value ) );
        }
 
        /**
index aa456f0..7bdf29a 100644 (file)
@@ -4,7 +4,8 @@
                        "Meno25",
                        "أحمد المحمودي",
                        "Khaled",
-                       "Fatz"
+                       "Fatz",
+                       "Hiba Alshawi"
                ]
        },
        "apihelp-main-param-format": "صيغة الخرج.",
@@ -24,5 +25,6 @@
        "apihelp-edit-param-watch": "أضف الصفحة إلى لائحة مراقبة المستعمل الحالي",
        "apihelp-emailuser-description": "مراسلة المستخدم",
        "apihelp-patrol-example-rcid": "ابحث عن تغيير جديد",
+       "apihelp-query+imageinfo-paramvalue-prop-userid": "إضافة هوية المستخدم الذي قام بتحميل كل إصدار ملف.",
        "apihelp-query+prefixsearch-param-offset": "عدد النتائج المراد تخطيها."
 }
index 01f07ff..0b6c167 100644 (file)
@@ -12,7 +12,8 @@
                        "Purodha",
                        "Andreasburmeister",
                        "Anomie",
-                       "Duder"
+                       "Duder",
+                       "Ljonka"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page/de|Dokumentation]]\n* [[mw:API:FAQ/de|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:API:Errors_and_warnings|API: Fehler und Warnungen]].",
        "apihelp-query+alldeletedrevisions-param-namespace": "Nur Seiten in diesem Namensraum auflisten.",
        "apihelp-query+allfileusages-param-from": "Titel der Datei, bei der die Aufzählung beginnen soll.",
        "apihelp-query+allfileusages-param-to": "Titel der Datei, bei der die Aufzählung enden soll.",
+       "apihelp-query+allfileusages-param-prop": "Informationsteile zum Einbinden:",
        "apihelp-query+allfileusages-param-limit": "Wie viele Gesamtobjekte zurückgegeben werden sollen.",
        "apihelp-query+allfileusages-param-dir": "Aufzählungsrichtung.",
        "apihelp-query+allfileusages-example-unique": "Einheitliche Dateititel auflisten",
        "apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
        "apihelp-query+filearchive-param-limit": "Wie viele Bilder insgesamt zurückgegeben werden sollen.",
+       "apihelp-query+filearchive-paramvalue-prop-sha1": "Ergänzt die SHA-1-Prüfsumme für das Bild.",
        "apihelp-query+filearchive-example-simple": "Eine Liste aller gelöschten Dateien auflisten",
        "apihelp-query+imageinfo-param-limit": "Wie viele Dateiversionen pro Datei zurückgegeben werden sollen.",
        "apihelp-query+imageinfo-param-start": "Zeitstempel, von dem die Liste beginnen soll.",
        "apihelp-query+links-example-simple": "Links von der <kbd>Hauptseite</kbd> abrufen",
        "apihelp-query+linkshere-description": "Alle Seiten finden, die auf die angegebenen Seiten verlinken.",
        "apihelp-query+logevents-description": "Ereignisse von den Logbüchern abrufen.",
+       "apihelp-query+pageswithprop-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
        "apihelp-query+prefixsearch-param-search": "Such-Zeichenfolge.",
+       "apihelp-query+search-param-prop": "Eigenschaften zur Rückgabe:",
        "apihelp-query+search-example-simple": "Nach <kbd>meaning</kbd> suchen.",
        "apihelp-query+search-example-text": "Texte nach <kbd>meaning</kbd> durchsuchen.",
        "apihelp-query+siteinfo-example-simple": "Websiteinformationen abrufen",
        "apihelp-query+tags-description": "Änderungs-Tags auflisten.",
        "apihelp-query+tags-example-simple": "Verfügbare Tags auflisten",
        "apihelp-query+usercontribs-description": "Alle Bearbeitungen von einem Benutzer abrufen.",
+       "apihelp-query+usercontribs-paramvalue-prop-ids": "Ergänzt die Seiten- und Versionskennung.",
+       "apihelp-query+userinfo-paramvalue-prop-realname": "Fügt den bürgerlichen Namen des Benutzers hinzu.",
        "apihelp-query+userinfo-example-simple": "Informationen über den aktuellen Benutzer abrufen",
        "apihelp-query+users-description": "Informationen über eine Liste von Benutzern abrufen.",
        "apihelp-rsd-description": "Ein RSD-Schema (Really Simple Discovery) exportieren.",
index 396f5da..2cb0e37 100644 (file)
        "apihelp-query+imageinfo-param-prop": "Which file information to get:",
        "apihelp-query+imageinfo-paramvalue-prop-timestamp": "Adds timestamp for the uploaded version.",
        "apihelp-query+imageinfo-paramvalue-prop-user": "Adds the user who uploaded each file version.",
-       "apihelp-query+imageinfo-paramvalue-prop-userid": "Add the user ID that uploaded each file version.",
+       "apihelp-query+imageinfo-paramvalue-prop-userid": "Add the ID of the user that uploaded each file version.",
        "apihelp-query+imageinfo-paramvalue-prop-comment": "Comment on the version.",
        "apihelp-query+imageinfo-paramvalue-prop-parsedcomment": "Parse the comment on the version.",
        "apihelp-query+imageinfo-paramvalue-prop-canonicaltitle": "Adds the canonical title of the file.",
        "apihelp-query+logevents-paramvalue-prop-details": "Lists additional details about the log event.",
        "apihelp-query+logevents-paramvalue-prop-tags": "Lists tags for the log event.",
        "apihelp-query+logevents-param-type": "Filter log entries to only this type.",
-       "apihelp-query+logevents-param-action": "Filter log actions to only this action. Overrides <var>$1type</var>. Wildcard actions like <kbd>action/*</kbd> allows to specify any string for the asterisk.",
+       "apihelp-query+logevents-param-action": "Filter log actions to only this action. Overrides <var>$1type</var>. In the list of possible values, values with the asterisk wildcard such as <kbd>action/*</kbd> can have different strings after the slash (/).",
        "apihelp-query+logevents-param-start": "The timestamp to start enumerating from.",
        "apihelp-query+logevents-param-end": "The timestamp to end enumerating.",
        "apihelp-query+logevents-param-user": "Filter entries to those made by the given user.",
        "apihelp-query+querypage-param-limit": "Number of results to return.",
        "apihelp-query+querypage-example-ancientpages": "Return results from [[Special:Ancientpages]].",
 
-       "apihelp-query+random-description": "Get a set of random pages.\n\nPages are listed in a fixed sequence, only the starting point is random. This means that if, for example, <samp>Main Page</samp> is the first random page in the list, <samp>List of fictional monkeys</samp> will <em>always</em> be second, <samp>List of people on stamps of Vanuatu</samp> third, etc.\n\nIf the number of pages in the namespace is lower than <var>$1limit</var>, fewer pages will be returned. The same page will not be returned twice.",
+       "apihelp-query+random-description": "Get a set of random pages.\n\nPages are listed in a fixed sequence, only the starting point is random. This means that if, for example, <samp>Main Page</samp> is the first random page in the list, <samp>List of fictional monkeys</samp> will <em>always</em> be second, <samp>List of people on stamps of Vanuatu</samp> third, etc.",
        "apihelp-query+random-param-namespace": "Return pages in these namespaces only.",
        "apihelp-query+random-param-limit": "Limit how many random pages will be returned.",
-       "apihelp-query+random-param-redirect": "Load a random redirect instead of a random page.",
+       "apihelp-query+random-param-redirect": "Use <kbd>$1filterredir=redirects</kbd> instead.",
+       "apihelp-query+random-param-filterredir": "How to filter for redirects.",
        "apihelp-query+random-example-simple": "Return two random pages from the main namespace.",
        "apihelp-query+random-example-generator": "Return page info about two random pages from the main namespace.",
 
        "api-help-param-type-password": "",
        "api-help-param-type-timestamp": "Type: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
        "api-help-param-type-user": "Type: {{PLURAL:$1|1=user name|2=list of user names}}",
-       "api-help-param-list": "{{PLURAL:$1|1=One value|2=Values (separate with <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=One of the following values|2=Values (separate with <kbd>{{!}}</kbd>)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Must be empty|Can be empty, or $2}}",
        "api-help-param-limit": "No more than $1 allowed.",
        "api-help-param-limit2": "No more than $1 ($2 for bots) allowed.",
index 1d04304..edc5b7a 100644 (file)
        "apihelp-query+categoryinfo-description": "Devuelve información acerca de las categorías dadas.",
        "apihelp-query+categoryinfo-example-simple": "Obtener información acerca de <kbd>Category:Foo&l